diff options
Diffstat (limited to 'sound/soc')
238 files changed, 6483 insertions, 3187 deletions
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 3bc4b2e416..42c2322cd1 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> // @@ -19,6 +19,8 @@ #include "../sof/amd/acp.h" #include "mach-config.h" +#define ACP_7_0_REV 0x70 + static int acp_quirk_data; static const struct config_entry config_table[] = { @@ -48,6 +50,19 @@ static const struct config_entry config_table[] = { }, }, { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"), + }, + }, + {} + }, + }, + { .flags = FLAG_AMD_SOF, .device = ACP_PCI_DEV_ID, .dmi_table = (const struct dmi_system_id []) { @@ -55,7 +70,6 @@ static const struct config_entry config_table[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Valve"), DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), }, }, {} @@ -147,15 +161,33 @@ static const struct config_entry config_table[] = { }, }; +static int snd_amd_acp_acpi_find_config(struct pci_dev *pci) +{ + const union acpi_object *obj; + int acp_flag = FLAG_AMD_LEGACY_ONLY_DMIC; + + if (!acpi_dev_get_property(ACPI_COMPANION(&pci->dev), "acp-audio-config-flag", + ACPI_TYPE_INTEGER, &obj)) + acp_flag = obj->integer.value; + + return acp_flag; +} + int snd_amd_acp_find_config(struct pci_dev *pci) { const struct config_entry *table = config_table; u16 device = pci->device; int i; - /* Do not enable FLAGS on older platforms with Rev id zero */ + /* Do not enable FLAGS on older platforms with Rev Id zero + * For platforms which has ACP 7.0 or higher, read the acp + * config flag from BIOS ACPI table and for older platforms + * read it from DMI tables. + */ if (!pci->revision) return 0; + else if (pci->revision >= ACP_7_0_REV) + return snd_amd_acp_acpi_find_config(pci); for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) { if (table->device != device) @@ -289,4 +321,5 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_machines); +MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 5fb3222129..84c963241d 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -73,6 +73,19 @@ config SND_AMD_ASOC_ACP63 Say Y if you want to enable AUDIO on ACP6.3 If unsure select "N". +config SND_AMD_ASOC_ACP70 + tristate "AMD ACP ASOC Acp7.0 Support" + depends on X86 && PCI + depends on ACPI + select SND_SOC_AMD_ACP_PCM + select SND_SOC_AMD_ACP_I2S + select SND_SOC_AMD_ACP_PDM + select SND_SOC_AMD_ACP_LEGACY_COMMON + help + This option enables Acp7.0 PDM support on AMD platform. + Say Y if you want to enable AUDIO on ACP7.0 + If unsure select "N". + config SND_SOC_AMD_MACH_COMMON tristate depends on X86 && PCI && I2C diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index dd85700f1c..ff5f7893b8 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -15,6 +15,7 @@ snd-acp-pci-objs := acp-pci.o snd-acp-renoir-objs := acp-renoir.o snd-acp-rembrandt-objs := acp-rembrandt.o snd-acp63-objs := acp63.o +snd-acp70-objs := acp70.o #machine specific driver snd-acp-mach-objs := acp-mach-common.o @@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o +obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 12ff0a558e..504d1b8c4c 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> // Vijendar Mukunda <Vijendar.Mukunda@amd.com> @@ -282,6 +282,22 @@ static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream, return ret; } + if (drvdata->tdm_mode) { + ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1, + 6144000, 49152000); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1, + 49152000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret); + return ret; + } + } + /* Set tdm/i2s1 master bclk ratio */ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format); if (ret < 0) { @@ -464,6 +480,22 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream, return ret; } + if (drvdata->tdm_mode) { + ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1, + 6144000, 49152000); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1, + 49152000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret); + return ret; + } + } + /* Set tdm/i2s1 master bclk ratio */ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format); if (ret < 0) { @@ -473,6 +505,13 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream, clk_set_rate(drvdata->wclk, srate); clk_set_rate(drvdata->bclk, srate * ch * format); + if (!drvdata->soc_mclk) { + ret = acp_clk_enable(drvdata, srate, ch * format); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret); + return ret; + } + } return 0; } @@ -1290,6 +1329,8 @@ SND_SOC_DAILINK_DEF(sof_hs, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs"))); SND_SOC_DAILINK_DEF(sof_hs_virtual, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual"))); +SND_SOC_DAILINK_DEF(sof_bt, + DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt"))); SND_SOC_DAILINK_DEF(sof_dmic, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic"))); SND_SOC_DAILINK_DEF(pdm_dmic, @@ -1348,6 +1389,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) if (drv_data->hs_cpu_id) num_links++; + if (drv_data->bt_cpu_id) + num_links++; if (drv_data->amp_cpu_id) num_links++; if (drv_data->dmic_cpu_id) @@ -1484,6 +1527,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].init = acp_card_maxim_init; } if (drv_data->amp_codec_id == MAX98388) { + links[i].dpcm_capture = 1; links[i].codecs = max98388; links[i].num_codecs = ARRAY_SIZE(max98388); links[i].ops = &acp_max98388_ops; @@ -1502,6 +1546,25 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) i++; } + if (drv_data->bt_cpu_id == I2S_BT) { + links[i].name = "acp-bt-codec"; + links[i].id = BT_BE_ID; + links[i].cpus = sof_bt; + links[i].num_cpus = ARRAY_SIZE(sof_bt); + links[i].platforms = sof_component; + links[i].num_platforms = ARRAY_SIZE(sof_component); + links[i].dpcm_playback = 1; + links[i].dpcm_capture = 1; + links[i].nonatomic = true; + links[i].no_pcm = 1; + if (!drv_data->bt_codec_id) { + /* Use dummy codec if codec id not specified */ + links[i].codecs = &snd_soc_dummy_dlc; + links[i].num_codecs = 1; + } + i++; + } + if (drv_data->dmic_cpu_id == DMIC) { links[i].name = "acp-dmic-codec"; links[i].id = DMIC_BE_ID; @@ -1722,4 +1785,5 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) } EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, SND_SOC_AMD_MACH); +MODULE_DESCRIPTION("AMD ACP Common Machine driver"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index cd681101be..a48546d8d4 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -28,6 +28,7 @@ enum be_id { HEADSET_BE_ID = 0, AMP_BE_ID, DMIC_BE_ID, + BT_BE_ID, }; enum cpu_endpoints { @@ -68,9 +69,11 @@ struct acp_mach_ops { struct acp_card_drvdata { unsigned int hs_cpu_id; unsigned int amp_cpu_id; + unsigned int bt_cpu_id; unsigned int dmic_cpu_id; unsigned int hs_codec_id; unsigned int amp_codec_id; + unsigned int bt_codec_id; unsigned int dmic_codec_id; unsigned int dai_fmt; unsigned int platform; diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 8c8b1dcac6..5f35b90eab 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -115,7 +115,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id goto unregister_dmic_dev; } - acp_init(chip); + ret = acp_init(chip); + if (ret) + goto unregister_dmic_dev; + res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL); if (!res) { ret = -ENOMEM; @@ -133,11 +136,9 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } } - if (flag == FLAG_AMD_LEGACY_ONLY_DMIC) { - ret = check_acp_pdm(pci, chip); - if (ret < 0) - goto skip_pdev_creation; - } + ret = check_acp_pdm(pci, chip); + if (ret < 0) + goto skip_pdev_creation; chip->flag = flag; memset(&pdevinfo, 0, sizeof(pdevinfo)); diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index a591482a07..b0e181c9a7 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -20,6 +20,7 @@ #include <sound/soc.h> #include <sound/soc-dai.h> #include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> #include "amd.h" #include "acp-mach.h" @@ -196,6 +197,11 @@ static int renoir_audio_probe(struct platform_device *pdev) acp_enable_interrupts(adata); acp_platform_register(dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); return 0; } @@ -208,11 +214,42 @@ static void renoir_audio_remove(struct platform_device *pdev) acp_platform_unregister(dev); } +static int __maybe_unused rn_pcm_resume(struct device *dev) +{ + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_stream *stream; + struct snd_pcm_substream *substream; + snd_pcm_uframes_t buf_in_frames; + u64 buf_size; + + spin_lock(&adata->acp_lock); + list_for_each_entry(stream, &adata->stream_list, list) { + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); + } + } + spin_unlock(&adata->acp_lock); + return 0; +} + +static const struct dev_pm_ops rn_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume) +}; + static struct platform_driver renoir_driver = { .probe = renoir_audio_probe, .remove_new = renoir_audio_remove, .driver = { .name = "acp_asoc_renoir", + .pm = &rn_dma_pm_ops, }, }; diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 354d0fc552..b86f65d420 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> // @@ -48,6 +48,7 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .platform = RENOIR, .tdm_mode = false, }; @@ -58,6 +59,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .platform = RENOIR, .tdm_mode = false, }; @@ -68,6 +70,7 @@ static struct acp_card_drvdata sof_nau8825_data = { .hs_codec_id = NAU8825, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .platform = REMBRANDT, .soc_mclk = true, .tdm_mode = false, }; @@ -79,6 +82,7 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .platform = REMBRANDT, .soc_mclk = true, .tdm_mode = false, }; @@ -86,9 +90,11 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { static struct acp_card_drvdata sof_nau8821_max98388_data = { .hs_cpu_id = I2S_SP, .amp_cpu_id = I2S_HS, + .bt_cpu_id = I2S_BT, .dmic_cpu_id = NONE, .hs_codec_id = NAU8821, .amp_codec_id = MAX98388, + .bt_codec_id = NONE, .dmic_codec_id = NONE, .soc_mclk = true, .tdm_mode = false, diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c index 6cd3352dc3..2b0aa270a3 100644 --- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -222,7 +222,6 @@ static int acp3x_es83xx_resume_post(struct snd_soc_card *card) static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) { - int ret = 0; priv->enable_spk_gpio.crs_entry_index = 0; priv->enable_hp_gpio.crs_entry_index = 1; @@ -245,7 +244,7 @@ static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) priv->enable_spk_gpio.active_low ? "low" : "high", priv->enable_hp_gpio.crs_entry_index, priv->enable_hp_gpio.active_low ? "low" : "high"); - return ret; + return 0; } static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv) @@ -359,6 +358,14 @@ static const struct dmi_system_id acp3x_es83xx_dmi_table[] = { .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"), }, .driver_data = (void *)(ES83XX_ENABLE_DMIC), diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index b871a216a6..4d342441a6 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -283,18 +283,16 @@ static int __maybe_unused acp63_pcm_resume(struct device *dev) spin_lock(&adata->acp_lock); list_for_each_entry(stream, &adata->stream_list, list) { - if (stream) { - substream = stream->substream; - if (substream && substream->runtime) { - buf_in_frames = (substream->runtime->buffer_size); - buf_size = frames_to_bytes(substream->runtime, buf_in_frames); - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, buf_size); - if (stream->dai_id) - restore_acp_i2s_params(substream, adata, stream); - else - restore_acp_pdm_params(substream, adata); - } + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); } } spin_unlock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index dd384c966a..0d7cdd4017 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -52,8 +52,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .playback = { .stream_name = "I2S SP Playback", .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 8, .rate_min = 8000, @@ -62,8 +62,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .capture = { .stream_name = "I2S SP Capture", .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 2, .rate_min = 8000, @@ -77,8 +77,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .playback = { .stream_name = "I2S BT Playback", .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 8, .rate_min = 8000, @@ -87,8 +87,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .capture = { .stream_name = "I2S BT Capture", .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 2, .rate_min = 8000, @@ -102,8 +102,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .playback = { .stream_name = "I2S HS Playback", .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 8, .rate_min = 8000, @@ -112,8 +112,8 @@ static struct snd_soc_dai_driver acp70_dai[] = { .capture = { .stream_name = "I2S HS Capture", .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 2, .channels_max = 8, .rate_min = 8000, @@ -229,8 +229,8 @@ static int __maybe_unused acp70_pcm_resume(struct device *dev) } } } - spin_unlock(&adata->acp_lock); - return 0; + spin_unlock(&adata->acp_lock); + return 0; } static const struct dev_pm_ops acp70_dma_pm_ops = { diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c index c4634a8a17..af56ff09f0 100644 --- a/sound/soc/amd/vangogh/pci-acp5x.c +++ b/sound/soc/amd/vangogh/pci-acp5x.c @@ -2,7 +2,7 @@ // // AMD Vangogh ACP PCI Driver // -// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. #include <linux/pci.h> #include <linux/module.h> @@ -13,6 +13,7 @@ #include <linux/pm_runtime.h> #include "acp5x.h" +#include "../mach-config.h" struct acp5x_dev_data { void __iomem *acp5x_base; @@ -129,9 +130,13 @@ static int snd_acp5x_probe(struct pci_dev *pci, int ret, i; u32 addr, val; - /* Return if acp config flag is defined */ + /* + * Return if ACP config flag is defined, except when board + * supports SOF while it is not being enabled in kernel config. + */ flag = snd_amd_acp_find_config(pci); - if (flag) + if (flag != FLAG_AMD_LEGACY && + (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH))) return -ENODEV; irqflags = IRQF_SHARED; @@ -259,7 +264,7 @@ disable_pci: return ret; } -static int __maybe_unused snd_acp5x_suspend(struct device *dev) +static int snd_acp5x_suspend(struct device *dev) { int ret; struct acp5x_dev_data *adata; @@ -274,7 +279,7 @@ static int __maybe_unused snd_acp5x_suspend(struct device *dev) return ret; } -static int __maybe_unused snd_acp5x_resume(struct device *dev) +static int snd_acp5x_resume(struct device *dev) { int ret; struct acp5x_dev_data *adata; @@ -289,9 +294,8 @@ static int __maybe_unused snd_acp5x_resume(struct device *dev) } static const struct dev_pm_ops acp5x_pm = { - SET_RUNTIME_PM_OPS(snd_acp5x_suspend, - snd_acp5x_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume) + RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL) + SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume) }; static void snd_acp5x_remove(struct pci_dev *pci) @@ -327,7 +331,7 @@ static struct pci_driver acp5x_driver = { .probe = snd_acp5x_probe, .remove = snd_acp5x_remove, .driver = { - .pm = &acp5x_pm, + .pm = pm_ptr(&acp5x_pm), } }; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 1d1452c29e..69c68d8e7a 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -314,6 +314,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "M7600RE"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"), } diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c index 6b68172563..8bb67d7d2b 100644 --- a/sound/soc/cirrus/edb93xx.c +++ b/sound/soc/cirrus/edb93xx.c @@ -11,7 +11,6 @@ */ #include <linux/platform_device.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/soc/cirrus/ep93xx.h> #include <sound/core.h> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3429419ca6..59f9742e9f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1076,6 +1076,10 @@ config SND_SOC_ES7134 config SND_SOC_ES7241 tristate "Everest Semi ES7241 CODEC" +config SND_SOC_ES83XX_DSM_COMMON + depends on ACPI + tristate + config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2078bb0d98..f53baa2b95 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -116,6 +116,7 @@ snd-soc-da9055-objs := da9055.o snd-soc-dmic-objs := dmic.o snd-soc-es7134-objs := es7134.o snd-soc-es7241-objs := es7241.o +snd-soc-es83xx-dsm-common-objs := es83xx-dsm-common.o snd-soc-es8316-objs := es8316.o snd-soc-es8326-objs := es8326.o snd-soc-es8328-objs := es8328.o @@ -505,6 +506,7 @@ obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o +obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h index 791c8c1065..0f750f654f 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.h +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -146,6 +146,7 @@ struct aw_device { unsigned int channel; unsigned int fade_step; + unsigned int prof_data_type; struct i2c_client *i2c; struct device *dev; diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 9ebe7c5101..f25f6e0d44 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -11,7 +11,6 @@ #include <linux/i2c.h> #include "aw88395_lib.h" #include "aw88395_device.h" -#include "aw88395_reg.h" #define AW88395_CRC8_POLYNOMIAL 0x8C DECLARE_CRC8_TABLE(aw_crc8_table); @@ -456,14 +455,6 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, goto parse_bin_failed; } - if (aw_dev->chip_id == AW88261_CHIP_ID) { - if (aw_bin->header_info[0].valid_data_len % 4) { - dev_err(aw_dev->dev, "bin data len get error!"); - ret = -EINVAL; - goto parse_bin_failed; - } - } - prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data + aw_bin->header_info[0].valid_data_addr; prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = @@ -528,7 +519,7 @@ static int aw_dev_parse_dev_type(struct aw_device *aw_dev, cfg_dde[i].dev_profile); return -EINVAL; } - + aw_dev->prof_data_type = cfg_dde[i].data_type; ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); if (ret < 0) { @@ -564,6 +555,7 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, cfg_dde[i].dev_profile); return -EINVAL; } + aw_dev->prof_data_type = cfg_dde[i].data_type; ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); if (ret < 0) { @@ -582,7 +574,7 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, return 0; } -static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, +static int aw_dev_cfg_get_reg_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info *all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; @@ -624,7 +616,7 @@ static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, return 0; } -static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, +static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info *all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; @@ -703,26 +695,20 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, goto exit; } - switch (aw_dev->chip_id) { - case AW88395_CHIP_ID: - case AW88399_CHIP_ID: - ret = aw88395_dev_cfg_get_valid_prof(aw_dev, all_prof_info); - if (ret < 0) - goto exit; + switch (aw_dev->prof_data_type) { + case ACF_SEC_TYPE_MULTIPLE_BIN: + ret = aw_dev_cfg_get_multiple_valid_prof(aw_dev, all_prof_info); break; - case AW88261_CHIP_ID: - case AW87390_CHIP_ID: - ret = aw88261_dev_cfg_get_valid_prof(aw_dev, all_prof_info); - if (ret < 0) - goto exit; + case ACF_SEC_TYPE_HDR_REG: + ret = aw_dev_cfg_get_reg_valid_prof(aw_dev, all_prof_info); break; default: - dev_err(aw_dev->dev, "valid prof unsupported"); + dev_err(aw_dev->dev, "unsupport data type\n"); ret = -EINVAL; break; } - - aw_dev->prof_info.prof_name_list = profile_name; + if (!ret) + aw_dev->prof_info.prof_name_list = profile_name; exit: devm_kfree(aw_dev->dev, all_prof_info); @@ -791,39 +777,23 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; - int ret; - switch (aw_dev->chip_id) { - case AW88395_CHIP_ID: - case AW88399_CHIP_ID: - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && - (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) - (*scene_num)++; - } - ret = 0; - break; - case AW88261_CHIP_ID: - case AW87390_CHIP_ID: - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || - (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && - (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) - (*scene_num)++; - } - ret = 0; - break; - default: - dev_err(aw_dev->dev, "unsupported device"); - ret = -EINVAL; - break; + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; } - return ret; + if ((*scene_num) == 0) { + dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num)); + return -EINVAL; + } + + return 0; } static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, @@ -834,37 +804,23 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; - int ret; - switch (aw_dev->chip_id) { - case AW88395_CHIP_ID: - case AW88399_CHIP_ID: - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->channel == cfg_dde[i].dev_index)) - (*scene_num)++; - } - ret = 0; - break; - case AW88261_CHIP_ID: - case AW87390_CHIP_ID: - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || - (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->channel == cfg_dde[i].dev_index)) - (*scene_num)++; - } - ret = 0; - break; - default: - dev_err(aw_dev->dev, "unsupported device"); - ret = -EINVAL; - break; + + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; } - return ret; + if ((*scene_num) == 0) { + dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num)); + return -EINVAL; + } + + return 0; } static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h index ede7deab6a..e64f24e971 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -95,10 +95,7 @@ #define AW88395_TM_REG (0x7C) enum aw88395_id { - AW88399_CHIP_ID = 0x2183, AW88395_CHIP_ID = 0x2049, - AW88261_CHIP_ID = 0x2113, - AW87390_CHIP_ID = 0x76, }; #define AW88395_REG_MAX (0x7D) diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 54f8457e84..9fcb805bf9 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -15,7 +15,6 @@ #include <sound/soc.h> #include "aw88399.h" #include "aw88395/aw88395_device.h" -#include "aw88395/aw88395_reg.h" static const struct regmap_config aw88399_remap_config = { .val_bits = 16, diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index 4f391099d0..5e9cdf725d 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -491,6 +491,7 @@ #define AW88399_CRC_FW_BASE_ADDR (0x4C0) #define AW88399_ACF_FILE "aw88399_acf.bin" #define AW88399_DEV_SYSST_CHECK_MAX (10) +#define AW88399_CHIP_ID 0x2183 #define AW88399_I2C_NAME "aw88399" diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 1380406184..d1350ffbf3 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -13,7 +13,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/platform_device.h> diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 63a538f747..ddb7d63213 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -18,14 +18,12 @@ #include <linux/regulator/consumer.h> #include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> -#include <linux/gpio.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/cs35l35.h> diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index f2fde6e652..f5bd32e434 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -17,15 +17,14 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/gpio/consumer.h> +#include <linux/irq.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> -#include <linux/gpio.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/cs35l36.h> diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 2eb397724b..cb4e83126b 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -5,6 +5,7 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> @@ -12,6 +13,15 @@ #include "cs35l56.h" static const struct reg_sequence cs35l56_patch[] = { + /* + * Firmware can change these to non-defaults to satisfy SDCA. + * Ensure that they are at known defaults. + */ + { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, + { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, + { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, + /* These are not reset by a soft-reset, so patch to defaults. */ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, @@ -41,7 +51,6 @@ static const struct reg_default cs35l56_reg_defaults[] = { { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, - { CS35L56_IRQ1_CFG, 0x00000000 }, { CS35L56_IRQ1_MASK_1, 0x83ffffff }, { CS35L56_IRQ1_MASK_2, 0xffff7fff }, { CS35L56_IRQ1_MASK_4, 0xe0ffffff }, @@ -194,6 +203,47 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } +/* + * The firmware boot sequence can overwrite the ASP1 config registers so that + * they don't match regmap's view of their values. Rewrite the values from the + * regmap cache into the hardware registers. + */ +int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) +{ + struct reg_sequence asp1_regs[] = { + { .reg = CS35L56_ASP1_ENABLES1 }, + { .reg = CS35L56_ASP1_CONTROL1 }, + { .reg = CS35L56_ASP1_CONTROL2 }, + { .reg = CS35L56_ASP1_CONTROL3 }, + { .reg = CS35L56_ASP1_FRAME_CONTROL1 }, + { .reg = CS35L56_ASP1_FRAME_CONTROL5 }, + { .reg = CS35L56_ASP1_DATA_CONTROL1 }, + { .reg = CS35L56_ASP1_DATA_CONTROL5 }, + }; + int i, ret; + + /* Read values from regmap cache into a write sequence */ + for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { + ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); + if (ret) + goto err; + } + + /* Write the values cache-bypassed so that they will be written to silicon */ + ret = regmap_multi_reg_write_bypassed(cs35l56_base->regmap, asp1_regs, + ARRAY_SIZE(asp1_regs)); + if (ret) + goto err; + + return 0; + +err: + dev_err(cs35l56_base->dev, "Failed to sync ASP1 registers: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_force_sync_asp1_registers_from_cache, SND_SOC_CS35L56_SHARED); + int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command) { unsigned int val; @@ -400,17 +450,6 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) unsigned int val; int ret; - /* Nothing to re-patch if we haven't done any patching yet. */ - if (!cs35l56_base->fw_patched) - return false; - - /* - * If we have control of RESET we will have asserted it so the firmware - * will need re-patching. - */ - if (cs35l56_base->reset_gpio) - return true; - /* * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so * can't be used here to test for memory retention. @@ -590,10 +629,35 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds } EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); +int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, + bool *fw_missing, unsigned int *fw_version) +{ + unsigned int prot_status; + int ret; + + ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status); + if (ret) { + dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret); + return ret; + } + + *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING); + + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version); + if (ret) { + dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, SND_SOC_CS35L56_SHARED); + int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) { int ret; - unsigned int devid, revid, otpid, secured; + unsigned int devid, revid, otpid, secured, fw_ver; + bool fw_missing; /* * When the system is not using a reset_gpio ensure the device is @@ -652,8 +716,13 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) return ret; } - dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", - cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid); + ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver); + if (ret) + return ret; + + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", + cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, + fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing); /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); @@ -668,6 +737,41 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) } EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED); +int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base) +{ + struct gpio_descs *descs; + int speaker_id; + int i, ret; + + /* Read the speaker type qualifier from the motherboard GPIOs */ + descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN); + if (!descs) { + return -ENOENT; + } else if (IS_ERR(descs)) { + ret = PTR_ERR(descs); + return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n"); + } + + speaker_id = 0; + for (i = 0; i < descs->ndescs; i++) { + ret = gpiod_get_value_cansleep(descs->desc[i]); + if (ret < 0) { + dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i); + goto err; + } + + speaker_id |= (ret << i); + } + + dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id); + ret = speaker_id; +err: + gpiod_put_array(descs); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, SND_SOC_CS35L56_SHARED); + static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = { [0x0C] = 128000, [0x0F] = 256000, diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 319347be05..6dd0319bc8 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -5,6 +5,7 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include <linux/acpi.h> #include <linux/completion.h> #include <linux/debugfs.h> #include <linux/delay.h> @@ -15,6 +16,7 @@ #include <linux/module.h> #include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -277,6 +279,21 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, static const struct snd_kcontrol_new sdw1_tx4_mux = SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); +static int cs35l56_asp1_cfg_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 cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Override register values set by firmware boot */ + return cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); + default: + return 0; + } +} + static int cs35l56_play_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -313,6 +330,9 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), + SND_SOC_DAPM_SUPPLY("ASP1 CFG", SND_SOC_NOPM, 0, 0, cs35l56_asp1_cfg_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -380,6 +400,9 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { { "AMP", NULL, "VDD_B" }, { "AMP", NULL, "VDD_AMP" }, + { "ASP1 Playback", NULL, "ASP1 CFG" }, + { "ASP1 Capture", NULL, "ASP1 CFG" }, + { "ASP1 Playback", NULL, "PLAY" }, { "SDW1 Playback", NULL, "PLAY" }, @@ -779,7 +802,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { } }; -static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) +static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56) { int ret; @@ -791,19 +814,10 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); } -static void cs35l56_patch(struct cs35l56_private *cs35l56) +static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing) { - unsigned int firmware_missing; int ret; - ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); - if (ret) { - dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); - return; - } - - firmware_missing &= CS35L56_FIRMWARE_MISSING; - /* * Disable SoundWire interrupts to prevent race with IRQ work. * Setting sdw_irq_no_unmask prevents the handler re-enabling @@ -876,34 +890,49 @@ static void cs35l56_dsp_work(struct work_struct *work) struct cs35l56_private *cs35l56 = container_of(work, struct cs35l56_private, dsp_work); + unsigned int firmware_version; + bool firmware_missing; + int ret; if (!cs35l56->base.init_done) return; pm_runtime_get_sync(cs35l56->base.dev); + ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version); + if (ret) + goto err; + /* Populate fw file qualifier with the revision and security state */ - if (!cs35l56->dsp.fwf_name) { - cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x%s-dsp1", + kfree(cs35l56->dsp.fwf_name); + if (firmware_missing) { + cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev); + } else { + /* Firmware files must match the running firmware version */ + cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, + "%02x%s-%06x-dsp1", cs35l56->base.rev, - cs35l56->base.secured ? "-s" : ""); - if (!cs35l56->dsp.fwf_name) - goto err; + cs35l56->base.secured ? "-s" : "", + firmware_version); } + if (!cs35l56->dsp.fwf_name) + goto err; + dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n", cs35l56->dsp.fwf_name, cs35l56->dsp.system_name); /* - * 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. + * The firmware cannot be patched if it is already running from + * patch RAM. In this case the firmware files are versioned to + * match the running firmware version and will only contain + * tunings. We do not need to shutdown the firmware to apply + * tunings so can use the lower cost reinit sequence instead. */ - if (cs35l56->base.secured) - cs35l56_secure_patch(cs35l56); + if (!firmware_missing) + cs35l56_reinit_patch(cs35l56); else - cs35l56_patch(cs35l56); + cs35l56_patch(cs35l56, firmware_missing); err: pm_runtime_mark_last_busy(cs35l56->base.dev); @@ -920,10 +949,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component) if (!cs35l56->dsp.system_name && (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) { - cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev, - GFP_KERNEL, - "%04x%04x", - vendor, device); + /* Append a speaker qualifier if there is a speaker ID */ + if (cs35l56->speaker_id >= 0) { + cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev, + GFP_KERNEL, + "%04x%04x-spkid%d", + vendor, device, + cs35l56->speaker_id); + } else { + cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev, + GFP_KERNEL, + "%04x%04x", + vendor, device); + } if (!cs35l56->dsp.system_name) return -ENOMEM; } @@ -1209,7 +1247,13 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) if (ret < 0) return 0; - cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL); + /* Append a speaker qualifier if there is a speaker ID */ + if (cs35l56->speaker_id >= 0) + cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d", + prop, cs35l56->speaker_id); + else + cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL); + if (cs35l56->dsp.system_name == NULL) return -ENOMEM; @@ -1218,12 +1262,101 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) return 0; } +/* + * Some SoundWire laptops have a spk-id-gpios property but it points to + * the wrong ACPI Device node so can't be used to get the GPIO. Try to + * find the SDCA node containing the GpioIo resource and add a GPIO + * mapping to it. + */ +static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false }; +static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = { + { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 }, + { } +}; + +static void cs35l56_acpi_dev_release_driver_gpios(void *adev) +{ + acpi_dev_remove_driver_gpios(adev); +} + +static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56) +{ + struct fwnode_handle *af01_fwnode; + const union acpi_object *obj; + struct gpio_desc *desc; + int ret; + + /* Find the SDCA node containing the GpioIo */ + af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01"); + if (!af01_fwnode) { + dev_dbg(cs35l56->base.dev, "No AF01 node\n"); + return -ENOENT; + } + + ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev), + "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj); + if (ret) { + dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret); + return -ENOENT; + } + + /* The broken properties we can handle are a 4-element package (one GPIO) */ + if (obj->package.count != 4) { + dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n", + obj->package.count); + return -ENOENT; + } + + /* Add a GPIO mapping if it doesn't already have one */ + if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) { + struct acpi_device *adev = to_acpi_device_node(af01_fwnode); + + /* + * Can't use devm_acpi_dev_add_driver_gpios() because the + * mapping isn't being added to the node pointed to by + * ACPI_COMPANION(). + */ + ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping); + if (ret) { + return dev_err_probe(cs35l56->base.dev, ret, + "Failed to add gpio mapping to AF01\n"); + } + + ret = devm_add_action_or_reset(cs35l56->base.dev, + cs35l56_acpi_dev_release_driver_gpios, + adev); + if (ret) + return ret; + + dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n"); + } + + desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL); + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n"); + } + + ret = gpiod_get_value_cansleep(desc); + gpiod_put(desc); + + if (ret < 0) { + dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n"); + return ret; + } + + dev_info(cs35l56->base.dev, "Got spk-id from AF01\n"); + + return ret; +} + int cs35l56_common_probe(struct cs35l56_private *cs35l56) { int ret; init_completion(&cs35l56->init_completion); mutex_init(&cs35l56->base.irq_lock); + cs35l56->speaker_id = -ENOENT; dev_set_drvdata(cs35l56->base.dev, cs35l56); @@ -1260,6 +1393,15 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); } + ret = cs35l56_get_speaker_id(&cs35l56->base); + if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT)) + ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56); + + if ((ret < 0) && (ret != -ENOENT)) + goto err; + + cs35l56->speaker_id = ret; + ret = cs35l56_get_firmware_uid(cs35l56); if (ret != 0) goto err; diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index d9fbf568a1..b000e7365e 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -44,6 +44,7 @@ struct cs35l56_private { bool sdw_attached; struct completion init_completion; + int speaker_id; u32 rx_mask; u32 tx_mask; u8 asp_slot_width; diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 9e6f8a048d..e864188ae5 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -13,9 +13,8 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -160,9 +159,7 @@ struct cs4271_private { /* Current sample rate for de-emphasis control */ int rate; /* GPIO driving Reset pin, if any */ - int gpio_nreset; - /* GPIO that disable serial bus, if any */ - int gpio_disable; + struct gpio_desc *reset; /* enable soft reset workaround */ bool enable_soft_reset; struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; @@ -487,12 +484,10 @@ static int cs4271_reset(struct snd_soc_component *component) { struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(cs4271->gpio_nreset)) { - gpio_direction_output(cs4271->gpio_nreset, 0); - mdelay(1); - gpio_set_value(cs4271->gpio_nreset, 1); - mdelay(1); - } + gpiod_direction_output(cs4271->reset, 1); + mdelay(1); + gpiod_set_value(cs4271->reset, 0); + mdelay(1); return 0; } @@ -612,9 +607,8 @@ static void cs4271_component_remove(struct snd_soc_component *component) { struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(cs4271->gpio_nreset)) - /* Set codec to the reset state */ - gpio_set_value(cs4271->gpio_nreset, 0); + /* Set codec to the reset state */ + gpiod_set_value(cs4271->reset, 1); regcache_mark_dirty(cs4271->regmap); regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); @@ -639,7 +633,6 @@ static const struct snd_soc_component_driver soc_component_dev_cs4271 = { static int cs4271_common_probe(struct device *dev, struct cs4271_private **c) { - struct cs4271_platform_data *cs4271plat = dev->platform_data; struct cs4271_private *cs4271; int i, ret; @@ -647,17 +640,11 @@ static int cs4271_common_probe(struct device *dev, if (!cs4271) return -ENOMEM; - cs4271->gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - - if (cs4271plat) - cs4271->gpio_nreset = cs4271plat->gpio_nreset; - - if (gpio_is_valid(cs4271->gpio_nreset)) { - ret = devm_gpio_request(dev, cs4271->gpio_nreset, - "CS4271 Reset"); - if (ret < 0) - return ret; - } + cs4271->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(cs4271->reset)) + return dev_err_probe(dev, PTR_ERR(cs4271->reset), + "error retrieving RESET GPIO\n"); + gpiod_set_consumer_name(cs4271->reset, "CS4271 Reset"); for (i = 0; i < ARRAY_SIZE(supply_names); i++) cs4271->supplies[i].supply = supply_names[i]; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 94bcab8126..2d11c5125f 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -15,7 +15,6 @@ #include <linux/types.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/acpi.h> diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 7785125b73..3d85ebc594 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -14,7 +14,7 @@ #include <dt-bindings/sound/cs42l42.h> #include <linux/device.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 54a3ea6064..24a598f2ed 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -507,7 +507,7 @@ static void cs42l43_start_load_detect(struct cs42l43_codec *priv) priv->load_detect_running = true; - if (priv->hp_ena) { + if (priv->hp_ena && !priv->hp_ilimited) { unsigned long time_left; reinit_completion(&priv->hp_shutdown); @@ -572,7 +572,7 @@ static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, priv->adc_ena); - if (priv->hp_ena) { + if (priv->hp_ena && !priv->hp_ilimited) { unsigned long time_left; reinit_completion(&priv->hp_startup); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 5009cf6412..a24b52c9dd 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -138,7 +138,87 @@ CS42L43_IRQ_ERROR(spkr_therm_warm) CS42L43_IRQ_ERROR(spkl_therm_warm) CS42L43_IRQ_ERROR(spkr_sc_detect) CS42L43_IRQ_ERROR(spkl_sc_detect) -CS42L43_IRQ_ERROR(hp_ilimit) + +static void cs42l43_hp_ilimit_clear_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + hp_ilimit_clear_work.work); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component); + + snd_soc_dapm_mutex_lock(dapm); + + priv->hp_ilimit_count--; + + if (priv->hp_ilimit_count) + queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work, + msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS)); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static void cs42l43_hp_ilimit_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + hp_ilimit_work); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component); + struct cs42l43 *cs42l43 = priv->core; + + snd_soc_dapm_mutex_lock(dapm); + + if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) { + if (!priv->hp_ilimit_count) + queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work, + msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS)); + + priv->hp_ilimit_count++; + snd_soc_dapm_mutex_unlock(dapm); + return; + } + + dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n", + CS42L43_HP_ILIMIT_BACKOFF_MS); + + priv->hp_ilimited = true; + + // No need to wait for disable, as just disabling for a period of time + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, 0); + + snd_soc_dapm_mutex_unlock(dapm); + + msleep(CS42L43_HP_ILIMIT_BACKOFF_MS); + + snd_soc_dapm_mutex_lock(dapm); + + if (priv->hp_ena && !priv->load_detect_running) { + unsigned long time_left; + + reinit_completion(&priv->hp_startup); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, priv->hp_ena); + + time_left = wait_for_completion_timeout(&priv->hp_startup, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "ilimit HP restore timed out\n"); + } + + priv->hp_ilimited = false; + + snd_soc_dapm_mutex_unlock(dapm); +} + +static irqreturn_t cs42l43_hp_ilimit(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + dev_dbg(priv->dev, "headphone ilimit IRQ\n"); + + queue_work(system_long_wq, &priv->hp_ilimit_work); + + return IRQ_HANDLED; +} #define CS42L43_IRQ_COMPLETE(name) \ static irqreturn_t cs42l43_##name(int irq, void *data) \ @@ -1452,13 +1532,13 @@ static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w, if (ret) return ret; - if (!priv->load_detect_running) + if (!priv->load_detect_running && !priv->hp_ilimited) regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, mask, val); break; case SND_SOC_DAPM_POST_PMU: case SND_SOC_DAPM_POST_PMD: - if (priv->load_detect_running) + if (priv->load_detect_running || priv->hp_ilimited) break; ret = cs42l43_dapm_wait_completion(&priv->hp_startup, @@ -2169,7 +2249,9 @@ static int cs42l43_codec_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); + INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work); INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); + INIT_WORK(&priv->hp_ilimit_work, cs42l43_hp_ilimit_work); pm_runtime_set_autosuspend_delay(priv->dev, 100); pm_runtime_use_autosuspend(priv->dev); @@ -2254,8 +2336,51 @@ static int cs42l43_codec_runtime_resume(struct device *dev) return 0; } -DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL, - cs42l43_codec_runtime_resume, NULL); +static int cs42l43_codec_suspend(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; + + disable_irq(cs42l43->irq); + + return 0; +} + +static int cs42l43_codec_suspend_noirq(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; + + enable_irq(cs42l43->irq); + + return 0; +} + +static int cs42l43_codec_resume(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; + + enable_irq(cs42l43->irq); + + return 0; +} + +static int cs42l43_codec_resume_noirq(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; + + disable_irq(cs42l43->irq); + + return 0; +} + +static const struct dev_pm_ops cs42l43_codec_pm_ops = { + SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend_noirq, cs42l43_codec_resume_noirq) + RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL) +}; static const struct platform_device_id cs42l43_codec_id_table[] = { { "cs42l43-codec", }, diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index bf4f728eea..125e36861d 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -28,6 +28,10 @@ #define CS42L43_HP_TIMEOUT_MS 2000 #define CS42L43_LOAD_TIMEOUT_MS 1000 +#define CS42L43_HP_ILIMIT_BACKOFF_MS 1000 +#define CS42L43_HP_ILIMIT_DECAY_MS 300 +#define CS42L43_HP_ILIMIT_MAX_COUNT 4 + #define CS42L43_ASP_MAX_CHANNELS 6 #define CS42L43_N_EQ_COEFFS 15 @@ -88,6 +92,11 @@ struct cs42l43_codec { bool button_detect_running; bool jack_present; int jack_override; + + struct work_struct hp_ilimit_work; + struct delayed_work hp_ilimit_clear_work; + bool hp_ilimited; + int hp_ilimit_count; }; #if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW) diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index d8ec325b9c..b6d829bbe3 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -11,12 +11,11 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> @@ -26,7 +25,6 @@ #include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> #include <linux/completion.h> @@ -238,7 +236,7 @@ static int cs43130_pll_config(struct snd_soc_component *component) struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); const struct cs43130_pll_params *pll_entry; - dev_dbg(component->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n", + dev_dbg(cs43130->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n", cs43130->mclk, cs43130->mclk_int); pll_entry = cs43130_get_pll_table(cs43130->mclk, cs43130->mclk_int); @@ -303,7 +301,7 @@ static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int cs43130->mclk = freq_in; break; default: - dev_err(component->dev, + dev_err(cs43130->dev, "unsupported pll input reference clock:%d\n", freq_in); return -EINVAL; } @@ -316,16 +314,44 @@ static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int cs43130->mclk_int = freq_out; break; default: - dev_err(component->dev, + dev_err(cs43130->dev, "unsupported pll output ref clock: %u\n", freq_out); return -EINVAL; } ret = cs43130_pll_config(component); - dev_dbg(component->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass); + dev_dbg(cs43130->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass); return ret; } +static int cs43130_wait_for_completion(struct cs43130_private *cs43130, struct completion *to_poll, + int time) +{ + int stickies, offset, flag, ret; + + if (cs43130->has_irq_line) { + ret = wait_for_completion_timeout(to_poll, msecs_to_jiffies(time)); + if (ret == 0) + return -ETIMEDOUT; + else + return 0; // Discard number of jiffies left till timeout and return success + } + + if (to_poll == &cs43130->xtal_rdy) { + offset = 0; + flag = CS43130_XTAL_RDY_INT; + } else if (to_poll == &cs43130->pll_rdy) { + offset = 0; + flag = CS43130_PLL_RDY_INT; + } else { + return -EINVAL; + } + + return regmap_read_poll_timeout(cs43130->regmap, CS43130_INT_STATUS_1 + offset, + stickies, (stickies & flag), + 1000, time * 1000); +} + static int cs43130_change_clksrc(struct snd_soc_component *component, enum cs43130_mclk_src_sel src) { @@ -346,7 +372,7 @@ static int cs43130_change_clksrc(struct snd_soc_component *component, mclk_int_decoded = CS43130_MCLK_24P5; break; default: - dev_err(component->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int); + dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int); return -EINVAL; } @@ -364,14 +390,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component, CS43130_XTAL_RDY_INT_MASK, 0); regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, CS43130_PDN_XTAL_MASK, 0); - ret = wait_for_completion_timeout(&cs43130->xtal_rdy, - msecs_to_jiffies(100)); + ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100); regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, CS43130_XTAL_RDY_INT_MASK, 1 << CS43130_XTAL_RDY_INT_SHIFT); - if (ret == 0) { - dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n"); - return -ETIMEDOUT; + if (ret) { + dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret); + return ret; } } @@ -400,14 +425,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component, CS43130_XTAL_RDY_INT_MASK, 0); regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, CS43130_PDN_XTAL_MASK, 0); - ret = wait_for_completion_timeout(&cs43130->xtal_rdy, - msecs_to_jiffies(100)); + ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100); regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, CS43130_XTAL_RDY_INT_MASK, 1 << CS43130_XTAL_RDY_INT_SHIFT); - if (ret == 0) { - dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n"); - return -ETIMEDOUT; + if (ret) { + dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret); + return ret; } } @@ -416,14 +440,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component, CS43130_PLL_RDY_INT_MASK, 0); regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, CS43130_PDN_PLL_MASK, 0); - ret = wait_for_completion_timeout(&cs43130->pll_rdy, - msecs_to_jiffies(100)); + ret = cs43130_wait_for_completion(cs43130, &cs43130->pll_rdy, 100); regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, CS43130_PLL_RDY_INT_MASK, 1 << CS43130_PLL_RDY_INT_SHIFT); - if (ret == 0) { - dev_err(component->dev, "Timeout waiting for PLL_READY interrupt\n"); - return -ETIMEDOUT; + if (ret) { + dev_err(cs43130->dev, "Error waiting for PLL_READY interrupt: %d\n", ret); + return ret; } regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, @@ -453,7 +476,7 @@ static int cs43130_change_clksrc(struct snd_soc_component *component, 1 << CS43130_PDN_PLL_SHIFT); break; default: - dev_err(component->dev, "Invalid MCLK source value\n"); + dev_err(cs43130->dev, "Invalid MCLK source value\n"); return -EINVAL; } @@ -595,6 +618,27 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, return -EINVAL; } + switch (cs43130->dais[dai_id].dai_invert) { + case SND_SOC_DAIFMT_NB_NF: + sclk_edge = 1; + lrck_edge = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + sclk_edge = 0; + lrck_edge = 0; + break; + case SND_SOC_DAIFMT_NB_IF: + sclk_edge = 1; + lrck_edge = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + sclk_edge = 0; + lrck_edge = 1; + break; + default: + return -EINVAL; + } + switch (cs43130->dais[dai_id].dai_mode) { case SND_SOC_DAIFMT_CBS_CFS: dai_mode_val = 0; @@ -607,8 +651,6 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, } frm_size = bitwidth_sclk * params_channels(params); - sclk_edge = 1; - lrck_edge = 0; loc_ch1 = 0; loc_ch2 = bitwidth_sclk * (params_channels(params) - 1); @@ -804,7 +846,7 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, dsd_speed = 1; break; default: - dev_err(component->dev, "Rate(%u) not supported\n", + dev_err(cs43130->dev, "Rate(%u) not supported\n", params_rate(params)); return -EINVAL; } @@ -875,7 +917,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, dsd_speed = 1; break; default: - dev_err(component->dev, "Rate(%u) not supported\n", + dev_err(cs43130->dev, "Rate(%u) not supported\n", params_rate(params)); return -EINVAL; } @@ -892,7 +934,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val); break; default: - dev_err(component->dev, "Invalid DAI (%d)\n", dai->id); + dev_err(cs43130->dev, "Invalid DAI (%d)\n", dai->id); return -EINVAL; } @@ -916,21 +958,21 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, if (!sclk) { /* at this point, SCLK must be set */ - dev_err(component->dev, "SCLK freq is not set\n"); + dev_err(cs43130->dev, "SCLK freq is not set\n"); return -EINVAL; } bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params); if (bitwidth_sclk < bitwidth_dai) { - dev_err(component->dev, "Format not supported: SCLK freq is too low\n"); + dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n"); return -EINVAL; } - dev_dbg(component->dev, + dev_dbg(cs43130->dev, "sclk = %u, fs = %d, bitwidth_dai = %u\n", sclk, params_rate(params), bitwidth_dai); - dev_dbg(component->dev, + dev_dbg(cs43130->dev, "bitwidth_sclk = %u, num_ch = %u\n", bitwidth_sclk, params_channels(params)); @@ -1189,7 +1231,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, } break; default: - dev_err(component->dev, "Invalid event = 0x%x\n", event); + dev_err(cs43130->dev, "Invalid event = 0x%x\n", event); return -EINVAL; } return 0; @@ -1246,7 +1288,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, } break; default: - dev_err(component->dev, "Invalid event = 0x%x\n", event); + dev_err(cs43130->dev, "Invalid event = 0x%x\n", event); return -EINVAL; } return 0; @@ -1322,7 +1364,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, } break; default: - dev_err(component->dev, "Invalid DAC event = 0x%x\n", event); + dev_err(cs43130->dev, "Invalid DAC event = 0x%x\n", event); return -EINVAL; } return 0; @@ -1360,13 +1402,21 @@ static int cs43130_hpin_event(struct snd_soc_dapm_widget *w, ARRAY_SIZE(hpin_postpmu_seq)); break; default: - dev_err(component->dev, "Invalid HPIN event = 0x%x\n", event); + dev_err(cs43130->dev, "Invalid HPIN event = 0x%x\n", event); return -EINVAL; } return 0; } +static const char * const bypass_mux_text[] = { + "Internal", + "Alternative", +}; +static SOC_ENUM_SINGLE_DECL(bypass_enum, SND_SOC_NOPM, 0, bypass_mux_text); +static const struct snd_kcontrol_new bypass_ctrl = SOC_DAPM_ENUM("Switch", bypass_enum); + static const struct snd_soc_dapm_widget digital_hp_widgets[] = { + SND_SOC_DAPM_MUX("Bypass Switch", SND_SOC_NOPM, 0, 0, &bypass_ctrl), SND_SOC_DAPM_OUTPUT("HPOUTA"), SND_SOC_DAPM_OUTPUT("HPOUTB"), @@ -1419,13 +1469,13 @@ static const struct snd_soc_dapm_route digital_hp_routes[] = { {"DSD", NULL, "XSPIN DSD"}, {"HiFi DAC", NULL, "ASPIN PCM"}, {"HiFi DAC", NULL, "DSD"}, - {"HPOUTA", NULL, "HiFi DAC"}, - {"HPOUTB", NULL, "HiFi DAC"}, + {"Bypass Switch", "Internal", "HiFi DAC"}, + {"HPOUTA", NULL, "Bypass Switch"}, + {"HPOUTB", NULL, "Bypass Switch"}, }; static const struct snd_soc_dapm_route analog_hp_routes[] = { - {"HPOUTA", NULL, "Analog Playback"}, - {"HPOUTB", NULL, "Analog Playback"}, + {"Bypass Switch", "Alternative", "Analog Playback"}, }; static struct snd_soc_dapm_route all_hp_routes[ @@ -1479,7 +1529,26 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; break; default: - dev_err(component->dev, "unsupported mode\n"); + dev_err(cs43130->dev, "unsupported mode\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_NF; + break; + case SND_SOC_DAIFMT_IB_NF: + cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_NF; + break; + case SND_SOC_DAIFMT_NB_IF: + cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_IF; + break; + case SND_SOC_DAIFMT_IB_IF: + cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_IF; + break; + default: + dev_err(cs43130->dev, "Unsupported invert mode 0x%x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); return -EINVAL; } @@ -1497,12 +1566,12 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B; break; default: - dev_err(component->dev, + dev_err(cs43130->dev, "unsupported audio format\n"); return -EINVAL; } - dev_dbg(component->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n", + dev_dbg(cs43130->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n", codec_dai->id, cs43130->dais[codec_dai->id].dai_mode, cs43130->dais[codec_dai->id].dai_format); @@ -1523,11 +1592,11 @@ static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; break; default: - dev_err(component->dev, "Unsupported DAI format.\n"); + dev_err(cs43130->dev, "Unsupported DAI format.\n"); return -EINVAL; } - dev_dbg(component->dev, "dai_mode = 0x%x\n", + dev_dbg(cs43130->dev, "dai_mode = 0x%x\n", cs43130->dais[codec_dai->id].dai_mode); return 0; @@ -1540,7 +1609,7 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai, struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); cs43130->dais[codec_dai->id].sclk = freq; - dev_dbg(component->dev, "dai_id = %d, sclk = %u\n", codec_dai->id, + dev_dbg(cs43130->dev, "dai_id = %d, sclk = %u\n", codec_dai->id, cs43130->dais[codec_dai->id].sclk); return 0; @@ -1630,7 +1699,7 @@ static int cs43130_component_set_sysclk(struct snd_soc_component *component, { struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); - dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n", + dev_dbg(cs43130->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n", clk_id, source, freq, dir); switch (freq) { @@ -1639,14 +1708,14 @@ static int cs43130_component_set_sysclk(struct snd_soc_component *component, cs43130->mclk = freq; break; default: - dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq); + dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", freq); return -EINVAL; } if (source == CS43130_MCLK_SRC_EXT) { cs43130->pll_bypass = true; } else { - dev_err(component->dev, "Invalid MCLK source\n"); + dev_err(cs43130->dev, "Invalid MCLK source\n"); return -EINVAL; } @@ -1933,7 +2002,6 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx, unsigned int reg; u32 addr; u16 impedance; - struct snd_soc_component *component = cs43130->component; switch (msk) { case CS43130_HPLOAD_DC_INT: @@ -1963,7 +2031,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx, else cs43130->hpload_dc[HP_RIGHT] = impedance; - dev_dbg(component->dev, "HP DC impedance (Ch %u): %u\n", !left_ch, + dev_dbg(cs43130->dev, "HP DC impedance (Ch %u): %u\n", !left_ch, impedance); } else { if (left_ch) @@ -1971,7 +2039,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx, else cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance; - dev_dbg(component->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n", + dev_dbg(cs43130->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n", cs43130->ac_freq[ac_idx], !left_ch, impedance); } @@ -1985,7 +2053,6 @@ static int cs43130_hpload_proc(struct cs43130_private *cs43130, int ret; unsigned int msk; u16 ac_reg_val; - struct snd_soc_component *component = cs43130->component; reinit_completion(&cs43130->hpload_evt); @@ -2008,17 +2075,17 @@ static int cs43130_hpload_proc(struct cs43130_private *cs43130, msecs_to_jiffies(1000)); regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk); if (!ret) { - dev_err(component->dev, "Timeout waiting for HPLOAD interrupt\n"); - return -1; + dev_err(cs43130->dev, "Timeout waiting for HPLOAD interrupt\n"); + return -ETIMEDOUT; } - dev_dbg(component->dev, "HP load stat: %x, INT_MASK_4: %x\n", + dev_dbg(cs43130->dev, "HP load stat: %x, INT_MASK_4: %x\n", cs43130->hpload_stat, msk); if ((cs43130->hpload_stat & (CS43130_HPLOAD_NO_DC_INT | CS43130_HPLOAD_UNPLUG_INT | CS43130_HPLOAD_OOR_INT)) || !(cs43130->hpload_stat & rslt_msk)) { - dev_dbg(component->dev, "HP load measure failed\n"); + dev_dbg(cs43130->dev, "HP load measure failed\n"); return -1; } @@ -2129,9 +2196,9 @@ static void cs43130_imp_meas(struct work_struct *wk) snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE, CS43130_JACK_MASK); - dev_dbg(component->dev, "Set HP output control. DC threshold\n"); + dev_dbg(cs43130->dev, "Set HP output control. DC threshold\n"); for (i = 0; i < CS43130_DC_THRESHOLD; i++) - dev_dbg(component->dev, "DC threshold[%d]: %u.\n", i, + dev_dbg(cs43130->dev, "DC threshold[%d]: %u.\n", i, cs43130->dc_threshold[i]); cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT], @@ -2165,7 +2232,6 @@ exit: static irqreturn_t cs43130_irq_thread(int irq, void *data) { struct cs43130_private *cs43130 = (struct cs43130_private *)data; - struct snd_soc_component *component = cs43130->component; unsigned int stickies[CS43130_NUM_INT]; unsigned int irq_occurrence = 0; unsigned int masks[CS43130_NUM_INT]; @@ -2183,8 +2249,6 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) for (j = 0; j < 8; j++) irq_occurrence += (stickies[i] >> j) & 1; } - dev_dbg(component->dev, "number of interrupts occurred (%u)\n", - irq_occurrence); if (!irq_occurrence) return IRQ_NONE; @@ -2201,7 +2265,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) { cs43130->hpload_stat = stickies[3]; - dev_err(component->dev, + dev_err(cs43130->dev, "DC load has not completed before AC load (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); @@ -2210,7 +2274,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) { cs43130->hpload_stat = stickies[3]; - dev_err(component->dev, "HP unplugged during measurement (%x)\n", + dev_err(cs43130->dev, "HP unplugged during measurement (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; @@ -2218,7 +2282,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_OOR_INT) { cs43130->hpload_stat = stickies[3]; - dev_err(component->dev, "HP load out of range (%x)\n", + dev_err(cs43130->dev, "HP load out of range (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; @@ -2226,7 +2290,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_AC_INT) { cs43130->hpload_stat = stickies[3]; - dev_dbg(component->dev, "HP AC load measurement done (%x)\n", + dev_dbg(cs43130->dev, "HP AC load measurement done (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; @@ -2234,7 +2298,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_DC_INT) { cs43130->hpload_stat = stickies[3]; - dev_dbg(component->dev, "HP DC load measurement done (%x)\n", + dev_dbg(cs43130->dev, "HP DC load measurement done (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; @@ -2242,7 +2306,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_ON_INT) { cs43130->hpload_stat = stickies[3]; - dev_dbg(component->dev, "HP load state machine on done (%x)\n", + dev_dbg(cs43130->dev, "HP load state machine on done (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; @@ -2250,19 +2314,19 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[3] & CS43130_HPLOAD_OFF_INT) { cs43130->hpload_stat = stickies[3]; - dev_dbg(component->dev, "HP load state machine off done (%x)\n", + dev_dbg(cs43130->dev, "HP load state machine off done (%x)\n", cs43130->hpload_stat); complete(&cs43130->hpload_evt); return IRQ_HANDLED; } if (stickies[0] & CS43130_XTAL_ERR_INT) { - dev_err(component->dev, "Crystal err: clock is not running\n"); + dev_err(cs43130->dev, "Crystal err: clock is not running\n"); return IRQ_HANDLED; } if (stickies[0] & CS43130_HP_UNPLUG_INT) { - dev_dbg(component->dev, "HP unplugged\n"); + dev_dbg(cs43130->dev, "HP unplugged\n"); cs43130->hpload_done = false; snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK); return IRQ_HANDLED; @@ -2271,7 +2335,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data) if (stickies[0] & CS43130_HP_PLUG_INT) { if (cs43130->dc_meas && !cs43130->hpload_done && !work_busy(&cs43130->work)) { - dev_dbg(component->dev, "HP load queue work\n"); + dev_dbg(cs43130->dev, "HP load queue work\n"); queue_work(cs43130->wq, &cs43130->work); } @@ -2303,19 +2367,19 @@ static int cs43130_probe(struct snd_soc_component *component) ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK, &cs43130->jack); if (ret < 0) { - dev_err(component->dev, "Cannot create jack\n"); + dev_err(cs43130->dev, "Cannot create jack\n"); return ret; } cs43130->hpload_done = false; if (cs43130->dc_meas) { - ret = sysfs_create_groups(&component->dev->kobj, hpload_groups); + ret = sysfs_create_groups(&cs43130->dev->kobj, hpload_groups); if (ret) return ret; cs43130->wq = create_singlethread_workqueue("cs43130_hp"); if (!cs43130->wq) { - sysfs_remove_groups(&component->dev->kobj, hpload_groups); + sysfs_remove_groups(&cs43130->dev->kobj, hpload_groups); return -ENOMEM; } INIT_WORK(&cs43130->work, cs43130_imp_meas); @@ -2367,14 +2431,12 @@ static const u16 cs43130_dc_threshold[CS43130_DC_THRESHOLD] = { 120, }; -static int cs43130_handle_device_data(struct i2c_client *i2c_client, - struct cs43130_private *cs43130) +static int cs43130_handle_device_data(struct cs43130_private *cs43130) { - struct device_node *np = i2c_client->dev.of_node; unsigned int val; int i; - if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) { + if (device_property_read_u32(cs43130->dev, "cirrus,xtal-ibias", &val) < 0) { /* Crystal is unused. System clock is used for external MCLK */ cs43130->xtal_ibias = CS43130_XTAL_UNUSED; return 0; @@ -2391,23 +2453,23 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client, cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA; break; default: - dev_err(&i2c_client->dev, + dev_err(cs43130->dev, "Invalid cirrus,xtal-ibias value: %d\n", val); return -EINVAL; } - cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure"); - cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure"); + cs43130->dc_meas = device_property_read_bool(cs43130->dev, "cirrus,dc-measure"); + cs43130->ac_meas = device_property_read_bool(cs43130->dev, "cirrus,ac-measure"); - if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq, - CS43130_AC_FREQ) < 0) { + if (!device_property_read_u16_array(cs43130->dev, "cirrus,ac-freq", cs43130->ac_freq, + CS43130_AC_FREQ)) { for (i = 0; i < CS43130_AC_FREQ; i++) cs43130->ac_freq[i] = cs43130_ac_freq[i]; } - if (of_property_read_u16_array(np, "cirrus,dc-threshold", + if (!device_property_read_u16_array(cs43130->dev, "cirrus,dc-threshold", cs43130->dc_threshold, - CS43130_DC_THRESHOLD) < 0) { + CS43130_DC_THRESHOLD)) { for (i = 0; i < CS43130_DC_THRESHOLD; i++) cs43130->dc_threshold[i] = cs43130_dc_threshold[i]; } @@ -2426,6 +2488,8 @@ static int cs43130_i2c_probe(struct i2c_client *client) if (!cs43130) return -ENOMEM; + cs43130->dev = &client->dev; + i2c_set_clientdata(client, cs43130); cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap); @@ -2434,29 +2498,30 @@ static int cs43130_i2c_probe(struct i2c_client *client) return ret; } - if (client->dev.of_node) { - ret = cs43130_handle_device_data(client, cs43130); + if (dev_fwnode(cs43130->dev)) { + ret = cs43130_handle_device_data(cs43130); if (ret != 0) return ret; } + for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++) cs43130->supplies[i].supply = cs43130_supply_names[i]; - ret = devm_regulator_bulk_get(&client->dev, + ret = devm_regulator_bulk_get(cs43130->dev, ARRAY_SIZE(cs43130->supplies), cs43130->supplies); if (ret != 0) { - dev_err(&client->dev, "Failed to request supplies: %d\n", ret); + dev_err(cs43130->dev, "Failed to request supplies: %d\n", ret); return ret; } ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies), cs43130->supplies); if (ret != 0) { - dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); + dev_err(cs43130->dev, "Failed to enable supplies: %d\n", ret); return ret; } - cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev, + cs43130->reset_gpio = devm_gpiod_get_optional(cs43130->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(cs43130->reset_gpio)) { ret = PTR_ERR(cs43130->reset_gpio); @@ -2470,7 +2535,7 @@ static int cs43130_i2c_probe(struct i2c_client *client) devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB); if (devid < 0) { ret = devid; - dev_err(&client->dev, "Failed to read device ID: %d\n", ret); + dev_err(cs43130->dev, "Failed to read device ID: %d\n", ret); goto err; } @@ -2481,7 +2546,7 @@ static int cs43130_i2c_probe(struct i2c_client *client) case CS43198_CHIP_ID: break; default: - dev_err(&client->dev, + dev_err(cs43130->dev, "CS43130 Device ID %X. Expected ID %X, %X, %X or %X\n", devid, CS43130_CHIP_ID, CS4399_CHIP_ID, CS43131_CHIP_ID, CS43198_CHIP_ID); @@ -2492,11 +2557,11 @@ static int cs43130_i2c_probe(struct i2c_client *client) cs43130->dev_id = devid; ret = regmap_read(cs43130->regmap, CS43130_REV_ID, ®); if (ret < 0) { - dev_err(&client->dev, "Get Revision ID failed\n"); + dev_err(cs43130->dev, "Get Revision ID failed\n"); goto err; } - dev_info(&client->dev, + dev_info(cs43130->dev, "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid, reg & 0xFF); @@ -2506,21 +2571,27 @@ static int cs43130_i2c_probe(struct i2c_client *client) init_completion(&cs43130->pll_rdy); init_completion(&cs43130->hpload_evt); - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, cs43130_irq_thread, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs43130", cs43130); - if (ret != 0) { - dev_err(&client->dev, "Failed to request IRQ: %d\n", ret); - goto err; + if (!client->irq) { + dev_dbg(cs43130->dev, "IRQ not found, will poll instead\n"); + cs43130->has_irq_line = 0; + } else { + ret = devm_request_threaded_irq(cs43130->dev, client->irq, + NULL, cs43130_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs43130", cs43130); + if (ret != 0) { + dev_err(cs43130->dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + cs43130->has_irq_line = 1; } cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO; - pm_runtime_set_autosuspend_delay(&client->dev, 100); - pm_runtime_use_autosuspend(&client->dev); - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(cs43130->dev, 100); + pm_runtime_use_autosuspend(cs43130->dev); + pm_runtime_set_active(cs43130->dev); + pm_runtime_enable(cs43130->dev); switch (cs43130->dev_id) { case CS43130_CHIP_ID: @@ -2556,11 +2627,11 @@ static int cs43130_i2c_probe(struct i2c_client *client) break; } - ret = devm_snd_soc_register_component(&client->dev, + ret = devm_snd_soc_register_component(cs43130->dev, &soc_component_dev_cs43130, cs43130_dai, ARRAY_SIZE(cs43130_dai)); if (ret < 0) { - dev_err(&client->dev, + dev_err(cs43130->dev, "snd_soc_register_component failed with ret = %d\n", ret); goto err; } @@ -2598,15 +2669,15 @@ static void cs43130_i2c_remove(struct i2c_client *client) cancel_work_sync(&cs43130->work); flush_workqueue(cs43130->wq); - device_remove_file(&client->dev, &dev_attr_hpload_dc_l); - device_remove_file(&client->dev, &dev_attr_hpload_dc_r); - device_remove_file(&client->dev, &dev_attr_hpload_ac_l); - device_remove_file(&client->dev, &dev_attr_hpload_ac_r); + device_remove_file(cs43130->dev, &dev_attr_hpload_dc_l); + device_remove_file(cs43130->dev, &dev_attr_hpload_dc_r); + device_remove_file(cs43130->dev, &dev_attr_hpload_ac_l); + device_remove_file(cs43130->dev, &dev_attr_hpload_ac_r); } gpiod_set_value_cansleep(cs43130->reset_gpio, 0); - pm_runtime_disable(&client->dev); + pm_runtime_disable(cs43130->dev); regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); } @@ -2669,6 +2740,7 @@ static const struct dev_pm_ops cs43130_runtime_pm = { NULL) }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id cs43130_of_match[] = { {.compatible = "cirrus,cs43130",}, {.compatible = "cirrus,cs4399",}, @@ -2678,6 +2750,17 @@ static const struct of_device_id cs43130_of_match[] = { }; MODULE_DEVICE_TABLE(of, cs43130_of_match); +#endif + +#if IS_ENABLED(CONFIG_ACPI) +static const struct acpi_device_id cs43130_acpi_match[] = { + { "CSC4399", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match); +#endif + static const struct i2c_device_id cs43130_i2c_id[] = { {"cs43130", 0}, @@ -2691,9 +2774,10 @@ MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id); static struct i2c_driver cs43130_i2c_driver = { .driver = { - .name = "cs43130", - .of_match_table = cs43130_of_match, - .pm = &cs43130_runtime_pm, + .name = "cs43130", + .of_match_table = of_match_ptr(cs43130_of_match), + .acpi_match_table = ACPI_PTR(cs43130_acpi_match), + .pm = &cs43130_runtime_pm, }, .id_table = cs43130_i2c_id, .probe = cs43130_i2c_probe, diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h index 90e8895275..dbdb5b262f 100644 --- a/sound/soc/codecs/cs43130.h +++ b/sound/soc/codecs/cs43130.h @@ -497,15 +497,18 @@ struct cs43130_dai { unsigned int sclk; unsigned int dai_format; unsigned int dai_mode; + unsigned int dai_invert; }; struct cs43130_private { + struct device *dev; struct snd_soc_component *component; struct regmap *regmap; struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; unsigned int dev_id; /* codec device ID */ int xtal_ibias; + bool has_irq_line; /* shared by both DAIs */ struct mutex clk_mutex; diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 9083228495..ca8f21aa48 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/platform_device.h> #include <linux/pm.h> diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 6c263086c4..cbcd02ec6b 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -45,6 +45,82 @@ struct es8326_priv { int jack_remove_retry; }; +static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h); + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h &= 0x20; + crosstalk_l &= 0xf0; + crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4; + ucontrol->value.integer.value[0] = crosstalk; + + return 0; +} + +static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + crosstalk = ucontrol->value.integer.value[0]; + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h = (crosstalk & 0x10) << 1; + crosstalk_l &= 0x0f; + crosstalk_l |= (crosstalk & 0x0f) << 4; + regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE, + 0x20, crosstalk_h); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l); + + return 0; +} + +static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h); + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h &= 0x10; + crosstalk_l &= 0x0f; + crosstalk = crosstalk_h | crosstalk_l; + ucontrol->value.integer.value[0] = crosstalk; + + return 0; +} + +static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + crosstalk = ucontrol->value.integer.value[0]; + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h = crosstalk & 0x10; + crosstalk_l &= 0xf0; + crosstalk_l |= crosstalk & 0x0f; + regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE, + 0x10, crosstalk_h); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l); + + return 0; +} + static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); @@ -102,6 +178,10 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, 0, 0x0f, 0, drc_target_tlv), + SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0, + es8326_crosstalk1_get, es8326_crosstalk1_set), + SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, + es8326_crosstalk2_get, es8326_crosstalk2_set), }; static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { @@ -117,12 +197,6 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0), - /* ADC Digital Mute */ - SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0), - /* Analog Power Supply*/ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1), SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1), @@ -132,20 +206,20 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL, + 4, 7, 0, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL, + 0, 7, 0, 0), + SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOR"), }; static const struct snd_soc_dapm_route es8326_dapm_routes[] = { - {"ADC L1", NULL, "MIC1"}, - {"ADC R1", NULL, "MIC2"}, - {"ADC L2", NULL, "MIC3"}, - {"ADC R2", NULL, "MIC4"}, - - {"ADC L", NULL, "ADC L1"}, - {"ADC R", NULL, "ADC R1"}, - {"ADC L", NULL, "ADC L2"}, - {"ADC R", NULL, "ADC R2"}, + {"ADC L", NULL, "MIC1"}, + {"ADC R", NULL, "MIC2"}, + {"ADC L", NULL, "MIC3"}, + {"ADC R", NULL, "MIC4"}, {"I2S OUT", NULL, "ADC L"}, {"I2S OUT", NULL, "ADC R"}, @@ -156,6 +230,9 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = { {"LHPMIX", NULL, "Left DAC"}, {"RHPMIX", NULL, "Right DAC"}, + {"HPOR", NULL, "HPOR Supply"}, + {"HPOL", NULL, "HPOL Supply"}, + {"HPOL", NULL, "LHPMIX"}, {"HPOR", NULL, "RHPMIX"}, }; @@ -198,77 +275,108 @@ struct _coeff_div { /* codec hifi mclk clock divider coefficients */ /* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */ -static const struct _coeff_div coeff_div[] = { - {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, - {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47}, - {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47}, - {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, - {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {64, 8000, 512000, 0x60, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, - - {64, 44100, 2822400, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {64, 48000, 3072000, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x23, 0x47}, - {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x23, 0x47}, - {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, - {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0x38, 0x08, 0x4f, 0x1f}, - {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, - {128, 8000, 1024000, 0x60, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {128, 16000, 2048000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - - {128, 44100, 5644800, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {128, 48000, 6144000, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x23, 0x47}, - {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x23, 0x47}, - {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, - {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, - {256, 8000, 2048000, 0x60, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - - {256, 44100, 11289600, 0x00, 0x00, 0x10, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {256, 48000, 12288000, 0x00, 0x00, 0x30, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x23, 0x47}, - {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, - {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {400, 48000, 19200000, 0x09, 0x04, 0x0f, 0x6d, 0x3a, 0x0A, 0x4F, 0x1F}, - {500, 48000, 24000000, 0x18, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {512, 16000, 8192000, 0x20, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - - {512, 44100, 22579200, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {512, 48000, 24576000, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, - {1024, 8000, 8192000, 0x60, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, +static const struct _coeff_div coeff_div_v0[] = { + {64, 8000, 512000, 0x60, 0x01, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {64, 16000, 1024000, 0x20, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {64, 44100, 2822400, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {64, 48000, 3072000, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {128, 8000, 1024000, 0x60, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {128, 16000, 2048000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {128, 44100, 5644800, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {128, 48000, 6144000, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + + {192, 32000, 6144000, 0xE0, 0x02, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {256, 8000, 2048000, 0x60, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {256, 16000, 4096000, 0x20, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {384, 32000, 12288000, 0xE0, 0x05, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {400, 48000, 19200000, 0xE9, 0x04, 0x0F, 0x6d, 0x4A, 0x0A, 0x1F, 0x1F}, + + {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0x4A, 0x0A, 0x1F, 0x1F}, + {512, 8000, 4096000, 0x60, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F}, + {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - - {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, - {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, - {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, - {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x5F}, - {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, - {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, +}; +static const struct _coeff_div coeff_div_v3[] = { + {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F}, + {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F}, + {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F}, + {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47}, + {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47}, + {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x3F}, + {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F}, + + {64, 8000, 512000, 0x60, 0x00, 0x35, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x3F}, + {64, 44100, 2822400, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {64, 48000, 3072000, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x8A, 0x1B, 0x23, 0x47}, + {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x23, 0x47}, + {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F}, + {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0xB8, 0x08, 0x4f, 0x1f}, + {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27}, + + {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47}, + {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47}, + {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {192, 32000, 6144000, 0xE0, 0x02, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0xCA, 0x1B, 0x1F, 0x3F}, + + {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27}, + {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47}, + {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, + {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {384, 32000, 12288000, 0xE0, 0x02, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F}, + + {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F}, + {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F}, + {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F}, + + {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, + {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27}, + {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27}, + {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x5F}, + {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, + {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x8A, 0x0A, 0x27, 0x27}, }; -static inline int get_coeff(int mclk, int rate) +static inline int get_coeff(int mclk, int rate, int array, + const struct _coeff_div *coeff_div) { int i; - for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + for (i = 0; i < array; i++) { if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) return i; } @@ -333,11 +441,19 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + const struct _coeff_div *coeff_div; struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); u8 srate = 0; - int coeff; + int coeff, array; - coeff = get_coeff(es8326->sysclk, params_rate(params)); + if (es8326->version == 0) { + coeff_div = coeff_div_v0; + array = ARRAY_SIZE(coeff_div_v0); + } else { + coeff_div = coeff_div_v3; + array = ARRAY_SIZE(coeff_div_v3); + } + coeff = get_coeff(es8326->sysclk, params_rate(params), array, coeff_div); /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -393,10 +509,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) unsigned int offset_l, offset_r; if (mute) { - regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); - regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, - ES8326_MUTE_MASK, ES8326_MUTE); - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0); + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ES8326_MUTE); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, + 0x30, 0x00); + } else { + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x0F); + } } else { if (!es8326->calibrated) { regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); @@ -409,11 +531,22 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); es8326->calibrated = true; } - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0); - regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); - regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); - regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, - ES8326_MUTE_MASK, ~(ES8326_MUTE)); + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ~(ES8326_MUTE)); + } else { + msleep(300); + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x00); + } } return 0; } @@ -430,29 +563,26 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, if (ret) return ret; - regmap_write(es8326->regmap, ES8326_RESET, 0x9f); - msleep(20); - regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02); + usleep_range(5000, 10000); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40); regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20); - regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); + regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - break; - case SND_SOC_BIAS_OFF: - clk_disable_unprepare(es8326->mclk); regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00); regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(es8326->mclk); + break; } return 0; @@ -466,7 +596,7 @@ static const struct snd_soc_dai_ops es8326_ops = { .set_fmt = es8326_set_dai_fmt, .set_sysclk = es8326_set_dai_sysclk, .mute_stream = es8326_mute, - .no_capture_mute = 1, + .no_capture_mute = 0, }; static struct snd_soc_dai_driver es8326_dai = { @@ -594,7 +724,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) iface = snd_soc_component_read(comp, ES8326_HPDET_STA); dev_dbg(comp->dev, "gpio flag %#04x", iface); - if (es8326->jack_remove_retry == 1) { + if ((es8326->jack_remove_retry == 1) && (es8326->version != ES8326_VERSION_B)) { if (iface & ES8326_HPINSERT_FLAG) es8326->jack_remove_retry = 2; else @@ -625,10 +755,12 @@ static void es8326_jack_detect_handler(struct work_struct *work) es8326->hp = 0; } regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03); /* * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event */ - if (es8326->jack_remove_retry == 0) { + if ((es8326->jack_remove_retry == 0) && (es8326->version != ES8326_VERSION_B)) { es8326->jack_remove_retry = 1; dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n", es8326->jack_remove_retry); @@ -644,14 +776,17 @@ static void es8326_jack_detect_handler(struct work_struct *work) if (es8326->hp == 0) { dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n"); /* - * set auto-check mode, then restart jack_detect_work after 100ms. + * set auto-check mode, then restart jack_detect_work after 400ms. * Don't report jack status. */ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08); queue_delayed_work(system_wq, &es8326->jack_detect_work, - msecs_to_jiffies(100)); + msecs_to_jiffies(400)); es8326->hp = 1; goto exit; } @@ -689,19 +824,16 @@ exit: static irqreturn_t es8326_irq(int irq, void *dev_id) { struct es8326_priv *es8326 = dev_id; - struct snd_soc_component *comp = es8326->component; if (!es8326->jack) goto out; - es8326_enable_micbias(comp); - if (es8326->jack->status & SND_JACK_HEADSET) queue_delayed_work(system_wq, &es8326->jack_detect_work, msecs_to_jiffies(10)); else queue_delayed_work(system_wq, &es8326->jack_detect_work, - msecs_to_jiffies(600)); + msecs_to_jiffies(300)); out: return IRQ_HANDLED; @@ -719,16 +851,18 @@ static int es8326_calibrate(struct snd_soc_component *component) if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) { dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n"); regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0); - regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01); + regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03); regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30); regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed); + regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08); regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1); regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03); regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f); - regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33); + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88); - regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + usleep_range(15000, 20000); regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + usleep_range(15000, 20000); regmap_write(es8326->regmap, ES8326_RESET, 0xc0); usleep_range(15000, 20000); @@ -765,28 +899,29 @@ static int es8326_resume(struct snd_soc_component *component) /* reset internal clock state */ regmap_write(es8326->regmap, ES8326_RESET, 0x1f); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); usleep_range(10000, 15000); - regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); + regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xe9); + regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xcb); /* set headphone default type and detect pin */ - regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81); + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83); regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); /* set internal oscillator as clock source of headpone cp */ - regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84); + regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); /* clock manager reset release */ regmap_write(es8326->regmap, ES8326_RESET, 0x17); /* set headphone detection as half scan mode */ - regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08); + regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d); regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00); /* enable headphone driver */ + regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4); regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7); usleep_range(2000, 5000); - regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab); - usleep_range(2000, 5000); - regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb); - usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33); regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); @@ -795,14 +930,13 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00); /* calibrate for B version */ es8326_calibrate(component); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa); + regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00); /* turn off headphone out */ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00); /* set ADC and DAC in low power mode */ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); - /* force micbias on */ - regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f); - regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08); regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); /* select vdda as micbias source */ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); @@ -810,6 +944,14 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08); regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15); + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | + ((es8326->version == ES8326_VERSION_B) ? + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + usleep_range(5000, 10000); + es8326_enable_micbias(es8326->component); + usleep_range(50000, 70000); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); regmap_write(es8326->regmap, ES8326_INT_SOURCE, (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); regmap_write(es8326->regmap, ES8326_INTOUT_IO, @@ -818,7 +960,7 @@ static int es8326_resume(struct snd_soc_component *component) (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL); @@ -826,10 +968,7 @@ static int es8326_resume(struct snd_soc_component *component) regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ES8326_MUTE); - regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | - ((es8326->version == ES8326_VERSION_B) ? - (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : - (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f); es8326->jack_remove_retry = 0; es8326->hp = 0; diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 90a08351d6..4234bbb900 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -72,6 +72,7 @@ #define ES8326_DAC_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 +#define ES8326_DAC_CROSSTALK 0x55 #define ES8326_HPJACK_TIMER 0x56 #define ES8326_HPDET_TYPE 0x57 #define ES8326_INT_SOURCE 0x58 @@ -100,7 +101,7 @@ #define ES8326_MUTE (3 << 0) /* ES8326_CLK_CTL */ -#define ES8326_CLK_ON (0x7f << 0) +#define ES8326_CLK_ON (0x7e << 0) #define ES8326_CLK_OFF (0 << 0) /* ES8326_CLK_INV */ diff --git a/sound/soc/codecs/es83xx-dsm-common.c b/sound/soc/codecs/es83xx-dsm-common.c new file mode 100644 index 0000000000..94fd7d54c5 --- /dev/null +++ b/sound/soc/codecs/es83xx-dsm-common.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) Intel Corporation, 2022 +// Copyright Everest Semiconductor Co.,Ltd + +#include <linux/module.h> +#include <linux/acpi.h> +#include "es83xx-dsm-common.h" + +/* UUID ("a9800c04-e016-343e-41f4-6bcce70f4332") */ +static const guid_t es83xx_dsm_guid = + GUID_INIT(0xa9800c04, 0xe016, 0x343e, + 0x41, 0xf4, 0x6b, 0xcc, 0xe7, 0x0f, 0x43, 0x32); + +#define ES83xx_DSM_REVID 1 + +int es83xx_dsm(struct device *dev, int arg, int *value) +{ + acpi_handle dhandle; + union acpi_object *obj; + int ret = 0; + + dhandle = ACPI_HANDLE(dev); + if (!dhandle) + return -ENOENT; + + obj = acpi_evaluate_dsm(dhandle, &es83xx_dsm_guid, ES83xx_DSM_REVID, + arg, NULL); + if (!obj) { + dev_err(dev, "%s: acpi_evaluate_dsm() failed\n", __func__); + ret = -EINVAL; + goto out; + } + + if (obj->type != ACPI_TYPE_INTEGER) { + dev_err(dev, "%s: object is not ACPI_TYPE_INTEGER\n", __func__); + ret = -EINVAL; + goto err; + } + + *value = obj->integer.value; +err: + ACPI_FREE(obj); +out: + return ret; +} +EXPORT_SYMBOL_GPL(es83xx_dsm); + +int es83xx_dsm_dump(struct device *dev) +{ + int value; + int ret; + + ret = es83xx_dsm(dev, PLATFORM_MAINMIC_TYPE_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_MAINMIC_TYPE %#x\n", value); + + ret = es83xx_dsm(dev, PLATFORM_HPMIC_TYPE_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_HPMIC_TYPE %#x\n", value); + + ret = es83xx_dsm(dev, PLATFORM_SPK_TYPE_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_SPK_TYPE %#x\n", value); + + ret = es83xx_dsm(dev, PLATFORM_HPDET_INV_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_HPDET_INV %#x\n", value); + + ret = es83xx_dsm(dev, PLATFORM_PCM_TYPE_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_PCM_TYPE %#x\n", value); + + ret = es83xx_dsm(dev, PLATFORM_MIC_DE_POP_ARG, &value); + if (ret < 0) + return ret; + dev_info(dev, "PLATFORM_MIC_DE_POP %#x\n", value); + + return 0; +} +EXPORT_SYMBOL_GPL(es83xx_dsm_dump); + +MODULE_DESCRIPTION("Everest Semi ES83xx DSM helpers"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es83xx-dsm-common.h b/sound/soc/codecs/es83xx-dsm-common.h new file mode 100644 index 0000000000..91c9a89e75 --- /dev/null +++ b/sound/soc/codecs/es83xx-dsm-common.h @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Intel Corporation, 2022 + * Copyright Everest Semiconductor Co.,Ltd + */ + +/* Definitions extracted from ASL file provided at + * https://github.com/thesofproject/linux/files/9398723/ESSX8326.zip + */ + +#ifndef _ES83XX_DSM_COMMON_H +#define _ES83XX_DSM_COMMON_H + +/*************************************************** + * DSM arguments * + ***************************************************/ + +#define PLATFORM_MAINMIC_TYPE_ARG 0x00 +#define PLATFORM_HPMIC_TYPE_ARG 0x01 +#define PLATFORM_SPK_TYPE_ARG 0x02 +#define PLATFORM_HPDET_INV_ARG 0x03 +#define PLATFORM_PCM_TYPE_ARG 0x04 + +#define PLATFORM_MIC_DE_POP_ARG 0x06 +#define PLATFORM_CODEC_TYPE_ARG 0x0E +#define PLATFORM_BUS_SLOT_ARG 0x0F + +#define HP_CODEC_LINEIN_PGA_GAIN_ARG 0x10 +#define MAIN_CODEC_LINEIN_PGA_GAIN_ARG 0x20 + +#define HP_CODEC_D2SEPGA_GAIN_ARG 0x11 +#define MAIN_CODEC_D2SEPGA_GAIN_ARG 0x21 + +#define HP_CODEC_ADC_VOLUME_ARG 0x12 +#define MAIN_CODEC_ADC_VOLUME_ARG 0x22 + +#define HP_CODEC_ADC_ALC_ENABLE_ARG 0x13 +#define MAIN_CODEC_ADC_ALC_ENABLE_ARG 0x23 + +#define HP_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x14 +#define MAIN_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x24 + +#define HP_CODEC_ADC_ALC_MAXGAIN_ARG 0x15 +#define MAIN_CODEC_ADC_ALC_MAXGAIN_ARG 0x25 + +#define HP_CODEC_ADC_ALC_MINGAIN_ARG 0x16 +#define MAIN_CODEC_ADC_ALC_MINGAIN_ARG 0x26 + +#define HP_CODEC_ADC_ALC_HLDTIME_ARG 0x17 +#define MAIN_CODEC_ADC_ALC_HLDTIME_ARG 0x27 + +#define HP_CODEC_ADC_ALC_DCYTIME_ARG 0x18 +#define MAIN_CODEC_ADC_ALC_DCYTIME_ARG 0x28 + +#define HP_CODEC_ADC_ALC_ATKTIME_ARG 0x19 +#define MAIN_CODEC_ADC_ALC_ATKTIME_ARG 0x29 + +#define HP_CODEC_ADC_ALC_NGTYPE_ARG 0x1a +#define MAIN_CODEC_ADC_ALC_NGTYPE_ARG 0x2a + +#define HP_CODEC_ADC_ALC_NGTHLD_ARG 0x1b +#define MAIN_CODEC_ADC_ALC_NGTHLD_ARG 0x2b + +#define MAIN_CODEC_ADC_GUI_STEP_ARG 0x2c +#define MAIN_CODEC_ADC_GUI_GAIN_RANGE_ARG 0x2c + +#define HEADPHONE_DUMMY_REMOVE_ENABLE_ARG 0x2e + +#define HP_CODEC_DAC_HPMIX_HIGAIN_ARG 0x40 +#define SPK_CODEC_DAC_HPMIX_HIGAIN_ARG 0x50 + +#define HP_CODEC_DAC_HPMIX_VOLUME_ARG 0x41 +#define SPK_CODEC_DAC_HPMIX_VOLUME_ARG 0x51 + +#define HP_CODEC_DAC_HPOUT_VOLUME_ARG 0x42 +#define SPK_CODEC_DAC_HPOUT_VOLUME_ARG 0x52 + +#define HP_CODEC_LDAC_VOLUME_ARG 0x44 +#define HP_CODEC_RDAC_VOLUME_ARG 0x54 + +#define SPK_CODEC_LDAC_VOLUME_ARG 0x45 +#define SPK_CODEC_RDAC_VOLUME_ARG 0x55 + +#define HP_CODEC_DAC_AUTOMUTE_ARG 0x46 +#define SPK_CODEC_DAC_AUTOMUTE_ARG 0x56 + +#define HP_CODEC_DAC_MONO_ARG 0x4A +#define SPK_CODEC_DAC_MONO_ARG 0x5A + +#define HP_CTL_IO_LEVEL_ARG 0x4B +#define SPK_CTL_IO_LEVEL_ARG 0x5B + +#define CODEC_GPIO0_FUNC_ARG 0x80 +#define CODEC_GPIO1_FUNC_ARG 0x81 +#define CODEC_GPIO2_FUNC_ARG 0x82 +#define CODEC_GPIO3_FUNC_ARG 0x83 +#define CODEC_GPIO4_FUNC_ARG 0x84 + +#define PLATFORM_MCLK_LRCK_FREQ_ARG 0x85 + +/*************************************************** + * Values for arguments * + ***************************************************/ + +/* Main and HP Mic */ +#define PLATFORM_MIC_DMIC_HIGH_LEVEL 0xAA +#define PLATFORM_MIC_DMIC_LOW_LEVEL 0x55 +#define PLATFORM_MIC_AMIC_LIN1RIN1 0xBB +#define PLATFORM_MIC_AMIC_LIN2RIN2 0xCC + +/* Speaker */ +#define PLATFORM_SPK_NONE 0x00 +#define PLATFORM_SPK_MONO 0x01 +#define PLATFORM_SPK_STEREO 0x02 + +/* Jack Detection */ +#define PLATFORM_HPDET_NORMAL 0x00 +#define PLATFORM_HPDET_INVERTED 0x01 + +/* PCM type (Port number + protocol) */ +/* + * RETURNED VALUE = 0x00, PCM PORT0, I2S + * 0x01, PCM PORT0, LJ + * 0x02, PCM PORT0, RJ + * 0x03, PCM PORT0, DSP-A + * 0x04, PCM PORT0, DSP-B + * 0x10, PCM PORT1, I2S + * 0x11, PCM PORT1, LJ + * 0x12, PCM PORT1, RJ + * 0x13, PCM PORT1, DSP-A + * 0x14, PCM PORT1, DSP-B + * 0xFF, Use default + * + * This is not used in Linux (defined by topology) and in + * Windows it's always DSP-A + */ + +/* Depop */ +#define PLATFORM_MIC_DE_POP_OFF 0x00 +#define PLATFORM_MIC_DE_POP_ON 0x01 + +/* Codec type */ +#define PLATFORM_CODEC_8316 16 +#define PLATFORM_CODEC_8326 26 +#define PLATFORM_CODEC_8336 36 +#define PLATFORM_CODEC_8395 95 +#define PLATFORM_CODEC_8396 96 + +/* Bus slot (on the host) */ +/* BIT[3:0] FOR BUS NUMBER, BIT[7:4] FOR SLOT NUMBER + * BIT[3:0] 0 for I2S0, 1 for IS21, 2 for I2S2. + * + * On Intel platforms this refers to SSP0..2. This information + * is not really useful for Linux, the information is already + * inferred from NHLT but can be used to double-check NHLT + */ + +/* Volume - Gain */ +#define LINEIN_GAIN_0db 0x00 /* gain = 0db */ +#define LINEIN_GAIN_3db 0x01 /* gain = +3db */ +#define LINEIN_GAIN_6db 0x02 /* gain = +6db */ +#define LINEIN_GAIN_9db 0x03 /* gain = +9db */ +#define LINEIN_GAIN_12db 0x04 /* gain = +12db */ +#define LINEIN_GAIN_15db 0x05 /* gain = +15db */ +#define LINEIN_GAIN_18db 0x06 /* gain = +18db */ +#define LINEIN_GAIN_21db 0x07 /* gain = +21db */ +#define LINEIN_GAIN_24db 0x08 /* gain = +24db */ +#define LINEIN_GAIN_27db 0x09 /* gain = +27db */ +#define LINEIN_GAIN_30db 0x0a /* gain = +30db */ + +#define ADC_GUI_STEP_3db 0x03 /* gain = +3db */ +#define ADC_GUI_STEP_6db 0x06 /* gain = +6db */ +#define ADC_GUI_STEP_10db 0x0a /* gain = +10db */ + +#define D2SEPGA_GAIN_0db 0x00 /* gain = 0db */ +#define D2SEPGA_GAIN_15db 0x01 /* gain = +15db */ + +/* ADC volume: base = 0db, -0.5db/setp, 0xc0 <-> -96db */ + +#define ADC_ALC_DISABLE 0x00 +#define ADC_ALC_ENABLE 0x01 + +#define ADC_ALC_TARGET_LEVEL_m16_5db 0x00 /* gain = -16.5db */ +#define ADC_ALC_TARGET_LEVEL_m15db 0x01 /* gain = -15db */ +#define ADC_ALC_TARGET_LEVEL_m13_5db 0x02 /* gain = -13.5db */ +#define ADC_ALC_TARGET_LEVEL_m12db 0x03 /* gain = -12db */ +#define ADC_ALC_TARGET_LEVEL_m10_5db 0x04 /* gain = -10.5db */ +#define ADC_ALC_TARGET_LEVEL_m9db 0x05 /* gain = -9db */ +#define ADC_ALC_TARGET_LEVEL_m7_5db 0x06 /* gain = -7.5db */ +#define ADC_ALC_TARGET_LEVEL_m6db 0x07 /* gain = -6db */ +#define ADC_ALC_TARGET_LEVEL_m4_5db 0x08 /* gain = -4.5db */ +#define ADC_ALC_TARGET_LEVEL_m_3db 0x09 /* gain = -3db */ +#define ADC_ALC_TARGET_LEVEL_m1_5db 0x0a /* gain = -1.5db */ + +#define ADC_ALC_MAXGAIN_m6_5db 0x00 /* gain = -6.5db */ +#define ADC_ALC_MAXGAIN_m5db 0x01 /* gain = -5db */ +#define ADC_ALC_MAXGAIN_m3_5db 0x02 /* gain = -3.5db */ +#define ADC_ALC_MAXGAIN_m2db 0x03 /* gain = -2db */ +#define ADC_ALC_MAXGAIN_m0_5db 0x04 /* gain = -0.5db */ +#define ADC_ALC_MAXGAIN_1db 0x05 /* gain = +1db */ +#define ADC_ALC_MAXGAIN_2_5db 0x06 /* gain = +2.5db */ +#define ADC_ALC_MAXGAIN_4db 0x07 /* gain = +4db */ +#define ADC_ALC_MAXGAIN_5_5db 0x08 /* gain = +5.5db */ +#define ADC_ALC_MAXGAIN_7db 0x09 /* gain = +7db */ +#define ADC_ALC_MAXGAIN_8_5db 0x0a /* gain = +8.5db */ +#define ADC_ALC_MAXGAIN_10db 0x0b /* gain = +10db */ +#define ADC_ALC_MAXGAIN_11_5db 0x0c /* gain = +11.5db */ +#define ADC_ALC_MAXGAIN_13db 0x0d /* gain = +13db */ +#define ADC_ALC_MAXGAIN_14_5db 0x0e /* gain = +14.5db */ +#define ADC_ALC_MAXGAIN_16db 0x0f /* gain = +16db */ +#define ADC_ALC_MAXGAIN_17_5db 0x10 /* gain = +17.5db */ +#define ADC_ALC_MAXGAIN_19db 0x11 /* gain = +19db */ +#define ADC_ALC_MAXGAIN_20_5db 0x12 /* gain = +20.5db */ +#define ADC_ALC_MAXGAIN_22db 0x13 /* gain = +22db */ +#define ADC_ALC_MAXGAIN_23_5db 0x14 /* gain = +23.5db */ +#define ADC_ALC_MAXGAIN_25db 0x15 /* gain = +25db */ +#define ADC_ALC_MAXGAIN_26_5db 0x16 /* gain = +26.5db */ +#define ADC_ALC_MAXGAIN_28db 0x17 /* gain = +28db */ +#define ADC_ALC_MAXGAIN_29_5db 0x18 /* gain = +29.5db */ +#define ADC_ALC_MAXGAIN_31db 0x19 /* gain = +31db */ +#define ADC_ALC_MAXGAIN_32_5db 0x1a /* gain = +32.5db */ +#define ADC_ALC_MAXGAIN_34db 0x1b /* gain = +34db */ +#define ADC_ALC_MAXGAIN_35_5db 0x1c /* gain = +35.5db */ + +#define ADC_ALC_MINGAIN_m12db 0x00 /* gain = -12db */ +#define ADC_ALC_MINGAIN_m10_5db 0x01 /* gain = -10.5db */ +#define ADC_ALC_MINGAIN_m9db 0x02 /* gain = -9db */ +#define ADC_ALC_MINGAIN_m7_5db 0x03 /* gain = -7.5db */ +#define ADC_ALC_MINGAIN_m6db 0x04 /* gain = -6db */ +#define ADC_ALC_MINGAIN_m4_51db 0x05 /* gain = -4.51db */ +#define ADC_ALC_MINGAIN_m3db 0x06 /* gain = -3db */ +#define ADC_ALC_MINGAIN_m1_5db 0x07 /* gain = -1.5db */ +#define ADC_ALC_MINGAIN_0db 0x08 /* gain = 0db */ +#define ADC_ALC_MINGAIN_1_5db 0x09 /* gain = +1.5db */ +#define ADC_ALC_MINGAIN_3db 0x0a /* gain = +3db */ +#define ADC_ALC_MINGAIN_4_5db 0x0b /* gain = +4.5db */ +#define ADC_ALC_MINGAIN_6db 0x0c /* gain = +6db */ +#define ADC_ALC_MINGAIN_7_5db 0x0d /* gain = +7.5db */ +#define ADC_ALC_MINGAIN_9db 0x0e /* gain = +9db */ +#define ADC_ALC_MINGAIN_10_5db 0x0f /* gain = +10.5db */ +#define ADC_ALC_MINGAIN_12db 0x10 /* gain = +12db */ +#define ADC_ALC_MINGAIN_13_5db 0x11 /* gain = +13.5db */ +#define ADC_ALC_MINGAIN_15db 0x12 /* gain = +15db */ +#define ADC_ALC_MINGAIN_16_5db 0x13 /* gain = +16.5db */ +#define ADC_ALC_MINGAIN_18db 0x14 /* gain = +18db */ +#define ADC_ALC_MINGAIN_19_5db 0x15 /* gain = +19.5db */ +#define ADC_ALC_MINGAIN_21db 0x16 /* gain = +21db */ +#define ADC_ALC_MINGAIN_22_5db 0x17 /* gain = +22.5db */ +#define ADC_ALC_MINGAIN_24db 0x18 /* gain = +24db */ +#define ADC_ALC_MINGAIN_25_5db 0x19 /* gain = +25.5db */ +#define ADC_ALC_MINGAIN_27db 0x1a /* gain = +27db */ +#define ADC_ALC_MINGAIN_28_5db 0x1b /* gain = +28.5db */ +#define ADC_ALC_MINGAIN_30db 0x1c /* gain = +30db */ + +/* ADC volume: step 1dB */ + +/* ALC Hold, Decay, Attack */ +#define ADC_ALC_HLDTIME_0_US 0x00 +#define ADC_ALC_HLDTIME_0000266_US 0x01 //time = 2.67ms +#define ADC_ALC_HLDTIME_0000533_US 0x02 //time = 5.33ms +#define ADC_ALC_HLDTIME_0001066_US 0x03 //time = 10.66ms +#define ADC_ALC_HLDTIME_0002132_US 0x04 //time = 21.32ms +#define ADC_ALC_HLDTIME_0004264_US 0x05 //time = 42.64ms +#define ADC_ALC_HLDTIME_0008538_US 0x06 //time = 85.38ms +#define ADC_ALC_HLDTIME_0017076_US 0x07 //time = 170.76ms +#define ADC_ALC_HLDTIME_0034152_US 0x08 //time = 341.52ms +#define ADC_ALC_HLDTIME_0680000_US 0x09 //time = 0.68s +#define ADC_ALC_HLDTIME_1360000_US 0x0a //time = 1.36s + +#define ADC_ALC_DCYTIME_000410_US 0x00 //time = 410us +#define ADC_ALC_DCYTIME_000820_US 0x01 //time = 820us +#define ADC_ALC_DCYTIME_001640_US 0x02 //time = 1.64ms +#define ADC_ALC_DCYTIME_003280_US 0x03 //time = 3.28ms +#define ADC_ALC_DCYTIME_006560_US 0x04 //time = 6.56ms +#define ADC_ALC_DCYTIME_013120_US 0x05 //time = 13.12ms +#define ADC_ALC_DCYTIME_026240_US 0x06 //time = 26.24ms +#define ADC_ALC_DCYTIME_058480_US 0x07 //time = 52.48ms +#define ADC_ALC_DCYTIME_104960_US 0x08 //time = 104.96ms +#define ADC_ALC_DCYTIME_209920_US 0x09 //time = 209.92ms +#define ADC_ALC_DCYTIME_420000_US 0x0a //time = 420ms + +#define ADC_ALC_ATKTIME_000104_US 0x00 //time = 104us +#define ADC_ALC_ATKTIME_000208_US 0x01 //time = 208us +#define ADC_ALC_ATKTIME_000416_US 0x02 //time = 416ms +#define ADC_ALC_ATKTIME_003832_US 0x03 //time = 832ms +#define ADC_ALC_ATKTIME_001664_US 0x04 //time = 1.664ms +#define ADC_ALC_ATKTIME_003328_US 0x05 //time = 3.328ms +#define ADC_ALC_ATKTIME_006656_US 0x06 //time = 6.656ms +#define ADC_ALC_ATKTIME_013312_US 0x07 //time = 13.312ms +#define ADC_ALC_ATKTIME_026624_US 0x08 //time = 26.624ms +#define ADC_ALC_ATKTIME_053248_US 0x09 //time = 53.248ms +#define ADC_ALC_ATKTIME_106496_US 0x0a //time = 106.496ms + +/* ALC Noise Gate */ +#define ADC_ALC_NGTYPE_DISABLE 0x00 //noise gate disable +#define ADC_ALC_NGTYPE_ENABLE_HOLD 0x01 //noise gate enable, hold gain type +#define ADC_ALC_NGTYPE_ENABLE_MUTE 0x03 //noise gate enable, mute type + +#define ADC_ALC_NGTHLD_m76_5db 0x00 /* Threshold = -76.5db */ +#define ADC_ALC_NGTHLD_m75db 0x01 /* Threshold = -75db */ +#define ADC_ALC_NGTHLD_m73_5db 0x02 /* Threshold = -73.5db */ +#define ADC_ALC_NGTHLD_m72db 0x03 /* Threshold = -72db */ +#define ADC_ALC_NGTHLD_m70_5db 0x04 /* Threshold = -70.5db */ +#define ADC_ALC_NGTHLD_m69db 0x05 /* Threshold = -69db */ +#define ADC_ALC_NGTHLD_m67_5db 0x06 /* Threshold = -67.5db */ +#define ADC_ALC_NGTHLD_m66db 0x07 /* Threshold = -66db */ +#define ADC_ALC_NGTHLD_m64_5db 0x08 /* Threshold = -64.5db */ +#define ADC_ALC_NGTHLD_m63db 0x09 /* Threshold = -63db */ +#define ADC_ALC_NGTHLD_m61_5db 0x0a /* Threshold = -61.5db */ +#define ADC_ALC_NGTHLD_m60db 0x0b /* Threshold = -60db */ +#define ADC_ALC_NGTHLD_m58_5db 0x0c /* Threshold = -58.5db */ +#define ADC_ALC_NGTHLD_m57db 0x0d /* Threshold = -57db */ +#define ADC_ALC_NGTHLD_m55_5db 0x0e /* Threshold = -55.5db */ +#define ADC_ALC_NGTHLD_m54db 0x0f /* Threshold = -54db */ +#define ADC_ALC_NGTHLD_m52_5db 0x10 /* Threshold = -52.5db */ +#define ADC_ALC_NGTHLD_m51db 0x11 /* Threshold = -51db */ +#define ADC_ALC_NGTHLD_m49_5db 0x12 /* Threshold = -49.5db */ +#define ADC_ALC_NGTHLD_m48db 0x13 /* Threshold = -48db */ +#define ADC_ALC_NGTHLD_m46_5db 0x14 /* Threshold = -46.5db */ +#define ADC_ALC_NGTHLD_m45db 0x15 /* Threshold = -45db */ +#define ADC_ALC_NGTHLD_m43_5db 0x16 /* Threshold = -43.5db */ +#define ADC_ALC_NGTHLD_m42db 0x17 /* Threshold = -42db */ +#define ADC_ALC_NGTHLD_m40_5db 0x18 /* Threshold = -40.5db */ +#define ADC_ALC_NGTHLD_m39db 0x19 /* Threshold = -39db */ +#define ADC_ALC_NGTHLD_m37_5db 0x1a /* Threshold = -37.5db */ +#define ADC_ALC_NGTHLD_m36db 0x1b /* Threshold = -36db */ +#define ADC_ALC_NGTHLD_m34_5db 0x1c /* Threshold = -34.5db */ +#define ADC_ALC_NGTHLD_m33db 0x1d /* Threshold = -33db */ +#define ADC_ALC_NGTHLD_m31_5db 0x1e /* Threshold = -31.5db */ +#define ADC_ALC_NGTHLD_m30db 0x1f /* Threshold = -30db */ + +/* Headphone dummy - Windows Specific flag, not needed for Linux */ + +/* HPMIX HIGAIN and VOLUME */ +#define DAC_HPMIX_HIGAIN_0db 0x00 /* gain = 0db */ +#define DAC_HPMIX_HIGAIN_m6db 0x88 /* gain = -6db */ + +#define DAC_HPMIX_VOLUME_m12db 0x00 /* volume = -12db */ +#define DAC_HPMIX_VOLUME_m10_5db 0x11 /* volume = -10.5db */ +#define DAC_HPMIX_VOLUME_m9db 0x22 /* volume = -9db */ +#define DAC_HPMIX_VOLUME_m7_5db 0x33 /* volume = -7.5db */ +#define DAC_HPMIX_VOLUME_m6db 0x44 /* volume = -6db */ +#define DAC_HPMIX_VOLUME_m4_5db 0x88 /* volume = -4.5db */ +#define DAC_HPMIX_VOLUME_m3db 0x99 /* volume = -3db */ +#define DAC_HPMIX_VOLUME_m1_5db 0xaa /* volume = -1.5db */ +#define DAC_HPMIX_VOLUME_0db 0xbb /* volume = 0db */ + +/* HPOUT VOLUME */ +#define DAC_HPOUT_VOLUME_0db 0x00 /* volume = 0db */ +#define DAC_HPOUT_VOLUME_m12db 0x11 /* volume = -12db */ +#define DAC_HPOUT_VOLUME_m24db 0x22 /* volume = -24db */ +#define DAC_HPOUT_VOLUME_m48db 0x33 /* volume = -48db */ + +/* LDAC/RDAC volume = 0db, -0.5db/setp, 0xc0 <-> -96db */ + +/* Automute */ +#define DAC_AUTOMUTE_NONE 0x00 /* no automute */ +#define DAC_AUTOMUTE_DIGITAL 0x01 /* digital mute */ +#define DAC_AUTOMUTE_ANALOG 0x02 /* analog mute */ + +/* Mono - Windows specific, on Linux the information comes from DAI/topology */ +#define HEADPHONE_MONO 0x01 /* on channel */ +#define HEADPHONE_STEREO 0x00 /* stereo */ + +/* Speaker and headphone GPIO control */ +#define GPIO_CTL_IO_LEVEL_LOW 0x00 /* low level enable */ +#define GPIO_CTL_IO_LEVEL_HIGH 0x01 /* high level enable */ + +/* GPIO */ +/* FIXME: for ES8396, no need to use */ + +/* Platform clocks */ +/* + * BCLK AND MCLK FREQ + * BIT[7:4] MCLK FREQ + * 0 - 19.2MHz + * 1 - 24MHz + * 2 - 12.288MHz + * F - Default for 19.2MHz + * + * BIT[3:0] BCLK FREQ + * 0 - 4.8MHz + * 1 - 2.4MHz + * 2 - 2.304MHz + * 3 - 3.072MHz + * 4 - 4.096MHz + * F - Default for 4.8MHz + */ + +int es83xx_dsm(struct device *dev, int arg, int *value); +int es83xx_dsm_dump(struct device *dev); + +#endif diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c index 5371ff0862..7bd7ddcd81 100644 --- a/sound/soc/codecs/hda-dai.c +++ b/sound/soc/codecs/hda-dai.c @@ -76,13 +76,16 @@ static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd struct hdac_stream *stream; struct hda_codec *codec; unsigned int format; + unsigned int bits; int ret; codec = dev_to_hda_codec(dai->dev); stream = substream->runtime->private_data; stream_info = snd_soc_dai_get_dma_data(dai, substream); - format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + + bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat, + stream_info->maxbps); + format = snd_hdac_stream_format(runtime->channels, bits, runtime->rate); ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream); if (ret < 0) { diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index d57b043d6b..d2117e36dd 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -52,6 +52,7 @@ static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count, stream->channels_max = pcm->stream[dir].channels_max; stream->rates = pcm->stream[dir].rates; stream->formats = pcm->stream[dir].formats; + stream->subformats = pcm->stream[dir].subformats; stream->sig_bits = pcm->stream[dir].maxbps; capture_dais: @@ -71,6 +72,7 @@ capture_dais: stream->channels_max = pcm->stream[dir].channels_max; stream->rates = pcm->stream[dir].rates; stream->formats = pcm->stream[dir].formats; + stream->subformats = pcm->stream[dir].subformats; stream->sig_bits = pcm->stream[dir].maxbps; } diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index b075689db2..6aa3223985 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -218,18 +218,16 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct hdac_hda_priv *hda_pvt; unsigned int format_val; unsigned int maxbps; + unsigned int bits; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) maxbps = dai->driver->playback.sig_bits; else maxbps = dai->driver->capture.sig_bits; + bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps); hda_pvt = snd_soc_component_get_drvdata(component); - format_val = snd_hdac_calc_stream_format(params_rate(params), - params_channels(params), - params_format(params), - maxbps, - 0); + format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params)); if (!format_val) { dev_err(dai->dev, "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n", diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index b9c5ffbfb5..e1a7f0b0c0 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -16,6 +16,7 @@ #include <linux/pm_runtime.h> #include <linux/hdmi.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <sound/pcm_params.h> #include <sound/jack.h> #include <sound/soc.h> @@ -468,13 +469,14 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; + unsigned int bits; int format; dai_map = &hdmi->dai_map[dai->id]; - format = snd_hdac_calc_stream_format(params_rate(hparams), - params_channels(hparams), params_format(hparams), - dai->driver->playback.sig_bits, 0); + bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD, + dai->driver->playback.sig_bits); + format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams)); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); if (!pcm) @@ -670,6 +672,7 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) err = snd_hdac_query_supported_pcm(hdev, cvt->nid, &cvt->params.rates, &cvt->params.formats, + NULL, &cvt->params.maxbps); if (err < 0) dev_err(&hdev->dev, @@ -1577,7 +1580,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, list_for_each_entry(cvt, &hdmi->cvt_list, head) { ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, - &rates, &formats, &bps); + &rates, &formats, NULL, &bps); if (ret) return ret; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 0938671700..d3abb7ce21 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -17,6 +17,7 @@ #include <sound/pcm_iec958.h> #include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ +#include <drm/drm_eld.h> #define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index b9f19fbd29..b24d6472ad 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -3884,7 +3884,7 @@ static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena return madera_set_fll_clks_reg(fll, ena, base + MADERA_FLL_CONTROL_6_OFFS, MADERA_FLL1_REFCLK_SRC_MASK, - MADERA_FLL1_REFCLK_DIV_SHIFT); + MADERA_FLL1_REFCLK_SRC_SHIFT); } static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena) diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index 47f000cd4d..97a5405947 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -169,6 +169,7 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *params = (void *)kcontrol->private_value; int i, reg, reg_val; u16 *val; + __be16 tmp; val = (u16 *)ucontrol->value.bytes.data; reg = NAU8810_REG_EQ1; @@ -177,8 +178,8 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol, /* conversion of 16-bit integers between native CPU format * and big endian format */ - reg_val = cpu_to_be16(reg_val); - memcpy(val + i, ®_val, sizeof(reg_val)); + tmp = cpu_to_be16(reg_val); + memcpy(val + i, &tmp, sizeof(tmp)); } return 0; @@ -201,6 +202,7 @@ static int nau8810_eq_put(struct snd_kcontrol *kcontrol, void *data; u16 *val, value; int i, reg, ret; + __be16 *tmp; data = kmemdup(ucontrol->value.bytes.data, params->max, GFP_KERNEL | GFP_DMA); @@ -213,7 +215,8 @@ static int nau8810_eq_put(struct snd_kcontrol *kcontrol, /* conversion of 16-bit integers between native CPU format * and big endian format */ - value = be16_to_cpu(*(val + i)); + tmp = (__be16 *)(val + i); + value = be16_to_cpup(tmp); ret = regmap_write(nau8810->regmap, reg + i, value); if (ret) { dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n", diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 6e1b6b2629..012e347e63 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1738,6 +1738,10 @@ static int nau8821_read_device_properties(struct device *dev, &nau8821->dmic_clk_threshold); if (ret) nau8821->dmic_clk_threshold = 3072000; + ret = device_property_read_u32(dev, "nuvoton,dmic-slew-rate", + &nau8821->dmic_slew_rate); + if (ret) + nau8821->dmic_slew_rate = 0; return 0; } @@ -1797,6 +1801,9 @@ static void nau8821_init_regs(struct nau8821 *nau8821) NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64); regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64); + regmap_update_bits(regmap, NAU8821_R13_DMIC_CTRL, + NAU8821_DMIC_SLEW_MASK, nau8821->dmic_slew_rate << + NAU8821_DMIC_SLEW_SFT); if (nau8821->left_input_single_end) { regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE, NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN); diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h index 00a888ed07..62eaad130b 100644 --- a/sound/soc/codecs/nau8821.h +++ b/sound/soc/codecs/nau8821.h @@ -236,6 +236,8 @@ #define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT) #define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT) #define NAU8821_DMIC_EN_SFT 0 +#define NAU8821_DMIC_SLEW_SFT 8 +#define NAU8821_DMIC_SLEW_MASK (0x7 << NAU8821_DMIC_SLEW_SFT) /* GPIO12_CTRL (0x1a) */ #define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */ @@ -573,6 +575,7 @@ struct nau8821 { int jack_eject_debounce; int fs; int dmic_clk_threshold; + int dmic_slew_rate; int key_enable; }; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e0da151508..20191a4473 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -428,6 +428,9 @@ struct rt5645_platform_data { /* Invert HP detect status polarity */ bool inv_hp_pol; + /* Only 1 speaker connected */ + bool mono_speaker; + /* Value to assign to snd_soc_card.long_name */ const char *long_name; @@ -3661,6 +3664,7 @@ static const struct rt5645_platform_data buddy_platform_data = { static const struct rt5645_platform_data gpd_win_platform_data = { .jd_mode = 3, .inv_jd1_1 = true, + .mono_speaker = true, .long_name = "gpd-win-pocket-rt5645", /* The GPD pocket has a diff. mic, for the win this does not matter. */ .in2_diff = true, @@ -3684,6 +3688,16 @@ static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = { .in2_diff = true, }; +static const struct rt5645_platform_data jd_mode3_monospk_platform_data = { + .jd_mode = 3, + .mono_speaker = true, +}; + +static const struct rt5645_platform_data jd_mode3_inv_data = { + .jd_mode = 3, + .inv_jd1_1 = true, +}; + static const struct rt5645_platform_data jd_mode3_platform_data = { .jd_mode = 3, }; @@ -3803,7 +3817,7 @@ static const struct dmi_system_id dmi_platform_data[] = { DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"), }, - .driver_data = (void *)&jd_mode3_platform_data, + .driver_data = (void *)&jd_mode3_monospk_platform_data, }, { .ident = "Lenovo Ideapad Miix 310", @@ -3865,6 +3879,24 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&ecs_ef20_platform_data, }, + { + .ident = "Acer Switch V 10 (SW5-017)", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "Meegopad T08", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), + DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), + }, + .driver_data = (void *)&jd_mode3_inv_data, + }, { } }; @@ -3879,24 +3911,68 @@ static bool rt5645_check_dp(struct device *dev) return false; } -static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) +static void rt5645_parse_dt(struct device *dev, struct rt5645_platform_data *pdata) { - rt5645->pdata.in2_diff = device_property_read_bool(dev, - "realtek,in2-differential"); - device_property_read_u32(dev, - "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin); - device_property_read_u32(dev, - "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin); - device_property_read_u32(dev, - "realtek,jd-mode", &rt5645->pdata.jd_mode); + pdata->in2_diff = device_property_read_bool(dev, "realtek,in2-differential"); + device_property_read_u32(dev, "realtek,dmic1-data-pin", &pdata->dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic2-data-pin", &pdata->dmic2_data_pin); + device_property_read_u32(dev, "realtek,jd-mode", &pdata->jd_mode); +} - return 0; +static void rt5645_get_pdata(struct device *codec_dev, struct rt5645_platform_data *pdata) +{ + const struct dmi_system_id *dmi_data; + + dmi_data = dmi_first_match(dmi_platform_data); + if (dmi_data) { + dev_info(codec_dev, "Detected %s platform\n", dmi_data->ident); + *pdata = *((struct rt5645_platform_data *)dmi_data->driver_data); + } else if (rt5645_check_dp(codec_dev)) { + rt5645_parse_dt(codec_dev, pdata); + } else { + *pdata = jd_mode3_platform_data; + } + + if (quirk != -1) { + pdata->in2_diff = QUIRK_IN2_DIFF(quirk); + pdata->level_trigger_irq = QUIRK_LEVEL_IRQ(quirk); + pdata->inv_jd1_1 = QUIRK_INV_JD1_1(quirk); + pdata->inv_hp_pol = QUIRK_INV_HP_POL(quirk); + pdata->jd_mode = QUIRK_JD_MODE(quirk); + pdata->dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk); + pdata->dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk); + } } +const char *rt5645_components(struct device *codec_dev) +{ + struct rt5645_platform_data pdata = { }; + static char buf[32]; + const char *mic; + int spk = 2; + + rt5645_get_pdata(codec_dev, &pdata); + + if (pdata.mono_speaker) + spk = 1; + + if (pdata.dmic1_data_pin && pdata.dmic2_data_pin) + mic = "dmics12"; + else if (pdata.dmic1_data_pin) + mic = "dmic1"; + else if (pdata.dmic2_data_pin) + mic = "dmic2"; + else + mic = "in2"; + + snprintf(buf, sizeof(buf), "cfg-spk:%d cfg-mic:%s", spk, mic); + + return buf; +} +EXPORT_SYMBOL_GPL(rt5645_components); + static int rt5645_i2c_probe(struct i2c_client *i2c) { - struct rt5645_platform_data *pdata = NULL; - const struct dmi_system_id *dmi_data; struct rt5645_priv *rt5645; int ret, i; unsigned int val; @@ -3909,29 +3985,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); - - dmi_data = dmi_first_match(dmi_platform_data); - if (dmi_data) { - dev_info(&i2c->dev, "Detected %s platform\n", dmi_data->ident); - pdata = dmi_data->driver_data; - } - - if (pdata) - rt5645->pdata = *pdata; - else if (rt5645_check_dp(&i2c->dev)) - rt5645_parse_dt(rt5645, &i2c->dev); - else - rt5645->pdata = jd_mode3_platform_data; - - if (quirk != -1) { - rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk); - rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk); - rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk); - rt5645->pdata.inv_hp_pol = QUIRK_INV_HP_POL(quirk); - rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk); - rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk); - rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk); - } + rt5645_get_pdata(&i2c->dev, &rt5645->pdata); if (has_acpi_companion(&i2c->dev)) { if (cht_rt5645_gpios) { diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index ac3de6f3bc..90816b2c54 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2201,4 +2201,7 @@ int rt5645_sel_asrc_clk_src(struct snd_soc_component *component, int rt5645_set_jack_detect(struct snd_soc_component *component, struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, struct snd_soc_jack *btn_jack); + +const char *rt5645_components(struct device *codec_dev); + #endif /* __RT5645_H__ */ diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 77246f84de..9550492605 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -74,6 +74,7 @@ struct rt5663_priv { int pll_out; int jack_type; + unsigned int irq; }; static const struct reg_sequence rt5663_patch_list[] = { @@ -3186,6 +3187,12 @@ static int rt5663_suspend(struct snd_soc_component *component) { struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + if (rt5663->irq) + disable_irq(rt5663->irq); + + cancel_delayed_work_sync(&rt5663->jack_detect_work); + cancel_delayed_work_sync(&rt5663->jd_unplug_work); + regcache_cache_only(rt5663->regmap, true); regcache_mark_dirty(rt5663->regmap); @@ -3201,6 +3208,9 @@ static int rt5663_resume(struct snd_soc_component *component) rt5663_irq(0, rt5663); + if (rt5663->irq) + enable_irq(rt5663->irq); + return 0; } #else @@ -3686,6 +3696,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c) __func__, ret); goto err_enable; } + rt5663->irq = i2c->irq; } ret = devm_snd_soc_register_component(&i2c->dev, diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index e67c2e19cb..1fdbef5fd6 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -763,12 +763,12 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt5682->disable_irq_lock); if (rt5682->disable_irq == true) { - mutex_lock(&rt5682->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); rt5682->disable_irq = false; - mutex_unlock(&rt5682->disable_irq_lock); } + mutex_unlock(&rt5682->disable_irq_lock); goto regmap_sync; } diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index c261c33c4b..3322056bbb 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -2971,6 +2971,8 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) &rt5682s->pdata.dmic_delay); device_property_read_u32(dev, "realtek,amic-delay-ms", &rt5682s->pdata.amic_delay); + device_property_read_u32(dev, "realtek,ldo-sel", + &rt5682s->pdata.ldo_dacref); if (device_property_read_string_array(dev, "clock-output-names", rt5682s->pdata.dai_clk_names, @@ -3250,6 +3252,27 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) break; } + /* LDO output voltage control */ + switch (rt5682s->pdata.ldo_dacref) { + case RT5682S_LDO_1_607V: + break; + case RT5682S_LDO_1_5V: + regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7, + RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_5V); + break; + case RT5682S_LDO_1_406V: + regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7, + RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_406V); + break; + case RT5682S_LDO_1_731V: + regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7, + RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_731V); + break; + default: + dev_warn(&i2c->dev, "invalid LDO output setting.\n"); + break; + } + INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler); INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler); diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index 1d79d432d0..67f42898de 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -1263,6 +1263,13 @@ #define RT5682S_JDH_NO_PLUG (0x1 << 4) #define RT5682S_JDH_PLUG (0x0 << 4) +/* Bias current control 7 (0x0110) */ +#define RT5682S_LDO_DACREF_MASK (0x3 << 4) +#define RT5682S_LDO_DACREF_1_607V (0x0 << 4) +#define RT5682S_LDO_DACREF_1_5V (0x1 << 4) +#define RT5682S_LDO_DACREF_1_406V (0x2 << 4) +#define RT5682S_LDO_DACREF_1_731V (0x3 << 4) + /* Charge Pump Internal Register1 (0x0125) */ #define RT5682S_CP_CLK_HP_MASK (0x3 << 4) #define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4) diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 935e597022..b8471b2d8f 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -438,13 +438,13 @@ static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt711->disable_irq_lock); if (rt711->disable_irq == true) { - mutex_lock(&rt711->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt711->disable_irq = false; - mutex_unlock(&rt711->disable_irq_lock); } + mutex_unlock(&rt711->disable_irq_lock); goto regmap_sync; } diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 3f5773310a..988451f24a 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -536,12 +536,12 @@ static int __maybe_unused rt711_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt711->disable_irq_lock); if (rt711->disable_irq == true) { - mutex_lock(&rt711->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); rt711->disable_irq = false; - mutex_unlock(&rt711->disable_irq_lock); } + mutex_unlock(&rt711->disable_irq_lock); goto regmap_sync; } diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6b644a89c5..ba877432ce 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -438,13 +438,14 @@ static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt712->disable_irq_lock); if (rt712->disable_irq == true) { - mutex_lock(&rt712->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt712->disable_irq = false; - mutex_unlock(&rt712->disable_irq_lock); } + mutex_unlock(&rt712->disable_irq_lock); goto regmap_sync; } diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index a38ec58622..e33836fffd 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -256,6 +256,9 @@ static int rt722_sdca_read_prop(struct sdw_slave *slave) /* wake-up event */ prop->wake_capable = 1; + /* Three data lanes are supported by rt722-sdca codec */ + prop->lane_control_support = true; + return 0; } @@ -464,13 +467,13 @@ static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt722->disable_irq_lock); if (rt722->disable_irq == true) { - mutex_lock(&rt722->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_6); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt722->disable_irq = false; - mutex_unlock(&rt722->disable_irq_lock); } + mutex_unlock(&rt722->disable_irq_lock); goto regmap_sync; } diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 962c2cdfa0..54561ae598 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -59,7 +59,6 @@ struct tas2562_data { enum tas256x_model { TAS2562, - TAS2563, TAS2564, TAS2110, }; @@ -721,7 +720,6 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) static const struct i2c_device_id tas2562_id[] = { { "tas2562", TAS2562 }, - { "tas2563", TAS2563 }, { "tas2564", TAS2564 }, { "tas2110", TAS2110 }, { } @@ -770,7 +768,6 @@ static int tas2562_probe(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, - { .compatible = "ti,tas2563", }, { .compatible = "ti,tas2564", }, { .compatible = "ti,tas2110", }, { }, diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 2f7f8b18c3..b5abff230e 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // -// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // // Copyright (C) 2022 - 2023 Texas Instruments Incorporated // https://www.ti.com // -// The TAS2781 driver implements a flexible and configurable +// The TAS2563/TAS2781 driver implements a flexible and configurable // algo coefficient setting for one, two, or even multiple -// TAS2781 chips. +// TAS2563/TAS2781 chips. // // Author: Shenghao Ding <shenghao-ding@ti.com> // Author: Kevin Lu <kevin-lu@ti.com> @@ -32,6 +32,7 @@ #include <sound/tas2781-tlv.h> static const struct i2c_device_id tasdevice_id[] = { + { "tas2563", TAS2563 }, { "tas2781", TAS2781 }, {} }; @@ -39,6 +40,7 @@ MODULE_DEVICE_TABLE(i2c, tasdevice_id); #ifdef CONFIG_OF static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,tas2563" }, { .compatible = "ti,tas2781" }, {}, }; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 43c648efd0..deb15b9599 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -3033,7 +3033,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w, { struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); u16 gain_reg; - int offset_val = 0; int val = 0; switch (w->reg) { @@ -3073,7 +3072,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: val = snd_soc_component_read(comp, gain_reg); - val += offset_val; snd_soc_component_write(comp, gain_reg, val); break; case SND_SOC_DAPM_POST_PMD: @@ -3294,7 +3292,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w, u16 gain_reg; u16 reg; int val; - int offset_val = 0; if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT0 INTERP"))) { reg = WCD9335_CDC_RX0_RX_PATH_CTL; @@ -3337,7 +3334,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: wcd9335_config_compander(comp, w->shift, event); val = snd_soc_component_read(comp, gain_reg); - val += offset_val; snd_soc_component_write(comp, gain_reg, val); break; case SND_SOC_DAPM_POST_PMD: diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 1b6e376f38..6813268e6a 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -13,7 +13,6 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/slimbus.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 75834ed036..6021aa5a56 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3587,10 +3587,8 @@ static int wcd938x_probe(struct platform_device *pdev) mutex_init(&wcd938x->micb_lock); ret = wcd938x_populate_dt_data(wcd938x, dev); - if (ret) { - dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + if (ret) return ret; - } ret = wcd938x_add_slave_components(wcd938x, dev, &match); if (ret) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 1d4259433f..8f862729a2 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -18,7 +18,7 @@ #include <linux/firmware.h> #include <linux/delay.h> #include <linux/fs.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <linux/workqueue.h> @@ -94,8 +94,7 @@ struct wm0010_priv { struct wm0010_pdata pdata; - int gpio_reset; - int gpio_reset_value; + struct gpio_desc *reset; struct regulator_bulk_data core_supplies[2]; struct regulator *dbvdd; @@ -174,8 +173,7 @@ static void wm0010_halt(struct snd_soc_component *component) case WM0010_STAGE2: case WM0010_FIRMWARE: /* Remember to put chip back into reset */ - gpio_set_value_cansleep(wm0010->gpio_reset, - wm0010->gpio_reset_value); + gpiod_set_value_cansleep(wm0010->reset, 1); /* Disable the regulators */ regulator_disable(wm0010->dbvdd); regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), @@ -610,7 +608,7 @@ static int wm0010_boot(struct snd_soc_component *component) } /* Release reset */ - gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value); + gpiod_set_value_cansleep(wm0010->reset, 0); spin_lock_irqsave(&wm0010->irq_lock, flags); wm0010->state = WM0010_OUT_OF_RESET; spin_unlock_irqrestore(&wm0010->irq_lock, flags); @@ -863,7 +861,6 @@ static int wm0010_probe(struct snd_soc_component *component) static int wm0010_spi_probe(struct spi_device *spi) { - unsigned long gpio_flags; int ret; int trigger; int irq; @@ -903,31 +900,11 @@ static int wm0010_spi_probe(struct spi_device *spi) return ret; } - if (wm0010->pdata.gpio_reset) { - wm0010->gpio_reset = wm0010->pdata.gpio_reset; - - if (wm0010->pdata.reset_active_high) - wm0010->gpio_reset_value = 1; - else - wm0010->gpio_reset_value = 0; - - if (wm0010->gpio_reset_value) - gpio_flags = GPIOF_OUT_INIT_HIGH; - else - gpio_flags = GPIOF_OUT_INIT_LOW; - - ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset, - gpio_flags, "wm0010 reset"); - if (ret < 0) { - dev_err(wm0010->dev, - "Failed to request GPIO for DSP reset: %d\n", - ret); - return ret; - } - } else { - dev_err(wm0010->dev, "No reset GPIO configured\n"); - return -EINVAL; - } + wm0010->reset = devm_gpiod_get(wm0010->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(wm0010->reset)) + return dev_err_probe(wm0010->dev, PTR_ERR(wm0010->reset), + "could not get RESET GPIO\n"); + gpiod_set_consumer_name(wm0010->reset, "wm0010 reset"); wm0010->state = WM0010_POWER_OFF; @@ -972,8 +949,7 @@ static void wm0010_spi_remove(struct spi_device *spi) { struct wm0010_priv *wm0010 = spi_get_drvdata(spi); - gpio_set_value_cansleep(wm0010->gpio_reset, - wm0010->gpio_reset_value); + gpiod_set_value_cansleep(wm0010->reset, 1); irq_set_irq_wake(wm0010->irq, 0); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index d7eeb41ba6..9fa6df4879 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -9,34 +9,23 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <sound/soc.h> #include <sound/soc-dapm.h> -#include <sound/wm1250-ev1.h> - -static const char *wm1250_gpio_names[WM1250_EV1_NUM_GPIOS] = { - "WM1250 CLK_ENA", - "WM1250 CLK_SEL0", - "WM1250 CLK_SEL1", - "WM1250 OSR", - "WM1250 MASTER", -}; struct wm1250_priv { - struct gpio gpios[WM1250_EV1_NUM_GPIOS]; + struct gpio_desc *clk_ena; + struct gpio_desc *clk_sel0; + struct gpio_desc *clk_sel1; + struct gpio_desc *osr; + struct gpio_desc *master; }; static int wm1250_ev1_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct wm1250_priv *wm1250 = dev_get_drvdata(component->dev); - int ena; - - if (wm1250) - ena = wm1250->gpios[WM1250_EV1_GPIO_CLK_ENA].gpio; - else - ena = -1; switch (level) { case SND_SOC_BIAS_ON: @@ -46,13 +35,11 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: - if (ena >= 0) - gpio_set_value_cansleep(ena, 1); + gpiod_set_value_cansleep(wm1250->clk_ena, 1); break; case SND_SOC_BIAS_OFF: - if (ena >= 0) - gpio_set_value_cansleep(ena, 0); + gpiod_set_value_cansleep(wm1250->clk_ena, 0); break; } @@ -80,28 +67,20 @@ static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, - 1); - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, - 1); + gpiod_set_value(wm1250->clk_sel0, 1); + gpiod_set_value(wm1250->clk_sel1, 1); break; case 16000: - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, - 0); - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, - 1); + gpiod_set_value(wm1250->clk_sel0, 0); + gpiod_set_value(wm1250->clk_sel1, 1); break; case 32000: - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, - 1); - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, - 0); + gpiod_set_value(wm1250->clk_sel0, 1); + gpiod_set_value(wm1250->clk_sel1, 0); break; case 64000: - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, - 0); - gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, - 0); + gpiod_set_value(wm1250->clk_sel0, 0); + gpiod_set_value(wm1250->clk_sel1, 0); break; default: return -EINVAL; @@ -150,45 +129,42 @@ static int wm1250_ev1_pdata(struct i2c_client *i2c) { struct wm1250_ev1_pdata *pdata = dev_get_platdata(&i2c->dev); struct wm1250_priv *wm1250; - int i, ret; if (!pdata) return 0; wm1250 = devm_kzalloc(&i2c->dev, sizeof(*wm1250), GFP_KERNEL); - if (!wm1250) { - ret = -ENOMEM; - goto err; - } - - for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) { - wm1250->gpios[i].gpio = pdata->gpios[i]; - wm1250->gpios[i].label = wm1250_gpio_names[i]; - wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW; - } - wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].flags = GPIOF_OUT_INIT_HIGH; - wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].flags = GPIOF_OUT_INIT_HIGH; - - ret = gpio_request_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios)); - if (ret != 0) { - dev_err(&i2c->dev, "Failed to get GPIOs: %d\n", ret); - goto err; - } + if (!wm1250) + return -ENOMEM; + + wm1250->clk_ena = devm_gpiod_get(&i2c->dev, "clk-ena", GPIOD_OUT_LOW); + if (IS_ERR(wm1250->clk_ena)) + return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_ena), + "failed to get clock enable GPIO\n"); + + wm1250->clk_sel0 = devm_gpiod_get(&i2c->dev, "clk-sel0", GPIOD_OUT_HIGH); + if (IS_ERR(wm1250->clk_sel0)) + return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel0), + "failed to get clock sel0 GPIO\n"); + + wm1250->clk_sel1 = devm_gpiod_get(&i2c->dev, "clk-sel1", GPIOD_OUT_HIGH); + if (IS_ERR(wm1250->clk_sel1)) + return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel1), + "failed to get clock sel1 GPIO\n"); + + wm1250->osr = devm_gpiod_get(&i2c->dev, "osr", GPIOD_OUT_LOW); + if (IS_ERR(wm1250->osr)) + return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->osr), + "failed to get OSR GPIO\n"); + + wm1250->master = devm_gpiod_get(&i2c->dev, "master", GPIOD_OUT_LOW); + if (IS_ERR(wm1250->master)) + return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->master), + "failed to get MASTER GPIO\n"); dev_set_drvdata(&i2c->dev, wm1250); - return ret; - -err: - return ret; -} - -static void wm1250_ev1_free(struct i2c_client *i2c) -{ - struct wm1250_priv *wm1250 = dev_get_drvdata(&i2c->dev); - - if (wm1250) - gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios)); + return 0; } static int wm1250_ev1_probe(struct i2c_client *i2c) @@ -221,18 +197,12 @@ static int wm1250_ev1_probe(struct i2c_client *i2c) &wm1250_ev1_dai, 1); if (ret != 0) { dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); - wm1250_ev1_free(i2c); return ret; } return 0; } -static void wm1250_ev1_remove(struct i2c_client *i2c) -{ - wm1250_ev1_free(i2c); -} - static const struct i2c_device_id wm1250_ev1_i2c_id[] = { { "wm1250-ev1", 0 }, { } @@ -244,7 +214,6 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .name = "wm1250-ev1", }, .probe = wm1250_ev1_probe, - .remove = wm1250_ev1_remove, .id_table = wm1250_ev1_i2c_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 9679906c6b..69c9c2bd7e 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -14,7 +14,7 @@ #include <linux/pm.h> #include <linux/firmware.h> #include <linux/gcd.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -79,6 +79,8 @@ struct wm2200_priv { struct snd_soc_component *component; struct wm2200_pdata pdata; struct regulator_bulk_data core_supplies[WM2200_NUM_CORE_SUPPLIES]; + struct gpio_desc *ldo_ena; + struct gpio_desc *reset; struct completion fll_lock; int fll_fout; @@ -975,9 +977,10 @@ static const struct reg_sequence wm2200_reva_patch[] = { static int wm2200_reset(struct wm2200_priv *wm2200) { - if (wm2200->pdata.reset) { - gpio_set_value_cansleep(wm2200->pdata.reset, 0); - gpio_set_value_cansleep(wm2200->pdata.reset, 1); + if (wm2200->reset) { + /* Descriptor flagged active low, so this will be inverted */ + gpiod_set_value_cansleep(wm2200->reset, 1); + gpiod_set_value_cansleep(wm2200->reset, 0); return 0; } else { @@ -2246,28 +2249,28 @@ static int wm2200_i2c_probe(struct i2c_client *i2c) return ret; } - if (wm2200->pdata.ldo_ena) { - ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena, - GPIOF_OUT_INIT_HIGH, - "WM2200 LDOENA"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n", - wm2200->pdata.ldo_ena, ret); - goto err_enable; - } + wm2200->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena", + GPIOD_OUT_HIGH); + if (IS_ERR(wm2200->ldo_ena)) { + ret = PTR_ERR(wm2200->ldo_ena); + dev_err(&i2c->dev, "Failed to request LDOENA GPIO %d\n", + ret); + goto err_enable; + } + if (wm2200->ldo_ena) { + gpiod_set_consumer_name(wm2200->ldo_ena, "WM2200 LDOENA"); msleep(2); } - if (wm2200->pdata.reset) { - ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset, - GPIOF_OUT_INIT_HIGH, - "WM2200 /RESET"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n", - wm2200->pdata.reset, ret); - goto err_ldo; - } + wm2200->reset = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(wm2200->reset)) { + ret = PTR_ERR(wm2200->reset); + dev_err(&i2c->dev, "Failed to request RESET GPIO %d\n", + ret); + goto err_ldo; } + gpiod_set_consumer_name(wm2200->reset, "WM2200 /RESET"); ret = regmap_read(wm2200->regmap, WM2200_SOFTWARE_RESET, ®); if (ret < 0) { @@ -2403,11 +2406,9 @@ err_pm_runtime: if (i2c->irq) free_irq(i2c->irq, wm2200); err_reset: - if (wm2200->pdata.reset) - gpio_set_value_cansleep(wm2200->pdata.reset, 0); + gpiod_set_value_cansleep(wm2200->reset, 1); err_ldo: - if (wm2200->pdata.ldo_ena) - gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + gpiod_set_value_cansleep(wm2200->ldo_ena, 0); err_enable: regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), wm2200->core_supplies); @@ -2421,10 +2422,9 @@ static void wm2200_i2c_remove(struct i2c_client *i2c) pm_runtime_disable(&i2c->dev); if (i2c->irq) free_irq(i2c->irq, wm2200); - if (wm2200->pdata.reset) - gpio_set_value_cansleep(wm2200->pdata.reset, 0); - if (wm2200->pdata.ldo_ena) - gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + /* Assert RESET, disable LDO */ + gpiod_set_value_cansleep(wm2200->reset, 1); + gpiod_set_value_cansleep(wm2200->ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), wm2200->core_supplies); } @@ -2436,8 +2436,7 @@ static int wm2200_runtime_suspend(struct device *dev) regcache_cache_only(wm2200->regmap, true); regcache_mark_dirty(wm2200->regmap); - if (wm2200->pdata.ldo_ena) - gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + gpiod_set_value_cansleep(wm2200->ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), wm2200->core_supplies); @@ -2457,8 +2456,8 @@ static int wm2200_runtime_resume(struct device *dev) return ret; } - if (wm2200->pdata.ldo_ena) { - gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 1); + if (wm2200->ldo_ena) { + gpiod_set_value_cansleep(wm2200->ldo_ena, 1); msleep(2); } diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index ff63723928..7ee4b45c08 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -15,7 +15,7 @@ #include <linux/pm.h> #include <linux/gcd.h> #include <linux/gpio/driver.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -55,6 +55,9 @@ struct wm5100_priv { struct snd_soc_component *component; struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES]; + struct gpio_desc *reset; + struct gpio_desc *ldo_ena; + struct gpio_desc *hp_pol; int rev; @@ -205,9 +208,9 @@ static void wm5100_free_sr(struct snd_soc_component *component, int rate) static int wm5100_reset(struct wm5100_priv *wm5100) { - if (wm5100->pdata.reset) { - gpio_set_value_cansleep(wm5100->pdata.reset, 0); - gpio_set_value_cansleep(wm5100->pdata.reset, 1); + if (wm5100->reset) { + gpiod_set_value_cansleep(wm5100->reset, 1); + gpiod_set_value_cansleep(wm5100->reset, 0); return 0; } else { @@ -1974,7 +1977,7 @@ static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode) if (WARN_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes))) return; - gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol); + gpiod_set_value_cansleep(wm5100->hp_pol, mode->hp_pol); regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1, WM5100_ACCDET_BIAS_SRC_MASK | WM5100_ACCDET_SRC, @@ -2299,11 +2302,7 @@ static void wm5100_init_gpio(struct i2c_client *i2c) wm5100->gpio_chip = wm5100_template_chip; wm5100->gpio_chip.ngpio = 6; wm5100->gpio_chip.parent = &i2c->dev; - - if (wm5100->pdata.gpio_base) - wm5100->gpio_chip.base = wm5100->pdata.gpio_base; - else - wm5100->gpio_chip.base = -1; + wm5100->gpio_chip.base = -1; ret = gpiochip_add_data(&wm5100->gpio_chip, wm5100); if (ret != 0) @@ -2349,35 +2348,20 @@ static int wm5100_probe(struct snd_soc_component *component) snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq, ARRAY_SIZE(wm5100_dapm_widgets_noirq)); - if (wm5100->pdata.hp_pol) { - ret = gpio_request_one(wm5100->pdata.hp_pol, - GPIOF_OUT_INIT_HIGH, "WM5100 HP_POL"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request HP_POL %d: %d\n", - wm5100->pdata.hp_pol, ret); - goto err_gpio; - } + wm5100->hp_pol = devm_gpiod_get_optional(&i2c->dev, "hp-pol", + GPIOD_OUT_HIGH); + if (IS_ERR(wm5100->hp_pol)) { + ret = PTR_ERR(wm5100->hp_pol); + dev_err(&i2c->dev, "Failed to request HP_POL GPIO: %d\n", + ret); + return ret; } return 0; - -err_gpio: - - return ret; -} - -static void wm5100_remove(struct snd_soc_component *component) -{ - struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); - - if (wm5100->pdata.hp_pol) { - gpio_free(wm5100->pdata.hp_pol); - } } static const struct snd_soc_component_driver soc_component_dev_wm5100 = { .probe = wm5100_probe, - .remove = wm5100_remove, .set_sysclk = wm5100_set_sysclk, .set_pll = wm5100_set_fll, .seq_notifier = wm5100_seq_notifier, @@ -2460,26 +2444,26 @@ static int wm5100_i2c_probe(struct i2c_client *i2c) goto err; } - if (wm5100->pdata.ldo_ena) { - ret = gpio_request_one(wm5100->pdata.ldo_ena, - GPIOF_OUT_INIT_HIGH, "WM5100 LDOENA"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n", - wm5100->pdata.ldo_ena, ret); - goto err_enable; - } + wm5100->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena", + GPIOD_OUT_HIGH); + if (IS_ERR(wm5100->ldo_ena)) { + ret = PTR_ERR(wm5100->ldo_ena); + dev_err(&i2c->dev, "Failed to request LDOENA GPIO: %d\n", ret); + goto err_enable; + } + if (wm5100->ldo_ena) { + gpiod_set_consumer_name(wm5100->ldo_ena, "WM5100 LDOENA"); msleep(2); } - if (wm5100->pdata.reset) { - ret = gpio_request_one(wm5100->pdata.reset, - GPIOF_OUT_INIT_HIGH, "WM5100 /RESET"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n", - wm5100->pdata.reset, ret); - goto err_ldo; - } + wm5100->reset = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(wm5100->reset)) { + ret = PTR_ERR(wm5100->reset); + dev_err(&i2c->dev, "Failed to request /RESET GPIO: %d\n", ret); + goto err_ldo; } + gpiod_set_consumer_name(wm5100->reset, "WM5100 /RESET"); ret = regmap_read(wm5100->regmap, WM5100_SOFTWARE_RESET, ®); if (ret < 0) { @@ -2619,15 +2603,9 @@ err_reset: if (i2c->irq) free_irq(i2c->irq, wm5100); wm5100_free_gpio(i2c); - if (wm5100->pdata.reset) { - gpio_set_value_cansleep(wm5100->pdata.reset, 0); - gpio_free(wm5100->pdata.reset); - } + gpiod_set_value_cansleep(wm5100->reset, 1); err_ldo: - if (wm5100->pdata.ldo_ena) { - gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); - gpio_free(wm5100->pdata.ldo_ena); - } + gpiod_set_value_cansleep(wm5100->ldo_ena, 0); err_enable: regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); @@ -2643,14 +2621,8 @@ static void wm5100_i2c_remove(struct i2c_client *i2c) if (i2c->irq) free_irq(i2c->irq, wm5100); wm5100_free_gpio(i2c); - if (wm5100->pdata.reset) { - gpio_set_value_cansleep(wm5100->pdata.reset, 0); - gpio_free(wm5100->pdata.reset); - } - if (wm5100->pdata.ldo_ena) { - gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); - gpio_free(wm5100->pdata.ldo_ena); - } + gpiod_set_value_cansleep(wm5100->reset, 1); + gpiod_set_value_cansleep(wm5100->ldo_ena, 0); } #ifdef CONFIG_PM @@ -2660,8 +2632,7 @@ static int wm5100_runtime_suspend(struct device *dev) regcache_cache_only(wm5100->regmap, true); regcache_mark_dirty(wm5100->regmap); - if (wm5100->pdata.ldo_ena) - gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); + gpiod_set_value_cansleep(wm5100->ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); @@ -2681,8 +2652,8 @@ static int wm5100_runtime_resume(struct device *dev) return ret; } - if (wm5100->pdata.ldo_ena) { - gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1); + if (wm5100->ldo_ena) { + gpiod_set_value_cansleep(wm5100->ldo_ena, 1); msleep(2); } diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index df6195778c..e738326e33 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -14,7 +14,7 @@ #include <linux/pm.h> #include <linux/gcd.h> #include <linux/gpio/driver.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -51,7 +51,7 @@ struct wm8996_priv { struct regmap *regmap; struct snd_soc_component *component; - int ldo1ena; + struct gpio_desc *ldo_ena; int sysclk; int sysclk_src; @@ -1596,9 +1596,9 @@ static int wm8996_set_bias_level(struct snd_soc_component *component, return ret; } - if (wm8996->pdata.ldo_ena >= 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, - 1); + if (wm8996->ldo_ena) { + gpiod_set_value_cansleep(wm8996->ldo_ena, + 1); msleep(5); } @@ -1615,8 +1615,8 @@ static int wm8996_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_OFF: regcache_cache_only(wm8996->regmap, true); - if (wm8996->pdata.ldo_ena >= 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + if (wm8996->ldo_ena) { + gpiod_set_value_cansleep(wm8996->ldo_ena, 0); regcache_cache_only(wm8996->regmap, true); } regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), @@ -2188,6 +2188,8 @@ static const struct gpio_chip wm8996_template_chip = { .direction_input = wm8996_gpio_direction_in, .get = wm8996_gpio_get, .can_sleep = 1, + .ngpio = 5, + .base = -1, }; static void wm8996_init_gpio(struct wm8996_priv *wm8996) @@ -2195,14 +2197,8 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996) int ret; wm8996->gpio_chip = wm8996_template_chip; - wm8996->gpio_chip.ngpio = 5; wm8996->gpio_chip.parent = wm8996->dev; - if (wm8996->pdata.gpio_base) - wm8996->gpio_chip.base = wm8996->pdata.gpio_base; - else - wm8996->gpio_chip.base = -1; - ret = gpiochip_add_data(&wm8996->gpio_chip, wm8996); if (ret != 0) dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret); @@ -2771,15 +2767,15 @@ static int wm8996_i2c_probe(struct i2c_client *i2c) memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev), sizeof(wm8996->pdata)); - if (wm8996->pdata.ldo_ena > 0) { - ret = gpio_request_one(wm8996->pdata.ldo_ena, - GPIOF_OUT_INIT_LOW, "WM8996 ENA"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n", - wm8996->pdata.ldo_ena, ret); - goto err; - } + wm8996->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena", + GPIOD_OUT_LOW); + if (IS_ERR(wm8996->ldo_ena)) { + ret = PTR_ERR(wm8996->ldo_ena); + dev_err(&i2c->dev, "Failed to request LDO ENA GPIO: %d\n", + ret); + goto err; } + gpiod_set_consumer_name(wm8996->ldo_ena, "WM8996 ENA"); for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) wm8996->supplies[i].supply = wm8996_supply_names[i]; @@ -2814,8 +2810,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c) goto err_gpio; } - if (wm8996->pdata.ldo_ena > 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1); + if (wm8996->ldo_ena) { + gpiod_set_value_cansleep(wm8996->ldo_ena, 1); msleep(5); } @@ -2847,8 +2843,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c) dev_info(&i2c->dev, "revision %c\n", (reg & WM8996_CHIP_REV_MASK) + 'A'); - if (wm8996->pdata.ldo_ena > 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + if (wm8996->ldo_ena) { + gpiod_set_value_cansleep(wm8996->ldo_ena, 0); regcache_cache_only(wm8996->regmap, true); } else { ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET, @@ -3054,12 +3050,10 @@ err_gpiolib: wm8996_free_gpio(wm8996); err_regmap: err_enable: - if (wm8996->pdata.ldo_ena > 0) - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + if (wm8996->ldo_ena) + gpiod_set_value_cansleep(wm8996->ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); err_gpio: - if (wm8996->pdata.ldo_ena > 0) - gpio_free(wm8996->pdata.ldo_ena); err: return ret; @@ -3070,10 +3064,8 @@ static void wm8996_i2c_remove(struct i2c_client *client) struct wm8996_priv *wm8996 = i2c_get_clientdata(client); wm8996_free_gpio(wm8996); - if (wm8996->pdata.ldo_ena > 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); - gpio_free(wm8996->pdata.ldo_ena); - } + if (wm8996->ldo_ena) + gpiod_set_value_cansleep(wm8996->ldo_ena, 0); } static const struct i2c_device_id wm8996_i2c_id[] = { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 9c0accf5e1..9cb9068c04 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -683,11 +683,12 @@ static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { - struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + struct cs_dsp_coeff_ctl *cs_ctl; struct wm_coeff_ctl *ctl; int ret; mutex_lock(&dsp->cs_dsp.pwr_lock); + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); mutex_unlock(&dsp->cs_dsp.pwr_lock); @@ -829,6 +830,23 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, } } + /* Check system-specific bin without wmfw before falling back to generic */ + if (dsp->wmfw_optional && system_name) { + if (asoc_component_prefix) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + asoc_component_prefix, "bin"); + + if (!*coeff_firmware) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + NULL, "bin"); + + if (*coeff_firmware) + return 0; + } + + /* Check legacy location */ if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "", NULL, NULL, "wmfw")) { wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, @@ -836,38 +854,15 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, return 0; } + /* Fall back to generic wmfw and optional matching bin */ ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, cirrus_dir, NULL, NULL, "wmfw"); - if (!ret) { + if (!ret || dsp->wmfw_optional) { wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, cirrus_dir, NULL, NULL, "bin"); return 0; } - if (dsp->wmfw_optional) { - if (system_name) { - if (asoc_component_prefix) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - asoc_component_prefix, "bin"); - - if (!*coeff_firmware) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, system_name, - NULL, "bin"); - } - - if (!*coeff_firmware) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - "", NULL, NULL, "bin"); - - if (!*coeff_firmware) - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, - cirrus_dir, NULL, NULL, "bin"); - - return 0; - } - adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n", cirrus_dir, dsp->part, dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name, diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 993d76b18b..f2653df84e 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1654,15 +1654,9 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, 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); @@ -1786,6 +1780,7 @@ static const struct snd_soc_dai_ops wsa884x_dai_ops = { .hw_free = wsa884x_hw_free, .mute_stream = wsa884x_mute_stream, .set_stream = wsa884x_set_stream, + .mute_unmute_on_trigger = true, }; static struct snd_soc_dai_driver wsa884x_dais[] = { diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index d14061e88e..270726c134 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -184,19 +184,6 @@ config SND_SOC_POWERPC_QMC_AUDIO comment "SoC Audio support for Freescale PPC boards:" -config SND_SOC_MPC8610_HPCD - tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" - # I2C is necessary for the CS4270 driver - depends on MPC8610_HPCD && I2C - select SND_SOC_FSL_SSI - select SND_SOC_FSL_UTILS - select SND_SOC_POWERPC_DMA - select SND_SOC_CS4270 - select SND_SOC_CS4270_VD33_ERRATA - default y if MPC8610_HPCD - help - Say Y if you want to enable audio on the Freescale MPC8610 HPCD. - config SND_SOC_P1022_DS tristate "ALSA SoC support for the Freescale P1022 DS board" # I2C is necessary for the WM8776 driver diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db7e97d0b..b45eda80c1 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -1,8 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -# MPC8610 HPCD Machine Support -snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o -obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o - # P1022 DS Machine Support snd-soc-p1022-ds-objs := p1022_ds.o obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 7518ab9d76..bc07f26ba3 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -305,8 +305,7 @@ SND_SOC_DAILINK_DEFS(hifi_fe, SND_SOC_DAILINK_DEFS(hifi_be, DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_DUMMY())); + DAILINK_COMP_ARRAY(COMP_EMPTY())); static const struct snd_soc_dai_link fsl_asoc_card_dai[] = { /* Default ASoC DAI Link*/ diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index f2d74ec05c..60929c36a0 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -10,9 +10,7 @@ #include <linux/moduleparam.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> -#include <linux/of.h> #include <linux/pm_runtime.h> -#include <linux/of.h> #include <linux/pm.h> #include <linux/slab.h> #include <sound/soc.h> diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 56d6b0b039..bfaaa45173 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -99,7 +99,7 @@ static void qmc_audio_pcm_write_complete(void *context) snd_pcm_period_elapsed(prtd->substream); } -static void qmc_audio_pcm_read_complete(void *context, size_t length) +static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) { struct qmc_dai_prtd *prtd = context; int ret; diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index a0c5c35817..e5bd63dab1 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -2,9 +2,8 @@ // Copyright 2017-2020 NXP #include <linux/module.h> -#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_reserved_mem.h> -#include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/of_gpio.h> #include <linux/slab.h> @@ -21,8 +20,11 @@ struct imx_rpmsg { struct snd_soc_dai_link dai; struct snd_soc_card card; unsigned long sysclk; + bool lpa; }; +static struct dev_pm_ops lpa_pm; + static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), @@ -39,6 +41,58 @@ static int imx_rpmsg_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; + if (data->lpa) { + struct snd_soc_component *codec_comp; + struct device_node *codec_np; + struct device_driver *codec_drv; + struct device *codec_dev = NULL; + + codec_np = data->dai.codecs->of_node; + if (codec_np) { + struct platform_device *codec_pdev; + struct i2c_client *codec_i2c; + + codec_i2c = of_find_i2c_device_by_node(codec_np); + if (codec_i2c) + codec_dev = &codec_i2c->dev; + if (!codec_dev) { + codec_pdev = of_find_device_by_node(codec_np); + if (codec_pdev) + codec_dev = &codec_pdev->dev; + } + } + if (codec_dev) { + codec_comp = snd_soc_lookup_component_nolocked(codec_dev, NULL); + if (codec_comp) { + int i, num_widgets; + const char *widgets; + struct snd_soc_dapm_context *dapm; + + num_widgets = of_property_count_strings(data->card.dev->of_node, + "ignore-suspend-widgets"); + for (i = 0; i < num_widgets; i++) { + of_property_read_string_index(data->card.dev->of_node, + "ignore-suspend-widgets", + i, &widgets); + dapm = snd_soc_component_get_dapm(codec_comp); + snd_soc_dapm_ignore_suspend(dapm, widgets); + } + } + codec_drv = codec_dev->driver; + if (codec_drv->pm) { + memcpy(&lpa_pm, codec_drv->pm, sizeof(lpa_pm)); + lpa_pm.suspend = NULL; + lpa_pm.resume = NULL; + lpa_pm.freeze = NULL; + lpa_pm.thaw = NULL; + lpa_pm.poweroff = NULL; + lpa_pm.restore = NULL; + codec_drv->pm = &lpa_pm; + } + put_device(codec_dev); + } + } + if (!data->sysclk) return 0; @@ -138,6 +192,9 @@ static int imx_rpmsg_probe(struct platform_device *pdev) goto fail; } + if (of_property_read_bool(np, "fsl,enable-lpa")) + data->lpa = true; + data->card.dev = &pdev->dev; data->card.owner = THIS_MODULE; data->card.dapm_widgets = imx_rpmsg_dapm_widgets; diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c deleted file mode 100644 index 52fb9e7bcc..0000000000 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ /dev/null @@ -1,451 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Freescale MPC8610HPCD ALSA SoC Machine driver -// -// Author: Timur Tabi <timur@freescale.com> -// -// Copyright 2007-2010 Freescale Semiconductor, Inc. - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/fsl/guts.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/slab.h> -#include <sound/soc.h> - -#include "fsl_dma.h" -#include "fsl_ssi.h" -#include "fsl_utils.h" - -/* There's only one global utilities register */ -static phys_addr_t guts_phys; - -/** - * mpc8610_hpcd_data: machine-specific ASoC device data - * - * This structure contains data for a single sound platform device on an - * MPC8610 HPCD. Some of the data is taken from the device tree. - */ -struct mpc8610_hpcd_data { - struct snd_soc_dai_link dai[2]; - struct snd_soc_card card; - unsigned int dai_format; - unsigned int codec_clk_direction; - unsigned int cpu_clk_direction; - unsigned int clk_frequency; - unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ - unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ - unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ - char codec_dai_name[DAI_NAME_SIZE]; - char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ -}; - -/** - * mpc8610_hpcd_machine_probe: initialize the board - * - * This function is used to initialize the board-specific hardware. - * - * Here we program the DMACR and PMUXCR registers. - */ -static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) -{ - struct mpc8610_hpcd_data *machine_data = - container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); - if (!guts) { - dev_err(card->dev, "could not map global utilities\n"); - return -ENOMEM; - } - - /* Program the signal routing between the SSI and the DMA */ - guts_set_dmacr(guts, machine_data->dma_id[0], - machine_data->dma_channel_id[0], - CCSR_GUTS_DMACR_DEV_SSI); - guts_set_dmacr(guts, machine_data->dma_id[1], - machine_data->dma_channel_id[1], - CCSR_GUTS_DMACR_DEV_SSI); - - guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], - machine_data->dma_channel_id[0], 0); - guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], - machine_data->dma_channel_id[1], 0); - - switch (machine_data->ssi_id) { - case 0: - clrsetbits_be32(&guts->pmuxcr, - CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); - break; - case 1: - clrsetbits_be32(&guts->pmuxcr, - CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); - break; - } - - iounmap(guts); - - return 0; -} - -/** - * mpc8610_hpcd_startup: program the board with various hardware parameters - * - * This function takes board-specific information, like clock frequencies - * and serial data formats, and passes that information to the codec and - * transport drivers. - */ -static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct mpc8610_hpcd_data *machine_data = - container_of(rtd->card, struct mpc8610_hpcd_data, card); - struct device *dev = rtd->card->dev; - int ret = 0; - - /* Tell the codec driver what the serial protocol is. */ - ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), machine_data->dai_format); - if (ret < 0) { - dev_err(dev, "could not set codec driver audio format\n"); - return ret; - } - - /* - * Tell the codec driver what the MCLK frequency is, and whether it's - * a slave or master. - */ - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), 0, - machine_data->clk_frequency, - machine_data->codec_clk_direction); - if (ret < 0) { - dev_err(dev, "could not set codec driver clock params\n"); - return ret; - } - - return 0; -} - -/** - * mpc8610_hpcd_machine_remove: Remove the sound device - * - * This function is called to remove the sound device for one SSI. We - * de-program the DMACR and PMUXCR register. - */ -static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) -{ - struct mpc8610_hpcd_data *machine_data = - container_of(card, struct mpc8610_hpcd_data, card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); - if (!guts) { - dev_err(card->dev, "could not map global utilities\n"); - return -ENOMEM; - } - - /* Restore the signal routing */ - - guts_set_dmacr(guts, machine_data->dma_id[0], - machine_data->dma_channel_id[0], 0); - guts_set_dmacr(guts, machine_data->dma_id[1], - machine_data->dma_channel_id[1], 0); - - switch (machine_data->ssi_id) { - case 0: - clrsetbits_be32(&guts->pmuxcr, - CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); - break; - case 1: - clrsetbits_be32(&guts->pmuxcr, - CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); - break; - } - - iounmap(guts); - - return 0; -} - -/** - * mpc8610_hpcd_ops: ASoC machine driver operations - */ -static const struct snd_soc_ops mpc8610_hpcd_ops = { - .startup = mpc8610_hpcd_startup, -}; - -/** - * mpc8610_hpcd_probe: platform probe function for the machine driver - * - * Although this is a machine driver, the SSI node is the "master" node with - * respect to audio hardware connections. Therefore, we create a new ASoC - * device for each new SSI node that has a codec attached. - */ -static int mpc8610_hpcd_probe(struct platform_device *pdev) -{ - struct device *dev = pdev->dev.parent; - /* ssi_pdev is the platform device for the SSI node that probed us */ - struct platform_device *ssi_pdev = to_platform_device(dev); - struct device_node *np = ssi_pdev->dev.of_node; - struct device_node *codec_np = NULL; - struct mpc8610_hpcd_data *machine_data; - struct snd_soc_dai_link_component *comp; - int ret; - const char *sprop; - const u32 *iprop; - - /* Find the codec node for this SSI. */ - codec_np = of_parse_phandle(np, "codec-handle", 0); - if (!codec_np) { - dev_err(dev, "invalid codec node\n"); - return -EINVAL; - } - - machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); - if (!machine_data) { - ret = -ENOMEM; - goto error_alloc; - } - - comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL); - if (!comp) { - ret = -ENOMEM; - goto error_alloc; - } - - machine_data->dai[0].cpus = &comp[0]; - machine_data->dai[0].codecs = &comp[1]; - machine_data->dai[0].platforms = &comp[2]; - - machine_data->dai[0].num_cpus = 1; - machine_data->dai[0].num_codecs = 1; - machine_data->dai[0].num_platforms = 1; - - machine_data->dai[1].cpus = &comp[3]; - machine_data->dai[1].codecs = &comp[4]; - machine_data->dai[1].platforms = &comp[5]; - - machine_data->dai[1].num_cpus = 1; - machine_data->dai[1].num_codecs = 1; - machine_data->dai[1].num_platforms = 1; - - machine_data->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev); - machine_data->dai[0].ops = &mpc8610_hpcd_ops; - - /* ASoC core can match codec with device node */ - machine_data->dai[0].codecs->of_node = codec_np; - - /* The DAI name from the codec (snd_soc_dai_driver.name) */ - machine_data->dai[0].codecs->dai_name = "cs4270-hifi"; - - /* We register two DAIs per SSI, one for playback and the other for - * capture. Currently, we only support codecs that have one DAI for - * both playback and capture. - */ - memcpy(&machine_data->dai[1], &machine_data->dai[0], - sizeof(struct snd_soc_dai_link)); - - /* Get the device ID */ - iprop = of_get_property(np, "cell-index", NULL); - if (!iprop) { - dev_err(&pdev->dev, "cell-index property not found\n"); - ret = -EINVAL; - goto error; - } - machine_data->ssi_id = be32_to_cpup(iprop); - - /* Get the serial format and clock direction. */ - sprop = of_get_property(np, "fsl,mode", NULL); - if (!sprop) { - dev_err(&pdev->dev, "fsl,mode property not found\n"); - ret = -EINVAL; - goto error; - } - - if (strcasecmp(sprop, "i2s-slave") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP; - machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; - - /* In i2s-slave mode, the codec has its own clock source, so we - * need to get the frequency from the device tree and pass it to - * the codec driver. - */ - iprop = of_get_property(codec_np, "clock-frequency", NULL); - if (!iprop || !*iprop) { - dev_err(&pdev->dev, "codec bus-frequency " - "property is missing or invalid\n"); - ret = -EINVAL; - goto error; - } - machine_data->clk_frequency = be32_to_cpup(iprop); - } else if (strcasecmp(sprop, "i2s-master") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC; - machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; - } else if (strcasecmp(sprop, "lj-slave") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP; - machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; - } else if (strcasecmp(sprop, "lj-master") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC; - machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; - } else if (strcasecmp(sprop, "rj-slave") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP; - machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; - } else if (strcasecmp(sprop, "rj-master") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC; - machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; - } else if (strcasecmp(sprop, "ac97-slave") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP; - machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; - } else if (strcasecmp(sprop, "ac97-master") == 0) { - machine_data->dai_format = - SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC; - machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; - machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; - } else { - dev_err(&pdev->dev, - "unrecognized fsl,mode property '%s'\n", sprop); - ret = -EINVAL; - goto error; - } - - if (!machine_data->clk_frequency) { - dev_err(&pdev->dev, "unknown clock frequency\n"); - ret = -EINVAL; - goto error; - } - - /* Find the playback DMA channel to use. */ - machine_data->dai[0].platforms->name = machine_data->platform_name[0]; - ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", - &machine_data->dai[0], - &machine_data->dma_channel_id[0], - &machine_data->dma_id[0]); - if (ret) { - dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); - goto error; - } - - /* Find the capture DMA channel to use. */ - machine_data->dai[1].platforms->name = machine_data->platform_name[1]; - ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", - &machine_data->dai[1], - &machine_data->dma_channel_id[1], - &machine_data->dma_id[1]); - if (ret) { - dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); - goto error; - } - - /* Initialize our DAI data structure. */ - machine_data->dai[0].stream_name = "playback"; - machine_data->dai[1].stream_name = "capture"; - machine_data->dai[0].name = machine_data->dai[0].stream_name; - machine_data->dai[1].name = machine_data->dai[1].stream_name; - - machine_data->card.probe = mpc8610_hpcd_machine_probe; - machine_data->card.remove = mpc8610_hpcd_machine_remove; - machine_data->card.name = pdev->name; /* The platform driver name */ - machine_data->card.owner = THIS_MODULE; - machine_data->card.dev = &pdev->dev; - machine_data->card.num_links = 2; - machine_data->card.dai_link = machine_data->dai; - - /* Register with ASoC */ - ret = snd_soc_register_card(&machine_data->card); - if (ret) { - dev_err(&pdev->dev, "could not register card\n"); - goto error; - } - - of_node_put(codec_np); - - return 0; - -error: - kfree(machine_data); -error_alloc: - of_node_put(codec_np); - return ret; -} - -/** - * mpc8610_hpcd_remove: remove the platform device - * - * This function is called when the platform device is removed. - */ -static void mpc8610_hpcd_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mpc8610_hpcd_data *machine_data = - container_of(card, struct mpc8610_hpcd_data, card); - - snd_soc_unregister_card(card); - kfree(machine_data); -} - -static struct platform_driver mpc8610_hpcd_driver = { - .probe = mpc8610_hpcd_probe, - .remove_new = mpc8610_hpcd_remove, - .driver = { - /* The name must match 'compatible' property in the device tree, - * in lowercase letters. - */ - .name = "snd-soc-mpc8610hpcd", - }, -}; - -/** - * mpc8610_hpcd_init: machine driver initialization. - * - * This function is called when this module is loaded. - */ -static int __init mpc8610_hpcd_init(void) -{ - struct device_node *guts_np; - struct resource res; - - pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); - - /* Get the physical address of the global utilities registers */ - guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); - if (of_address_to_resource(guts_np, 0, &res)) { - pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); - of_node_put(guts_np); - return -EINVAL; - } - guts_phys = res.start; - of_node_put(guts_np); - - return platform_driver_register(&mpc8610_hpcd_driver); -} - -/** - * mpc8610_hpcd_exit: machine driver exit - * - * This function is called when this driver is unloaded. - */ -static void __exit mpc8610_hpcd_exit(void) -{ - platform_driver_unregister(&mpc8610_hpcd_driver); -} - -module_init(mpc8610_hpcd_init); -module_exit(mpc8610_hpcd_exit); - -MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); -MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 76a9f1e8cd..83e3ba773f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -9,7 +9,6 @@ #include <linux/clk.h> #include <linux/device.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 8acaa2ddb3..9efd31206c 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -58,12 +58,32 @@ * | |-> codec13 * +-+ * - * [Multi-CPU/Codec] + * [Multi-CPU/Codec-0] * +-+ +-+ * cpu1 <--| |<-@--------->| |-> codec1 * cpu2 <--| | | |-> codec2 * +-+ +-+ * + * [Multi-CPU/Codec-1] + * + * +-+ +-+ + * | |<-@--------->| | + * | | | | + * cpu8 <--| |<----------->| |-> codec14 + * cpu9 <--| |<---+------->| |-> codec15 + * +-+ \------>| |-> codec16 + * +-+ + * + * [Multi-CPU/Codec-2] + * + * +-+ +-+ + * | |<-@--------->| | + * | | | | + * cpu10 <-| |<----------->| |-> codec17 + * cpu11 <-| |<-----+----->| |-> codec18 + * cpu12 <-| |<----/ +-+ + * +-+ + * * [DPCM] * * CPU3/CPU4 are converting rate to 44100 @@ -144,16 +164,39 @@ */ &cpu0 - /* [Semi-Multi] */ + /* + * [Semi-Multi] + * cpu7/codec12/codec13 + */ &sm0 /* - * [Multi-CPU/Codec]: cpu side only + * [Multi-CPU/Codec-0]: cpu side only * cpu1/cpu2/codec1/codec2 */ &mcpu0 /* + * [Multi-CPU/Codec-1]: cpu side only + * cpu8/cpu9/codec14/codec15/codec16 + * + * Because it will reach to the maximum of sound minor number, + * disable it so far. + * If you want to try it, please disable some other one instead. + */ + //&mcpu1 + + /* + * [Multi-CPU/Codec-2]: cpu side only + * cpu10/cpu11/cpu12/codec17/codec18 + * + * Because it will reach to the maximum of sound minor number, + * disable it so far. + * If you want to try it, please disable some other one instead. + */ + //&mcpu2 + + /* * [DPCM]: both FE / BE * cpu3/cpu4/codec3 */ @@ -182,64 +225,259 @@ #address-cells = <1>; #size-cells = <0>; + /* + * [Multi-CPU-0] + * + * +---+ +---+ + * cpu1 <--|A X|<-@------->|x a|-> codec1 + * cpu2 <--|B | | b|-> codec2 + * +---+ +---+ + */ ports@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; - /* [Multi-CPU] */ - 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>; }; }; + mcpu0: port@0 { reg = <0>; mcpu00_ep: endpoint { remote-endpoint = <&mcodec00_ep>; };};/* (X) to pair */ + port@1 { reg = <1>; mcpu01_ep: endpoint { remote-endpoint = <&cpu1_ep>; };};/* (A) Multi Element */ + port@2 { reg = <2>; mcpu02_ep: endpoint { remote-endpoint = <&cpu2_ep>; };};/* (B) Multi Element */ }; - /* [Multi-Codec] */ + /* + * [Multi-Codec-0] + * + * +---+ +---+ + * cpu1 <--|A X|<-@------->|x a|-> codec1 + * cpu2 <--|B | | b|-> codec2 + * +---+ +---+ + */ ports@1 { 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>; }; }; + port@0 { reg = <0>; mcodec00_ep: endpoint { remote-endpoint = <&mcpu00_ep>; };};/* (x) to pair */ + port@1 { reg = <1>; mcodec01_ep: endpoint { remote-endpoint = <&codec1_ep>; };};/* (a) Multi Element */ + port@2 { reg = <2>; mcodec02_ep: endpoint { remote-endpoint = <&codec2_ep>; };};/* (b) Multi Element */ }; - /* [DPCM-Multi]::BE */ + /* + * [DPCM-Multi]::BE + * + * FE BE + * **** +---+ + * cpu5 <-@--* *-----@--->|x a|-> codec4 + * cpu6 <-@--* * | b|-> codec5 + * **** +---+ + */ ports@2 { 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>; }; }; + port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; };};/* (x) to pair */ + port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; };};/* (a) Multi Element */ + port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; };};/* (b) Multi Element */ }; - /* [Codec2Codec-Multi]::CPU */ + /* + * [Codec2Codec-Multi]::CPU + * + * +---+ + * +-@->|X A|-> codec8 + * | | B|-> codec9 + * | +---+ + * | +---+ + * +--->|x a|-> codec10 + * | b|-> codec11 + * +---+ + */ ports@3 { 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>; }; }; + port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; };};/* (X) to pair */ + port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; };};/* (A) Multi Element */ + port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; };};/* (B) Multi Element */ }; - /* [Codec2Codec-Multi]::Codec */ + /* + * [Codec2Codec-Multi]::Codec + * + * +---+ + * +-@->|X A|-> codec8 + * | | B|-> codec9 + * | +---+ + * | +---+ + * +--->|x a|-> codec10 + * | b|-> codec11 + * +---+ + */ ports@4 { 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>; }; }; + port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; };};/* (x) to pair */ + port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; };};/* (a) Multi Element */ + port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; };};/* (b) Multi Element */ }; - /* [Semi-Multi] */ + /* + * [Semi-Multi] + * + * +---+ + * cpu7 <-@------->|X A|-> codec12 + * | B|-> codec13 + * +---+ + */ 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>; }; }; + port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; };};/* (X) to pair */ + port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; };};/* (A) Multi Element */ + port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; };};/* (B) Multi Element */ + }; + + /* + * [Multi-CPU-1] + * + * +---+ +---+ + * | X|<-@------->|x | + * | | | | + * cpu8 <--|A 1|<--------->|3 a|-> codec14 + * cpu9 <--|B 2|<---+----->|4 b|-> codec15 + * +---+ \---->|5 c|-> codec16 + * +---+ + */ + ports@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + mcpu1: port@0 { reg = <0>; mcpu10_ep: endpoint { remote-endpoint = <&mcodec10_ep>; };}; /* (X) to pair */ + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcpu11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu8_ep>; }; /* (A) Multi Element */ + mcpu11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec11_ep_0>; }; /* (1) connected Codec */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcpu12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu9_ep>; }; /* (B) Multi Element */ + mcpu12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec12_ep_0>; }; /* (2) connected Codec */ + mcpu12_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcodec13_ep_0>; }; /* (2) connected Codec */ + }; + }; + + /* + * [Multi-Codec-1] + * + * +---+ +---+ + * | X|<-@------->|x | + * | | | | + * cpu8 <--|A 1|<--------->|3 a|-> codec14 + * cpu9 <--|B 2|<---+----->|4 b|-> codec15 + * +---+ \---->|5 c|-> codec16 + * +---+ + */ + ports@7 { + reg = <7>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec10_ep: endpoint { remote-endpoint = <&mcpu10_ep>; };}; /* (x) to pair */ + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcodec11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec14_ep>; }; /* (a) Multi Element */ + mcodec11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu11_ep_0>; }; /* (3) connected CPU */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcodec12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec15_ep>; }; /* (b) Multi Element */ + mcodec12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_0>; }; /* (4) connected CPU */ + }; + port@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + mcodec13_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec16_ep>; }; /* (c) Multi Element */ + mcodec13_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_1>; }; /* (5) connected CPU */ + }; + }; + + /* + * [Multi-CPU-2] + * + * +---+ +---+ + * | X|<-@------->|x | + * | | | | + * cpu10 <-|A 1|<--------->|4 a|-> codec17 + * cpu11 <-|B 2|<-----+--->|5 b|-> codec18 + * cpu12 <-|C 3|<----/ +---+ + * +---+ + */ + ports@8 { + reg = <8>; + #address-cells = <1>; + #size-cells = <0>; + mcpu2: port@0 { reg = <0>; mcpu20_ep: endpoint { remote-endpoint = <&mcodec20_ep>; };}; /* (X) to pair */ + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcpu21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu10_ep>; }; /* (A) Multi Element */ + mcpu21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec21_ep_0>; }; /* (1) connected Codec */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcpu22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu11_ep>; }; /* (B) Multi Element */ + mcpu22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_0>; }; /* (2) connected Codec */ + }; + port@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + mcpu23_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu12_ep>; }; /* (C) Multi Element */ + mcpu23_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_1>; }; /* (3) connected Codec */ + }; + }; + + /* + * [Multi-Codec-2] + * + * +---+ +---+ + * | X|<-@------->|x | + * | | | | + * cpu10 <-|A 1|<--------->|4 a|-> codec17 + * cpu11 <-|B 2|<-----+--->|5 b|-> codec18 + * cpu12 <-|C 3|<----/ +---+ + * +---+ + */ + ports@9 { + reg = <9>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec20_ep: endpoint { remote-endpoint = <&mcpu20_ep>; };}; /* (x) to pair */ + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcodec21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec17_ep>; }; /* (a) Multi Element */ + mcodec21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu21_ep_0>; }; /* (4) connected CPU */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcodec22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec18_ep>; }; /* (b) Multi Element */ + mcodec22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu22_ep_0>; }; /* (5) connected CPU */ + mcodec22_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcpu23_ep_0>; }; /* (5) connected CPU */ + }; }; }; @@ -252,11 +490,27 @@ #address-cells = <1>; #size-cells = <0>; - /* [DPCM]::FE */ + /* + * [DPCM]::FE + * + * FE BE + * **** + * cpu3 <-@(fe00)--* *--(be0)@--> codec3 + * cpu4 <-@(fe01)--* * (44.1kHz) + * **** + */ 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 */ + /* + * [DPCM-Multi]::FE + * + * FE BE + * **** +-+ + * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4 + * cpu6 <-@(fe11)--* * | |-> codec5 + * **** +-+ + */ fe10: port@2 { reg = <2>; fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; }; fe11: port@3 { reg = <3>; fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; }; }; @@ -266,10 +520,26 @@ #address-cells = <1>; #size-cells = <0>; - /* [DPCM]::BE */ + /* + * [DPCM]::BE + * + * FE BE + * **** + * cpu3 <-@(fe00)--* *--(be0)@--> codec3 + * cpu4 <-@(fe01)--* * (44.1kHz) + * **** + */ be0: port@0 { reg = <0>; be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; - /* [DPCM-Multi]::BE */ + /* + * [DPCM-Multi]::BE + * + * FE BE + * **** +-+ + * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4 + * cpu6 <-@(fe11)--* * | |-> codec5 + * **** +-+ + */ be1: port@1 { reg = <1>; be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; }; }; @@ -277,7 +547,13 @@ codec2codec { #address-cells = <1>; #size-cells = <0>; - /* [Codec2Codec] */ + /* + * [Codec2Codec] + * + * +-@(c2c)-> codec6 + * | + * +--------> codec7 + */ ports@0 { reg = <0>; @@ -289,7 +565,18 @@ port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; }; - /* [Codec2Codec-Multi] */ + /* + * [Codec2Codec-Multi] + * + * +-+ + * +-@(c2c_m)-->| |-> codec8 + * | | |-> codec9 + * | +-+ + * | +-+ + * +----------->| |-> codec10 + * | |-> codec11 + * +-+ + */ ports@1 { reg = <1>; @@ -323,9 +610,9 @@ /* [Normal] */ cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; - /* [Multi-CPU] */ - port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; - port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; + /* [Multi-CPU-0] */ + port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu01_ep>; }; }; + port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu02_ep>; }; }; /* [DPCM]::FE */ port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; @@ -337,6 +624,14 @@ /* [Semi-Multi] */ sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; }; + + /* [Multi-CPU-1] */ + port@8 { reg = <8>; cpu8_ep: endpoint { remote-endpoint = <&mcpu11_ep>; }; }; + port@9 { reg = <9>; cpu9_ep: endpoint { remote-endpoint = <&mcpu12_ep>; }; }; + /* [Multi-CPU-2] */ + port@a { reg = <10>; cpu10_ep: endpoint { remote-endpoint = <&mcpu21_ep>; }; }; + port@b { reg = <11>; cpu11_ep: endpoint { remote-endpoint = <&mcpu22_ep>; }; }; + port@c { reg = <12>; cpu12_ep: endpoint { remote-endpoint = <&mcpu23_ep>; }; }; }; }; @@ -363,9 +658,9 @@ /* [Normal] */ port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; - /* [Multi-Codec] */ - port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; - port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; + /* [Multi-Codec-0] */ + port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec01_ep>; }; }; + port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec02_ep>; }; }; /* [DPCM]::BE */ port@3 { @@ -395,6 +690,13 @@ port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; }; port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; }; + /* [Multi-Codec-1] */ + port@e { reg = <14>; codec14_ep: endpoint { remote-endpoint = <&mcodec11_ep>; }; }; + port@f { reg = <15>; codec15_ep: endpoint { remote-endpoint = <&mcodec12_ep>; }; }; + port@10 { reg = <16>; codec16_ep: endpoint { remote-endpoint = <&mcodec13_ep>; }; }; + /* [Multi-Codec-2] */ + port@11 { reg = <17>; codec17_ep: endpoint { remote-endpoint = <&mcodec21_ep>; }; }; + port@12 { reg = <18>; codec18_ep: endpoint { remote-endpoint = <&mcodec22_ep>; }; }; }; }; }; diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 7146611df7..62606e20be 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -8,7 +8,6 @@ // based on ${LINUX}/sound/soc/generic/audio-graph-card.c #include <linux/clk.h> #include <linux/device.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> @@ -83,32 +82,32 @@ Multi-CPU/Codec ************************************ -It has connection part (= X) and list part (= y). -links indicates connection part of CPU side (= A). +It has link connection part (= X,x) and list part (= A,B,a,b). +"links" is connection part of CPU side (= @). - +-+ (A) +-+ - CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1 - CPU2 --(y) | | | | (y)-- Codec2 - +-+ +-+ + +----+ +---+ + CPU1 --|A X| <-@----> |x a|-- Codec1 + CPU2 --|B | | b|-- Codec2 + +----+ +---+ - sound { - compatible = "audio-graph-card2"; + sound { + compatible = "audio-graph-card2"; -(A) links = <&mcpu>; +(@) links = <&mcpu>; - multi { - ports@0 { -(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; -(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; -(y) port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; - }; - ports@1 { -(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; -(y) port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; -(y) port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; - }; + multi { + ports@0 { +(@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair + port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element + port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element + }; + ports@1 { + port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair + port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element + port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element }; }; + }; CPU { ports { @@ -328,9 +327,9 @@ static struct device_node *graph_get_next_multi_ep(struct device_node **port) /* * multi { * ports { - * => lnk: port@0 { ... }; - * port@1 { ep { ... = rep0 } }; - * port@2 { ep { ... = rep1 } }; + * => lnk: port@0 { ... }; // to pair + * port@1 { ep { ... = rep0 } }; // Multi Element + * port@2 { ep { ... = rep1 } }; // Multi Element * ... * }; * }; @@ -504,40 +503,203 @@ static int __graph_parse_node(struct simple_util_priv *priv, return 0; } -static int graph_parse_node(struct simple_util_priv *priv, - enum graph_type gtype, - struct device_node *port, - struct link_info *li, int is_cpu) +static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, + int *nm_idx, int cpu_idx, + struct device_node *mcpu_port) { - struct device_node *ep; - int ret = 0; + /* + * +---+ +---+ + * | X|<-@------->|x | + * | | | | + * cpu0 <--|A 1|<--------->|4 a|-> codec0 + * cpu1 <--|B 2|<-----+--->|5 b|-> codec1 + * cpu2 <--|C 3|<----/ +---+ + * +---+ + * + * multi { + * ports { + * port@0 { mcpu_top_ep {... = mcodec_ep; }; }; // (X) to pair + * <mcpu_port> port@1 { mcpu0_ep { ... = cpu0_ep; }; // (A) Multi Element + * mcpu0_ep_0 { ... = mcodec0_ep_0; }; }; // (1) connected Codec + * port@2 { mcpu1_ep { ... = cpu1_ep; }; // (B) Multi Element + * mcpu1_ep_0 { ... = mcodec1_ep_0; }; }; // (2) connected Codec + * port@3 { mcpu2_ep { ... = cpu2_ep; }; // (C) Multi Element + * mcpu2_ep_0 { ... = mcodec1_ep_1; }; }; // (3) connected Codec + * }; + * + * ports { + * port@0 { mcodec_top_ep {... = mcpu_ep; }; }; // (x) to pair + * <mcodec_port>port@1 { mcodec0_ep { ... = codec0_ep; }; // (a) Multi Element + * mcodec0_ep_0 { ... = mcpu0_ep_0; }; }; // (4) connected CPU + * port@2 { mcodec1_ep { ... = codec1_ep; }; // (b) Multi Element + * mcodec1_ep_0 { ... = mcpu1_ep_0; }; // (5) connected CPU + * mcodec1_ep_1 { ... = mcpu2_ep_0; }; }; // (5) connected CPU + * }; + * }; + */ + struct device_node *mcpu_ep = port_to_endpoint(mcpu_port); + struct device_node *mcpu_ep_n = mcpu_ep; + struct device_node *mcpu_port_top = of_get_next_child(of_get_parent(mcpu_port), NULL); + struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top); + struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top); + struct device_node *mcodec_port_top = of_get_parent(mcodec_ep_top); + struct device_node *mcodec_ports = of_get_parent(mcodec_port_top); + int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); + int ret = -EINVAL; - if (graph_lnk_is_multi(port)) { - int idx; + if (cpu_idx > dai_link->num_cpus) + goto mcpu_err; - of_node_get(port); + while (1) { + struct device_node *mcodec_ep_n; + struct device_node *mcodec_port_i; + struct device_node *mcodec_port; + int codec_idx; - for (idx = 0;; idx++) { - ep = graph_get_next_multi_ep(&port); - if (!ep) - break; + if (*nm_idx > nm_max) + break; - ret = __graph_parse_node(priv, gtype, ep, - li, is_cpu, idx); - of_node_put(ep); - if (ret < 0) + mcpu_ep_n = of_get_next_child(mcpu_port, mcpu_ep_n); + if (!mcpu_ep_n) { + ret = 0; + break; + } + + mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n); + mcodec_port = of_get_parent(mcodec_ep_n); + + if (mcodec_ports != of_get_parent(mcodec_port)) + goto mcpu_err; + + codec_idx = 0; + mcodec_port_i = of_get_next_child(mcodec_ports, NULL); + while (1) { + if (codec_idx > dai_link->num_codecs) + goto mcodec_err; + + mcodec_port_i = of_get_next_child(mcodec_ports, mcodec_port_i); + + if (!mcodec_port_i) + goto mcodec_err; + + if (mcodec_port_i == mcodec_port) break; + + codec_idx++; } - } else { - /* Single CPU / Codec */ - ep = port_to_endpoint(port); - ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); + + dai_link->ch_maps[*nm_idx].cpu = cpu_idx; + dai_link->ch_maps[*nm_idx].codec = codec_idx; + + (*nm_idx)++; + + of_node_put(mcodec_port_i); +mcodec_err: + of_node_put(mcodec_port); + of_node_put(mcpu_ep_n); + of_node_put(mcodec_ep_n); + } +mcpu_err: + of_node_put(mcpu_ep); + of_node_put(mcpu_port_top); + of_node_put(mcpu_ep_top); + of_node_put(mcodec_ep_top); + of_node_put(mcodec_port_top); + of_node_put(mcodec_ports); + + return ret; +} + +static int graph_parse_node_multi(struct simple_util_priv *priv, + enum graph_type gtype, + struct device_node *port, + struct link_info *li, int is_cpu) +{ + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct device *dev = simple_priv_to_dev(priv); + struct device_node *ep; + int ret = -ENOMEM; + int nm_idx = 0; + int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); + + /* + * create ch_maps if CPU:Codec = N:M + * DPCM is out of scope + */ + if (gtype != GRAPH_DPCM && !dai_link->ch_maps && + dai_link->num_cpus > 1 && dai_link->num_codecs > 1 && + dai_link->num_cpus != dai_link->num_codecs) { + + dai_link->ch_maps = devm_kcalloc(dev, nm_max, + sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL); + if (!dai_link->ch_maps) + goto multi_err; + } + + for (int idx = 0;; idx++) { + /* + * multi { + * ports { + * <port> port@0 { ... }; // to pair + * port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element + * port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element + * }; + * }; + * + * cpu { + * ports { + * <ep> port@0 { cpu1_ep { ... = mcpu1_ep };}; + * }; + * }; + */ + ep = graph_get_next_multi_ep(&port); + if (!ep) + break; + + ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx); of_node_put(ep); + if (ret < 0) + goto multi_err; + + /* CPU:Codec = N:M */ + if (is_cpu && dai_link->ch_maps) { + ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port); + if (ret < 0) + goto multi_err; + } } + if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max)) + ret = -EINVAL; + +multi_err: return ret; } +static int graph_parse_node_single(struct simple_util_priv *priv, + enum graph_type gtype, + struct device_node *port, + struct link_info *li, int is_cpu) +{ + struct device_node *ep = port_to_endpoint(port); + int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); + + of_node_put(ep); + + return ret; +} + +static int graph_parse_node(struct simple_util_priv *priv, + enum graph_type gtype, + struct device_node *port, + struct link_info *li, int is_cpu) +{ + if (graph_lnk_is_multi(port)) + return graph_parse_node_multi(priv, gtype, port, li, is_cpu); + else + return graph_parse_node_single(priv, gtype, port, li, is_cpu); +} + static void graph_parse_daifmt(struct device_node *node, unsigned int *daifmt, unsigned int *bit_frame) { @@ -597,6 +759,7 @@ static void graph_link_init(struct simple_util_priv *priv, struct device_node *ep; struct device_node *ports; unsigned int daifmt = 0, daiclk = 0; + bool playback_only = 0, capture_only = 0; unsigned int bit_frame = 0; if (graph_lnk_is_multi(port)) { @@ -635,6 +798,11 @@ static void graph_link_init(struct simple_util_priv *priv, if (is_cpu_node) daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); + graph_util_parse_link_direction(port, &playback_only, &capture_only); + + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; + dai_link->dai_fmt = daifmt | daiclk; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; @@ -920,17 +1088,33 @@ static int graph_counter(struct device_node *lnk) * * multi { * ports { - * => lnk: port@0 { ... }; - * port@1 { ... }; - * port@2 { ... }; + * => lnk: port@0 { ... }; // to pair + * port@1 { ... }; // Multi Element + * port@2 { ... }; // Multi Element * ... * }; * }; * * ignore first lnk part */ - if (graph_lnk_is_multi(lnk)) - return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1; + if (graph_lnk_is_multi(lnk)) { + struct device_node *ports = of_get_parent(lnk); + struct device_node *port = NULL; + int cnt = 0; + + /* + * CPU/Codec = N:M case has many endpoints. + * We can't use of_graph_get_endpoint_count() here + */ + while(1) { + port = of_get_next_child(ports, port); + if (!port) + break; + cnt++; + } + + return cnt - 1; + } /* * Single CPU / Codec */ diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index cfa70a56ff..81077d16d2 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -5,7 +5,6 @@ // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> @@ -1129,6 +1128,25 @@ parse_dai_end: } EXPORT_SYMBOL_GPL(graph_util_parse_dai); +int graph_util_parse_link_direction(struct device_node *np, + bool *playback_only, bool *capture_only) +{ + bool is_playback_only = false; + bool is_capture_only = false; + + is_playback_only = of_property_read_bool(np, "playback-only"); + is_capture_only = of_property_read_bool(np, "capture-only"); + + if (is_playback_only && is_capture_only) + return -EINVAL; + + *playback_only = is_playback_only; + *capture_only = is_capture_only; + + return 0; +} +EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index dd7d2a0772..250ae3781d 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -15,7 +15,6 @@ #include <linux/clk.h> #include <linux/jiffies.h> #include <linux/io.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 8e91eece99..8360ce5574 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -236,6 +236,82 @@ static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = { {}, }; +static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC5682", + .drv_name = "avs_rt5682", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5682-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC0298", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "10EC1308", + .drv_name = "avs_rt1308", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt1308-tplg.bin", + }, + { + .id = "ESSX8336", + .drv_name = "avs_es8336", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "es8336-tplg.bin", + }, + {}, +}; + static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { { .drv_name = "avs_i2s_test", @@ -296,6 +372,15 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines), AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines), AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), + AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), {}, }; diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 6060894954..c018f84fe0 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -277,16 +277,24 @@ static int avs_da7219_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_da7219_driver_ids[] = { + { + .name = "avs_da7219", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); + static struct platform_driver avs_da7219_driver = { .probe = avs_da7219_probe, .driver = { .name = "avs_da7219", .pm = &snd_soc_pm_ops, }, + .id_table = avs_da7219_driver_ids, }; module_platform_driver(avs_da7219_driver); MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_da7219"); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index c270646faf..ba2bc7f689 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -77,15 +77,23 @@ static int avs_dmic_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_dmic_driver_ids[] = { + { + .name = "avs_dmic", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); + static struct platform_driver avs_dmic_driver = { .probe = avs_dmic_probe, .driver = { .name = "avs_dmic", .pm = &snd_soc_pm_ops, }, + .id_table = avs_dmic_driver_ids, }; module_platform_driver(avs_dmic_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_dmic"); diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index f972ef64d2..1090082e7d 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -307,15 +307,23 @@ static int avs_es8336_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_es8336_driver_ids[] = { + { + .name = "avs_es8336", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); + static struct platform_driver avs_es8336_driver = { .probe = avs_es8336_probe, .driver = { .name = "avs_es8336", .pm = &snd_soc_pm_ops, }, + .id_table = avs_es8336_driver_ids, }; module_platform_driver(avs_es8336_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_es8336"); diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index 8876558f19..79b4aca413 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -155,8 +155,6 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) return 0; } -SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); - static struct snd_soc_dai_link probing_link = { .name = "probing-LINK", .id = -1, @@ -164,8 +162,8 @@ static struct snd_soc_dai_link probing_link = { .no_pcm = 1, .dpcm_playback = 1, .dpcm_capture = 1, - .cpus = dummy, - .num_cpus = ARRAY_SIZE(dummy), + .cpus = &snd_soc_dummy_dlc, + .num_cpus = 1, .init = avs_probing_link_init, }; @@ -218,12 +216,21 @@ static int avs_hdaudio_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_hdaudio_driver_ids[] = { + { + .name = "avs_hdaudio", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); + static struct platform_driver avs_hdaudio_driver = { .probe = avs_hdaudio_probe, .driver = { .name = "avs_hdaudio", .pm = &snd_soc_pm_ops, }, + .id_table = avs_hdaudio_driver_ids, }; module_platform_driver(avs_hdaudio_driver) @@ -231,4 +238,3 @@ module_platform_driver(avs_hdaudio_driver) MODULE_DESCRIPTION("Intel HD-Audio machine driver"); MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_hdaudio"); diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 3d03e1eed3..28f254eb0d 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -185,15 +185,23 @@ static int avs_i2s_test_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_i2s_test_driver_ids[] = { + { + .name = "avs_i2s_test", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); + static struct platform_driver avs_i2s_test_driver = { .probe = avs_i2s_test_probe, .driver = { .name = "avs_i2s_test", .pm = &snd_soc_pm_ops, }, + .id_table = avs_i2s_test_driver_ids, }; module_platform_driver(avs_i2s_test_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_i2s_test"); diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index 6ba7b65642..a83b95f251 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -135,15 +135,23 @@ static int avs_max98357a_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_max98357a_driver_ids[] = { + { + .name = "avs_max98357a", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); + static struct platform_driver avs_max98357a_driver = { .probe = avs_max98357a_probe, .driver = { .name = "avs_max98357a", .pm = &snd_soc_pm_ops, }, + .id_table = avs_max98357a_driver_ids, }; module_platform_driver(avs_max98357a_driver) MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_max98357a"); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index cc7dfdf720..3b980a025e 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -192,15 +192,23 @@ static int avs_max98373_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_max98373_driver_ids[] = { + { + .name = "avs_max98373", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); + static struct platform_driver avs_max98373_driver = { .probe = avs_max98373_probe, .driver = { .name = "avs_max98373", .pm = &snd_soc_pm_ops, }, + .id_table = avs_max98373_driver_ids, }; module_platform_driver(avs_max98373_driver) MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_max98373"); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index fb0175f37d..86dd2b228d 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -189,15 +189,23 @@ static int avs_max98927_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_max98927_driver_ids[] = { + { + .name = "avs_max98927", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); + static struct platform_driver avs_max98927_driver = { .probe = avs_max98927_probe, .driver = { .name = "avs_max98927", .pm = &snd_soc_pm_ops, }, + .id_table = avs_max98927_driver_ids, }; module_platform_driver(avs_max98927_driver) MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_max98927"); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index d98b5deb78..1c1e2083f4 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -294,15 +294,23 @@ static int avs_nau8825_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_nau8825_driver_ids[] = { + { + .name = "avs_nau8825", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); + static struct platform_driver avs_nau8825_driver = { .probe = avs_nau8825_probe, .driver = { .name = "avs_nau8825", .pm = &snd_soc_pm_ops, }, + .id_table = avs_nau8825_driver_ids, }; module_platform_driver(avs_nau8825_driver) MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_nau8825"); diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c index 411acaee74..a9469b5ecb 100644 --- a/sound/soc/intel/avs/boards/probe.c +++ b/sound/soc/intel/avs/boards/probe.c @@ -50,15 +50,23 @@ static int avs_probe_mb_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_probe_mb_driver_ids[] = { + { + .name = "avs_probe_mb", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); + static struct platform_driver avs_probe_mb_driver = { .probe = avs_probe_mb_probe, .driver = { .name = "avs_probe_mb", .pm = &snd_soc_pm_ops, }, + .id_table = avs_probe_mb_driver_ids, }; module_platform_driver(avs_probe_mb_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_probe_mb"); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index 157183b1de..bfcb8845fd 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -257,15 +257,23 @@ static int avs_rt274_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt274_driver_ids[] = { + { + .name = "avs_rt274", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); + static struct platform_driver avs_rt274_driver = { .probe = avs_rt274_probe, .driver = { .name = "avs_rt274", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt274_driver_ids, }; module_platform_driver(avs_rt274_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt274"); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 131237471e..28d7d86b1c 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -228,15 +228,23 @@ static int avs_rt286_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt286_driver_ids[] = { + { + .name = "avs_rt286", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); + static struct platform_driver avs_rt286_driver = { .probe = avs_rt286_probe, .driver = { .name = "avs_rt286", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt286_driver_ids, }; module_platform_driver(avs_rt286_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt286"); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index ea32a7690c..80f490b9e1 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -247,15 +247,23 @@ static int avs_rt298_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt298_driver_ids[] = { + { + .name = "avs_rt298", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); + static struct platform_driver avs_rt298_driver = { .probe = avs_rt298_probe, .driver = { .name = "avs_rt298", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt298_driver_ids, }; module_platform_driver(avs_rt298_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt298"); diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index ad486a52e5..60105f453a 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -173,15 +173,23 @@ static int avs_rt5514_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt5514_driver_ids[] = { + { + .name = "avs_rt5514", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); + static struct platform_driver avs_rt5514_driver = { .probe = avs_rt5514_probe, .driver = { .name = "avs_rt5514", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt5514_driver_ids, }; module_platform_driver(avs_rt5514_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt5514"); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index 3effd789a4..b4762c2a7b 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -246,15 +246,23 @@ static int avs_rt5663_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt5663_driver_ids[] = { + { + .name = "avs_rt5663", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); + static struct platform_driver avs_rt5663_driver = { .probe = avs_rt5663_probe, .driver = { .name = "avs_rt5663", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt5663_driver_ids, }; module_platform_driver(avs_rt5663_driver); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt5663"); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 84e850c0b0..243f979fda 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -322,16 +322,24 @@ static int avs_rt5682_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_rt5682_driver_ids[] = { + { + .name = "avs_rt5682", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); + static struct platform_driver avs_rt5682_driver = { .probe = avs_rt5682_probe, .driver = { .name = "avs_rt5682", .pm = &snd_soc_pm_ops, }, + .id_table = avs_rt5682_driver_ids, }; module_platform_driver(avs_rt5682_driver) MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_rt5682"); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index 6bcab9deae..4a0e136835 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -181,15 +181,23 @@ static int avs_ssm4567_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } +static const struct platform_device_id avs_ssm4567_driver_ids[] = { + { + .name = "avs_ssm4567", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); + static struct platform_driver avs_ssm4567_driver = { .probe = avs_ssm4567_probe, .driver = { .name = "avs_ssm4567", .pm = &snd_soc_pm_ops, }, + .id_table = avs_ssm4567_driver_ids, }; module_platform_driver(avs_ssm4567_driver) MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:avs_ssm4567"); diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 65dd8f140f..e83ce6a357 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -371,7 +371,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) hstream = hdac_stream(estream); /* code loading performed with default format */ - sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); + sdfmt = snd_hdac_stream_format(1, 32, 48000); ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab); if (ret < 0) goto release_stream; @@ -438,7 +438,7 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) stream = hdac_stream(estream); /* code loading performed with default format */ - sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0); + sdfmt = snd_hdac_stream_format(1, 32, 48000); ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab); if (ret < 0) goto release_stream; diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index aa8b50b931..3aa16ee8d3 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -87,7 +87,7 @@ static bool avs_test_hw_params(struct snd_pcm_hw_params *params, return (params_rate(params) == fmt->sampling_freq && params_channels(params) == fmt->num_channels && params_physical_width(params) == fmt->bit_depth && - params_width(params) == fmt->valid_bit_depth); + snd_pcm_hw_params_bits(params) == fmt->valid_bit_depth); } static struct avs_tplg_path * diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 463dbba184..4dfc5a1ebb 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -17,6 +17,7 @@ #include "avs.h" #include "path.h" #include "topology.h" +#include "../../codecs/hda.h" struct avs_dma_data { struct avs_tplg_path_template *template; @@ -335,20 +336,25 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct hdac_ext_stream *link_stream = runtime->private_data; + struct snd_soc_pcm_stream *stream_info; + struct hdac_ext_stream *link_stream; struct hdac_ext_link *link; struct hda_codec *codec; struct hdac_bus *bus; unsigned int format_val; + unsigned int bits; int ret; + link_stream = runtime->private_data; if (link_stream->link_prepared) return 0; codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev); bus = &codec->bus->core; - format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); + bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat, + stream_info->sig_bits); + format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate); snd_hdac_ext_stream_reset(link_stream); snd_hdac_ext_stream_setup(link_stream, format_val); @@ -600,10 +606,12 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_stream *stream_info; struct avs_dma_data *data; struct avs_dev *adev = to_avs_dev(dai->dev); struct hdac_ext_stream *host_stream; unsigned int format_val; + unsigned int bits; int ret; data = snd_soc_dai_get_dma_data(dai, substream); @@ -614,8 +622,10 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so snd_hdac_stream_reset(hdac_stream(host_stream)); - format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - runtime->sample_bits, 0); + stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); + bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat, + stream_info->sig_bits); + format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate); ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); if (ret < 0) @@ -1064,8 +1074,10 @@ static const struct snd_pcm_hardware avs_pcm_hardware = { SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, @@ -1216,8 +1228,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = { .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX, }, .capture = { .channels_min = 1, @@ -1225,8 +1239,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = { .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX, }, }; @@ -1301,16 +1317,20 @@ static const struct snd_soc_dai_driver hda_cpu_dai = { .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX, }, .capture = { .channels_min = 1, .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX, }, }; @@ -1387,6 +1407,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component) ret = -ENOMEM; goto exit; } + + if (!hda_codec_is_display(codec)) { + dais[i].playback.formats = pcm->stream[0].formats; + dais[i].playback.subformats = pcm->stream[0].subformats; + dais[i].playback.rates = pcm->stream[0].rates; + dais[i].playback.channels_min = pcm->stream[0].channels_min; + dais[i].playback.channels_max = pcm->stream[0].channels_max; + dais[i].playback.sig_bits = pcm->stream[0].maxbps; + } } if (pcm->stream[1].substreams) { @@ -1397,6 +1426,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component) ret = -ENOMEM; goto exit; } + + if (!hda_codec_is_display(codec)) { + dais[i].capture.formats = pcm->stream[1].formats; + dais[i].capture.subformats = pcm->stream[1].subformats; + dais[i].capture.rates = pcm->stream[1].rates; + dais[i].capture.channels_min = pcm->stream[1].channels_min; + dais[i].capture.channels_max = pcm->stream[1].channels_max; + dais[i].capture.sig_bits = pcm->stream[1].maxbps; + } } dai = snd_soc_register_dai(component, &dais[i], false); diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index bdc6b30dc0..817e543036 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -140,8 +140,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, bps = snd_pcm_format_physical_width(format); if (bps < 0) return bps; - format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out, - format, bps, 0); + format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); if (ret < 0) return ret; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 41020409ff..48b3c67c91 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1514,8 +1514,16 @@ static int avs_dai_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - if (pcm) + u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | + SNDRV_PCM_SUBFMTBIT_MSBITS_24 | + SNDRV_PCM_SUBFMTBIT_MSBITS_MAX; + + if (pcm) { dai_drv->ops = &avs_dai_fe_ops; + dai_drv->capture.subformats = fe_subformats; + dai_drv->playback.subformats = fe_subformats; + } + return 0; } @@ -1534,6 +1542,9 @@ static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_s /* Open LINK (BE) pipes last and close them first to prevent xruns. */ link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE; link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE; + } else { + /* Do not ignore codec capabilities. */ + link->dpcm_merged_format = 1; } return 0; diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 9e427f00de..8fd5e7f830 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -229,6 +229,7 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_ES8316 + select SND_SOC_ES83XX_DSM_COMMON help This adds support for ASoC machine driver for Intel(R) Baytrail & Cherrytrail platforms with ES8316 audio codec. @@ -686,6 +687,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_RT712_SDCA_DMIC_SDW select SND_SOC_RT715_SDW select SND_SOC_RT715_SDCA_SDW + select SND_SOC_RT722_SDCA_SDW select SND_SOC_RT1308_SDW select SND_SOC_RT1308 select SND_SOC_RT1316_SDW diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 943bf8b80e..bbf796a5f7 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -41,9 +41,10 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_rt5682.o sof_sdw_rt700.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_rt715_sdca.o sof_sdw_rt722_sdca.o \ sof_sdw_cs42l42.o sof_sdw_cs42l43.o \ sof_sdw_cs_amp.o \ + sof_sdw_dmic.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 diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 816fad8c1f..540f7a2931 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -797,6 +797,9 @@ static int broxton_audio_probe(struct platform_device *pdev) broxton_audio_card.name = "glkda7219max"; /* Fixup the SSP entries for geminilake */ for (i = 0; i < ARRAY_SIZE(broxton_dais); i++) { + if (!broxton_dais[i].codecs->dai_name) + continue; + /* MAXIM_CODEC is connected to SSP1. */ if (!strcmp(broxton_dais[i].codecs->dai_name, BXT_MAXIM_CODEC_DAI)) { @@ -822,6 +825,9 @@ static int broxton_audio_probe(struct platform_device *pdev) broxton_audio_card.name = "cmlda7219max"; for (i = 0; i < ARRAY_SIZE(broxton_dais); i++) { + if (!broxton_dais[i].codecs->dai_name) + continue; + /* MAXIM_CODEC is connected to SSP1. */ if (!strcmp(broxton_dais[i].codecs->dai_name, BXT_MAXIM_CODEC_DAI)) { diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 4631106f2a..c0eb65c14a 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -604,7 +604,8 @@ static int broxton_audio_probe(struct platform_device *pdev) int i; for (i = 0; i < ARRAY_SIZE(broxton_rt298_dais); i++) { - if (!strncmp(card->dai_link[i].codecs->name, "i2c-INT343A:00", + if (card->dai_link[i].codecs->name && + !strncmp(card->dai_link[i].codecs->name, "i2c-INT343A:00", I2C_NAME_SIZE)) { if (!strncmp(card->name, "broxton-rt298", PLATFORM_NAME_SIZE)) { diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 10a84a2c10..c014d85a08 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -241,7 +241,8 @@ static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) { - if (!strcmp(byt_cht_cx2072x_dais[i].codecs->name, + if (byt_cht_cx2072x_dais[i].codecs->name && + !strcmp(byt_cht_cx2072x_dais[i].codecs->name, "i2c-14F10720:00")) { dai_index = i; break; diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 7e5eea6900..f4ac3ddd14 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -245,7 +245,8 @@ static int bytcht_da7213_probe(struct platform_device *pdev) /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(dailink); i++) { - if (!strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) { + if (dailink[i].codecs->name && + !strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) { dai_index = i; break; } diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 8a0b0e864f..2fcec2e02b 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -27,6 +27,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-acpi.h> +#include "../../codecs/es83xx-dsm-common.h" #include "../atom/sst-atom-controls.h" #include "../common/soc-intel-quirks.h" @@ -461,6 +462,66 @@ static const struct dmi_system_id byt_cht_es8316_quirk_table[] = { {} }; +static int byt_cht_es8316_get_quirks_from_dsm(struct byt_cht_es8316_private *priv, + bool is_bytcr) +{ + int ret, val1, val2, dsm_quirk = 0; + + if (is_bytcr) + dsm_quirk |= BYT_CHT_ES8316_SSP0; + + ret = es83xx_dsm(priv->codec_dev, PLATFORM_MAINMIC_TYPE_ARG, &val1); + if (ret < 0) + return ret; + + ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPMIC_TYPE_ARG, &val2); + if (ret < 0) + return ret; + + if (val1 == PLATFORM_MIC_AMIC_LIN1RIN1 && val2 == PLATFORM_MIC_AMIC_LIN2RIN2) { + dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP; + } else if (val1 == PLATFORM_MIC_AMIC_LIN2RIN2 && val2 == PLATFORM_MIC_AMIC_LIN1RIN1) { + dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN2_MAP; + } else { + dev_warn(priv->codec_dev, "Unknown mic settings mainmic 0x%02x hpmic 0x%02x\n", + val1, val2); + return -EINVAL; + } + + ret = es83xx_dsm(priv->codec_dev, PLATFORM_SPK_TYPE_ARG, &val1); + if (ret < 0) + return ret; + + switch (val1) { + case PLATFORM_SPK_MONO: + dsm_quirk |= BYT_CHT_ES8316_MONO_SPEAKER; + break; + case PLATFORM_SPK_STEREO: + break; + default: + dev_warn(priv->codec_dev, "Unknown speaker setting 0x%02x\n", val1); + return -EINVAL; + } + + ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPDET_INV_ARG, &val1); + if (ret < 0) + return ret; + + switch (val1) { + case PLATFORM_HPDET_NORMAL: + break; + case PLATFORM_HPDET_INVERTED: + dsm_quirk |= BYT_CHT_ES8316_JD_INVERTED; + break; + default: + dev_warn(priv->codec_dev, "Unknown hpdet-inv setting 0x%02x\n", val1); + return -EINVAL; + } + + quirk = dsm_quirk; + return 0; +} + static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -470,10 +531,10 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) struct byt_cht_es8316_private *priv; const struct dmi_system_id *dmi_id; struct fwnode_handle *fwnode; + bool sof_parent, is_bytcr; const char *platform_name; struct acpi_device *adev; struct device *codec_dev; - bool sof_parent; unsigned int cnt = 0; int dai_index = 0; int i; @@ -485,7 +546,8 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) { - if (!strcmp(byt_cht_es8316_dais[i].codecs->name, + if (byt_cht_es8316_dais[i].codecs->name && + !strcmp(byt_cht_es8316_dais[i].codecs->name, "i2c-ESSX8316:00")) { dai_index = i; break; @@ -520,12 +582,16 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) return ret; } + es83xx_dsm_dump(priv->codec_dev); + /* Check for BYTCR or other platform and setup quirks */ + is_bytcr = soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0; dmi_id = dmi_first_match(byt_cht_es8316_quirk_table); if (dmi_id) { quirk = (unsigned long)dmi_id->driver_data; - } else if (soc_intel_is_byt() && - mach->mach_params.acpi_ipc_irq_index == 0) { + } else if (!byt_cht_es8316_get_quirks_from_dsm(priv, is_bytcr)) { + dev_info(dev, "Using ACPI DSM info for quirks\n"); + } else if (is_bytcr) { /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */ quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP | BYT_CHT_ES8316_MONO_SPEAKER; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a290f498ba..05f38d1f7d 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -1664,7 +1664,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) { - if (!strcmp(byt_rt5640_dais[i].codecs->name, + if (byt_rt5640_dais[i].codecs->name && + !strcmp(byt_rt5640_dais[i].codecs->name, "i2c-10EC5640:00")) { dai_index = i; break; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index f9fe8414f4..80c841b000 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -910,7 +910,8 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) /* fix index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) { - if (!strcmp(byt_rt5651_dais[i].codecs->name, + if (byt_rt5651_dais[i].codecs->name && + !strcmp(byt_rt5651_dais[i].codecs->name, "i2c-10EC5651:00")) { dai_index = i; break; diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index 6978ebde66..cccb5e90c0 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -605,7 +605,8 @@ static int snd_byt_wm5102_mc_probe(struct platform_device *pdev) /* find index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_wm5102_dais); i++) { - if (!strcmp(byt_wm5102_dais[i].codecs->name, + if (byt_wm5102_dais[i].codecs->name && + !strcmp(byt_wm5102_dais[i].codecs->name, "wm5102-codec")) { dai_index = i; break; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index df23a581c1..eb41b7115d 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -40,7 +40,6 @@ struct cht_acpi_card { struct cht_mc_private { struct snd_soc_jack jack; struct cht_acpi_card *acpi_card; - char codec_name[SND_ACPI_I2C_ID_LEN]; struct clk *mclk; }; @@ -534,6 +533,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) const char *platform_name; struct cht_mc_private *drv; struct acpi_device *adev; + struct device *codec_dev; bool sof_parent; bool found = false; bool is_bytcr = false; @@ -566,14 +566,14 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } card->dev = &pdev->dev; - sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id); /* set correct codec name */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) - if (!strcmp(card->dai_link[i].codecs->name, + if (cht_dailink[i].codecs->name && + !strcmp(cht_dailink[i].codecs->name, "i2c-10EC5645:00")) { - card->dai_link[i].codecs->name = drv->codec_name; dai_index = i; + break; } /* fixup codec name based on HID */ @@ -583,7 +583,14 @@ static int snd_cht_mc_probe(struct platform_device *pdev) "i2c-%s", acpi_dev_name(adev)); cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name; } + /* acpi_get_first_physical_node() returns a borrowed ref, no need to deref */ + codec_dev = acpi_get_first_physical_node(adev); acpi_dev_put(adev); + if (!codec_dev) + return -EPROBE_DEFER; + + snd_soc_card_chtrt5645.components = rt5645_components(codec_dev); + snd_soc_card_chtrt5650.components = rt5645_components(codec_dev); /* * swap SSP0 if bytcr is detected diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index f6da24f3c4..be2d1a8dbc 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -93,8 +93,12 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, * when codec is runtime suspended. Codec needs clock for jack * detection and button press. */ - snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, - 48000 * 512, SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, + 48000 * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "failed to set codec sysclk: %d\n", ret); + return ret; + } if (ctx->mclk) clk_disable_unprepare(ctx->mclk); @@ -462,7 +466,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* find index of codec dai */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { - if (!strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) { + if (cht_dailink[i].codecs->name && + !strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) { dai_index = i; break; } diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c index ce2967850c..4f2cb8e529 100644 --- a/sound/soc/intel/boards/sof_board_helpers.c +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -3,6 +3,7 @@ // Copyright(c) 2023 Intel Corporation. All rights reserved. #include <sound/soc.h> +#include "../common/soc-intel-quirks.h" #include "hda_dsp_common.h" #include "sof_board_helpers.h" @@ -86,6 +87,55 @@ static struct snd_soc_dai_link_component platform_component[] = { } }; +int sof_intel_board_set_codec_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec codec_type, int ssp_codec) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: codec %s, ssp %d\n", be_id, + sof_ssp_get_codec_name(codec_type), ssp_codec); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + if (soc_intel_is_byt() || soc_intel_is_cht()) { + /* backward-compatibility for BYT/CHT boards */ + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port", + ssp_codec); + } else { + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", + ssp_codec); + } + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs - caller to handle */ + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_codec_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + int sof_intel_board_set_dmic_link(struct device *dev, struct snd_soc_dai_link *link, int be_id, enum sof_dmic_be_type be_type) @@ -198,7 +248,287 @@ int sof_intel_board_set_intel_hdmi_link(struct device *dev, } EXPORT_SYMBOL_NS(sof_intel_board_set_intel_hdmi_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); +int sof_intel_board_set_ssp_amp_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec amp_type, int ssp_amp) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id, + sof_ssp_get_codec_name(amp_type), ssp_amp); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs - caller to handle */ + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; /* feedback stream or firmware-generated echo reference */ + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_ssp_amp_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_bt_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_bt) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + link->codecs = &snd_soc_dummy_dlc; + link->num_codecs = 1; + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_bt_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_hdmi_in_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_hdmi) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + link->codecs = &snd_soc_dummy_dlc; + link->num_codecs = 1; + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_hdmi_in_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +static int calculate_num_links(struct sof_card_private *ctx) +{ + int num_links = 0; + + /* headphone codec */ + if (ctx->codec_type != CODEC_NONE) + num_links++; + + /* dmic01 and dmic16k */ + if (ctx->dmic_be_num > 0) + num_links++; + + if (ctx->dmic_be_num > 1) + num_links++; + + /* idisp HDMI */ + num_links += ctx->hdmi_num; + + /* speaker amp */ + if (ctx->amp_type != CODEC_NONE) + num_links++; + + /* BT audio offload */ + if (ctx->bt_offload_present) + num_links++; + + /* HDMI-In */ + num_links += hweight32(ctx->ssp_mask_hdmi_in); + + return num_links; +} + +int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) +{ + struct snd_soc_dai_link *links; + int num_links; + int i; + int idx = 0; + int ret; + int ssp_hdmi_in = 0; + + num_links = calculate_num_links(ctx); + + links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), + GFP_KERNEL); + if (!links) + return -ENOMEM; + + /* headphone codec */ + if (ctx->codec_type != CODEC_NONE) { + ret = sof_intel_board_set_codec_link(dev, &links[idx], idx, + ctx->codec_type, + ctx->ssp_codec); + if (ret) { + dev_err(dev, "fail to set codec link, ret %d\n", ret); + return ret; + } + + ctx->codec_link = &links[idx]; + idx++; + } + + /* dmic01 and dmic16k */ + if (ctx->dmic_be_num > 0) { + /* at least we have dmic01 */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, + SOF_DMIC_01); + if (ret) { + dev_err(dev, "fail to set dmic01 link, ret %d\n", ret); + return ret; + } + + idx++; + } + + if (ctx->dmic_be_num > 1) { + /* set up 2 BE links at most */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, + SOF_DMIC_16K); + if (ret) { + dev_err(dev, "fail to set dmic16k link, ret %d\n", ret); + return ret; + } + + idx++; + } + + /* idisp HDMI */ + for (i = 1; i <= ctx->hdmi_num; i++) { + ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx, + i, + ctx->hdmi.idisp_codec); + if (ret) { + dev_err(dev, "fail to set hdmi link, ret %d\n", ret); + return ret; + } + + idx++; + } + + /* speaker amp */ + if (ctx->amp_type != CODEC_NONE) { + ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx, + ctx->amp_type, + ctx->ssp_amp); + if (ret) { + dev_err(dev, "fail to set amp link, ret %d\n", ret); + return ret; + } + + ctx->amp_link = &links[idx]; + idx++; + } + + /* BT audio offload */ + if (ctx->bt_offload_present) { + ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, + ctx->ssp_bt); + if (ret) { + dev_err(dev, "fail to set bt link, ret %d\n", ret); + return ret; + } + + idx++; + } + + /* HDMI-In */ + for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { + ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx, + ssp_hdmi_in); + if (ret) { + dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret); + return ret; + } + + idx++; + } + + if (idx != num_links) { + dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx, + num_links); + return -EINVAL; + } + + card->dai_link = links; + card->num_links = num_links; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers"); MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON); diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index df99f576c1..3b36058118 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -30,6 +30,13 @@ struct sof_rt5682_private { * @amp_type: type of speaker amplifier * @dmic_be_num: number of Intel PCH DMIC BE link * @hdmi_num: number of Intel HDMI BE link + * @ssp_codec: ssp port number of headphone BE link + * @ssp_amp: ssp port number of speaker BE link + * @ssp_bt: ssp port number of BT offload BE link + * @ssp_mask_hdmi_in: ssp port mask of HDMI-IN BE link + * @bt_offload_present: true to create BT offload BE link + * @codec_link: pointer to headset codec dai link + * @amp_link: pointer to speaker amplifier dai link * @rt5682: private data for rt5682 machine driver */ struct sof_card_private { @@ -42,6 +49,16 @@ struct sof_card_private { int dmic_be_num; int hdmi_num; + int ssp_codec; + int ssp_amp; + int ssp_bt; + unsigned long ssp_mask_hdmi_in; + + bool bt_offload_present; + + struct snd_soc_dai_link *codec_link; + struct snd_soc_dai_link *amp_link; + union { struct sof_rt5682_private rt5682; }; @@ -53,12 +70,26 @@ enum sof_dmic_be_type { }; int sof_intel_board_card_late_probe(struct snd_soc_card *card); +int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx); +int sof_intel_board_set_codec_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec codec_type, int ssp_codec); int sof_intel_board_set_dmic_link(struct device *dev, struct snd_soc_dai_link *link, int be_id, enum sof_dmic_be_type be_type); int sof_intel_board_set_intel_hdmi_link(struct device *dev, struct snd_soc_dai_link *link, int be_id, int hdmi_id, bool idisp_codec); +int sof_intel_board_set_ssp_amp_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec amp_type, int ssp_amp); +int sof_intel_board_set_bt_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_bt); +int sof_intel_board_set_hdmi_in_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_hdmi); #endif /* __SOF_INTEL_BOARD_HELPERS_H */ diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c index 1f760fc4ca..c2442bf8ce 100644 --- a/sound/soc/intel/boards/sof_cs42l42.c +++ b/sound/soc/intel/boards/sof_cs42l42.c @@ -138,13 +138,6 @@ static const struct snd_soc_ops sof_cs42l42_ops = { .hw_params = sof_cs42l42_hw_params, }; -static struct snd_soc_dai_link_component platform_component[] = { - { - /* name might be overridden during probe */ - .name = "0000:00:1f.3" - } -}; - static int sof_card_late_probe(struct snd_soc_card *card) { return sof_intel_board_card_late_probe(card); @@ -189,147 +182,11 @@ static struct snd_soc_dai_link_component cs42l42_component[] = { } }; -static int create_spk_amp_dai_links(struct device *dev, - struct snd_soc_dai_link *links, - struct snd_soc_dai_link_component *cpus, - int *id, enum sof_ssp_codec amp_type, - int ssp_amp) -{ - int ret = 0; - - /* speaker amp */ - if (amp_type == CODEC_NONE) - return 0; - - links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", - ssp_amp); - if (!links[*id].name) { - ret = -ENOMEM; - goto devm_err; - } - - links[*id].id = *id; - - switch (amp_type) { - case CODEC_MAX98357A: - max_98357a_dai_link(&links[*id]); - break; - case CODEC_MAX98360A: - max_98360a_dai_link(&links[*id]); - break; - default: - dev_err(dev, "invalid amp type %d\n", amp_type); - return -EINVAL; - } - - links[*id].platforms = platform_component; - links[*id].num_platforms = ARRAY_SIZE(platform_component); - links[*id].dpcm_playback = 1; - /* firmware-generated echo reference */ - links[*id].dpcm_capture = 1; - - links[*id].no_pcm = 1; - links[*id].cpus = &cpus[*id]; - links[*id].num_cpus = 1; - - links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", ssp_amp); - if (!links[*id].cpus->dai_name) { - ret = -ENOMEM; - goto devm_err; - } - - (*id)++; - -devm_err: - return ret; -} - -static int create_hp_codec_dai_links(struct device *dev, - struct snd_soc_dai_link *links, - struct snd_soc_dai_link_component *cpus, - int *id, int ssp_codec) -{ - /* codec SSP */ - links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", - ssp_codec); - if (!links[*id].name) - goto devm_err; - - links[*id].id = *id; - links[*id].codecs = cs42l42_component; - links[*id].num_codecs = ARRAY_SIZE(cs42l42_component); - links[*id].platforms = platform_component; - links[*id].num_platforms = ARRAY_SIZE(platform_component); - links[*id].init = sof_cs42l42_init; - links[*id].exit = sof_cs42l42_exit; - links[*id].ops = &sof_cs42l42_ops; - links[*id].dpcm_playback = 1; - links[*id].dpcm_capture = 1; - links[*id].no_pcm = 1; - links[*id].cpus = &cpus[*id]; - links[*id].num_cpus = 1; - - links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_codec); - if (!links[*id].cpus->dai_name) - goto devm_err; - - (*id)++; - - return 0; - -devm_err: - return -ENOMEM; -} - -static int create_bt_offload_dai_links(struct device *dev, - struct snd_soc_dai_link *links, - struct snd_soc_dai_link_component *cpus, - int *id, int ssp_bt) -{ - /* bt offload */ - if (!(sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT)) - return 0; - - links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", - ssp_bt); - if (!links[*id].name) - goto devm_err; - - links[*id].id = *id; - links[*id].codecs = &snd_soc_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; - links[*id].dpcm_capture = 1; - links[*id].no_pcm = 1; - links[*id].cpus = &cpus[*id]; - links[*id].num_cpus = 1; - - links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_bt); - if (!links[*id].cpus->dai_name) - goto devm_err; - - (*id)++; - - return 0; - -devm_err: - return -ENOMEM; -} - static struct snd_soc_dai_link * sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, int ssp_codec, int ssp_amp, int ssp_bt, int dmic_be_num, int hdmi_num, bool idisp_codec) { - struct snd_soc_dai_link_component *cpus; struct snd_soc_dai_link *links; int ret; int id = 0; @@ -338,9 +195,7 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL); - cpus = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links, - sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); - if (!links || !cpus) + if (!links) goto devm_err; link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT; @@ -350,20 +205,52 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, switch (link_type) { case LINK_HP: - ret = create_hp_codec_dai_links(dev, links, cpus, &id, ssp_codec); - if (ret < 0) { + ret = sof_intel_board_set_codec_link(dev, &links[id], id, + CODEC_CS42L42, + ssp_codec); + if (ret) { dev_err(dev, "fail to create hp codec dai links, ret %d\n", ret); goto devm_err; } + + /* codec-specific fields */ + links[id].codecs = cs42l42_component; + links[id].num_codecs = ARRAY_SIZE(cs42l42_component); + links[id].init = sof_cs42l42_init; + links[id].exit = sof_cs42l42_exit; + links[id].ops = &sof_cs42l42_ops; + + id++; break; case LINK_SPK: - ret = create_spk_amp_dai_links(dev, links, cpus, &id, - amp_type, ssp_amp); - if (ret < 0) { - dev_err(dev, "fail to create spk amp dai links, ret %d\n", - ret); - goto devm_err; + if (amp_type != CODEC_NONE) { + ret = sof_intel_board_set_ssp_amp_link(dev, + &links[id], + id, + amp_type, + ssp_amp); + if (ret) { + dev_err(dev, "fail to create spk amp dai links, ret %d\n", + ret); + goto devm_err; + } + + /* codec-specific fields */ + switch (amp_type) { + case CODEC_MAX98357A: + max_98357a_dai_link(&links[id]); + break; + case CODEC_MAX98360A: + max_98360a_dai_link(&links[id]); + break; + default: + dev_err(dev, "invalid amp type %d\n", + amp_type); + goto devm_err; + } + + id++; } break; case LINK_DMIC: @@ -413,11 +300,17 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, } break; case LINK_BT: - ret = create_bt_offload_dai_links(dev, links, cpus, &id, ssp_bt); - if (ret < 0) { - dev_err(dev, "fail to create bt offload dai links, ret %d\n", - ret); - goto devm_err; + if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { + ret = sof_intel_board_set_bt_link(dev, + &links[id], id, + ssp_bt); + if (ret) { + dev_err(dev, "fail to create bt offload dai links, ret %d\n", + ret); + goto devm_err; + } + + id++; } break; case LINK_NONE: @@ -440,7 +333,7 @@ static int sof_audio_probe(struct platform_device *pdev) struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; - int ret, ssp_bt, ssp_amp, ssp_codec; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -469,25 +362,29 @@ static int sof_audio_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk); - ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >> + /* port number of peripherals attached to ssp interface */ + ctx->ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >> SOF_CS42L42_SSP_BT_SHIFT; - ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >> + ctx->ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >> SOF_CS42L42_SSP_AMP_SHIFT; - ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK; + ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK; /* compute number of dai links */ sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num; if (ctx->amp_type != CODEC_NONE) sof_audio_card_cs42l42.num_links++; - if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) + if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { + ctx->bt_offload_present = true; sof_audio_card_cs42l42.num_links++; + } dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type, - ssp_codec, ssp_amp, ssp_bt, - ctx->dmic_be_num, ctx->hdmi_num, + ctx->ssp_codec, ctx->ssp_amp, + ctx->ssp_bt, ctx->dmic_be_num, + ctx->hdmi_num, ctx->hdmi.idisp_codec); if (!dai_links) return -ENOMEM; diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 3c00afc328..cf29747182 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -61,16 +61,21 @@ static int max_98373_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai; + int ret = 0; int j; for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { /* DEV0 tdm slot configuration */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); - } - if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); + } else if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { /* DEV1 tdm slot configuration */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); + } + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", + ret); + return ret; } } return 0; diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index dc2821a012..719c2fbaf5 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -138,13 +138,6 @@ static struct snd_soc_ops sof_nau8825_ops = { .hw_params = sof_nau8825_hw_params, }; -static struct snd_soc_dai_link_component platform_component[] = { - { - /* name might be overridden during probe */ - .name = "0000:00:1f.3" - } -}; - static int sof_card_late_probe(struct snd_soc_card *card) { struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); @@ -207,168 +200,69 @@ static struct snd_soc_dai_link_component nau8825_component[] = { } }; -static struct snd_soc_dai_link * -sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, - int ssp_codec, int ssp_amp, int dmic_be_num, - int hdmi_num, bool idisp_codec) +static int +sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) { - struct snd_soc_dai_link_component *cpus; - struct snd_soc_dai_link *links; - int i; - int id = 0; int ret; - links = devm_kcalloc(dev, sof_audio_card_nau8825.num_links, - sizeof(struct snd_soc_dai_link), GFP_KERNEL); - cpus = devm_kcalloc(dev, sof_audio_card_nau8825.num_links, - sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); - if (!links || !cpus) - goto devm_err; - - /* codec SSP */ - links[id].name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d-Codec", ssp_codec); - if (!links[id].name) - goto devm_err; - - links[id].id = id; - links[id].codecs = nau8825_component; - links[id].num_codecs = ARRAY_SIZE(nau8825_component); - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].init = sof_nau8825_codec_init; - links[id].exit = sof_nau8825_codec_exit; - links[id].ops = &sof_nau8825_ops; - links[id].dpcm_playback = 1; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].cpus = &cpus[id]; - links[id].num_cpus = 1; - - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_codec); - if (!links[id].cpus->dai_name) - goto devm_err; - - id++; - - /* dmic */ - if (dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, &links[id], id, - SOF_DMIC_01); - if (ret) - return NULL; - - id++; - } - - if (dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, &links[id], id, - SOF_DMIC_16K); - if (ret) - return NULL; + ret = sof_intel_board_set_dai_link(dev, card, ctx); + if (ret) + return ret; - id++; + if (!ctx->codec_link) { + dev_err(dev, "codec link not available"); + return -EINVAL; } - /* HDMI */ - for (i = 1; i <= hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, &links[id], id, - i, idisp_codec); - if (ret) - return NULL; + /* codec-specific fields for headphone codec */ + ctx->codec_link->codecs = nau8825_component; + ctx->codec_link->num_codecs = ARRAY_SIZE(nau8825_component); + ctx->codec_link->init = sof_nau8825_codec_init; + ctx->codec_link->exit = sof_nau8825_codec_exit; + ctx->codec_link->ops = &sof_nau8825_ops; - id++; - } + if (ctx->amp_type == CODEC_NONE) + return 0; - /* speaker amp */ - if (amp_type != CODEC_NONE) { - links[id].name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d-Codec", ssp_amp); - if (!links[id].name) - goto devm_err; - - links[id].id = id; - - switch (amp_type) { - case CODEC_MAX98360A: - max_98360a_dai_link(&links[id]); - break; - case CODEC_MAX98373: - links[id].codecs = max_98373_components; - links[id].num_codecs = ARRAY_SIZE(max_98373_components); - links[id].init = max_98373_spk_codec_init; - links[id].ops = &max_98373_ops; - break; - case CODEC_NAU8318: - nau8318_set_dai_link(&links[id]); - break; - case CODEC_RT1015P: - sof_rt1015p_dai_link(&links[id]); - break; - case CODEC_RT1019P: - sof_rt1019p_dai_link(&links[id]); - break; - default: - dev_err(dev, "invalid amp type %d\n", amp_type); - return NULL; - } - - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].dpcm_playback = 1; - /* feedback stream or firmware-generated echo reference */ - links[id].dpcm_capture = 1; - - links[id].no_pcm = 1; - links[id].cpus = &cpus[id]; - links[id].num_cpus = 1; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_amp); - if (!links[id].cpus->dai_name) - goto devm_err; - id++; + if (!ctx->amp_link) { + dev_err(dev, "amp link not available"); + return -EINVAL; } - /* BT audio offload */ - if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { - int port = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> - SOF_BT_OFFLOAD_SSP_SHIFT; - - links[id].id = id; - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", port); - if (!links[id].cpus->dai_name) - goto devm_err; - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); - if (!links[id].name) - goto devm_err; - links[id].codecs = &snd_soc_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; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].num_cpus = 1; + /* codec-specific fields for speaker amplifier */ + switch (ctx->amp_type) { + case CODEC_MAX98360A: + max_98360a_dai_link(ctx->amp_link); + break; + case CODEC_MAX98373: + ctx->amp_link->codecs = max_98373_components; + ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components); + ctx->amp_link->init = max_98373_spk_codec_init; + ctx->amp_link->ops = &max_98373_ops; + break; + case CODEC_NAU8318: + nau8318_set_dai_link(ctx->amp_link); + break; + case CODEC_RT1015P: + sof_rt1015p_dai_link(ctx->amp_link); + break; + case CODEC_RT1019P: + sof_rt1019p_dai_link(ctx->amp_link); + break; + default: + dev_err(dev, "invalid amp type %d\n", ctx->amp_type); + return -EINVAL; } - return links; -devm_err: - return NULL; + return 0; } static int sof_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; - struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; - int ret, ssp_amp, ssp_codec; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -393,28 +287,22 @@ static int sof_audio_probe(struct platform_device *pdev) if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) ctx->hdmi.idisp_codec = true; - ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >> - SOF_NAU8825_SSP_AMP_SHIFT; + /* port number of peripherals attached to ssp interface */ + ctx->ssp_bt = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> + SOF_BT_OFFLOAD_SSP_SHIFT; - ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK; - - /* compute number of dai links */ - sof_audio_card_nau8825.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num; + ctx->ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >> + SOF_NAU8825_SSP_AMP_SHIFT; - if (ctx->amp_type != CODEC_NONE) - sof_audio_card_nau8825.num_links++; + ctx->ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK; if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) - sof_audio_card_nau8825.num_links++; - - dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type, - ssp_codec, ssp_amp, - ctx->dmic_be_num, ctx->hdmi_num, - ctx->hdmi.idisp_codec); - if (!dai_links) - return -ENOMEM; + ctx->bt_offload_present = true; - sof_audio_card_nau8825.dai_link = dai_links; + /* update dai_link */ + ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx); + if (ret) + return ret; /* update codec_conf */ switch (ctx->amp_type) { @@ -465,49 +353,7 @@ static const struct platform_device_id board_ids[] = { SOF_NAU8825_NUM_HDMIDEV(4)), }, { - .name = "adl_max98373_8825", - .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | - SOF_NAU8825_SSP_AMP(1) | - SOF_NAU8825_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - /* The limitation of length of char array, shorten the name */ - .name = "adl_mx98360a_8825", - .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | - SOF_NAU8825_SSP_AMP(1) | - SOF_NAU8825_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - - }, - { - .name = "adl_rt1015p_8825", - .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | - SOF_NAU8825_SSP_AMP(1) | - SOF_NAU8825_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_nau8318_8825", - .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | - SOF_NAU8825_SSP_AMP(1) | - SOF_NAU8825_NUM_HDMIDEV(4) | - 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_NAU8825_SSP_AMP(1) | - SOF_NAU8825_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "rpl_mx98360a_8825", + .name = "adl_nau8825_def", .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | SOF_NAU8825_SSP_AMP(1) | SOF_NAU8825_NUM_HDMIDEV(4) | @@ -515,7 +361,7 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "rpl_nau8318_8825", + .name = "rpl_nau8825_def", .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | SOF_NAU8825_SSP_AMP(1) | SOF_NAU8825_NUM_HDMIDEV(4) | diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 06ad15af46..02705034e5 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -158,46 +158,6 @@ 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-MAX98360_ALC5682I_I2S"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_DISCRETE_I2S_BT"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .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_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) | @@ -463,13 +423,6 @@ static struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; -static struct snd_soc_dai_link_component platform_component[] = { - { - /* name might be overridden during probe */ - .name = "0000:00:1f.3" - } -}; - static int sof_card_late_probe(struct snd_soc_card *card) { struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); @@ -570,69 +523,45 @@ static struct snd_soc_dai_link_component rt5650_components[] = { } }; -static struct snd_soc_dai_link * -sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec codec_type, - enum sof_ssp_codec amp_type, int ssp_codec, - int ssp_amp, int dmic_be_num, int hdmi_num, - bool idisp_codec, bool is_legacy_cpu) +static int +sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) { - struct snd_soc_dai_link_component *cpus; - struct snd_soc_dai_link *links; - int i; - int id = 0; int ret; - int hdmi_id_offset = 0; - - links = devm_kcalloc(dev, sof_audio_card_rt5682.num_links, - sizeof(struct snd_soc_dai_link), GFP_KERNEL); - cpus = devm_kcalloc(dev, sof_audio_card_rt5682.num_links, - sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); - if (!links || !cpus) - goto devm_err; - /* codec SSP */ - links[id].name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d-Codec", ssp_codec); - if (!links[id].name) - goto devm_err; + ret = sof_intel_board_set_dai_link(dev, card, ctx); + if (ret) + return ret; - links[id].id = id; + if (!ctx->codec_link) { + dev_err(dev, "codec link not available"); + return -EINVAL; + } - switch (codec_type) { + /* codec-specific fields for headphone codec */ + switch (ctx->codec_type) { case CODEC_RT5650: - links[id].codecs = &rt5650_components[0]; - links[id].num_codecs = 1; + ctx->codec_link->codecs = &rt5650_components[0]; + ctx->codec_link->num_codecs = 1; break; case CODEC_RT5682: - links[id].codecs = rt5682_component; - links[id].num_codecs = ARRAY_SIZE(rt5682_component); + ctx->codec_link->codecs = rt5682_component; + ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682_component); break; case CODEC_RT5682S: - links[id].codecs = rt5682s_component; - links[id].num_codecs = ARRAY_SIZE(rt5682s_component); + ctx->codec_link->codecs = rt5682s_component; + ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682s_component); break; default: - dev_err(dev, "invalid codec type %d\n", codec_type); - return NULL; + dev_err(dev, "invalid codec type %d\n", ctx->codec_type); + return -EINVAL; } - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].init = sof_rt5682_codec_init; - links[id].exit = sof_rt5682_codec_exit; - links[id].ops = &sof_rt5682_ops; - links[id].dpcm_playback = 1; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].cpus = &cpus[id]; - links[id].num_cpus = 1; - if (is_legacy_cpu) { - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "ssp%d-port", - ssp_codec); - if (!links[id].cpus->dai_name) - goto devm_err; - } else { + ctx->codec_link->init = sof_rt5682_codec_init; + ctx->codec_link->exit = sof_rt5682_codec_exit; + ctx->codec_link->ops = &sof_rt5682_ops; + + if (!ctx->rt5682.is_legacy_cpu) { /* * Currently, On SKL+ platforms MCLK will be turned off in sof * runtime suspended, and it will go into runtime suspended @@ -642,184 +571,66 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec codec_type, * avoid the noise. * It can be removed once we can control MCLK by driver. */ - links[id].ignore_pmdown_time = 1; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_codec); - if (!links[id].cpus->dai_name) - goto devm_err; - } - id++; - - /* dmic */ - if (dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, &links[id], id, - SOF_DMIC_01); - if (ret) - return NULL; - - id++; - } - - if (dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, &links[id], id, - SOF_DMIC_16K); - if (ret) - return NULL; - - id++; - } - - /* HDMI */ - for (i = 1; i <= hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, &links[id], id, - i, idisp_codec); - if (ret) - return NULL; - - id++; + ctx->codec_link->ignore_pmdown_time = 1; } - /* speaker amp */ - if (amp_type != CODEC_NONE) { - links[id].name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d-Codec", ssp_amp); - if (!links[id].name) - goto devm_err; - - links[id].id = id; - - switch (amp_type) { - case CODEC_MAX98357A: - max_98357a_dai_link(&links[id]); - break; - case CODEC_MAX98360A: - max_98360a_dai_link(&links[id]); - break; - case CODEC_MAX98373: - links[id].codecs = max_98373_components; - links[id].num_codecs = ARRAY_SIZE(max_98373_components); - links[id].init = max_98373_spk_codec_init; - links[id].ops = &max_98373_ops; - break; - case CODEC_MAX98390: - max_98390_dai_link(dev, &links[id]); - break; - case CODEC_RT1011: - sof_rt1011_dai_link(&links[id]); - break; - case CODEC_RT1015: - sof_rt1015_dai_link(&links[id]); - break; - case CODEC_RT1015P: - sof_rt1015p_dai_link(&links[id]); - break; - case CODEC_RT1019P: - sof_rt1019p_dai_link(&links[id]); - break; - case CODEC_RT5650: - /* use AIF2 to support speaker pipeline */ - links[id].codecs = &rt5650_components[1]; - links[id].num_codecs = 1; - links[id].init = rt5650_spk_init; - links[id].ops = &sof_rt5682_ops; - break; - default: - dev_err(dev, "invalid amp type %d\n", amp_type); - return NULL; - } - - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].dpcm_playback = 1; - /* feedback stream or firmware-generated echo reference */ - links[id].dpcm_capture = 1; - - links[id].no_pcm = 1; - links[id].cpus = &cpus[id]; - links[id].num_cpus = 1; - if (is_legacy_cpu) { - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "ssp%d-port", - ssp_amp); - if (!links[id].cpus->dai_name) - goto devm_err; - - } else { - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", - ssp_amp); - if (!links[id].cpus->dai_name) - goto devm_err; - } - id++; - } + if (ctx->amp_type == CODEC_NONE) + return 0; - /* BT audio offload */ - if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { - int port = (sof_rt5682_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> - SOF_BT_OFFLOAD_SSP_SHIFT; - - links[id].id = id; - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", port); - if (!links[id].cpus->dai_name) - goto devm_err; - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); - if (!links[id].name) - goto devm_err; - links[id].codecs = &snd_soc_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; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].num_cpus = 1; + if (!ctx->amp_link) { + dev_err(dev, "amp link not available"); + return -EINVAL; } - /* HDMI-In SSP */ - if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) { - unsigned long hdmi_in_ssp = (sof_rt5682_quirk & - SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; - int port = 0; - - for_each_set_bit(port, &hdmi_in_ssp, 32) { - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", port); - if (!links[id].cpus->dai_name) - return NULL; - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port); - if (!links[id].name) - return NULL; - links[id].id = id + hdmi_id_offset; - links[id].codecs = &snd_soc_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; - links[id].no_pcm = 1; - links[id].num_cpus = 1; - id++; - } + /* codec-specific fields for speaker amplifier */ + switch (ctx->amp_type) { + case CODEC_MAX98357A: + max_98357a_dai_link(ctx->amp_link); + break; + case CODEC_MAX98360A: + max_98360a_dai_link(ctx->amp_link); + break; + case CODEC_MAX98373: + ctx->amp_link->codecs = max_98373_components; + ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components); + ctx->amp_link->init = max_98373_spk_codec_init; + ctx->amp_link->ops = &max_98373_ops; + break; + case CODEC_MAX98390: + max_98390_dai_link(dev, ctx->amp_link); + break; + case CODEC_RT1011: + sof_rt1011_dai_link(ctx->amp_link); + break; + case CODEC_RT1015: + sof_rt1015_dai_link(ctx->amp_link); + break; + case CODEC_RT1015P: + sof_rt1015p_dai_link(ctx->amp_link); + break; + case CODEC_RT1019P: + sof_rt1019p_dai_link(ctx->amp_link); + break; + case CODEC_RT5650: + /* use AIF2 to support speaker pipeline */ + ctx->amp_link->codecs = &rt5650_components[1]; + ctx->amp_link->num_codecs = 1; + ctx->amp_link->init = rt5650_spk_init; + ctx->amp_link->ops = &sof_rt5682_ops; + break; + default: + dev_err(dev, "invalid amp type %d\n", ctx->amp_type); + return -EINVAL; } - return links; -devm_err: - return NULL; + return 0; } static int sof_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; - struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; - int ret, ssp_amp, ssp_codec; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -885,34 +696,25 @@ static int sof_audio_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "sof_rt5682_quirk = %lx\n", sof_rt5682_quirk); - ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >> - SOF_RT5682_SSP_AMP_SHIFT; + /* port number/mask of peripherals attached to ssp interface */ + ctx->ssp_mask_hdmi_in = (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; - ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK; + ctx->ssp_bt = (sof_rt5682_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> + SOF_BT_OFFLOAD_SSP_SHIFT; - /* compute number of dai links */ - sof_audio_card_rt5682.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num; + ctx->ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >> + SOF_RT5682_SSP_AMP_SHIFT; - if (ctx->amp_type != CODEC_NONE) - sof_audio_card_rt5682.num_links++; + ctx->ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK; if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) - sof_audio_card_rt5682.num_links++; - - if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) - sof_audio_card_rt5682.num_links += - hweight32((sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT); - - dai_links = sof_card_dai_links_create(&pdev->dev, ctx->codec_type, - ctx->amp_type, ssp_codec, ssp_amp, - ctx->dmic_be_num, ctx->hdmi_num, - ctx->hdmi.idisp_codec, - ctx->rt5682.is_legacy_cpu); - if (!dai_links) - return -ENOMEM; + ctx->bt_offload_present = true; - sof_audio_card_rt5682.dai_link = dai_links; + /* update dai_link */ + ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_rt5682, ctx); + if (ret) + return ret; /* update codec_conf */ switch (ctx->amp_type) { @@ -1147,6 +949,15 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(0) | SOF_RT5682_NUM_HDMIDEV(3)), }, + { + .name = "mtl_rt5650", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(2) | + SOF_RT5682_SSP_AMP(0) | + SOF_RT5682_NUM_HDMIDEV(3) | + SOF_BT_OFFLOAD_SSP(1) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 1e788859c8..cf14bd3053 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -236,6 +236,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { SOF_SDW_PCH_DMIC | RT711_JD2_100K), }, + { + /* NUC15 LAPRC710 skews */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + SOF_SDW_PCH_DMIC | + RT711_JD2_100K), + }, /* TigerLake-SDCA devices */ { .callback = sof_sdw_quirk_cb, @@ -570,16 +581,14 @@ int sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link_ch_map *ch_maps; 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) + if (!rtd->dai_link->ch_maps) return 0; /* Identical data will be sent to all codecs in playback */ @@ -605,13 +614,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream, * 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); - } - } + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) + ch_maps->ch_mask = ch_mask << (i * step); + return 0; } @@ -861,6 +866,36 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .part_id = 0x722, + .version_id = 3, + .dais = { + { + .direction = {true, true}, + .dai_name = "rt722-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 = "rt722-sdca-aif2", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + /* No feedback capability is provided by rt722-sdca codec driver*/ + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .init = sof_sdw_rt722_spk_init, + }, + { + .direction = {false, true}, + .dai_name = "rt722-sdca-aif3", + .dai_type = SOF_SDW_DAI_TYPE_MIC, + .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, + .init = sof_sdw_rt722_sdca_dmic_init, + }, + }, + .dai_num = 3, + }, + { .part_id = 0x8373, .dais = { { @@ -1350,15 +1385,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, +static void set_dailink_map(struct snd_soc_dai_link_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; + for (i = 0; i < codec_num; i++) { + sdw_codec_ch_maps[i].cpu = i / step; + sdw_codec_ch_maps[i].codec = i; + } } static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; @@ -1453,7 +1490,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, *ignore_pch_dmic = true; for_each_pcm_streams(stream) { - struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps; + struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps; char *name, *cpu_name; int playback, capture; static const char * const sdw_stream_name[] = { @@ -1530,7 +1567,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, 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; + dai_links[*link_index].ch_maps = sdw_codec_ch_maps; ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, playback, group_id, adr_index, dai_index); if (ret < 0) { @@ -1947,7 +1984,7 @@ static int mc_probe(struct platform_device *pdev) /* Register the card */ ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); mc_dailink_exit_loop(card); return ret; } diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index e6b98523b4..f16456945e 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -138,12 +138,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, 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_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, @@ -189,6 +183,18 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); +/* RT722-SDCA support */ +int sof_sdw_rt722_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_rt722_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); + /* MAXIM codec support */ int sof_sdw_maxim_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c index 98f6546f48..f88c01552a 100644 --- a/sound/soc/intel/boards/sof_sdw_cs_amp.c +++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c @@ -9,15 +9,24 @@ #include <linux/errno.h> #include <sound/soc.h> #include <sound/soc-acpi.h> +#include <sound/soc-dai.h> #include "sof_sdw_common.h" #define CODEC_NAME_SIZE 8 +static const struct snd_soc_dapm_widget sof_widgets[] = { + SND_SOC_DAPM_SPK("Speakers", NULL), +}; + static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) { const char *dai_name = rtd->dai_link->codecs->dai_name; struct snd_soc_card *card = rtd->card; char codec_name[CODEC_NAME_SIZE]; + char widget_name[16]; + struct snd_soc_dapm_route route = { "Speakers", NULL, widget_name }; + struct snd_soc_dai *codec_dai; + int i, ret; snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); card->components = devm_kasprintf(card->dev, GFP_KERNEL, @@ -26,17 +35,34 @@ static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) if (!card->components) return -ENOMEM; + ret = snd_soc_dapm_new_controls(&card->dapm, sof_widgets, + ARRAY_SIZE(sof_widgets)); + if (ret) { + dev_err(card->dev, "widgets addition failed: %d\n", ret); + return ret; + } + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!strstr(codec_dai->name, "cs35l56")) + continue; + + snprintf(widget_name, sizeof(widget_name), "%s SPK", + codec_dai->component->name_prefix); + ret = snd_soc_dapm_add_routes(&card->dapm, &route, 1); + if (ret) + return ret; + } + return 0; } - int sof_sdw_cs_amp_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) { - /* Count amp number and do init on playback link only. */ + /* Do init on playback link only. */ if (!playback) return 0; diff --git a/sound/soc/intel/boards/sof_sdw_rt722_sdca.c b/sound/soc/intel/boards/sof_sdw_rt722_sdca.c new file mode 100644 index 0000000000..fe3a2bff95 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt722_sdca.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023 Intel Corporation + +/* + * sof_sdw_rt722_sdca - Helpers to handle RT722-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 rt722_spk_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route rt722_spk_map[] = { + { "Speaker", NULL, "rt722 SPK" }, +}; + +static const struct snd_kcontrol_new rt722_spk_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int rt722_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:rt722", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt722_spk_controls, + ARRAY_SIZE(rt722_spk_controls)); + if (ret) { + dev_err(card->dev, "failed to add rt722 spk controls: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt722_spk_widgets, + ARRAY_SIZE(rt722_spk_widgets)); + if (ret) { + dev_err(card->dev, "failed to add rt722 spk widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt722_spk_map, ARRAY_SIZE(rt722_spk_map)); + if (ret) + dev_err(rtd->dev, "failed to add rt722 spk map: %d\n", ret); + + return ret; +} + +int sof_sdw_rt722_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 = rt722_spk_init; + + return 0; +} + +static int rt722_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:%s", + card->components, component->name_prefix); + if (!card->components) + return -ENOMEM; + + return 0; +} + +int sof_sdw_rt722_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 = rt722_sdca_dmic_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index 49a513399d..d9c283829f 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -63,6 +63,11 @@ static const struct snd_soc_dapm_route rt713_sdca_map[] = { { "rt713 MIC2", NULL, "Headset Mic" }, }; +static const struct snd_soc_dapm_route rt722_sdca_map[] = { + { "Headphone", NULL, "rt722 HP" }, + { "rt722 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"), @@ -117,6 +122,9 @@ static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) } else if (strstr(component->name_prefix, "rt713")) { ret = snd_soc_dapm_add_routes(&card->dapm, rt713_sdca_map, ARRAY_SIZE(rt713_sdca_map)); + } else if (strstr(component->name_prefix, "rt722")) { + ret = snd_soc_dapm_add_routes(&card->dapm, rt722_sdca_map, + ARRAY_SIZE(rt722_sdca_map)); } else { dev_err(card->dev, "%s is not supported\n", component->name_prefix); return -EINVAL; @@ -184,10 +192,10 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, int ret; /* - * headset should be initialized once. - * Do it with dai link for playback. + * Jack detection should be only initialized once for headsets since + * the playback/capture is sharing the same jack */ - if (!playback) + if (ctx->headset_codec_dev) return 0; sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 137ba64254..ee2e813bf4 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -27,21 +27,10 @@ #define SOF_AMPLIFIER_SSP_MASK (GENMASK(3, 0)) /* HDMI capture*/ -#define SOF_SSP_HDMI_CAPTURE_PRESENT BIT(4) -#define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT 5 -#define SOF_NO_OF_HDMI_CAPTURE_SSP_MASK (GENMASK(6, 5)) -#define SOF_NO_OF_HDMI_CAPTURE_SSP(quirk) \ - (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) - -#define SOF_HDMI_CAPTURE_1_SSP_SHIFT 7 -#define SOF_HDMI_CAPTURE_1_SSP_MASK (GENMASK(9, 7)) -#define SOF_HDMI_CAPTURE_1_SSP(quirk) \ - (((quirk) << SOF_HDMI_CAPTURE_1_SSP_SHIFT) & SOF_HDMI_CAPTURE_1_SSP_MASK) - -#define SOF_HDMI_CAPTURE_2_SSP_SHIFT 10 -#define SOF_HDMI_CAPTURE_2_SSP_MASK (GENMASK(12, 10)) -#define SOF_HDMI_CAPTURE_2_SSP(quirk) \ - (((quirk) << SOF_HDMI_CAPTURE_2_SSP_SHIFT) & SOF_HDMI_CAPTURE_2_SSP_MASK) +#define SOF_HDMI_CAPTURE_SSP_MASK_SHIFT 4 +#define SOF_HDMI_CAPTURE_SSP_MASK_MASK (GENMASK(9, 4)) +#define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \ + (((quirk) << SOF_HDMI_CAPTURE_SSP_MASK_SHIFT) & SOF_HDMI_CAPTURE_SSP_MASK_MASK) /* HDMI playback */ #define SOF_HDMI_PLAYBACK_PRESENT BIT(13) @@ -82,13 +71,6 @@ static struct snd_soc_card sof_ssp_amp_card = { .late_probe = sof_card_late_probe, }; -static struct snd_soc_dai_link_component platform_component[] = { - { - /* name might be overridden during probe */ - .name = "0000:00:1f.3" - } -}; - /* BE ID defined in sof-tgl-rt1308-hdmi-ssp.m4 */ #define HDMI_IN_BE_ID 0 #define SPK_BE_ID 2 @@ -98,66 +80,53 @@ static struct snd_soc_dai_link_component platform_component[] = { static struct snd_soc_dai_link * sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, - int ssp_codec, int dmic_be_num, int hdmi_num, + int ssp_amp, int dmic_be_num, int hdmi_num, bool idisp_codec) { - struct snd_soc_dai_link_component *cpus; struct snd_soc_dai_link *links; int i; int id = 0; int ret; bool fixed_be = false; int be_id; + unsigned long ssp_mask_hdmi_in; links = devm_kcalloc(dev, sof_ssp_amp_card.num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL); - cpus = devm_kcalloc(dev, sof_ssp_amp_card.num_links, - sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); - if (!links || !cpus) + if (!links) return NULL; /* HDMI-In SSP */ - if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) { - int num_of_hdmi_ssp = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >> + SOF_HDMI_CAPTURE_SSP_MASK_SHIFT; + + if (ssp_mask_hdmi_in) { + int port = 0; /* the topology supports HDMI-IN uses fixed BE ID for DAI links */ fixed_be = true; - for (i = 1; i <= num_of_hdmi_ssp; i++) { - int port = (i == 1 ? (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >> - SOF_HDMI_CAPTURE_1_SSP_SHIFT : - (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_2_SSP_MASK) >> - SOF_HDMI_CAPTURE_2_SSP_SHIFT); - - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", port); - if (!links[id].cpus->dai_name) - return NULL; - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port); - if (!links[id].name) + be_id = HDMI_IN_BE_ID; + for_each_set_bit(port, &ssp_mask_hdmi_in, 32) { + ret = sof_intel_board_set_hdmi_in_link(dev, &links[id], + be_id, port); + if (ret) return NULL; - links[id].id = fixed_be ? (HDMI_IN_BE_ID + i - 1) : id; - links[id].codecs = &snd_soc_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; - links[id].no_pcm = 1; - links[id].num_cpus = 1; + id++; + be_id++; } } /* codec SSP */ if (amp_type != CODEC_NONE) { - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); - if (!links[id].name) + be_id = fixed_be ? SPK_BE_ID : id; + ret = sof_intel_board_set_ssp_amp_link(dev, &links[id], be_id, + amp_type, ssp_amp); + if (ret) return NULL; - links[id].id = fixed_be ? SPK_BE_ID : id; - + /* codec-specific fields */ switch (amp_type) { case CODEC_CS35L41: cs35l41_set_dai_link(&links[id]); @@ -170,18 +139,6 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, return NULL; } - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].dpcm_playback = 1; - /* feedback from amplifier or firmware-generated echo reference */ - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].cpus = &cpus[id]; - links[id].num_cpus = 1; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_codec); - if (!links[id].cpus->dai_name) - return NULL; - id++; } @@ -224,29 +181,14 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, int port = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> SOF_BT_OFFLOAD_SSP_SHIFT; - links[id].id = id; - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, - "SSP%d Pin", port); - if (!links[id].cpus->dai_name) - goto devm_err; - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); - if (!links[id].name) - goto devm_err; - links[id].codecs = &snd_soc_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; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - links[id].num_cpus = 1; + ret = sof_intel_board_set_bt_link(dev, &links[id], id, port); + if (ret) + return NULL; + id++; } return links; -devm_err: - return NULL; } static int sof_ssp_amp_probe(struct platform_device *pdev) @@ -254,7 +196,7 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; - int ret, ssp_codec; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -270,7 +212,14 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) else ctx->dmic_be_num = 0; - ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK; + /* port number/mask of peripherals attached to ssp interface */ + ctx->ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >> + SOF_HDMI_CAPTURE_SSP_MASK_SHIFT; + + ctx->ssp_bt = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> + SOF_BT_OFFLOAD_SSP_SHIFT; + + ctx->ssp_amp = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK; /* set number of dai links */ sof_ssp_amp_card.num_links = ctx->dmic_be_num; @@ -278,9 +227,8 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) if (ctx->amp_type != CODEC_NONE) sof_ssp_amp_card.num_links++; - if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) - sof_ssp_amp_card.num_links += (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + if (ctx->ssp_mask_hdmi_in) + sof_ssp_amp_card.num_links += hweight32(ctx->ssp_mask_hdmi_in); if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) { ctx->hdmi_num = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_PLAYBACK_MASK) >> @@ -297,11 +245,13 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) ctx->hdmi_num = 0; } - if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) + if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { + ctx->bt_offload_present = true; sof_ssp_amp_card.num_links++; + } dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type, - ssp_codec, ctx->dmic_be_num, + ctx->ssp_amp, ctx->dmic_be_num, ctx->hdmi_num, ctx->hdmi.idisp_codec); if (!dai_links) @@ -343,10 +293,8 @@ static const struct platform_device_id board_ids[] = { { .name = "tgl_rt1308_hdmi_ssp", .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(2) | - SOF_NO_OF_HDMI_CAPTURE_SSP(2) | - SOF_HDMI_CAPTURE_1_SSP(1) | - SOF_HDMI_CAPTURE_2_SSP(5) | - SOF_SSP_HDMI_CAPTURE_PRESENT), + SOF_HDMI_CAPTURE_SSP_MASK(0x22)), + /* SSP 1 and SSP 5 are used for HDMI IN */ }, { .name = "adl_cs35l41", @@ -358,28 +306,22 @@ static const struct platform_device_id board_ids[] = { }, { .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 | + .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) | + /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_NO_OF_HDMI_PLAYBACK(3) | SOF_HDMI_PLAYBACK_PRESENT), }, { .name = "rpl_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 | + .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) | + /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_NO_OF_HDMI_PLAYBACK(3) | SOF_HDMI_PLAYBACK_PRESENT), }, { .name = "mtl_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 | + .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) | + /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_NO_OF_HDMI_PLAYBACK(3) | SOF_HDMI_PLAYBACK_PRESENT), }, diff --git a/sound/soc/intel/boards/sof_ssp_common.c b/sound/soc/intel/boards/sof_ssp_common.c index 41a258e45a..96072790e9 100644 --- a/sound/soc/intel/boards/sof_ssp_common.c +++ b/sound/soc/intel/boards/sof_ssp_common.c @@ -96,6 +96,27 @@ enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev) } EXPORT_SYMBOL_NS(sof_ssp_detect_amp_type, SND_SOC_INTEL_SOF_SSP_COMMON); +const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(codecs); i++) { + if (codecs[i].codec_type != codec_type) + continue; + + return codecs[i].name; + } + for (i = 0; i < ARRAY_SIZE(amps); i++) { + if (amps[i].codec_type != codec_type) + continue; + + return amps[i].name; + } + + return NULL; +} +EXPORT_SYMBOL_NS(sof_ssp_get_codec_name, SND_SOC_INTEL_SOF_SSP_COMMON); + MODULE_DESCRIPTION("ASoC Intel SOF Common Machine Driver Helpers"); MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_ssp_common.h b/sound/soc/intel/boards/sof_ssp_common.h index e3fd6fb1db..d24888bc99 100644 --- a/sound/soc/intel/boards/sof_ssp_common.h +++ b/sound/soc/intel/boards/sof_ssp_common.h @@ -68,4 +68,13 @@ enum sof_ssp_codec { enum sof_ssp_codec sof_ssp_detect_codec_type(struct device *dev); enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev); +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SOF_SSP_COMMON) +const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type); +#else +static inline const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type) +{ + return NULL; +} +#endif + #endif /* __SOF_SSP_COMMON_H */ 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 6e712ad954..d3d913458c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -528,14 +528,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .id = "10508825", - .drv_name = "adl_max98373_8825", + .drv_name = "adl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg", }, { .id = "10508825", - .drv_name = "adl_mx98360a_8825", + .drv_name = "adl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98360a_amp, .sof_tplg_filename = "sof-adl-max98360a-nau8825.tplg", @@ -549,14 +549,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .id = "10508825", - .drv_name = "adl_rt1015p_8825", + .drv_name = "adl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt1015p_amp, .sof_tplg_filename = "sof-adl-rt1015-nau8825.tplg", }, { .id = "10508825", - .drv_name = "adl_nau8318_8825", + .drv_name = "adl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_nau8318_amp, .sof_tplg_filename = "sof-adl-nau8318-nau8825.tplg", diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 9f35b77deb..5897bb6b28 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -22,6 +22,20 @@ static const struct snd_soc_acpi_endpoint single_endpoint = { .group_id = 0, }; +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -31,6 +45,33 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_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 lnl_rvp[] = { { .mask = BIT(0), @@ -40,6 +81,30 @@ static const struct snd_soc_acpi_link_adr lnl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_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, + }, + {} +}; + /* 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 */ @@ -62,6 +127,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg", }, { + .link_mask = GENMASK(3, 0), + .links = lnl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", + }, + { .link_mask = BIT(0), .links = lnl_rvp, .drv_name = "sof_sdw", 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 9008b67682..feb12c6c85 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -40,6 +40,11 @@ static const struct snd_soc_acpi_codecs mtl_lt6911_hdmi = { .codecs = {"INTC10B0"} }; +static const struct snd_soc_acpi_codecs mtl_rt5650_amp = { + .num_codecs = 1, + .codecs = {"10EC5650"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { { .comp_ids = &mtl_rt5682_rt5682s_hp, @@ -77,6 +82,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .id = "10EC5650", + .drv_name = "mtl_rt5650", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &mtl_rt5650_amp, + .sof_tplg_filename = "sof-mtl-rt5650.tplg", + }, /* place amp-only boards in the end of table */ { .id = "INTC10B0", @@ -123,6 +135,31 @@ static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { }, }; +/* + * RT722 is a multi-function codec, three endpoints are created for + * its headset, amp and dmic functions. + */ +static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_endpoint spk_2_endpoint = { .num = 0, .aggregated = 1, @@ -164,6 +201,15 @@ static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { + { + .adr = 0x000030025d072201ull, + .num_endpoints = ARRAY_SIZE(rt722_endpoints), + .endpoints = rt722_endpoints, + .name_prefix = "rt722" + } +}; + static const struct snd_soc_acpi_adr_device rt713_0_single_adr[] = { { .adr = 0x000031025D071301ull, @@ -355,6 +401,15 @@ static const struct snd_soc_acpi_link_adr mtl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr mtl_rt722_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt722_0_single_adr), + .adr_d = rt722_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = { { .mask = BIT(0), @@ -422,6 +477,25 @@ static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12_rt1713_l3[] = {} }; +static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt713_0_single_adr), + .adr_d = rt713_0_single_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group2_adr), + .adr_d = rt1316_1_group2_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group2_adr), + .adr_d = rt1316_2_group2_adr, + }, + {} +}; + static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = { { .adr = 0x000230019F836300ull, @@ -508,6 +582,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", }, { + .link_mask = GENMASK(2, 0), + .links = mtl_rt713_l0_rt1316_l12, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", + }, + { .link_mask = BIT(3) | BIT(0), .links = mtl_712_only, .drv_name = "sof_sdw", @@ -533,6 +613,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { }, { .link_mask = BIT(0), + .links = mtl_rt722_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt722-l0.tplg", + }, + { + .link_mask = BIT(0), .links = mtl_rvp, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt711.tplg", 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 5b6f57e3a5..c0a643f472 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -402,21 +402,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { }, { .id = "10508825", - .drv_name = "rpl_max98373_8825", + .drv_name = "rpl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98373_amp, .sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg", }, { .id = "10508825", - .drv_name = "rpl_mx98360a_8825", + .drv_name = "rpl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98360a_amp, .sof_tplg_filename = "sof-rpl-max98360a-nau8825.tplg", }, { .id = "10508825", - .drv_name = "rpl_nau8318_8825", + .drv_name = "rpl_nau8825_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_nau8318_amp, .sof_tplg_filename = "sof-rpl-nau8318-nau8825.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 5804926c8b..e5f721ba5e 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,20 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint spk_2_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 2, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_3_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 3, + .group_id = 1, +}; + static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { { .num = 0, @@ -400,6 +414,64 @@ static const struct snd_soc_acpi_link_adr tgl_712_only[] = { {} }; +static const struct snd_soc_acpi_adr_device cs42l43_3_adr[] = { + { + .adr = 0x00033001FA424301ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "cs42l43" + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = { + { + .adr = 0x00003301FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003201FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP2" + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = { + { + .adr = 0x00013701FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP8" + }, + { + .adr = 0x00013601FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP7" + } +}; + +static const struct snd_soc_acpi_link_adr tgl_cs42l43_cs35l56[] = { + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs42l43_3_adr), + .adr_d = cs42l43_3_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l56_0_adr), + .adr_d = cs35l56_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56_1_adr), + .adr_d = cs35l56_1_adr, + }, + {} +}; + static const struct snd_soc_acpi_codecs tgl_max98373_amp = { .num_codecs = 1, .codecs = {"MX98373"} @@ -495,6 +567,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 = 0xB, + .links = tgl_cs42l43_cs35l56, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-tgl-cs42l43-l3-cs35l56-l01.tplg", + }, + { .link_mask = 0xF, /* 4 active links required */ .links = tgl_3_in_1_default, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 174aae6e03..613b27b8da 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -127,6 +127,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; + unsigned int bits; int err; hstream = snd_hdac_get_stream(bus, params->stream, @@ -137,8 +138,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); - format_val = snd_hdac_calc_stream_format(params->s_freq, - params->ch, params->format, params->host_bps, 0); + bits = snd_hdac_stream_format_bits(params->format, SNDRV_PCM_SUBFORMAT_STD, + params->host_bps); + format_val = snd_hdac_stream_format(params->ch, bits, params->s_freq); dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", format_val, params->s_freq, params->ch, params->format); @@ -165,6 +167,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) struct hdac_ext_stream *stream; struct hdac_ext_link *link; unsigned char stream_tag; + unsigned int bits; hstream = snd_hdac_get_stream(bus, params->stream, params->link_dma_id + 1); @@ -173,8 +176,10 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); - format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, - params->format, params->link_bps, 0); + + bits = snd_hdac_stream_format_bits(params->format, SNDRV_PCM_SUBFORMAT_STD, + params->link_bps); + format_val = snd_hdac_stream_format(params->ch, bits, params->s_freq); dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", format_val, params->s_freq, params->ch, params->format); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index b93d455744..296b434caf 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -252,6 +252,7 @@ config SND_SOC_MT8188_MT6359 select SND_SOC_NAU8315 select SND_SOC_NAU8825 select SND_SOC_RT5682S + select SND_SOC_ES8326 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/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c index c1390b3734..6982e83342 100644 --- a/sound/soc/mediatek/mt7986/mt7986-wm8960.c +++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c @@ -144,7 +144,7 @@ static int mt7986_wm8960_machine_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err(&pdev->dev, "%s snd_soc_register_card fail: %d\n", __func__, ret); + dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__); goto err_of_node_put; } diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 33d477cc2e..a391066ab2 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -34,6 +34,8 @@ #define NAU8825_HS_PRESENT BIT(0) #define RT5682S_HS_PRESENT BIT(1) +#define ES8326_HS_PRESENT BIT(2) +#define MAX98390_TWO_AMP BIT(3) /* * Maxim MAX98390 */ @@ -48,6 +50,11 @@ */ #define NAU8825_CODEC_DAI "nau8825-hifi" +/* + * ES8326 + */ +#define ES8326_CODEC_DAI "ES8326 HiFi" + #define SOF_DMA_DL2 "SOF_DMA_DL2" #define SOF_DMA_DL3 "SOF_DMA_DL3" #define SOF_DMA_UL4 "SOF_DMA_UL4" @@ -726,7 +733,7 @@ static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +static int mt8188_headset_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); @@ -775,68 +782,13 @@ static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) return 0; }; -static int mt8188_rt5682s_codec_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); - struct mt8188_mt6359_priv *priv = soc_card_data->mach_priv; - struct snd_soc_component *component = snd_soc_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 rt5682s 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 rt5682s 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) +static void mt8188_headset_codec_exit(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; snd_soc_component_set_jack(component, NULL, NULL); } -static void mt8188_rt5682s_codec_exit(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = snd_soc_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) @@ -943,6 +895,30 @@ static const struct snd_soc_ops mt8188_sof_be_ops = { .hw_params = mt8188_sof_be_hw_params, }; +static int mt8188_es8326_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + unsigned int rate = params_rate(params); + int ret; + + /* Configure MCLK for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set MCLK %d\n", ret); + return ret; + } + + /* Configure MCLK for cpu */ + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT); +} + +static const struct snd_soc_ops mt8188_es8326_ops = { + .hw_params = mt8188_es8326_hw_params, +}; + static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -1252,7 +1228,7 @@ static void mt8188_fixup_controls(struct snd_soc_card *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 | RT5682S_HS_PRESENT)) { + if (card_data->quirk & (NAU8825_HS_PRESENT | RT5682S_HS_PRESENT | ES8326_HS_PRESENT)) { struct snd_soc_dapm_widget *w, *next_w; for_each_card_widgets_safe(card, w, next_w) { @@ -1293,6 +1269,7 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) struct mt8188_card_data *card_data; struct snd_soc_dai_link *dai_link; bool init_mt6359 = false; + bool init_es8326 = false; bool init_nau8825 = false; bool init_rt5682s = false; bool init_max98390 = false; @@ -1399,7 +1376,14 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) 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; + /* + * The TDM protocol settings with fixed 4 slots are defined in + * mt8188_max98390_ops. Two amps is I2S mode, + * SOC and codec don't require TDM settings. + */ + if (!(card_data->quirk & MAX98390_TWO_AMP)) { + dai_link->ops = &mt8188_max98390_ops; + } if (!init_max98390) { dai_link->init = mt8188_max98390_codec_init; init_max98390 = true; @@ -1407,17 +1391,24 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev) } 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; + dai_link->init = mt8188_headset_codec_init; + dai_link->exit = mt8188_headset_codec_exit; init_nau8825 = true; } } else if (!strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) { dai_link->ops = &mt8188_rt5682s_i2s_ops; if (!init_rt5682s) { - dai_link->init = mt8188_rt5682s_codec_init; - dai_link->exit = mt8188_rt5682s_codec_exit; + dai_link->init = mt8188_headset_codec_init; + dai_link->exit = mt8188_headset_codec_exit; init_rt5682s = true; } + } else if (!strcmp(dai_link->codecs->dai_name, ES8326_CODEC_DAI)) { + dai_link->ops = &mt8188_es8326_ops; + if (!init_es8326) { + dai_link->init = mt8188_headset_codec_init; + dai_link->exit = mt8188_headset_codec_exit; + init_es8326 = true; + } } else { if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) { if (!init_dumb) { @@ -1457,13 +1448,19 @@ static struct mt8188_card_data mt8188_nau8825_card = { static struct mt8188_card_data mt8188_rt5682s_card = { .name = "mt8188_rt5682s", - .quirk = RT5682S_HS_PRESENT, + .quirk = RT5682S_HS_PRESENT | MAX98390_TWO_AMP, +}; + +static struct mt8188_card_data mt8188_es8326_card = { + .name = "mt8188_es8326", + .quirk = ES8326_HS_PRESENT | MAX98390_TWO_AMP, }; static const struct of_device_id mt8188_mt6359_dt_match[] = { { .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, }, { .compatible = "mediatek,mt8188-nau8825", .data = &mt8188_nau8825_card, }, { .compatible = "mediatek,mt8188-rt5682s", .data = &mt8188_rt5682s_card, }, + { .compatible = "mediatek,mt8188-es8326", .data = &mt8188_es8326_card, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match); diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 5bd6addd14..bfcb2c486c 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1208,7 +1208,8 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) dai_link->ignore = 0; } - if (strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0) + if (dai_link->num_codecs && dai_link->codecs[0].dai_name && + strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0) dai_link->ops = &mt8192_rt1015_i2s_ops; if (!dai_link->platforms->name) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 1e33863c85..620d7ade19 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -1795,10 +1795,6 @@ static const struct snd_kcontrol_new mt8195_memif_controls[] = { MT8195_AFE_IRQ_28), }; -static const struct snd_soc_component_driver mt8195_afe_pcm_dai_component = { - .name = "mt8195-afe-pcm-dai", -}; - static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = { [MT8195_AFE_MEMIF_DL2] = { .name = "DL2", @@ -3037,7 +3033,6 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct reset_control *rstc; int i, irq_id, ret; - struct snd_soc_component *component; ret = of_reserved_mem_device_init(dev); if (ret) @@ -3170,36 +3165,12 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) /* register component */ ret = devm_snd_soc_register_component(dev, &mt8195_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(dev, sizeof(*component), GFP_KERNEL); - if (!component) { - ret = -ENOMEM; - goto err_pm_put; - } - - ret = snd_soc_component_initialize(component, - &mt8195_afe_pcm_dai_component, - 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_dai_component\n"); - goto err_pm_put; - } - ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults, ARRAY_SIZE(mt8195_afe_reg_defaults)); if (ret) @@ -3224,8 +3195,6 @@ err_pm_put: static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev) { - snd_soc_unregister_component(&pdev->dev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) mt8195_afe_runtime_suspend(&pdev->dev); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 4feb9fb769..53fd8a897b 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -934,12 +934,11 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -1237,8 +1236,6 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .dpcm_capture = 1, - .init = mt8195_rt5682_init, - .ops = &mt8195_rt5682_etdm_ops, .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, SND_SOC_DAILINK_REG(ETDM2_IN_BE), }, @@ -1249,7 +1246,6 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .dpcm_playback = 1, - .ops = &mt8195_rt5682_etdm_ops, .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, SND_SOC_DAILINK_REG(ETDM1_OUT_BE), }, @@ -1381,7 +1377,7 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link; struct mtk_soc_card_data *soc_card_data; struct mt8195_mt6359_priv *mach_priv; - struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node; + struct device_node *platform_node, *adsp_node, *codec_node, *dp_node, *hdmi_node; struct mt8195_card_data *card_data; int is5682s = 0; int init6359 = 0; @@ -1401,8 +1397,12 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev) if (!card->name) card->name = card_data->name; - if (strstr(card->name, "_5682s")) + if (strstr(card->name, "_5682s")) { + codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682s"); is5682s = 1; + } else + codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682i"); + soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*card_data), GFP_KERNEL); if (!soc_card_data) return -ENOMEM; @@ -1488,12 +1488,27 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev) dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } - } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || - strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { - dai_link->codecs->name = - is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; - dai_link->codecs->dai_name = - is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0) { + if (!codec_node) { + dev_err(&pdev->dev, "Codec not found!\n"); + } else { + dai_link->codecs->of_node = codec_node; + dai_link->codecs->name = NULL; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + dai_link->init = mt8195_rt5682_init; + dai_link->ops = &mt8195_rt5682_etdm_ops; + } + } else if (strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + if (!codec_node) { + dev_err(&pdev->dev, "Codec not found!\n"); + } else { + dai_link->codecs->of_node = codec_node; + dai_link->codecs->name = NULL; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + dai_link->ops = &mt8195_rt5682_etdm_ops; + } } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || strcmp(dai_link->name, "UL_SRC1_BE") == 0 || strcmp(dai_link->name, "UL_SRC2_BE") == 0) { diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e6bca90709..f03c748093 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -35,7 +35,6 @@ config SND_MMP_SOC_SSPA tristate "SoC Audio via MMP SSPA ports" depends on ARCH_MMP select SND_SOC_GENERIC_DMAENGINE_PCM - select SND_ARM help Say Y if you want to add support for codecs attached to the MMP SSPA interface. diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index e7b00d1d9e..762491d6f2 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -221,4 +221,16 @@ config SND_SOC_SC7280 SC7280 SoC-based systems. Say Y or M if you want to use audio device on this SoCs. +config SND_SOC_X1E80100 + tristate "SoC Machine driver for X1E80100 boards" + depends on QCOM_APR && SOUNDWIRE + depends on COMMON_CLK + select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON + select SND_SOC_QCOM_SDW + help + Add support for audio on Qualcomm Technologies Inc. + X1E80100 SoC-based systems. + Say Y or M if you want to use audio device on this SoCs. + endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 254350d9dc..34f3fcb8ee 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -29,6 +29,7 @@ snd-soc-sm8250-objs := sm8250.o snd-soc-sc8280xp-objs := sc8280xp.o snd-soc-qcom-common-objs := common.o snd-soc-qcom-sdw-objs := sdw.o +snd-soc-x1e80100-objs := x1e80100.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o @@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o +obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o #DSP lib obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index efbdbb4dd7..4834a56eaa 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -344,4 +344,4 @@ module_platform_driver(apq8016_sbc_platform_driver); MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 7ee6df02b9..4f6594cc72 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -142,4 +142,4 @@ static struct platform_driver msm_snd_apq8096_driver = { module_platform_driver(msm_snd_apq8096_driver); MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 483bbf53a5..756706d5b4 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -239,4 +239,4 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, return 0; } EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 8e58e814a9..9005c85f8c 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -305,5 +305,5 @@ static struct platform_driver apq8016_lpass_cpu_platform_driver = { module_platform_driver(apq8016_lpass_cpu_platform_driver); MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 88b80ed45c..b0f3e02cb0 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -1294,4 +1294,4 @@ void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev) EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_shutdown); MODULE_DESCRIPTION("QTi LPASS CPU Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c index 24b1a7523a..ce753ebc08 100644 --- a/sound/soc/qcom/lpass-hdmi.c +++ b/sound/soc/qcom/lpass-hdmi.c @@ -251,4 +251,4 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = { EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops); MODULE_DESCRIPTION("QTi LPASS HDMI Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index e0e9ad3582..5c874139f3 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -177,4 +177,4 @@ static struct platform_driver ipq806x_lpass_cpu_platform_driver = { module_platform_driver(ipq806x_lpass_cpu_platform_driver); MODULE_DESCRIPTION("QTi LPASS CPU Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 333c427cfd..addd2c4bdd 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1384,4 +1384,4 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register); MODULE_DESCRIPTION("QTi LPASS Platform Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index 22063b8345..e6bcdf6ed7 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -322,4 +322,4 @@ static struct platform_driver sc7180_lpass_cpu_platform_driver = { module_platform_driver(sc7180_lpass_cpu_platform_driver); MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 2f222bd4ff..27a2bf9a66 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -398,7 +398,7 @@ struct lpass_pcm_data { }; /* register the platform driver from the CPU DAI driver */ -int asoc_qcom_lpass_platform_register(struct platform_device *); +int asoc_qcom_lpass_platform_register(struct platform_device *pdev); void asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev); int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev); diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 5974c7929d..5291deac0a 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -267,6 +267,21 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt); +static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels) +{ + if (num_channels == 1) { + ch_map[0] = PCM_CHANNEL_FL; + } else if (num_channels == 2) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + } else if (num_channels == 4) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LS; + ch_map[3] = PCM_CHANNEL_RS; + } +} + static void apm_populate_container_config(struct apm_container_obj *cfg, struct audioreach_container *cont) { @@ -829,10 +844,15 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, media_format->num_channels = cfg->num_channels; if (num_channels == 1) { - media_format->channel_mapping[0] = PCM_CHANNEL_L; + media_format->channel_mapping[0] = PCM_CHANNEL_FL; } else if (num_channels == 2) { - media_format->channel_mapping[0] = PCM_CHANNEL_L; - media_format->channel_mapping[1] = PCM_CHANNEL_R; + media_format->channel_mapping[0] = PCM_CHANNEL_FL; + media_format->channel_mapping[1] = PCM_CHANNEL_FR; + } else if (num_channels == 4) { + media_format->channel_mapping[0] = PCM_CHANNEL_FL; + media_format->channel_mapping[1] = PCM_CHANNEL_FR; + media_format->channel_mapping[2] = PCM_CHANNEL_LS; + media_format->channel_mapping[3] = PCM_CHANNEL_RS; } rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -864,12 +884,8 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, 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; - } + audioreach_set_channel_mapping(mp3_cfg->channel_mapping, + mcfg->num_channels); break; case SND_AUDIOCODEC_AAC: media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; @@ -1057,7 +1073,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; - if (num_channels > 2) { + if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); return -EINVAL; } @@ -1089,13 +1105,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->q_factor = mcfg->bit_width - 1; media_cfg->bits_per_sample = mcfg->bit_width; - if (num_channels == 1) { - media_cfg->channel_mapping[0] = PCM_CHANNEL_L; - } else if (num_channels == 2) { - media_cfg->channel_mapping[0] = PCM_CHANNEL_L; - media_cfg->channel_mapping[1] = PCM_CHANNEL_R; - - } + audioreach_set_channel_mapping(media_cfg->channel_mapping, + num_channels); rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -1116,7 +1127,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, struct gpr_pkt *pkt; void *p; - if (num_channels > 2) { + if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); return -EINVAL; } @@ -1153,12 +1164,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, 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; - } + audioreach_set_channel_mapping(cfg->channel_mapping, + num_channels); } else { rc = audioreach_set_compr_media_format(header, p, mcfg); if (rc) { diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index e38111ffd7..2c82917b71 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -158,8 +158,6 @@ struct param_id_enc_bitrate_param { #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 #define BIT_WIDTH_16 16 diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 91d39f6ad0..ef7557be5d 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -553,13 +553,13 @@ struct q6afe_port { }; struct afe_cmd_remote_lpass_core_hw_vote_request { - uint32_t hw_block_id; - char client_name[8]; + uint32_t hw_block_id; + char client_name[8]; } __packed; struct afe_cmd_remote_lpass_core_hw_devote_request { - uint32_t hw_block_id; - uint32_t client_handle; + uint32_t hw_block_id; + uint32_t client_handle; } __packed; diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index b799ac7246..00bbd291be 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -123,7 +123,7 @@ static struct snd_pcm_hardware q6apm_dai_hardware_playback = { .fifo_size = 0, }; -static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) +static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *priv) { struct q6apm_dai_rtd *prtd = priv; struct snd_pcm_substream *substream = prtd->substream; @@ -134,7 +134,7 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo prtd->state = Q6APM_STREAM_STOPPED; break; case APM_CLIENT_EVENT_DATA_WRITE_DONE: - spin_lock_irqsave(&prtd->lock, flags); + spin_lock_irqsave(&prtd->lock, flags); prtd->pos += prtd->pcm_count; spin_unlock_irqrestore(&prtd->lock, flags); snd_pcm_period_elapsed(substream); @@ -143,7 +143,7 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo break; case APM_CLIENT_EVENT_DATA_READ_DONE: - spin_lock_irqsave(&prtd->lock, flags); + spin_lock_irqsave(&prtd->lock, flags); prtd->pos += prtd->pcm_count; spin_unlock_irqrestore(&prtd->lock, flags); snd_pcm_period_elapsed(substream); @@ -157,7 +157,7 @@ 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) + void *payload, void *priv) { struct q6apm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; @@ -352,7 +352,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, spin_lock_init(&prtd->lock); prtd->substream = substream; - prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id); if (IS_ERR(prtd->graph)) { dev_err(dev, "%s: Could not allocate memory\n", __func__); ret = PTR_ERR(prtd->graph); @@ -496,7 +496,7 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component, return -ENOMEM; prtd->cstream = stream; - prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id); if (IS_ERR(prtd->graph)) { ret = PTR_ERR(prtd->graph); kfree(prtd); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 0103d8dae5..519e1b3a3f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -35,16 +35,16 @@ enum { #define ASM_LAST_BUFFER_FLAG BIT(30) struct q6asm_flac_cfg { - u32 sample_rate; - u32 ext_sample_rate; - u32 min_frame_size; - u32 max_frame_size; - u16 stream_info_present; - u16 min_blk_size; - u16 max_blk_size; - u16 ch_cfg; - u16 sample_size; - u16 md5_sum; + u32 sample_rate; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 md5_sum; }; struct q6asm_wma_cfg { diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 130b22a34f..70572c83e1 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -545,6 +545,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap if (mod) { int pn, id = 0; + mod->module_id = module_id; mod->max_ip_port = max_ip_port; mod->max_op_port = max_op_port; @@ -1271,7 +1272,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) ret = request_firmware(&fw, tplg_fw_name, dev); if (ret < 0) { - dev_err(dev, "tplg firmware loading %s failed %d \n", tplg_fw_name, ret); + dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); goto err; } diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index b0320a74d5..029780d6fe 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -6,7 +6,6 @@ #include <dt-bindings/sound/sc7180-lpass.h> #include <dt-bindings/sound/qcom,q6afe.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> @@ -578,4 +577,4 @@ static struct platform_driver sc7180_snd_driver = { module_platform_driver(sc7180_snd_driver); MODULE_DESCRIPTION("sc7180 ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index e6efc2d10b..b7fd503a16 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -14,8 +14,6 @@ #include "common.h" #include "sdw.h" -#define DRIVER_NAME "sc8280xp" - struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; @@ -50,6 +48,17 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } +static void sc8280xp_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id]; + + pdata->sruntime[cpu_dai->id] = NULL; + sdw_release_stream(sruntime); +} + static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -110,6 +119,8 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) } static const struct snd_soc_ops sc8280xp_be_ops = { + .startup = qcom_snd_sdw_startup, + .shutdown = sc8280xp_snd_shutdown, .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, @@ -152,13 +163,16 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) if (ret) return ret; - card->driver_name = DRIVER_NAME; + card->driver_name = of_device_get_match_data(dev); sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,sc8280xp-sndcard",}, + {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, + {.compatible = "qcom,sm8450-sndcard", "sm8450"}, + {.compatible = "qcom,sm8550-sndcard", "sm8550"}, + {.compatible = "qcom,sm8650-sndcard", "sm8650"}, {} }; @@ -174,4 +188,4 @@ static struct platform_driver snd_sc8280xp_driver = { module_platform_driver(snd_sc8280xp_driver); MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 252a0f0819..75701546b6 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -625,4 +625,4 @@ static struct platform_driver sdm845_snd_driver = { module_platform_driver(sdm845_snd_driver); MODULE_DESCRIPTION("sdm845 ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c index dd275123d3..7f5089bbe0 100644 --- a/sound/soc/qcom/sdw.c +++ b/sound/soc/qcom/sdw.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2018, Linaro Limited. +// Copyright (c) 2018-2023, Linaro Limited. // Copyright (c) 2018, The Linux Foundation. All rights reserved. #include <dt-bindings/sound/qcom,q6afe.h> @@ -7,6 +7,49 @@ #include <sound/soc.h> #include "sdw.h" +/** + * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card + * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup() + * + * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set + * Soundwire stream runtime to each codec DAI. + * + * The shutdown() callback should call sdw_release_stream() on the same + * sdw_stream_runtime. + * + * Return: 0 or errno + */ +int qcom_snd_sdw_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime; + struct snd_soc_dai *codec_dai; + int ret, i; + + sruntime = sdw_alloc_stream(cpu_dai->name); + if (!sruntime) + return -ENOMEM; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_stream(codec_dai, sruntime, + substream->stream); + if (ret < 0 && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set sdw stream on %s\n", + codec_dai->name); + goto err_set_stream; + } + } + + return 0; + +err_set_stream: + sdw_release_stream(sruntime); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup); + int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream, struct sdw_stream_runtime *sruntime, bool *stream_prepared) @@ -117,4 +160,4 @@ int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream, return 0; } EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/sdw.h b/sound/soc/qcom/sdw.h index d74cbb84da..392e3455f1 100644 --- a/sound/soc/qcom/sdw.h +++ b/sound/soc/qcom/sdw.h @@ -6,6 +6,7 @@ #include <linux/soundwire/sdw.h> +int qcom_snd_sdw_startup(struct snd_pcm_substream *substream); int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream, struct sdw_stream_runtime *runtime, bool *stream_prepared); diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 9cc869fd70..d70df72c01 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -66,7 +66,19 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) default: break; } - return 0; + + return qcom_snd_sdw_startup(substream); +} + +static void sm2450_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + data->sruntime[cpu_dai->id] = NULL; + sdw_release_stream(sruntime); } static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, @@ -103,6 +115,7 @@ static int sm8250_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sm8250_be_ops = { .startup = sm8250_snd_startup, + .shutdown = sm2450_snd_shutdown, .hw_params = sm8250_snd_hw_params, .hw_free = sm8250_snd_hw_free, .prepare = sm8250_snd_prepare, @@ -169,4 +182,4 @@ static struct platform_driver snd_sm8250_driver = { module_platform_driver(snd_sm8250_driver); MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); MODULE_DESCRIPTION("SM8250 ASoC Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index 553165f11d..c8d5ac43a1 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -140,4 +140,4 @@ static struct platform_driver storm_platform_driver = { module_platform_driver(storm_platform_driver); MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c new file mode 100644 index 0000000000..c3c8bf7ffb --- /dev/null +++ b/sound/soc/qcom/x1e80100.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023, Linaro Limited + +#include <dt-bindings/sound/qcom,q6afe.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/soundwire/sdw.h> +#include <sound/pcm.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "common.h" +#include "qdsp6/q6afe.h" +#include "sdw.h" + +struct x1e80100_snd_data { + bool stream_prepared[AFE_PORT_MAX]; + struct snd_soc_card *card; + struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; + struct snd_soc_jack jack; + bool jack_setup; +}; + +static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); +} + +static void x1e80100_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + data->sruntime[cpu_dai->id] = NULL; + sdw_release_stream(sruntime); +} + +static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + 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); + + rate->min = rate->max = 48000; + switch (cpu_dai->id) { + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: + channels->min = 1; + break; + default: + break; + } + + return 0; +} + +static int x1e80100_snd_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 = snd_soc_rtd_to_cpu(rtd, 0); + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + + return qcom_snd_sdw_hw_params(substream, params, &data->sruntime[cpu_dai->id]); +} + +static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + return qcom_snd_sdw_prepare(substream, sruntime, + &data->stream_prepared[cpu_dai->id]); +} + +static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + return qcom_snd_sdw_hw_free(substream, sruntime, + &data->stream_prepared[cpu_dai->id]); +} + +static const struct snd_soc_ops x1e80100_be_ops = { + .startup = qcom_snd_sdw_startup, + .shutdown = x1e80100_snd_shutdown, + .hw_params = x1e80100_snd_hw_params, + .hw_free = x1e80100_snd_hw_free, + .prepare = x1e80100_snd_prepare, +}; + +static void x1e80100_add_be_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm == 1) { + link->init = x1e80100_snd_init; + link->be_hw_params_fixup = x1e80100_be_hw_params_fixup; + link->ops = &x1e80100_be_ops; + } + } +} + +static int x1e80100_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct x1e80100_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + card->owner = THIS_MODULE; + card->dev = dev; + dev_set_drvdata(dev, card); + snd_soc_card_set_drvdata(card, data); + + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + + card->driver_name = "x1e80100"; + x1e80100_add_be_ops(card); + + return devm_snd_soc_register_card(dev, card); +} + +static const struct of_device_id snd_x1e80100_dt_match[] = { + { .compatible = "qcom,x1e80100-sndcard", }, + {} +}; +MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match); + +static struct platform_driver snd_x1e80100_driver = { + .probe = x1e80100_platform_probe, + .driver = { + .name = "snd-x1e80100", + .of_match_table = snd_x1e80100_dt_match, + }, +}; +module_platform_driver(snd_x1e80100_driver); +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); +MODULE_DESCRIPTION("Qualcomm X1E80100 ASoC Machine Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 1a504ebd3a..6c89c73312 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -446,7 +446,7 @@ static const struct rockchip_sound_route rockchip_routes[] = { struct dailink_match_data { const char *compatible; - struct bus_type *bus_type; + const struct bus_type *bus_type; }; static const struct dailink_match_data dailink_match[] = { diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index e95f3d3f04..110ae14dd7 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -157,8 +157,7 @@ SND_SOC_DAILINK_DEFS(primary, SND_SOC_DAILINK_DEFS(mixer, DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_DUMMY())); + DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(secondary, DAILINK_COMP_ARRAY(COMP_EMPTY()), diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 2ef47aa2c7..84601ba43b 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1379,7 +1379,9 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev io->chan = dma_request_channel(mask, shdma_chan_filter, (void *)io->dma_id); #else - io->chan = dma_request_slave_channel(dev, is_play ? "tx" : "rx"); + io->chan = dma_request_chan(dev, is_play ? "tx" : "rx"); + if (IS_ERR(io->chan)) + io->chan = NULL; #endif if (io->chan) { struct dma_slave_config cfg = {}; diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 230c48648a..afd69c6eb6 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -111,6 +111,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) ws = 7; break; } + } else { + /* + * SSI8 is not connected to ADG. + * Thus SSI9 is using ws = 8 + */ + if (id == 9) + ws = 8; } return (0x6 + ws) << 8; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b2bd45e87b..8b5b583a2b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -576,6 +576,28 @@ free_rtd: return NULL; } +static void snd_soc_fill_dummy_dai(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int i; + + /* + * COMP_DUMMY() creates size 0 array on dai_link. + * Fill it as dummy DAI in case of CPU/Codec here. + * Do nothing for Platform. + */ + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->num_cpus == 0 && dai_link->cpus) { + dai_link->num_cpus = 1; + dai_link->cpus = &snd_soc_dummy_dlc; + } + if (dai_link->num_codecs == 0 && dai_link->codecs) { + dai_link->num_codecs = 1; + dai_link->codecs = &snd_soc_dummy_dlc; + } + } +} + static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1015,6 +1037,97 @@ component_dai_empty: return -EINVAL; } +#define MAX_DEFAULT_CH_MAP_SIZE 8 +static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 1, .codec = 1 }, + { .cpu = 2, .codec = 2 }, + { .cpu = 3, .codec = 3 }, + { .cpu = 4, .codec = 4 }, + { .cpu = 5, .codec = 5 }, + { .cpu = 6, .codec = 6 }, + { .cpu = 7, .codec = 7 }, +}; +static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 0, .codec = 1 }, + { .cpu = 0, .codec = 2 }, + { .cpu = 0, .codec = 3 }, + { .cpu = 0, .codec = 4 }, + { .cpu = 0, .codec = 5 }, + { .cpu = 0, .codec = 6 }, + { .cpu = 0, .codec = 7 }, +}; +static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 1, .codec = 0 }, + { .cpu = 2, .codec = 0 }, + { .cpu = 3, .codec = 0 }, + { .cpu = 4, .codec = 0 }, + { .cpu = 5, .codec = 0 }, + { .cpu = 6, .codec = 0 }, + { .cpu = 7, .codec = 0 }, +}; +static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link_ch_map *ch_maps; + int i; + + /* + * dai_link->ch_maps indicates how CPU/Codec are connected. + * It will be a map seen from a larger number of DAI. + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + + /* it should have ch_maps if connection was N:M */ + if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 && + dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) { + dev_err(card->dev, "need to have ch_maps when N:M connection (%s)", + dai_link->name); + return -EINVAL; + } + + /* do nothing if it has own maps */ + if (dai_link->ch_maps) + goto sanity_check; + + /* check default map size */ + if (dai_link->num_cpus > MAX_DEFAULT_CH_MAP_SIZE || + dai_link->num_codecs > MAX_DEFAULT_CH_MAP_SIZE) { + dev_err(card->dev, "soc-core.c needs update default_connection_maps"); + return -EINVAL; + } + + /* Compensate missing map for ... */ + if (dai_link->num_cpus == dai_link->num_codecs) + dai_link->ch_maps = default_ch_map_sync; /* for 1:1 or N:N */ + else if (dai_link->num_cpus < dai_link->num_codecs) + dai_link->ch_maps = default_ch_map_1cpu; /* for 1:N */ + else + dai_link->ch_maps = default_ch_map_1codec; /* for N:1 */ + +sanity_check: + dev_dbg(card->dev, "dai_link %s\n", dai_link->stream_name); + for_each_link_ch_maps(dai_link, i, ch_maps) { + if ((ch_maps->cpu >= dai_link->num_cpus) || + (ch_maps->codec >= dai_link->num_codecs)) { + dev_err(card->dev, + "unexpected dai_link->ch_maps[%d] index (cpu(%d/%d) codec(%d/%d))", + i, + ch_maps->cpu, dai_link->num_cpus, + ch_maps->codec, dai_link->num_codecs); + return -EINVAL; + } + + dev_dbg(card->dev, " [%d] cpu%d <-> codec%d\n", + i, ch_maps->cpu, ch_maps->codec); + } + + return 0; +} + /** * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card * @card: The ASoC card to which the pcm_runtime has @@ -1105,6 +1218,9 @@ static int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!snd_soc_is_matching_component(platform, component)) continue; + if (snd_soc_component_is_dummy(component) && component->num_dai) + continue; + snd_soc_rtd_add_component(rtd, component); } } @@ -1121,8 +1237,13 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, int num_dai_link) { for (int i = 0; i < num_dai_link; i++) { - int ret = snd_soc_add_pcm_runtime(card, dai_link + i); + int ret; + ret = snd_soc_compensate_channel_connection_map(card, dai_link + i); + if (ret < 0) + return ret; + + ret = snd_soc_add_pcm_runtime(card, dai_link + i); if (ret < 0) return ret; } @@ -1206,7 +1327,7 @@ found: * * To avoid such issue, loop from 63 to 0 here. * Small number of SND_SOC_POSSIBLE_xxx will be Hi priority. - * Basic/Default settings of each part and aboves are defined + * Basic/Default settings of each part and above are defined * as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx. */ for (i = 63; i >= 0; i--) { @@ -1752,7 +1873,7 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str) * @flavour: The flavour "differentiator" for the card amongst its peers. * * An Intel machine driver may be used by many different devices but are - * difficult for userspace to differentiate, since machine drivers ususally + * difficult for userspace to differentiate, since machine drivers usually * use their own name as the card short name and leave the card long name * blank. To differentiate such devices and fix bugs due to lack of * device-specific configurations, this function allows DMI info to be used @@ -1773,7 +1894,7 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str) * We only keep number and alphabet characters and a few separator characters * in the card long name since UCM in the user space uses the card long names * as card configuration directory names and AudoConf cannot support special - * charactors like SPACE. + * characters like SPACE. * * Returns 0 on success, otherwise a negative error code. */ @@ -2038,6 +2159,8 @@ static int snd_soc_bind_card(struct snd_soc_card *card) mutex_lock(&client_mutex); snd_soc_card_mutex_lock_root(card); + snd_soc_fill_dummy_dai(card); + snd_soc_dapm_init(&card->dapm, card, NULL); /* check whether any platform is ignore machine FE and using topology */ @@ -2368,7 +2491,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); /** * snd_soc_add_dai_controls - add an array of controls to a DAI. - * Convienience function to add a list of controls. + * Convenience function to add a list of controls. * * @dai: DAI to add controls to * @controls: array of controls to add diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3844f777c8..bffeea8027 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -320,7 +320,8 @@ EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); /* create a new dapm widget */ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( - const struct snd_soc_dapm_widget *_widget) + const struct snd_soc_dapm_widget *_widget, + const char *prefix) { struct snd_soc_dapm_widget *w; @@ -328,13 +329,19 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( if (!w) return NULL; - /* - * w->name is duplicated in caller, but w->sname isn't. - * Duplicate it here if defined - */ + if (prefix) + w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, _widget->name); + else + w->name = kstrdup_const(_widget->name, GFP_KERNEL); + if (!w->name) { + kfree(w); + return NULL; + } + if (_widget->sname) { w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); if (!w->sname) { + kfree_const(w->name); kfree(w); return NULL; } @@ -3629,20 +3636,12 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, { enum snd_soc_dapm_direction dir; struct snd_soc_dapm_widget *w; - const char *prefix; int ret = -ENOMEM; - if ((w = dapm_cnew_widget(widget)) == NULL) + w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm)); + if (!w) goto cnew_failed; - prefix = soc_dapm_prefix(dapm); - if (prefix) - w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); - else - w->name = kstrdup_const(widget->name, GFP_KERNEL); - if (!w->name) - goto name_failed; - switch (w->id) { case snd_soc_dapm_regulator_supply: w->regulator = devm_regulator_get(dapm->dev, widget->name); @@ -3767,7 +3766,6 @@ request_failed: dev_err_probe(dapm->dev, ret, "ASoC: Failed to request %s\n", w->name); kfree_const(w->name); -name_failed: kfree_const(w->sname); kfree(w); cnew_failed: @@ -4438,11 +4436,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; - int i; /* for each BE DAI link... */ for_each_card_rtds(card, rtd) { + struct snd_soc_dai_link_ch_map *ch_maps; + int i; + /* * dynamic FE links have no fixed DAI mapping. * CODEC<->CODEC links have no direct connection. @@ -4450,39 +4451,15 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) if (rtd->dai_link->dynamic) continue; - if (rtd->dai_link->num_cpus == 1) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - dapm_connect_dai_pair(card, rtd, codec_dai, - snd_soc_rtd_to_cpu(rtd, 0)); - } else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - dapm_connect_dai_pair(card, rtd, codec_dai, - snd_soc_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; - } + /* + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_rtd_ch_maps(rtd, i, ch_maps) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu); + codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec); - 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, - snd_soc_rtd_to_cpu(rtd, cpu_id)); - } - } else { - dev_err(card->dev, - "%s: codec number %d < cpu number %d is not supported\n", - __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus); + dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai); } } } diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 2d25748ca7..b27e89ff6a 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -263,7 +263,7 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; int min = mc->min; int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; + unsigned int mask = (1ULL << fls(max)) - 1; unsigned int invert = mc->invert; int val; int ret; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index f6d1b2e117..77ee103b7c 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -292,6 +292,7 @@ static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, int stream, int action) { + struct snd_soc_component *component; struct snd_soc_dai *dai; int i; @@ -299,6 +300,13 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, for_each_rtd_dais(rtd, i, dai) snd_soc_dai_action(dai, stream, action); + + /* Increments/Decrements the active count for components without DAIs */ + for_each_rtd_components(rtd, i, component) { + if (component->num_dai) + continue; + component->active += action; + } } EXPORT_SYMBOL_GPL(snd_soc_runtime_action); @@ -554,6 +562,12 @@ static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, hw->formats &= p->formats; } +static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw, + struct snd_soc_pcm_stream *p) +{ + hw->subformats &= p->subformats; +} + /** * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream * @rtd: ASoC PCM runtime @@ -592,6 +606,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); + soc_pcm_hw_update_subformat(hw, cpu_stream); } cpu_chan_min = hw->channels_min; cpu_chan_max = hw->channels_max; @@ -613,6 +628,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, codec_stream); soc_pcm_hw_update_rate(hw, codec_stream); soc_pcm_hw_update_format(hw, codec_stream); + soc_pcm_hw_update_subformat(hw, codec_stream); } /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ @@ -1047,6 +1063,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + struct snd_soc_dai_link_ch_map *ch_maps; unsigned int ch_mask = 0; int j; @@ -1060,22 +1077,20 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, /* copy params for each cpu */ tmp_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. + * see + * soc.h :: [dai_link->ch_maps Image sample] */ - 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; - } + for_each_rtd_ch_maps(rtd, j, ch_maps) + if (ch_maps->cpu == i) + ch_mask |= ch_maps->ch_mask; /* fixup cpu channel number */ if (ch_mask) soc_pcm_codec_params_fixup(&tmp_params, ch_mask); -hw_params: ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out; @@ -1710,6 +1725,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); + soc_pcm_hw_update_subformat(hw, cpu_stream); } } @@ -1747,6 +1763,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); soc_pcm_hw_update_format(hw, codec_stream); + soc_pcm_hw_update_subformat(hw, codec_stream); } } } @@ -2823,35 +2840,20 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, } } } else { + struct snd_soc_dai_link_ch_map *ch_maps; struct snd_soc_dai *codec_dai; /* Adapt stream for codec2codec links */ 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 (dai_link->num_cpus == 1) { - cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - } else if (dai_link->num_cpus == dai_link->num_codecs) { - cpu_dai = snd_soc_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 = snd_soc_rtd_to_cpu(rtd, cpu_id); - } else { - dev_err(rtd->card->dev, - "%s codec number %d < cpu number %d is not supported\n", - __func__, rtd->dai_link->num_codecs, - rtd->dai_link->num_cpus); - return -EINVAL; - } + /* + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_rtd_ch_maps(rtd, i, ch_maps) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu); + codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec); if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c index 2cd3540cec..70cbccc42a 100644 --- a/sound/soc/soc-topology-test.c +++ b/sound/soc/soc-topology-test.c @@ -9,6 +9,7 @@ #include <sound/core.h> #include <sound/soc.h> #include <sound/soc-topology.h> +#include <kunit/device.h> #include <kunit/test.h> /* ===== HELPER FUNCTIONS =================================================== */ @@ -21,26 +22,19 @@ */ static struct device *test_dev; -static struct device_driver test_drv = { - .name = "sound-soc-topology-test-driver", -}; - static int snd_soc_tplg_test_init(struct kunit *test) { - test_dev = root_device_register("sound-soc-topology-test"); + test_dev = kunit_device_register(test, "sound-soc-topology-test"); test_dev = get_device(test_dev); if (!test_dev) return -ENODEV; - test_dev->driver = &test_drv; - return 0; } static void snd_soc_tplg_test_exit(struct kunit *test) { put_device(test_dev); - root_device_unregister(test_dev); } /* diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index a741ed96e7..32ffd345e0 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -126,6 +126,17 @@ config SND_SOC_SOF_STRICT_ABI_CHECKS If you are not involved in SOF releases and CI development, select "N". +config SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION + bool "SOF allow fallback to newer IPC version" + help + This option will allow the kernel to try to 'fallback' to a newer IPC + version if there are missing firmware files to satisfy the default IPC + version. + IPC version fallback to older versions is not affected by this option, + it is always available. + Say Y if you are involved in SOF development and need this option. + If not, select N. + config SND_SOC_SOF_DEBUG bool "SOF debugging features" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index ef6fd43d0b..3624124575 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ + fw-file-profile.o # IPC implementations ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index 3a0c7688dc..2d72c6d55d 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -13,7 +13,6 @@ #include "../sof-priv.h" #include "../sof-audio.h" #include "../ops.h" -#include "../sof-audio.h" #include "acp.h" #include "acp-dsp-offset.h" #include <sound/sof/xtensa.h> diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index fcb54f545f..b44b1b1adb 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Balakishore Pati <Balakishore.pati@amd.com> // Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index e05eb7a86d..aad904839b 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -173,7 +173,7 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) adata = sdev->pdata->hw_pdata; - if (adata->signed_fw_image) + if (adata->quirks && adata->quirks->signed_fw_image) size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; else size_fw = adata->fw_bin_size; @@ -267,29 +267,49 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata = plat_data->hw_pdata; + const char *fw_filename; int ret; - ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev); + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_code_bin); + if (!fw_filename) + return -ENOMEM; + + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware code bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0, - (void *)sdev->basefw.fw->data, sdev->basefw.fw->size); + (void *)sdev->basefw.fw->data, + sdev->basefw.fw->size); + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_data_bin); + if (!fw_filename) + return -ENOMEM; - ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev); + ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware data bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, - (void *)adata->fw_dbin->data, adata->fw_dbin->size); + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); return ret; } EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index c6f637f298..1b6f5724c8 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -20,21 +20,23 @@ #include "acp.h" #include "acp-dsp-offset.h" -#define SECURED_FIRMWARE 1 - static bool enable_fw_debug; module_param(enable_fw_debug, bool, 0444); MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); +static struct acp_quirk_entry quirk_valve_galileo = { + .signed_fw_image = true, + .skip_iram_dram_size_mod = true, +}; + const struct dmi_system_id acp_sof_quirk_table[] = { { - /* Valve Jupiter device */ + /* Steam Deck OLED device */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Valve"), DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), }, - .driver_data = (void *)SECURED_FIRMWARE, + .driver_data = &quirk_valve_galileo, }, {} }; @@ -255,7 +257,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } - if (adata->signed_fw_image) + if (adata->quirks && adata->quirks->signed_fw_image) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); @@ -278,6 +280,17 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, return ret; } + /* psp_send_cmd only required for vangogh platform (rev - 5) */ + if (desc->rev == 5 && !(adata->quirks && adata->quirks->skip_iram_dram_size_mod)) { + /* Modify IRAM and DRAM size */ + ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | IRAM_DRAM_FENCE_2); + if (ret) + return ret; + ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | MBOX_ISREADY_FLAG); + if (ret) + return ret; + } + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER, fw_qualifier, fw_qualifier & DSP_FW_RUN_ENABLE, ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US); @@ -343,13 +356,15 @@ static irqreturn_t acp_irq_thread(int irq, void *context) const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); unsigned int count = ACP_HW_SEM_RETRY_COUNT; - while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) { - /* Wait until acquired HW Semaphore lock or timeout */ - count--; - if (!count) { - dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__); - return IRQ_NONE; - } + spin_lock_irq(&sdev->ipc_lock); + /* Wait until acquired HW Semaphore lock or timeout */ + while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset) && --count) + ; + spin_unlock_irq(&sdev->ipc_lock); + + if (!count) { + dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__); + return IRQ_NONE; } sof_ops(sdev)->irq_thread(irq, sdev); @@ -480,7 +495,6 @@ EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); - struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; const struct sof_amd_acp_desc *chip; const struct dmi_system_id *dmi_id; @@ -522,6 +536,10 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) goto unregister_dev; } + ret = acp_init(sdev); + if (ret < 0) + goto free_smn_dev; + sdev->ipc_irq = pci->irq; ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, IRQF_SHARED, "AudioDSP", sdev); @@ -531,10 +549,6 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) goto free_smn_dev; } - ret = acp_init(sdev); - if (ret < 0) - goto free_ipc_irq; - sdev->dsp_box.offset = 0; sdev->dsp_box.size = BOX_SIZE_512; @@ -544,28 +558,27 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; sdev->debug_box.size = BOX_SIZE_1024; - adata->signed_fw_image = false; dmi_id = dmi_first_match(acp_sof_quirk_table); - if (dmi_id && dmi_id->driver_data) { - adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "%s/sof-%s-code.bin", - plat_data->fw_filename_prefix, - chip->name); - if (!adata->fw_code_bin) { - ret = -ENOMEM; - goto free_ipc_irq; + if (dmi_id) { + adata->quirks = dmi_id->driver_data; + + if (adata->quirks->signed_fw_image) { + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-code.bin", + chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-data.bin", + chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } } - - adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "%s/sof-%s-data.bin", - plat_data->fw_filename_prefix, - chip->name); - if (!adata->fw_data_bin) { - ret = -ENOMEM; - goto free_ipc_irq; - } - - adata->signed_fw_image = dmi_id->driver_data; } adata->enable_fw_debug = enable_fw_debug; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index c536cfde0e..7d4a8b6b3c 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -74,9 +74,14 @@ #define MP0_C2PMSG_114_REG 0x3810AC8 #define MP0_C2PMSG_73_REG 0x3810A24 #define MBOX_ACP_SHA_DMA_COMMAND 0x70000 +#define MBOX_ACP_IRAM_DRAM_FENCE_COMMAND 0x80000 #define MBOX_DELAY_US 1000 #define MBOX_READY_MASK 0x80000000 #define MBOX_STATUS_MASK 0xFFFF +#define MBOX_ISREADY_FLAG 0x40000000 +#define IRAM_DRAM_FENCE_0 0X0 +#define IRAM_DRAM_FENCE_1 0X01 +#define IRAM_DRAM_FENCE_2 0X02 #define BOX_SIZE_512 0x200 #define BOX_SIZE_1024 0x400 @@ -188,6 +193,11 @@ struct sof_amd_acp_desc { u32 probe_reg_offset; }; +struct acp_quirk_entry { + bool signed_fw_image; + bool skip_iram_dram_size_mod; +}; + /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; @@ -208,7 +218,7 @@ struct acp_dev_data { u8 *data_buf; dma_addr_t sram_dma_addr; u8 *sram_data_buf; - bool signed_fw_image; + struct acp_quirk_entry *quirks; struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c index de15d21aa6..bc6ffdb547 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -143,6 +143,7 @@ EXPORT_SYMBOL_NS(sof_vangogh_ops, SND_SOC_SOF_AMD_COMMON); int sof_vangogh_ops_init(struct snd_sof_dev *sdev) { const struct dmi_system_id *dmi_id; + struct acp_quirk_entry *quirks; /* common defaults */ memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); @@ -151,8 +152,12 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); dmi_id = dmi_first_match(acp_sof_quirk_table); - if (dmi_id && dmi_id->driver_data) - sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + if (dmi_id) { + quirks = dmi_id->driver_data; + + if (quirks->signed_fw_image) + sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + } return 0; } diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d7b090224f..425b023b03 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <sound/sof.h> #include "sof-priv.h" +#include "sof-of-dev.h" #include "ops.h" #define CREATE_TRACE_POINTS @@ -143,6 +144,233 @@ void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state) } EXPORT_SYMBOL(sof_set_fw_state); +static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_sof_of_mach *mach = desc->of_machines; + + if (!mach) + return NULL; + + for (; mach->compatible; mach++) { + if (of_machine_is_compatible(mach->compatible)) { + sof_pdata->tplg_filename = mach->sof_tplg_filename; + if (mach->fw_filename) + sof_pdata->fw_filename = mach->fw_filename; + + return mach; + } + } + + return NULL; +} + +/* SOF Driver enumeration */ +static int sof_machine_check(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { + const struct snd_sof_of_mach *of_mach; + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + goto nocodec; + + /* find machine */ + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + + if (sof_pdata->subsystem_id_set) { + mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor; + mach->mach_params.subsystem_device = sof_pdata->subsystem_device; + mach->mach_params.subsystem_id_set = true; + } + + snd_sof_set_mach_params(mach, sdev); + return 0; + } + + of_mach = sof_of_machine_select(sdev); + if (of_mach) { + sof_pdata->of_machine = of_mach; + return 0; + } + + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) { + dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; + } + } else { + dev_warn(sdev->dev, "Force to use nocodec mode\n"); + } + +nocodec: + /* select nocodec mode */ + dev_warn(sdev->dev, "Using nocodec machine driver\n"); + mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); + if (!mach) + return -ENOMEM; + + mach->drv_name = "sof-nocodec"; + if (!sof_pdata->tplg_filename) + sof_pdata->tplg_filename = desc->nocodec_tplg_filename; + + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); + + return 0; +} + +static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + struct sof_loadable_file_profile out_profile; + struct device *dev = sdev->dev; + int ret; + + if (base_profile->ipc_type != plat_data->desc->ipc_default) + dev_info(dev, + "Module parameter used, overriding default IPC %d to %d\n", + plat_data->desc->ipc_default, base_profile->ipc_type); + + if (base_profile->fw_path) + dev_dbg(dev, "Module parameter used, changed fw path to %s\n", + base_profile->fw_path); + else if (base_profile->fw_path_postfix) + dev_dbg(dev, "Path postfix appended to default fw path: %s\n", + base_profile->fw_path_postfix); + + if (base_profile->fw_lib_path) + dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n", + base_profile->fw_lib_path); + else if (base_profile->fw_lib_path_postfix) + dev_dbg(dev, "Path postfix appended to default fw_lib path: %s\n", + base_profile->fw_lib_path_postfix); + + if (base_profile->fw_name) + dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", + base_profile->fw_name); + + if (base_profile->tplg_path) + dev_dbg(dev, "Module parameter used, changed tplg path to %s\n", + base_profile->tplg_path); + + if (base_profile->tplg_name) + dev_dbg(dev, "Module parameter used, changed tplg name to %s\n", + base_profile->tplg_name); + + ret = sof_create_ipc_file_profile(sdev, base_profile, &out_profile); + if (ret) + return ret; + + plat_data->ipc_type = out_profile.ipc_type; + plat_data->fw_filename = out_profile.fw_name; + plat_data->fw_filename_prefix = out_profile.fw_path; + plat_data->fw_lib_prefix = out_profile.fw_lib_path; + plat_data->tplg_filename_prefix = out_profile.tplg_path; + + return 0; +} + +static int validate_sof_ops(struct snd_sof_dev *sdev) +{ + int ret; + + /* init ops, if necessary */ + ret = sof_ops_init(sdev); + if (ret < 0) + return ret; + + /* check all mandatory ops */ + if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { + dev_err(sdev->dev, "missing mandatory ops\n"); + sof_ops_free(sdev); + return -EINVAL; + } + + if (!sdev->dspless_mode_selected && + (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || + !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || + !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { + dev_err(sdev->dev, "missing mandatory DSP ops\n"); + sof_ops_free(sdev); + return -EINVAL; + } + + return 0; +} + +static int sof_init_sof_ops(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + + /* check IPC support */ + if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) { + dev_err(sdev->dev, + "ipc_type %d is not supported on this platform, mask is %#x\n", + base_profile->ipc_type, plat_data->desc->ipc_supported_mask); + return -EINVAL; + } + + /* + * Save the selected IPC type and a topology name override before + * selecting ops since platform code might need this information + */ + plat_data->ipc_type = base_profile->ipc_type; + plat_data->tplg_filename = base_profile->tplg_name; + + return validate_sof_ops(sdev); +} + +static int sof_init_environment(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base; + int ret; + + /* probe the DSP hardware */ + ret = snd_sof_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to probe DSP %d\n", ret); + sof_ops_free(sdev); + return ret; + } + + /* check machine info */ + ret = sof_machine_check(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to get machine info %d\n", ret); + goto err_machine_check; + } + + ret = sof_select_ipc_and_paths(sdev); + if (!ret && plat_data->ipc_type != base_profile->ipc_type) { + /* IPC type changed, re-initialize the ops */ + sof_ops_free(sdev); + + ret = validate_sof_ops(sdev); + if (ret < 0) { + snd_sof_remove(sdev); + return ret; + } + } + +err_machine_check: + if (ret) { + snd_sof_remove(sdev); + sof_ops_free(sdev); + } + + return ret; +} + /* * FW Boot State Transition Diagram * @@ -188,23 +416,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) struct snd_sof_pdata *plat_data = sdev->pdata; int ret; - /* probe the DSP hardware */ - ret = snd_sof_probe(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); - goto probe_err; - } + /* Initialize loadable file paths and check the environment validity */ + ret = sof_init_environment(sdev); + if (ret) + return ret; sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); - /* check machine info */ - ret = sof_machine_check(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to get machine info %d\n", - ret); - goto dsp_err; - } - /* set up platform component driver */ snd_sof_new_platform_drv(sdev); @@ -324,9 +542,7 @@ fw_load_err: ipc_err: dbg_err: snd_sof_free_debug(sdev); -dsp_err: snd_sof_remove(sdev); -probe_err: snd_sof_remove_late(sdev); sof_ops_free(sdev); @@ -381,34 +597,11 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) } } - /* check IPC support */ - if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { - dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", - plat_data->ipc_type, plat_data->desc->ipc_supported_mask); - return -EINVAL; - } - - /* init ops, if necessary */ - ret = sof_ops_init(sdev); - if (ret < 0) + /* Initialize sof_ops based on the initial selected IPC version */ + ret = sof_init_sof_ops(sdev); + if (ret) return ret; - /* check all mandatory ops */ - if (!sof_ops(sdev) || !sof_ops(sdev)->probe) { - sof_ops_free(sdev); - dev_err(dev, "missing mandatory ops\n"); - return -EINVAL; - } - - if (!sdev->dspless_mode_selected && - (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read || - !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || - !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) { - sof_ops_free(sdev); - dev_err(dev, "missing mandatory DSP ops\n"); - return -EINVAL; - } - INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); @@ -527,6 +720,40 @@ int snd_sof_device_shutdown(struct device *dev) } EXPORT_SYMBOL(snd_sof_device_shutdown); +/* Machine driver registering and unregistering */ +int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = pdata; + const char *drv_name; + const void *mach; + int size; + + drv_name = plat_data->machine->drv_name; + mach = plat_data->machine; + size = sizeof(*plat_data->machine); + + /* register machine driver, pass machine info as pdata */ + plat_data->pdev_mach = + platform_device_register_data(sdev->dev, drv_name, + PLATFORM_DEVID_NONE, mach, size); + if (IS_ERR(plat_data->pdev_mach)) + return PTR_ERR(plat_data->pdev_mach); + + dev_dbg(sdev->dev, "created machine %s\n", + dev_name(&plat_data->pdev_mach->dev)); + + return 0; +} +EXPORT_SYMBOL(sof_machine_register); + +void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = pdata; + + platform_device_unregister(plat_data->pdev_mach); +} +EXPORT_SYMBOL(sof_machine_unregister); + MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c new file mode 100644 index 0000000000..b56b142324 --- /dev/null +++ b/sound/soc/sof/fw-file-profile.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// + +#include <linux/firmware.h> +#include <sound/sof.h> +#include <sound/sof/ext_manifest4.h> +#include "sof-priv.h" + +static int sof_test_firmware_file(struct device *dev, + struct sof_loadable_file_profile *profile, + enum sof_ipc_type *ipc_type_to_adjust) +{ + enum sof_ipc_type fw_ipc_type; + const struct firmware *fw; + const char *fw_filename; + const u32 *magic; + int ret; + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, + profile->fw_name); + if (!fw_filename) + return -ENOMEM; + + ret = firmware_request_nowarn(&fw, fw_filename, dev); + if (ret < 0) { + dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); + kfree(fw_filename); + return ret; + } + + /* firmware file exists, check the magic number */ + magic = (const u32 *)fw->data; + switch (*magic) { + case SOF_EXT_MAN_MAGIC_NUMBER: + fw_ipc_type = SOF_IPC_TYPE_3; + break; + case SOF_EXT_MAN4_MAGIC_NUMBER: + fw_ipc_type = SOF_IPC_TYPE_4; + break; + default: + dev_err(dev, "Invalid firmware magic: %#x\n", *magic); + ret = -EINVAL; + goto out; + } + + if (ipc_type_to_adjust) { + *ipc_type_to_adjust = fw_ipc_type; + } else if (fw_ipc_type != profile->ipc_type) { + dev_err(dev, + "ipc type mismatch between %s and expected: %d vs %d\n", + fw_filename, fw_ipc_type, profile->ipc_type); + ret = -EINVAL; + } +out: + release_firmware(fw); + kfree(fw_filename); + + return ret; +} + +static int sof_test_topology_file(struct device *dev, + struct sof_loadable_file_profile *profile) +{ + const struct firmware *fw; + const char *tplg_filename; + int ret; + + if (!profile->tplg_path || !profile->tplg_name) + return 0; + + tplg_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->tplg_path, + profile->tplg_name); + if (!tplg_filename) + return -ENOMEM; + + ret = firmware_request_nowarn(&fw, tplg_filename, dev); + if (!ret) + release_firmware(fw); + else + dev_dbg(dev, "Failed to open topology file: %s\n", tplg_filename); + + kfree(tplg_filename); + + return ret; +} + +static bool sof_platform_uses_generic_loader(struct snd_sof_dev *sdev) +{ + return (sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_raw || + sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_memcpy); +} + +static int +sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, + const struct sof_dev_desc *desc, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + bool fw_lib_path_allocated = false; + struct device *dev = sdev->dev; + bool fw_path_allocated = false; + int ret = 0; + + /* firmware path */ + if (base_profile->fw_path) { + out_profile->fw_path = base_profile->fw_path; + } else if (base_profile->fw_path_postfix) { + out_profile->fw_path = devm_kasprintf(dev, GFP_KERNEL, "%s/%s", + desc->default_fw_path[ipc_type], + base_profile->fw_path_postfix); + if (!out_profile->fw_path) + return -ENOMEM; + + fw_path_allocated = true; + } else { + out_profile->fw_path = desc->default_fw_path[ipc_type]; + } + + /* firmware filename */ + if (base_profile->fw_name) + out_profile->fw_name = base_profile->fw_name; + else + out_profile->fw_name = desc->default_fw_filename[ipc_type]; + + /* + * Check the custom firmware path/filename and adjust the ipc_type to + * match with the existing file for the remaining path configuration. + * + * For default path and firmware name do a verification before + * continuing further. + */ + if ((base_profile->fw_path || base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) { + ret = sof_test_firmware_file(dev, out_profile, &ipc_type); + if (ret) + return ret; + + if (!(desc->ipc_supported_mask & BIT(ipc_type))) { + dev_err(dev, "Unsupported IPC type %d needed by %s/%s\n", + ipc_type, out_profile->fw_path, + out_profile->fw_name); + return -EINVAL; + } + } + + /* firmware library path */ + if (base_profile->fw_lib_path) { + out_profile->fw_lib_path = base_profile->fw_lib_path; + } else if (desc->default_lib_path[ipc_type]) { + if (base_profile->fw_lib_path_postfix) { + out_profile->fw_lib_path = devm_kasprintf(dev, + GFP_KERNEL, "%s/%s", + desc->default_lib_path[ipc_type], + base_profile->fw_lib_path_postfix); + if (!out_profile->fw_lib_path) { + ret = -ENOMEM; + goto out; + } + + fw_lib_path_allocated = true; + } else { + out_profile->fw_lib_path = desc->default_lib_path[ipc_type]; + } + } + + if (base_profile->fw_path_postfix) + out_profile->fw_path_postfix = base_profile->fw_path_postfix; + + if (base_profile->fw_lib_path_postfix) + out_profile->fw_lib_path_postfix = base_profile->fw_lib_path_postfix; + + /* topology path */ + if (base_profile->tplg_path) + out_profile->tplg_path = base_profile->tplg_path; + else + out_profile->tplg_path = desc->default_tplg_path[ipc_type]; + + /* topology name */ + out_profile->tplg_name = plat_data->tplg_filename; + + out_profile->ipc_type = ipc_type; + + /* Test only default firmware file */ + if ((!base_profile->fw_path && !base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) + ret = sof_test_firmware_file(dev, out_profile, NULL); + + if (!ret) + ret = sof_test_topology_file(dev, out_profile); + +out: + if (ret) { + /* Free up path strings created with devm_kasprintf */ + if (fw_path_allocated) + devm_kfree(dev, out_profile->fw_path); + if (fw_lib_path_allocated) + devm_kfree(dev, out_profile->fw_lib_path); + + memset(out_profile, 0, sizeof(*out_profile)); + } + + return ret; +} + +static void +sof_print_missing_firmware_info(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, + struct sof_loadable_file_profile *base_profile) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const struct sof_dev_desc *desc = plat_data->desc; + struct device *dev = sdev->dev; + int ipc_type_count, i; + char *marker; + + dev_err(dev, "SOF firmware and/or topology file not found.\n"); + dev_info(dev, "Supported default profiles\n"); + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) + ipc_type_count = SOF_IPC_TYPE_COUNT - 1; + else + ipc_type_count = base_profile->ipc_type; + + for (i = 0; i <= ipc_type_count; i++) { + if (!(desc->ipc_supported_mask & BIT(i))) + continue; + + if (i == ipc_type) + marker = "Requested"; + else + marker = "Fallback"; + + dev_info(dev, "- ipc type %d (%s):\n", i, marker); + if (base_profile->fw_path_postfix) + dev_info(dev, " Firmware file: %s/%s/%s\n", + desc->default_fw_path[i], + base_profile->fw_path_postfix, + desc->default_fw_filename[i]); + else + dev_info(dev, " Firmware file: %s/%s\n", + desc->default_fw_path[i], + desc->default_fw_filename[i]); + + dev_info(dev, " Topology file: %s/%s\n", + desc->default_tplg_path[i], + plat_data->tplg_filename); + } + + if (base_profile->fw_path || base_profile->fw_name || + base_profile->tplg_path || base_profile->tplg_name) + dev_info(dev, "Verify the path/name override module parameters.\n"); + + dev_info(dev, "Check if you have 'sof-firmware' package installed.\n"); + dev_info(dev, "Optionally it can be manually downloaded from:\n"); + dev_info(dev, " https://github.com/thesofproject/sof-bin/\n"); +} + +static void sof_print_profile_info(struct snd_sof_dev *sdev, + enum sof_ipc_type ipc_type, + struct sof_loadable_file_profile *profile) +{ + struct device *dev = sdev->dev; + + if (ipc_type != profile->ipc_type) + dev_info(dev, + "Using fallback IPC type %d (requested type was %d)\n", + profile->ipc_type, ipc_type); + + dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type); + + /* The firmware path is only valid when generic loader is used */ + if (sof_platform_uses_generic_loader(sdev)) + dev_info(dev, " Firmware file: %s/%s\n", + profile->fw_path, profile->fw_name); + + if (profile->fw_lib_path) + dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); + dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name); +} + +int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile) +{ + const struct sof_dev_desc *desc = sdev->pdata->desc; + int ipc_fallback_start, ret, i; + + memset(out_profile, 0, sizeof(*out_profile)); + + ret = sof_file_profile_for_ipc_type(sdev, base_profile->ipc_type, desc, + base_profile, out_profile); + if (!ret) + goto out; + + /* + * No firmware file was found for the requested IPC type, as fallback + * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is selected, check + * all IPC versions in a backwards direction (from newer to older) + * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is not selected, + * check only older IPC versions than the selected/default version + */ + if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) + ipc_fallback_start = SOF_IPC_TYPE_COUNT - 1; + else + ipc_fallback_start = (int)base_profile->ipc_type - 1; + + for (i = ipc_fallback_start; i >= 0 ; i--) { + if (i == base_profile->ipc_type || + !(desc->ipc_supported_mask & BIT(i))) + continue; + + ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile, + out_profile); + if (!ret) + break; + } + +out: + if (ret) + sof_print_missing_firmware_info(sdev, base_profile->ipc_type, + base_profile); + else + sof_print_profile_info(sdev, base_profile->ipc_type, out_profile); + + return ret; +} +EXPORT_SYMBOL(sof_create_ipc_file_profile); diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 170740bce8..d777e70250 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -603,6 +603,7 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 2680f061ba..1b976fa500 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -313,6 +313,13 @@ static struct snd_soc_dai_driver imx8m_dai[] = { .channels_max = 32, }, }, +{ + .name = "micfil", + .capture = { + .channels_min = 1, + .channels_max = 8, + }, +}, }; static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, @@ -465,6 +472,7 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c index ca6edb85ff..2badca7578 100644 --- a/sound/soc/sof/imx/imx8ulp.c +++ b/sound/soc/sof/imx/imx8ulp.c @@ -463,6 +463,7 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, /* PM */ diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 776b66389c..dee6c7f73e 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -55,7 +55,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data; - sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 598cf50aba..85e1e4760d 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -402,7 +402,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data; - sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 26105d8f1b..88c236b9a0 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -57,6 +57,9 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, + .get_dai_frame_counter = hda_dsp_get_stream_llp, + .get_host_byte_counter = hda_dsp_get_stream_ldp, + /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 87935554b1..92ec5db467 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -7,6 +7,7 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> +#include <sound/hda_register.h> #include <sound/hda-mlink.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> @@ -208,14 +209,16 @@ static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); unsigned int link_bps; unsigned int format_val; + unsigned int bits; 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); + bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, + link_bps); + format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params)); 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)); @@ -238,11 +241,11 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) { unsigned int format_val; + unsigned int bits; - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), - params_physical_width(params), - 0); + bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, + params_physical_width(params)); + format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params)); 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)); @@ -258,6 +261,7 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, snd_pcm_format_t format; unsigned int channels; unsigned int width; + unsigned int bits; channels = params_channels(params); format = params_format(params); @@ -269,10 +273,8 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, width = 32; } - format_val = snd_hdac_calc_stream_format(params_rate(params), channels, - format, - width, - 0); + bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width); + format_val = snd_hdac_stream_format(channels, bits, params_rate(params)); dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, params_rate(params), channels, format); @@ -361,6 +363,16 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_stream_clear(hext_stream); + + /* + * Save the LLP registers in case the stream is + * restarting due PAUSE_RELEASE, or START without a pcm + * close/open since in this case the LLP register is not reset + * to 0 and the delay calculation will return with invalid + * results. + */ + hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL); + hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU); break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index a20deaf3b4..f4cbc0ad5d 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -621,6 +621,9 @@ void hda_ops_free(struct snd_sof_dev *sdev) if (!hda_use_tplg_nhlt) intel_nhlt_free(ipc4_data->nhlt); + + kfree(sdev->private); + sdev->private = NULL; } } EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 46fb2d1425..b81f231abe 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -510,9 +510,8 @@ cleanup: return chip_info->init_core_mask; /* disable DSP */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, - SOF_HDA_REG_PP_PPCTL, - SOF_HDA_PPCTL_GPROCEN, 0); + hda_dsp_ctrl_ppcap_enable(sdev, false); + return ret; } @@ -520,14 +519,15 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct hdac_ext_stream *hext_stream; struct firmware stripped_firmware; struct sof_ipc4_msg msg = {}; struct snd_dma_buffer dmab; int ret, ret1; - /* IMR booting will restore the libraries as well, skip the loading */ - if (reload && hda->booted_from_imr) + /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */ + if (reload && hda->booted_from_imr && ipc4_data->fw_context_save) return 0; /* the fw_lib has been verified during loading, we can trust the validity here */ diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 18f07364d2..d7b446f3f9 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -259,8 +259,37 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32); + /* + * The dsp_max_burst_size_in_ms is the length of the maximum burst size + * of the host DMA in the ALSA buffer. + * + * On playback start the DMA will transfer dsp_max_burst_size_in_ms + * amount of data in one initial burst to fill up the host DMA buffer. + * Consequent DMA burst sizes are shorter and their length can vary. + * To make sure that userspace allocate large enough ALSA buffer we need + * to place a constraint on the buffer time. + * + * On capture the DMA will transfer 1ms chunks. + * + * Exact dsp_max_burst_size_in_ms constraint is racy, so set the + * constraint to a minimum of 2x dsp_max_burst_size_in_ms. + */ + if (spcm->stream[direction].dsp_max_burst_size_in_ms) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, + spcm->stream[direction].dsp_max_burst_size_in_ms * USEC_PER_MSEC * 2, + UINT_MAX); + /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; + + /* + * Reset the llp cache values (they are used for LLP compensation in + * case the counter is not reset) + */ + dsp_stream->pplcllpl = 0; + dsp_stream->pplcllpu = 0; + return 0; } diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index f2ebadbbcc..c603aaf0ca 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1054,3 +1054,73 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, return pos; } + +#define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l)) + +/** + * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream + * @sdev: SOF device + * @component: ASoC component + * @substream: PCM substream + * + * Returns the raw Linear Link Position value + */ +u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + u32 llp_l, llp_u; + + /* + * The pplc_addr have been calculated during probe in + * hda_dsp_stream_init(): + * pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + * SOF_HDA_PPLC_BASE + + * SOF_HDA_PPLC_MULTI * total_stream + + * SOF_HDA_PPLC_INTERVAL * stream_index + * + * Use this pre-calculated address to avoid repeated re-calculation. + */ + llp_l = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL); + llp_u = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU); + + /* Compensate the LLP counter with the saved offset */ + if (hext_stream->pplcllpl || hext_stream->pplcllpu) + return merge_u64(llp_u, llp_l) - + merge_u64(hext_stream->pplcllpu, hext_stream->pplcllpl); + + return merge_u64(llp_u, llp_l); +} + +/** + * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream + * @sdev: SOF device + * @component: ASoC component + * @substream: PCM substream + * + * Returns the raw Linear Link Position value + */ +u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + u32 ldp_l, ldp_u; + + /* + * The pphc_addr have been calculated during probe in + * hda_dsp_stream_init(): + * pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + * SOF_HDA_PPHC_BASE + + * SOF_HDA_PPHC_INTERVAL * stream_index + * + * Use this pre-calculated address to avoid repeated re-calculation. + */ + ldp_l = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPL); + ldp_u = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPU); + + return ((u64)ldp_u << 32) | ldp_l; +} diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 744c0dd576..fe4ae349da 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1350,8 +1350,7 @@ void hda_dsp_remove(struct snd_sof_dev *sdev) if (!sdev->dspless_mode_selected) { /* disable DSP IRQ */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - SOF_HDA_PPCTL_PIE, 0); + hda_dsp_ctrl_ppcap_int_enable(sdev, false); } /* disable CIE and GIE interrupts */ @@ -1366,8 +1365,7 @@ void hda_dsp_remove(struct snd_sof_dev *sdev) chip->power_down_dsp(sdev); /* disable DSP */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - SOF_HDA_PPCTL_GPROCEN, 0); + hda_dsp_ctrl_ppcap_enable(sdev, false); skip_disable_dsp: free_irq(sdev->ipc_irq, sdev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1592e27bc1..efb42117df 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -657,6 +657,12 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, int direction, bool can_sleep); +u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); +u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 8e29d6bb6f..0406985919 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -123,7 +123,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data; - sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index db94b45e53..555a51c688 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -118,9 +118,11 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) sof_lnl_ops.resume = lnl_hda_dsp_resume; sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; - sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; + /* dsp core get/put */ + sof_lnl_ops.core_get = mtl_dsp_core_get; + sof_lnl_ops.core_put = mtl_dsp_core_put; - sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; @@ -129,6 +131,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; + ipc4_data->fw_context_save = true; + /* External library loading support */ ipc4_data->load_library = hda_dsp_ipc4_load_library; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 7946110e7a..060c34988e 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -440,7 +440,8 @@ 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; unsigned int status; - u32 ipc_hdr; + u32 ipc_hdr, flags; + char *dump_msg; int ret; /* step 1: purge FW request */ @@ -493,8 +494,18 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) return 0; err: - snd_sof_dsp_dbg_dump(sdev, "MTL DSP init fail", 0); + flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL; + + /* after max boot attempts make sure that the dump is printed */ + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) + flags &= ~SOF_DBG_DUMP_OPTIONAL; + + dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", + hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); + snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE); + + kfree(dump_msg); return ret; } @@ -615,19 +626,7 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } -u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct hdac_stream *hstream = substream->runtime->private_data; - u32 llp_l, llp_u; - - llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index)); - llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index)); - return ((u64)llp_u << 32) | llp_l; -} - -static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) +int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; @@ -640,7 +639,7 @@ static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) return 0; } -static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) +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; @@ -696,9 +695,7 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) 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; - - sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; @@ -707,6 +704,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; + ipc4_data->fw_context_save = true; + /* External library loading support */ ipc4_data->load_library = hda_dsp_ipc4_load_library; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 95696b3d7c..ea8c1b83f7 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -6,12 +6,6 @@ * Copyright(c) 2020-2022 Intel Corporation. All rights reserved. */ -/* HDA Registers */ -#define MTL_PPLCLLPL_BASE 0x948 -#define MTL_PPLCLLPU_STRIDE 0x10 -#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE) -#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE) - /* DSP Registers */ #define MTL_HFDSSCS 0x1000 #define MTL_HFDSSCS_SPA_MASK BIT(16) @@ -103,6 +97,5 @@ int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); void mtl_ipc_dump(struct snd_sof_dev *sdev); -u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); +int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core); +int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core); diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index 78a57eb9cb..b26ffe767f 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -36,7 +36,7 @@ static const struct sof_dev_desc lnl_desc = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl", }, .default_tplg_path = { - [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_4] = "sof-lnl.ri", diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index 60d5e73cda..cacc985d80 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -50,6 +50,36 @@ static const struct sof_dev_desc mtl_desc = { .ops_free = hda_ops_free, }; +static const struct sof_dev_desc arl_desc = { + .use_acpi_target_states = true, + .machines = snd_soc_acpi_intel_arl_machines, + .alt_machines = snd_soc_acpi_intel_arl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &mtl_chip_info, + .ipc_supported_mask = BIT(SOF_IPC_TYPE_4), + .ipc_default = SOF_IPC_TYPE_4, + .dspless_mode_supported = true, /* Only supported for HDaudio */ + .default_fw_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/arl", + }, + .default_lib_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/arl", + }, + .default_tplg_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg", + }, + .default_fw_filename = { + [SOF_IPC_TYPE_4] = "sof-arl.ri", + }, + .nocodec_tplg_filename = "sof-arl-nocodec.tplg", + .ops = &sof_mtl_ops, + .ops_init = sof_mtl_ops_init, + .ops_free = hda_ops_free, +}; + static const struct sof_dev_desc arl_s_desc = { .use_acpi_target_states = true, .machines = snd_soc_acpi_intel_arl_machines, @@ -84,6 +114,7 @@ static const struct sof_dev_desc arl_s_desc = { static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_MTL, &mtl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, &arl_s_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ARL, &arl_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 0660d4b2ac..a361ee9d11 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -33,18 +33,18 @@ static const struct sof_dev_desc tgl_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/tgl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/tgl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-tgl.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-tgl.ri", }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -66,18 +66,18 @@ static const struct sof_dev_desc tglh_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/tgl-h", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl-h", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/tgl-h", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl-h", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-tgl-h.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-tgl-h.ri", }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -98,18 +98,18 @@ static const struct sof_dev_desc ehl_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/ehl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/ehl", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/ehl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/ehl", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-ehl.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-ehl.ri", }, .nocodec_tplg_filename = "sof-ehl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -131,18 +131,18 @@ static const struct sof_dev_desc adls_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/adl-s", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-s", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/adl-s", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-s", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-adl-s.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-adl-s.ri", }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -164,18 +164,18 @@ static const struct sof_dev_desc adl_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/adl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/adl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-adl.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-adl.ri", }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -197,18 +197,18 @@ static const struct sof_dev_desc adl_n_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/adl-n", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-n", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/adl-n", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-n", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-adl-n.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-adl-n.ri", }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -230,18 +230,18 @@ static const struct sof_dev_desc rpls_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/rpl-s", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl-s", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/rpl-s", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl-s", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-rpl-s.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-rpl-s.ri", }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, @@ -263,18 +263,18 @@ static const struct sof_dev_desc rpl_desc = { .dspless_mode_supported = true, /* Only supported for HDaudio */ .default_fw_path = { [SOF_IPC_TYPE_3] = "intel/sof", - [SOF_IPC_TYPE_4] = "intel/avs/rpl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl", }, .default_lib_path = { - [SOF_IPC_TYPE_4] = "intel/avs-lib/rpl", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl", }, .default_tplg_path = { [SOF_IPC_TYPE_3] = "intel/sof-tplg", - [SOF_IPC_TYPE_4] = "intel/avs-tplg", + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, .default_fw_filename = { [SOF_IPC_TYPE_3] = "sof-rpl.ri", - [SOF_IPC_TYPE_4] = "dsp_basefw.bin", + [SOF_IPC_TYPE_4] = "sof-rpl.ri", }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d24e64e71b..93824e6ce5 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -62,7 +62,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev) /* probe/remove/shutdown */ sof_skl_ops.shutdown = hda_dsp_shutdown; - sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index f7de1f5ba0..c2bb04c89b 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -82,7 +82,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data; - sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; @@ -91,6 +91,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; + ipc4_data->fw_context_save = true; + /* External library loading support */ ipc4_data->load_library = hda_dsp_ipc4_load_library; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 2d0addcbc8..330f04bcd7 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -384,6 +384,17 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_IMX_MICFIL: + rate->min = private->dai_config->micfil.pdm_rate; + rate->max = private->dai_config->micfil.pdm_rate; + channels->min = private->dai_config->micfil.pdm_ch; + channels->max = private->dai_config->micfil.pdm_ch; + + dev_dbg(component->dev, + "MICFIL PDM rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index d96555438c..d47698f4be 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -72,6 +72,8 @@ static const struct sof_topology_token buffer_tokens[] = { offsetof(struct sof_ipc_buffer, size)}, {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_buffer, caps)}, + {SOF_TKN_BUF_FLAGS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, flags)}, }; /* DAI */ @@ -286,6 +288,16 @@ static const struct sof_topology_token acpi2s_tokens[] = { offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, }; +/* MICFIL PDM */ +static const struct sof_topology_token micfil_pdm_tokens[] = { + {SOF_TKN_IMX_MICFIL_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_micfil_params, pdm_rate)}, + {SOF_TKN_IMX_MICFIL_CH, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)}, +}; + /* Core tokens */ static const struct sof_topology_token core_tokens[] = { {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -322,6 +334,8 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)}, [SOF_ACPDMIC_TOKENS] = {"ACPDMIC tokens", acpdmic_tokens, ARRAY_SIZE(acpdmic_tokens)}, [SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)}, + [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens", + micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)}, }; /** @@ -1138,6 +1152,37 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_da return 0; } +static int sof_link_micfil_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + config->hdr.size = size; + + /* parse the required set of MICFIL PDM tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->micfil, SOF_MICFIL_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dev_info(scomp->dev, "MICFIL PDM config dai_index %d channel %d rate %d\n", + config->dai_index, config->micfil.pdm_ch, config->micfil.pdm_rate); + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1176,6 +1221,7 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; struct sof_dai_private_data *private = dai->private; u32 size = sizeof(*config); + int ret; /* handle master/slave and inverted clocks */ sof_dai_set_format(hw_config, config); @@ -1184,12 +1230,14 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_ memset(&config->acpbt, 0, sizeof(config->acpbt)); config->hdr.size = size; - config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + ret = sof_update_ipc_object(scomp, &config->acpbt, SOF_ACPI2S_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; - dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n", config->dai_index, config->acpbt.tdm_slots, - config->acpbt.fsync_rate); + config->acpbt.fsync_rate, config->acpbt.tdm_mode); dai->number_configs = 1; dai->current_config = 0; @@ -1561,6 +1609,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_IMX_ESAI: ret = sof_link_esai_load(scomp, slink, config, dai); break; + case SOF_DAI_IMX_MICFIL: + ret = sof_link_micfil_load(scomp, slink, config, dai); + break; case SOF_DAI_AMD_BT: ret = sof_link_acp_bt_load(scomp, slink, config, dai); break; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index b4cdcec33e..1be9519de9 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -240,6 +240,50 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, return ret; } +static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc4_control_msg_payload *data; + struct sof_ipc4_msg *msg = &cdata->msg; + size_t data_size; + unsigned int i; + int ret; + + if (!scontrol->comp_data_dirty) + return; + + if (!pm_runtime_active(scomp->dev)) + return; + + data_size = struct_size(data, chanv, scontrol->num_channels); + data = kmalloc(data_size, GFP_KERNEL); + if (!data) + return; + + data->id = cdata->index; + data->num_elems = scontrol->num_channels; + msg->data_ptr = data; + msg->data_size = data_size; + + scontrol->comp_data_dirty = false; + ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, true); + msg->data_ptr = NULL; + msg->data_size = 0; + if (!ret) { + for (i = 0; i < scontrol->num_channels; i++) { + cdata->chanv[i].channel = data->chanv[i].channel; + cdata->chanv[i].value = data->chanv[i].value; + } + } else { + dev_err(scomp->dev, "Failed to read control data for %s\n", + scontrol->name); + scontrol->comp_data_dirty = true; + } + + kfree(data); +} + static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol) { @@ -290,6 +334,8 @@ static int sof_ipc4_switch_get(struct snd_sof_control *scontrol, struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; unsigned int i; + sof_ipc4_refresh_generic_control(scontrol); + /* read back each channel */ for (i = 0; i < scontrol->num_channels; i++) ucontrol->value.integer.value[i] = cdata->chanv[i].value; @@ -347,6 +393,8 @@ static int sof_ipc4_enum_get(struct snd_sof_control *scontrol, struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; unsigned int i; + sof_ipc4_refresh_generic_control(scontrol); + /* read back each channel */ for (i = 0; i < scontrol->num_channels; i++) ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; @@ -601,6 +649,136 @@ sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); } +#define PARAM_ID_FROM_EXTENSION(_ext) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \ + >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT) + +static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) +{ + struct sof_ipc4_msg *ipc4_msg = ipc_message; + struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr; + struct sof_ipc4_control_msg_payload *msg_data; + struct sof_ipc4_control_data *cdata; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; + struct snd_sof_widget *swidget; + struct snd_kcontrol *kc = NULL; + bool scontrol_found = false; + u32 event_param_id; + int i, type; + + if (ndata->event_data_size < sizeof(*msg_data)) { + dev_err(sdev->dev, + "%s: Invalid event data size for module %u.%u: %u\n", + __func__, ndata->module_id, ndata->instance_id, + ndata->event_data_size); + return; + } + + event_param_id = ndata->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK; + switch (event_param_id) { + case SOF_IPC4_SWITCH_CONTROL_PARAM_ID: + type = SND_SOC_TPLG_TYPE_MIXER; + break; + case SOF_IPC4_ENUM_CONTROL_PARAM_ID: + type = SND_SOC_TPLG_TYPE_ENUM; + break; + default: + dev_err(sdev->dev, + "%s: Invalid control type for module %u.%u: %u\n", + __func__, ndata->module_id, ndata->instance_id, + event_param_id); + return; + } + + /* Find the swidget based on ndata->module_id and ndata->instance_id */ + swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id, + ndata->instance_id); + if (!swidget) { + dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n", + __func__, ndata->module_id, ndata->instance_id); + return; + } + + /* Find the scontrol which is the source of the notification */ + msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data; + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id) { + u32 local_param_id; + + cdata = scontrol->ipc_control_data; + /* + * The scontrol's param_id is stored in the IPC message + * template's extension + */ + local_param_id = PARAM_ID_FROM_EXTENSION(cdata->msg.extension); + if (local_param_id == event_param_id && + msg_data->id == cdata->index) { + scontrol_found = true; + break; + } + } + } + + if (!scontrol_found) { + dev_err(sdev->dev, + "%s: Failed to find control on widget %s: %u:%u\n", + __func__, swidget->widget->name, ndata->event_id & 0xffff, + msg_data->id); + return; + } + + if (msg_data->num_elems) { + /* + * The message includes the updated value/data, update the + * control's local cache using the received notification + */ + for (i = 0; i < msg_data->num_elems; i++) { + u32 channel = msg_data->chanv[i].channel; + + if (channel >= scontrol->num_channels) { + dev_warn(sdev->dev, + "Invalid channel index for %s: %u\n", + scontrol->name, i); + + /* + * Mark the scontrol as dirty to force a refresh + * on next read + */ + scontrol->comp_data_dirty = true; + break; + } + + cdata->chanv[channel].value = msg_data->chanv[i].value; + } + } else { + /* + * Mark the scontrol as dirty because the value/data is changed + * in firmware, forcing a refresh on next read access + */ + scontrol->comp_data_dirty = true; + } + + /* + * Look up the ALSA kcontrol of the scontrol to be able to send a + * notification to user space + */ + widget = swidget->widget; + for (i = 0; i < widget->num_kcontrols; i++) { + /* skip non matching types or non matching indexes within type */ + if (widget->dobj.widget.kcontrol_type[i] == type && + widget->kcontrol_news[i].index == cdata->index) { + kc = widget->kcontrols[i]; + break; + } + } + + if (!kc) + return; + + snd_ctl_notify_one(swidget->scomp->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); +} + /* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { @@ -674,6 +852,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .bytes_ext_put = sof_ipc4_bytes_ext_put, .bytes_ext_get = sof_ipc4_bytes_ext_get, .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get, + .update = sof_ipc4_control_update, .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, .set_up_volume_table = sof_ipc4_set_up_volume_table, }; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 4d37e89b59..c79479afa8 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -391,6 +391,9 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) goto out; } break; + case SOF_IPC4_FW_CONTEXT_SAVE: + ipc4_data->fw_context_save = *tuple->value; + break; default: break; } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index ea70c0d7cf..a99fced908 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -15,6 +15,28 @@ #include "ipc4-topology.h" #include "ipc4-fw-reg.h" +/** + * struct sof_ipc4_timestamp_info - IPC4 timestamp info + * @host_copier: the host copier of the pcm stream + * @dai_copier: the dai copier of the pcm stream + * @stream_start_offset: reported by fw in memory window (converted to frames) + * @stream_end_offset: reported by fw in memory window (converted to frames) + * @llp_offset: llp offset in memory window + * @boundary: wrap boundary should be used for the LLP frame counter + * @delay: Calculated and stored in pointer callback. The stored value is + * returned in the delay callback. + */ +struct sof_ipc4_timestamp_info { + struct sof_ipc4_copier *host_copier; + struct sof_ipc4_copier *dai_copier; + u64 stream_start_offset; + u64 stream_end_offset; + u32 llp_offset; + + u64 boundary; + snd_pcm_sframes_t delay; +}; + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { @@ -406,8 +428,19 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, } /* return if this is the final state */ - if (state == SOF_IPC4_PIPE_PAUSED) + if (state == SOF_IPC4_PIPE_PAUSED) { + struct sof_ipc4_timestamp_info *time_info; + + /* + * Invalidate the stream_start_offset to make sure that it is + * going to be updated if the stream resumes + */ + time_info = spcm->stream[substream->stream].private; + if (time_info) + time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; + goto free; + } skip_pause_transition: /* else set the RUNNING/RESET state in the DSP */ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); @@ -447,14 +480,12 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component, /* determine the pipeline state */ switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - state = SOF_IPC4_PIPE_PAUSED; - break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: state = SOF_IPC4_PIPE_RUNNING; break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: state = SOF_IPC4_PIPE_PAUSED; @@ -686,6 +717,10 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm if (abi_version < SOF_IPC4_FW_REGS_ABI_VER) support_info = false; + /* For delay reporting the get_host_byte_counter callback is needed */ + if (!sof_ops(sdev) || !sof_ops(sdev)->get_host_byte_counter) + support_info = false; + for_each_pcm_streams(stream) { pipeline_list = &spcm->stream[stream].pipeline_list; @@ -779,10 +814,8 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) + sdev->fw_info_box.offset; sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); - if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) { - dev_info(sdev->dev, "no llp found, fall back to default HDA path"); + if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) info->llp_offset = 0; - } } static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, @@ -820,7 +853,6 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, struct sof_ipc4_copier *host_copier = time_info->host_copier; struct sof_ipc4_copier *dai_copier = time_info->dai_copier; struct sof_ipc4_pipeline_registers ppl_reg; - u64 stream_start_position; u32 dai_sample_size; u32 ch, node_index; u32 offset; @@ -837,38 +869,51 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) return -EINVAL; - stream_start_position = ppl_reg.stream_start_offset; ch = dai_copier->data.out_format.fmt_cfg; ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch); dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch; - /* convert offset to sample count */ - do_div(stream_start_position, dai_sample_size); - time_info->stream_start_offset = stream_start_position; + + /* convert offsets to frame count */ + time_info->stream_start_offset = ppl_reg.stream_start_offset; + do_div(time_info->stream_start_offset, dai_sample_size); + time_info->stream_end_offset = ppl_reg.stream_end_offset; + do_div(time_info->stream_end_offset, dai_sample_size); + + /* + * Calculate the wrap boundary need to be used for delay calculation + * The host counter is in bytes, it will wrap earlier than the frames + * based link counter. + */ + time_info->boundary = div64_u64(~((u64)0), + frames_to_bytes(substream->runtime, 1)); + /* Initialize the delay value to 0 (no delay) */ + time_info->delay = 0; return 0; } -static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + snd_pcm_uframes_t *pointer) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; - snd_pcm_uframes_t head_ptr, tail_ptr; + snd_pcm_uframes_t head_cnt, tail_cnt; struct snd_sof_pcm_stream *stream; + u64 dai_cnt, host_cnt, host_ptr; struct snd_sof_pcm *spcm; - u64 tmp_ptr; int ret; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) - return 0; + return -EOPNOTSUPP; stream = &spcm->stream[substream->stream]; time_info = stream->private; if (!time_info) - return 0; + return -EOPNOTSUPP; /* * stream_start_offset is updated to memory window by FW based on @@ -878,45 +923,116 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info); if (ret < 0) - return 0; + return -EOPNOTSUPP; } + /* For delay calculation we need the host counter */ + host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream); + host_ptr = host_cnt; + + /* convert the host_cnt to frames */ + host_cnt = div64_u64(host_cnt, frames_to_bytes(substream->runtime, 1)); + /* - * HDaudio links don't support the LLP counter reported by firmware - * the link position is read directly from hardware registers. + * If the LLP counter is not reported by firmware in the SRAM window + * then read the dai (link) counter via host accessible means if + * available. */ if (!time_info->llp_offset) { - tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream); - if (!tmp_ptr) - return 0; + dai_cnt = snd_sof_pcm_get_dai_frame_counter(sdev, component, substream); + if (!dai_cnt) + return -EOPNOTSUPP; } else { sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp)); - tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; + dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; } + dai_cnt += time_info->stream_end_offset; - /* In two cases dai dma position is not accurate + /* In two cases dai dma counter is not accurate * (1) dai pipeline is started before host pipeline - * (2) multiple streams mixed into one. Each stream has the same dai dma position + * (2) multiple streams mixed into one. Each stream has the same dai dma + * counter * - * Firmware calculates correct stream_start_offset for all cases including above two. - * Driver subtracts stream_start_offset from dai dma position to get accurate one + * Firmware calculates correct stream_start_offset for all cases + * including above two. + * Driver subtracts stream_start_offset from dai dma counter to get + * accurate one */ - tmp_ptr -= time_info->stream_start_offset; - /* Calculate the delay taking into account that both pointer can wrap */ - div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr); + /* + * On stream start the dai counter might not yet have reached the + * stream_start_offset value which means that no frames have left the + * DSP yet from the audio stream (on playback, capture streams have + * offset of 0 as we start capturing right away). + * In this case we need to adjust the distance between the counters by + * increasing the host counter by (offset - dai_counter). + * Otherwise the dai_counter needs to be adjusted to reflect the number + * of valid frames passed on the DAI side. + * + * The delay is the difference between the counters on the two + * sides of the DSP. + */ + if (dai_cnt < time_info->stream_start_offset) { + host_cnt += time_info->stream_start_offset - dai_cnt; + dai_cnt = 0; + } else { + dai_cnt -= time_info->stream_start_offset; + } + + /* Wrap the dai counter at the boundary where the host counter wraps */ + div64_u64_rem(dai_cnt, time_info->boundary, &dai_cnt); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - head_ptr = substream->runtime->status->hw_ptr; - tail_ptr = tmp_ptr; + head_cnt = host_cnt; + tail_cnt = dai_cnt; } else { - head_ptr = tmp_ptr; - tail_ptr = substream->runtime->status->hw_ptr; + head_cnt = dai_cnt; + tail_cnt = host_cnt; + } + + if (head_cnt < tail_cnt) { + time_info->delay = time_info->boundary - tail_cnt + head_cnt; + goto out; } - if (head_ptr < tail_ptr) - return substream->runtime->boundary - tail_ptr + head_ptr; + time_info->delay = head_cnt - tail_cnt; + +out: + /* + * Convert the host byte counter to PCM pointer which wraps in buffer + * and it is in frames + */ + div64_u64_rem(host_ptr, snd_pcm_lib_buffer_bytes(substream), &host_ptr); + *pointer = bytes_to_frames(substream->runtime, host_ptr); + + return 0; +} + +static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_ipc4_timestamp_info *time_info; + struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return 0; + + stream = &spcm->stream[substream->stream]; + time_info = stream->private; + /* + * Report the stored delay value calculated in the pointer callback. + * In the unlikely event that the calculation was skipped/aborted, the + * default 0 delay returned. + */ + if (time_info) + return time_info->delay; + + /* No delay information available, report 0 as delay */ + return 0; - return head_ptr - tail_ptr; } const struct sof_ipc_pcm_ops ipc4_pcm_ops = { @@ -926,6 +1042,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = { .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup, .pcm_setup = sof_ipc4_pcm_setup, .pcm_free = sof_ipc4_pcm_free, + .pointer = sof_ipc4_pcm_pointer, .delay = sof_ipc4_pcm_delay, .ipc_first_on_start = true, .platform_stop_during_hw_free = true, diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 9e69b7c291..9db9002b44 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -81,26 +81,13 @@ struct sof_ipc4_fw_data { u32 mtrace_log_bytes; int max_num_pipelines; u32 max_libs_count; + bool fw_context_save; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ }; -/** - * struct sof_ipc4_timestamp_info - IPC4 timestamp info - * @host_copier: the host copier of the pcm stream - * @dai_copier: the dai copier of the pcm stream - * @stream_start_offset: reported by fw in memory window - * @llp_offset: llp offset in memory window - */ -struct sof_ipc4_timestamp_info { - struct sof_ipc4_copier *host_copier; - struct sof_ipc4_copier *dai_copier; - u64 stream_start_offset; - u32 llp_offset; -}; - extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; @@ -115,6 +102,9 @@ 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 snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, + u32 module_id, int instance_id); + 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, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index e012b6e166..4d3d415179 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -167,6 +167,26 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, }; +struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, + u32 module_id, int instance_id) +{ + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + struct sof_ipc4_fw_module *fw_module = swidget->module_info; + + /* Only active module instances have valid instance_id */ + if (!swidget->use_count) + continue; + + if (fw_module && fw_module->man4_module_entry.id == module_id && + swidget->instance_id == instance_id) + return swidget; + } + + return NULL; +} + static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt, int num_formats) { @@ -392,8 +412,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_copier *ipc4_copier; + struct snd_sof_pcm *spcm; int node_type = 0; - int ret; + int ret, dir; ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); if (!ipc4_copier) @@ -427,6 +448,25 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) } dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type); + spcm = snd_sof_find_spcm_comp(scomp, swidget->comp_id, &dir); + if (!spcm) + goto skip_gtw_cfg; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + struct snd_sof_pcm_stream *sps = &spcm->stream[dir]; + + sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms, + SOF_COPIER_DEEP_BUFFER_TOKENS, + swidget->tuples, + swidget->num_tuples, sizeof(u32), 1); + /* Set default DMA buffer size if it is not specified in topology */ + if (!sps->dsp_max_burst_size_in_ms) + sps->dsp_max_burst_size_in_ms = SOF_IPC4_MIN_DMA_BUFFER_SIZE; + } else { + /* Capture data is copied from DSP to host in 1ms bursts */ + spcm->stream[dir].dsp_max_burst_size_in_ms = 1; + } + skip_gtw_cfg: ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); if (!ipc4_copier->gtw_attr) { @@ -2372,6 +2412,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget } if (swidget->id != snd_soc_dapm_scheduler) { + int module_id = msg->primary & SOF_IPC4_MOD_ID_MASK; + ret = sof_ipc4_widget_assign_instance_id(sdev, swidget); if (ret < 0) { dev_err(sdev->dev, "failed to assign instance id for %s\n", @@ -2387,9 +2429,15 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK; msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id); + + dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", + swidget->widget->name, swidget->pipeline_id, module_id, + swidget->instance_id, swidget->core); + } else { + dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", + swidget->widget->name, swidget->pipeline_id, + swidget->instance_id, swidget->core); } - dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n", - swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core); msg->data_size = ipc_size; msg->data_ptr = ipc_data; diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 8441f4ae40..ac5c6bc66d 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -78,6 +78,9 @@ static const struct sof_ipc4_fw_status { {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"}, }; +typedef void (*ipc4_notification_handler)(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *msg); + static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status) { int i, ret; @@ -573,46 +576,64 @@ EXPORT_SYMBOL(sof_ipc4_find_debug_slot_offset_by_type); static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg) { - int inbox_offset, inbox_size, outbox_offset, outbox_size; - /* no need to re-check version/ABI for subsequent boots */ if (!sdev->first_boot) return 0; - /* Set up the windows for IPC communication */ - inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev); - if (inbox_offset < 0) { - dev_err(sdev->dev, "%s: No mailbox offset\n", __func__); - return inbox_offset; - } - inbox_size = SOF_IPC4_MSG_MAX_SIZE; - outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX); - outbox_size = SOF_IPC4_MSG_MAX_SIZE; + sof_ipc4_create_exception_debugfs_node(sdev); - sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX); - sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers); - sdev->dsp_box.offset = inbox_offset; - sdev->dsp_box.size = inbox_size; - sdev->host_box.offset = outbox_offset; - sdev->host_box.size = outbox_size; + return sof_ipc4_init_msg_memory(sdev); +} - sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev, - SOF_IPC4_DEBUG_WINDOW_IDX); +static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct sof_ipc4_notify_module_data *data = ipc4_msg->data_ptr; + + /* + * If the notification includes additional, module specific data, then + * we need to re-allocate the buffer and re-read the whole payload, + * including the event_data + */ + if (data->event_data_size) { + void *new; + int ret; + + ipc4_msg->data_size += data->event_data_size; + + new = krealloc(ipc4_msg->data_ptr, ipc4_msg->data_size, GFP_KERNEL); + if (!new) { + ipc4_msg->data_size -= data->event_data_size; + return; + } - sof_ipc4_create_exception_debugfs_node(sdev); + /* re-read the whole payload */ + ipc4_msg->data_ptr = new; + ret = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, + ipc4_msg->data_size); + if (ret < 0) { + dev_err(sdev->dev, + "Failed to read the full module notification: %d\n", + ret); + return; + } + data = ipc4_msg->data_ptr; + } - dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n", - inbox_offset, inbox_size); - dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n", - outbox_offset, outbox_size); - dev_dbg(sdev->dev, "debug box 0x%x\n", sdev->debug_box.offset); + /* Handle ALSA kcontrol notification */ + if ((data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK) == + SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL) { + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - return sof_ipc4_init_msg_memory(sdev); + if (tplg_ops->control->update) + tplg_ops->control->update(sdev, ipc4_msg); + } } static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) { struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data; + ipc4_notification_handler handler_func = NULL; size_t data_size = 0; int err; @@ -648,6 +669,10 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) case SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT: snd_sof_dsp_panic(sdev, 0, true); break; + case SOF_IPC4_NOTIFY_MODULE_NOTIFICATION: + data_size = sizeof(struct sof_ipc4_notify_module_data); + handler_func = sof_ipc4_module_notification_handler; + break; default: dev_dbg(sdev->dev, "Unhandled DSP message: %#x|%#x\n", ipc4_msg->primary, ipc4_msg->extension); @@ -660,9 +685,20 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) return; ipc4_msg->data_size = data_size; - snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size); + err = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size); + if (err < 0) { + dev_err(sdev->dev, "failed to read IPC notification data: %d\n", err); + kfree(ipc4_msg->data_ptr); + ipc4_msg->data_ptr = NULL; + ipc4_msg->data_size = 0; + return; + } } + /* Handle notifications with payload */ + if (handler_func) + handler_func(sdev, ipc4_msg); + sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true); if (data_size) { @@ -732,11 +768,38 @@ static const struct sof_ipc_pm_ops ipc4_pm_ops = { static int sof_ipc4_init(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + int inbox_offset; mutex_init(&ipc4_data->pipeline_state_mutex); xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); + /* Set up the windows for IPC communication */ + inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev); + if (inbox_offset < 0) { + dev_err(sdev->dev, "%s: No mailbox offset\n", __func__); + return inbox_offset; + } + + sdev->dsp_box.offset = inbox_offset; + sdev->dsp_box.size = SOF_IPC4_MSG_MAX_SIZE; + sdev->host_box.offset = snd_sof_dsp_get_window_offset(sdev, + SOF_IPC4_OUTBOX_WINDOW_IDX); + sdev->host_box.size = SOF_IPC4_MSG_MAX_SIZE; + + sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev, + SOF_IPC4_DEBUG_WINDOW_IDX); + + sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, + SOF_IPC4_INBOX_WINDOW_IDX); + sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers); + + dev_dbg(sdev->dev, "mailbox upstream %#x - size %#x\n", + sdev->dsp_box.offset, SOF_IPC4_MSG_MAX_SIZE); + dev_dbg(sdev->dev, "mailbox downstream %#x - size %#x\n", + sdev->host_box.offset, SOF_IPC4_MSG_MAX_SIZE); + dev_dbg(sdev->dev, "debug box %#x\n", sdev->debug_box.offset); + return 0; } diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h index d41e904e66..3552756796 100644 --- a/sound/soc/sof/mediatek/adsp_helper.h +++ b/sound/soc/sof/mediatek/adsp_helper.h @@ -15,17 +15,13 @@ struct mtk_adsp_chip_info { phys_addr_t pa_sram; phys_addr_t pa_dram; /* adsp dram physical base */ - phys_addr_t pa_shared_dram; /* adsp dram physical base */ phys_addr_t pa_cfgreg; u32 sramsize; u32 dramsize; u32 cfgregsize; - u32 shared_size; void __iomem *va_sram; /* corresponding to pa_sram */ void __iomem *va_dram; /* corresponding to pa_dram */ void __iomem *va_cfgreg; - void __iomem *shared_sram; /* part of va_sram */ - void __iomem *shared_dram; /* part of va_dram */ phys_addr_t adsp_bootup_addr; int dram_offset; /*dram offset between system and dsp view*/ diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index b69fa788b1..0d2d7d697d 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -96,29 +96,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) struct mtk_adsp_chip_info *adsp = data; int ret; - mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!mem_region) { - dev_err(dev, "no dma memory-region phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); - if (ret) { - dev_err(dev, "of_address_to_resource dma failed\n"); - return ret; - } - - dev_dbg(dev, "DMA %pR\n", &res); - - adsp->pa_shared_dram = (phys_addr_t)res.start; - adsp->shared_size = resource_size(&res); - if (adsp->pa_shared_dram & DRAM_REMAP_MASK) { - dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n", - (u32)adsp->pa_shared_dram); - return -EINVAL; - } - ret = of_reserved_mem_device_init(dev); if (ret) { dev_err(dev, "of_reserved_mem_device_init failed\n"); @@ -248,26 +225,6 @@ static int adsp_memory_remap_init(struct snd_sof_dev *sdev, struct mtk_adsp_chip return 0; } -static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) -{ - struct device *dev = &pdev->dev; - struct mtk_adsp_chip_info *adsp = data; - - /* remap shared-dram base to be non-cachable */ - adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, - adsp->shared_size); - if (!adsp->shared_dram) { - dev_err(dev, "failed to ioremap base %pa size %#x\n", - adsp->shared_dram, adsp->shared_size); - return -ENOMEM; - } - - dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", - adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size); - - return 0; -} - static int mt8186_run(struct snd_sof_dev *sdev) { u32 adsp_bootup_addr; @@ -324,12 +281,6 @@ static int mt8186_dsp_probe(struct snd_sof_dev *sdev) priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM]; - ret = adsp_shared_base_ioremap(pdev, priv->adsp); - if (ret) { - dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n"); - return ret; - } - sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; sdev->bar[DSP_SECREG_BAR] = priv->adsp->va_secreg; sdev->bar[DSP_BUSREG_BAR] = priv->adsp->va_busreg; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index cac0a085f6..8ee7ee2463 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -96,29 +96,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) struct mtk_adsp_chip_info *adsp = data; int ret; - mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!mem_region) { - dev_err(dev, "no dma memory-region phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); - if (ret) { - dev_err(dev, "of_address_to_resource dma failed\n"); - return ret; - } - - dev_dbg(dev, "DMA %pR\n", &res); - - adsp->pa_shared_dram = (phys_addr_t)res.start; - adsp->shared_size = resource_size(&res); - if (adsp->pa_shared_dram & DRAM_REMAP_MASK) { - dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n", - (u32)adsp->pa_shared_dram); - return -EINVAL; - } - ret = of_reserved_mem_device_init(dev); if (ret) { dev_err(dev, "of_reserved_mem_device_init failed\n"); @@ -238,26 +215,6 @@ static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info return 0; } -static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) -{ - struct device *dev = &pdev->dev; - struct mtk_adsp_chip_info *adsp = data; - - /* remap shared-dram base to be non-cachable */ - adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, - adsp->shared_size); - if (!adsp->shared_dram) { - dev_err(dev, "failed to ioremap base %pa size %#x\n", - adsp->shared_dram, adsp->shared_size); - return -ENOMEM; - } - - dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", - adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size); - - return 0; -} - static int mt8195_run(struct snd_sof_dev *sdev) { u32 adsp_bootup_addr; @@ -338,12 +295,6 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev) } priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM]; - ret = adsp_shared_base_ioremap(pdev, priv->adsp); - if (ret) { - dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n"); - goto err_adsp_sram_power_off; - } - sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 6538d9f4fe..2505a1e1ce 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -523,12 +523,26 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, return 0; } -static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static inline u64 +snd_sof_pcm_get_dai_frame_counter(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position) - return sof_ops(sdev)->get_stream_position(sdev, component, substream); + if (sof_ops(sdev) && sof_ops(sdev)->get_dai_frame_counter) + return sof_ops(sdev)->get_dai_frame_counter(sdev, component, + substream); + + return 0; +} + +static inline u64 +snd_sof_pcm_get_host_byte_counter(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->get_host_byte_counter) + return sof_ops(sdev)->get_host_byte_counter(sdev, component, + substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 33d576b176..f03cee94bc 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -388,13 +388,21 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; + int ret = -EOPNOTSUPP; /* nothing to do for BE */ if (rtd->dai_link->no_pcm) return 0; + if (pcm_ops && pcm_ops->pointer) + ret = pcm_ops->pointer(component, substream, &host); + + if (ret != -EOPNOTSUPP) + return ret ? ret : host; + /* use dsp ops pointer callback directly if set */ if (sof_ops(sdev)->pcm_pointer) return sof_ops(sdev)->pcm_pointer(sdev, substream); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 84a4a0a331..2977f0a63f 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -74,18 +74,10 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3]; - - /* alternate fw and tplg filenames ? */ - if (fw_path) - sof_pdata->fw_filename_prefix = fw_path; - else - sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3]; - - if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3]; + + sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; + sof_pdata->ipc_file_profile_base.fw_path = fw_path; + sof_pdata->ipc_file_profile_base.tplg_path = tplg_path; /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_acpi_probe_complete; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 77cc64ac71..9163975c9c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,7 +11,6 @@ #include <linux/bitfield.h> #include <trace/events/sof.h> #include "sof-audio.h" -#include "sof-of-dev.h" #include "ops.h" static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, @@ -1006,122 +1005,3 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK); } EXPORT_SYMBOL(sof_dai_get_bclk); - -static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev) -{ - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_sof_of_mach *mach = desc->of_machines; - - if (!mach) - return NULL; - - for (; mach->compatible; mach++) { - if (of_machine_is_compatible(mach->compatible)) { - sof_pdata->tplg_filename = mach->sof_tplg_filename; - if (mach->fw_filename) - sof_pdata->fw_filename = mach->fw_filename; - - return mach; - } - } - - return NULL; -} - -/* - * SOF Driver enumeration. - */ -int sof_machine_check(struct snd_sof_dev *sdev) -{ - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_soc_acpi_mach *mach; - - if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { - const struct snd_sof_of_mach *of_mach; - - if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && - sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) - goto nocodec; - - /* find machine */ - mach = snd_sof_machine_select(sdev); - if (mach) { - sof_pdata->machine = mach; - - if (sof_pdata->subsystem_id_set) { - mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor; - mach->mach_params.subsystem_device = sof_pdata->subsystem_device; - mach->mach_params.subsystem_id_set = true; - } - - snd_sof_set_mach_params(mach, sdev); - return 0; - } - - of_mach = sof_of_machine_select(sdev); - if (of_mach) { - sof_pdata->of_machine = of_mach; - return 0; - } - - if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) { - dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; - } - } else { - dev_warn(sdev->dev, "Force to use nocodec mode\n"); - } - -nocodec: - /* select nocodec mode */ - dev_warn(sdev->dev, "Using nocodec machine driver\n"); - mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); - if (!mach) - return -ENOMEM; - - mach->drv_name = "sof-nocodec"; - if (!sof_pdata->tplg_filename) - sof_pdata->tplg_filename = desc->nocodec_tplg_filename; - - sof_pdata->machine = mach; - snd_sof_set_mach_params(mach, sdev); - - return 0; -} -EXPORT_SYMBOL(sof_machine_check); - -int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) -{ - struct snd_sof_pdata *plat_data = pdata; - const char *drv_name; - const void *mach; - int size; - - drv_name = plat_data->machine->drv_name; - mach = plat_data->machine; - size = sizeof(*plat_data->machine); - - /* register machine driver, pass machine info as pdata */ - plat_data->pdev_mach = - platform_device_register_data(sdev->dev, drv_name, - PLATFORM_DEVID_NONE, mach, size); - if (IS_ERR(plat_data->pdev_mach)) - return PTR_ERR(plat_data->pdev_mach); - - dev_dbg(sdev->dev, "created machine %s\n", - dev_name(&plat_data->pdev_mach->dev)); - - return 0; -} -EXPORT_SYMBOL(sof_machine_register); - -void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) -{ - struct snd_sof_pdata *plat_data = pdata; - - if (!IS_ERR_OR_NULL(plat_data->pdev_mach)) - platform_device_unregister(plat_data->pdev_mach); -} -EXPORT_SYMBOL(sof_machine_unregister); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a6d6bcd00c..85b26e3fef 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -103,7 +103,10 @@ struct snd_sof_dai_config_data { * additional memory in the SOF PCM stream structure * @pcm_free: Function pointer for PCM free that can be used for freeing any * additional memory in the SOF PCM stream structure - * @delay: Function pointer for pcm delay calculation + * @pointer: Function pointer for pcm pointer + * Note: the @pointer callback may return -EOPNOTSUPP which should be + * handled in a same way as if the callback is not provided + * @delay: Function pointer for pcm delay reporting * @reset_hw_params_during_stop: Flag indicating whether the hw_params should be reset during the * STOP pcm trigger * @ipc_first_on_start: Send IPC before invoking platform trigger during @@ -124,6 +127,9 @@ struct sof_ipc_pcm_ops { int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); + int (*pointer)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + snd_pcm_uframes_t *pointer); snd_pcm_sframes_t (*delay)(struct snd_soc_component *component, struct snd_pcm_substream *substream); bool reset_hw_params_during_stop; @@ -275,6 +281,7 @@ enum sof_tokens { SOF_GAIN_TOKENS, SOF_ACPDMIC_TOKENS, SOF_ACPI2S_TOKENS, + SOF_MICFIL_TOKENS, /* this should be the last */ SOF_TOKEN_COUNT, @@ -320,6 +327,7 @@ struct snd_sof_pcm_stream { struct work_struct period_elapsed_work; struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */ bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ + unsigned int dsp_max_burst_size_in_ms; /* The maximum size of the host DMA burst in ms */ /* * flag to indicate that the DSP pipelines should be kept * active or not while suspending the stream diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 7cc9e8f18d..30f771ac7b 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -381,8 +381,6 @@ static const struct snd_soc_component_driver sof_probes_component = { .legacy_dai_naming = 1, }; -SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); - static int sof_probes_client_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { @@ -475,7 +473,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev, links[0].cpus = &cpus[0]; links[0].num_cpus = 1; links[0].cpus->dai_name = "Probe Extraction CPU DAI"; - links[0].codecs = dummy; + links[0].codecs = &snd_soc_dummy_dlc; links[0].num_codecs = 1; links[0].platforms = platform_component; links[0].num_platforms = ARRAY_SIZE(platform_component); diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index c6be8a91e7..b9a499e92b 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -64,17 +64,10 @@ int sof_of_probe(struct platform_device *pdev) sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3]; - if (fw_path) - sof_pdata->fw_filename_prefix = fw_path; - else - sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3]; - - if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3]; + sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default; + sof_pdata->ipc_file_profile_base.fw_path = fw_path; + sof_pdata->ipc_file_profile_base.tplg_path = tplg_path; /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_of_probe_complete; diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 64b326e3ef..aab5c900ce 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -190,6 +190,7 @@ static void sof_pci_probe_complete(struct device *dev) int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { + struct sof_loadable_file_profile *path_override; struct device *dev = &pci->dev; const struct sof_dev_desc *desc = (const struct sof_dev_desc *)pci_id->driver_data; @@ -232,106 +233,39 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->desc = desc; sof_pdata->dev = dev; - sof_pdata->ipc_type = desc->ipc_default; + path_override = &sof_pdata->ipc_file_profile_base; if (sof_pci_ipc_type < 0) { - sof_pdata->ipc_type = desc->ipc_default; + path_override->ipc_type = desc->ipc_default; + } else if (sof_pci_ipc_type < SOF_IPC_TYPE_COUNT) { + path_override->ipc_type = sof_pci_ipc_type; } else { - dev_info(dev, "overriding default IPC %d to requested %d\n", - desc->ipc_default, sof_pci_ipc_type); - if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) { - dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type); - ret = -EINVAL; - goto out; - } - if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) { - dev_err(dev, "invalid request value %d, supported mask is %#x\n", - sof_pci_ipc_type, desc->ipc_supported_mask); - ret = -EINVAL; - goto out; - } - sof_pdata->ipc_type = sof_pci_ipc_type; + dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type); + ret = -EINVAL; + goto out; } - if (fw_filename) { - sof_pdata->fw_filename = fw_filename; + path_override->fw_path = fw_path; + path_override->fw_name = fw_filename; + path_override->fw_lib_path = lib_path; + path_override->tplg_path = tplg_path; - dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", - sof_pdata->fw_filename); - } else { - sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type]; + if (dmi_check_system(community_key_platforms) && + sof_dmi_use_community_key) { + path_override->fw_path_postfix = "community"; + path_override->fw_lib_path_postfix = "community"; } /* - * for platforms using the SOF community key, change the - * default path automatically to pick the right files from the - * linux-firmware tree. This can be overridden with the - * fw_path kernel parameter, e.g. for developers. - */ - - /* alternate fw and tplg filenames ? */ - if (fw_path) { - sof_pdata->fw_filename_prefix = fw_path; - - dev_dbg(dev, - "Module parameter used, changed fw path to %s\n", - sof_pdata->fw_filename_prefix); - - } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { - sof_pdata->fw_filename_prefix = - devm_kasprintf(dev, GFP_KERNEL, "%s/%s", - sof_pdata->desc->default_fw_path[sof_pdata->ipc_type], - "community"); - - dev_dbg(dev, - "Platform uses community key, changed fw path to %s\n", - sof_pdata->fw_filename_prefix); - } else { - sof_pdata->fw_filename_prefix = - sof_pdata->desc->default_fw_path[sof_pdata->ipc_type]; - } - - if (lib_path) { - sof_pdata->fw_lib_prefix = lib_path; - - dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n", - sof_pdata->fw_lib_prefix); - - } else if (sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]) { - if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { - sof_pdata->fw_lib_prefix = - devm_kasprintf(dev, GFP_KERNEL, "%s/%s", - sof_pdata->desc->default_lib_path[sof_pdata->ipc_type], - "community"); - - dev_dbg(dev, - "Platform uses community key, changed fw_lib path to %s\n", - sof_pdata->fw_lib_prefix); - } else { - sof_pdata->fw_lib_prefix = - sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]; - } - } - - if (tplg_path) - sof_pdata->tplg_filename_prefix = tplg_path; - else - sof_pdata->tplg_filename_prefix = - sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type]; - - /* * the topology filename will be provided in the machine descriptor, unless * it is overridden by a module parameter or DMI quirk. */ if (tplg_filename) { - sof_pdata->tplg_filename = tplg_filename; - - dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n", - sof_pdata->tplg_filename); + path_override->tplg_name = tplg_filename; } else { dmi_check_system(sof_tplg_table); if (sof_dmi_override_tplg_name) - sof_pdata->tplg_filename = sof_dmi_override_tplg_name; + path_override->tplg_name = sof_dmi_override_tplg_name; } /* set callback to be called on successful device probe to enable runtime_pm */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f4185012eb..09eb2de445 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -255,13 +255,25 @@ struct snd_sof_dsp_ops { int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ /* - * optional callback to retrieve the link DMA position for the substream - * when the position is not reported in the shared SRAM windows but - * instead from a host-accessible hardware counter. + * optional callback to retrieve the number of frames left/arrived from/to + * the DSP on the DAI side (link/codec/DMIC/etc). + * + * The callback is used when the firmware does not provide this information + * via the shared SRAM window and it can be retrieved by host. */ - u64 (*get_stream_position)(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ + u64 (*get_dai_frame_counter)(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); /* optional */ + + /* + * Optional callback to retrieve the number of bytes left/arrived from/to + * the DSP on the host side (bytes between host ALSA buffer and DSP). + * + * The callback is needed for ALSA delay reporting. + */ + u64 (*get_host_byte_counter)(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); /* optional */ /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, @@ -696,6 +708,13 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); extern struct snd_compress_ops sof_compressed_ops; /* + * Firmware (firmware, libraries, topologies) file location + */ +int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, + struct sof_loadable_file_profile *base_profile, + struct sof_loadable_file_profile *out_profile); + +/* * Firmware loading. */ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev); @@ -814,8 +833,6 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev, int sof_stream_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); -int sof_machine_check(struct snd_sof_dev *sdev); - /* SOF client support */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT) int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7133ec1332..617a225fff 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -289,13 +289,14 @@ static const struct sof_dai_types sof_dais[] = { {"ALH", SOF_DAI_INTEL_ALH}, {"SAI", SOF_DAI_IMX_SAI}, {"ESAI", SOF_DAI_IMX_ESAI}, - {"ACP", SOF_DAI_AMD_BT}, + {"ACPBT", SOF_DAI_AMD_BT}, {"ACPSP", SOF_DAI_AMD_SP}, {"ACPDMIC", SOF_DAI_AMD_DMIC}, {"ACPHS", SOF_DAI_AMD_HS}, {"AFE", SOF_DAI_MEDIATEK_AFE}, {"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL}, {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, + {"MICFIL", SOF_DAI_IMX_MICFIL}, }; @@ -1955,6 +1956,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ token_id = SOF_ACPDMIC_TOKENS; num_tuples += token_list[SOF_ACPDMIC_TOKENS].count; break; + case SOF_DAI_AMD_BT: case SOF_DAI_AMD_SP: case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: @@ -1962,6 +1964,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ token_id = SOF_ACPI2S_TOKENS; num_tuples += token_list[SOF_ACPI2S_TOKENS].count; break; + case SOF_DAI_IMX_MICFIL: + token_id = SOF_MICFIL_TOKENS; + num_tuples += token_list[SOF_MICFIL_TOKENS].count; + break; default: break; } diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c index 6cfab8844d..57bd1a0728 100644 --- a/sound/soc/sprd/sprd-pcm-compress.c +++ b/sound/soc/sprd/sprd-pcm-compress.c @@ -160,7 +160,7 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component, return -ENODEV; } - sgt = sg = devm_kcalloc(dev, sg_num, sizeof(*sg), GFP_KERNEL); + sgt = sg = kcalloc(sg_num, sizeof(*sg), GFP_KERNEL); if (!sg) { ret = -ENOMEM; goto sg_err; @@ -250,12 +250,12 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component, dma->desc->callback_param = cstream; } - devm_kfree(dev, sg); + kfree(sg); return 0; config_err: - devm_kfree(dev, sg); + kfree(sg); sg_err: dma_release_channel(dma->chan); return ret; diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index 2c21a86421..ba824f14a3 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c @@ -461,10 +461,6 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, return 0; } -static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = { - .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, -}; - static int sti_uniperiph_probe(struct platform_device *pdev) { struct sti_uniperiph_data *priv; @@ -493,8 +489,7 @@ static int sti_uniperiph_probe(struct platform_device *pdev) if (ret < 0) return ret; - return devm_snd_dmaengine_pcm_register(&pdev->dev, - &dmaengine_pcm_config, 0); + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); } static struct platform_driver sti_uniperiph_driver = { diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index e713feca25..8011afe93c 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -12,12 +12,11 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/io.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reset.h> @@ -39,11 +38,15 @@ static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97) u32 readback; unsigned long timeout; - /* reset line is not driven by DAC pad group, have to toggle GPIO */ - gpio_set_value(workdata->reset_gpio, 0); + /* + * The reset line is not driven by DAC pad group, have to toggle GPIO. + * The RESET line is active low but this is abstracted by the GPIO + * library. + */ + gpiod_set_value(workdata->reset_gpio, 1); udelay(2); - gpio_set_value(workdata->reset_gpio, 1); + gpiod_set_value(workdata->reset_gpio, 0); udelay(2); timeout = jiffies + msecs_to_jiffies(100); @@ -66,14 +69,10 @@ static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97) * the controller cmd is not working, have to toggle sync line * manually. */ - gpio_request(workdata->sync_gpio, "codec-sync"); - - gpio_direction_output(workdata->sync_gpio, 1); - + gpiod_direction_output(workdata->sync_gpio, 1); udelay(2); - gpio_set_value(workdata->sync_gpio, 0); + gpiod_set_value(workdata->sync_gpio, 0); udelay(2); - gpio_free(workdata->sync_gpio); timeout = jiffies + msecs_to_jiffies(100); @@ -342,28 +341,26 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) goto err_clk_put; } - ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, - "nvidia,codec-reset-gpio", 0); - if (gpio_is_valid(ac97->reset_gpio)) { - ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio, - GPIOF_OUT_INIT_HIGH, "codec-reset"); - if (ret) { - dev_err(&pdev->dev, "could not get codec-reset GPIO\n"); - goto err_clk_put; - } - } else { - dev_err(&pdev->dev, "no codec-reset GPIO supplied\n"); - ret = -EINVAL; + /* Obtain RESET de-asserted */ + ac97->reset_gpio = devm_gpiod_get(&pdev->dev, + "nvidia,codec-reset", + GPIOD_OUT_LOW); + if (IS_ERR(ac97->reset_gpio)) { + ret = PTR_ERR(ac97->reset_gpio); + dev_err(&pdev->dev, "no RESET GPIO supplied: %d\n", ret); goto err_clk_put; } - - ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node, - "nvidia,codec-sync-gpio", 0); - if (!gpio_is_valid(ac97->sync_gpio)) { - dev_err(&pdev->dev, "no codec-sync GPIO supplied\n"); - ret = -EINVAL; + gpiod_set_consumer_name(ac97->reset_gpio, "codec-reset"); + + ac97->sync_gpio = devm_gpiod_get(&pdev->dev, + "nvidia,codec-sync", + GPIOD_OUT_LOW); + if (IS_ERR(ac97->sync_gpio)) { + ret = PTR_ERR(ac97->sync_gpio); + dev_err(&pdev->dev, "no codec-sync GPIO supplied: %d\n", ret); goto err_clk_put; } + gpiod_set_consumer_name(ac97->sync_gpio, "codec-sync"); ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h index 870ea09ff3..116d7b2db2 100644 --- a/sound/soc/tegra/tegra20_ac97.h +++ b/sound/soc/tegra/tegra20_ac97.h @@ -80,7 +80,7 @@ struct tegra20_ac97 { struct snd_dmaengine_dai_dma_data playback_dma_data; struct reset_control *reset; struct regmap *regmap; - int reset_gpio; - int sync_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *sync_gpio; }; #endif /* __TEGRA20_AC97_H__ */ diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 142e8d4eef..42acb56543 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -98,8 +98,8 @@ int tegra_pcm_open(struct snd_soc_component *component, return ret; } - chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name); - if (!chan) { + chan = dma_request_chan(cpu_dai->dev, dmap->chan_name); + if (IS_ERR(chan)) { dev_err(cpu_dai->dev, "dmaengine request slave channel failed! (%s)\n", dmap->chan_name); |