From 1dafede34dda19fc2878724145dc16c0b51dc174 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 20 Jan 2022 17:15:28 -0600 Subject: ASoC: SOF: add _D3_PERSISTENT flag to fw_ready message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bit definition to the fw_ready message, to denote if the FW supports the IMR (Isolated Memory Region) restoring feature. If the bit is set, the driver can skip downloading the firmware again during system resume or runtime resume. Bump the ABI version to 3.19 to make it aligned with FW side. Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120231532.196926-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/info.h | 1 + include/uapi/sound/sof/abi.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/info.h b/include/sound/sof/info.h index 0b7101aef596..65e86e4e9fd8 100644 --- a/include/sound/sof/info.h +++ b/include/sound/sof/info.h @@ -25,6 +25,7 @@ #define SOF_IPC_INFO_LOCKS BIT(1) #define SOF_IPC_INFO_LOCKSV BIT(2) #define SOF_IPC_INFO_GDB BIT(3) +#define SOF_IPC_INFO_D3_PERSISTENT BIT(4) /* extended data types that can be appended onto end of sof_ipc_fw_ready */ enum sof_ipc_ext_data { diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index fe2cfae94b45..f4232d289a22 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,7 +26,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 18 +#define SOF_ABI_MINOR 19 #define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ -- cgit v1.2.3 From bd586a0292e0724fac571a2e4b629134ab2c686c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 20 Jan 2022 17:15:29 -0600 Subject: ASoC: SOF: Intel: use inclusive language for SSP clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduced provider/consumer terms, and CBP_CFP acronyms for codec drivers, let's use them as well in SOF. Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120231532.196926-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 6 +++--- sound/soc/sof/intel/hda.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 33306d2023a7..43de6f1d62a9 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -101,14 +101,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) goto err; } - /* DSP is powered up, set all SSPs to slave mode */ + /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */ for (i = 0; i < chip->ssp_count; i++) { snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, chip->ssp_base_offset + i * SSP_DEV_MEM_SIZE + SSP_SSC1_OFFSET, - SSP_SET_SLAVE, - SSP_SET_SLAVE); + SSP_SET_CBP_CFP, + SSP_SET_CBP_CFP); } /* step 2: purge FW request */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 03a6bb7a165c..7838a998ea95 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -383,9 +383,9 @@ /* SSP Registers */ #define SSP_SSC1_OFFSET 0x4 -#define SSP_SET_SCLK_SLAVE BIT(25) -#define SSP_SET_SFRM_SLAVE BIT(24) -#define SSP_SET_SLAVE (SSP_SET_SCLK_SLAVE | SSP_SET_SFRM_SLAVE) +#define SSP_SET_SCLK_CONSUMER BIT(25) +#define SSP_SET_SFRM_CONSUMER BIT(24) +#define SSP_SET_CBP_CFP (SSP_SET_SCLK_CONSUMER | SSP_SET_SFRM_CONSUMER) #define HDA_IDISP_ADDR 2 #define HDA_IDISP_CODEC(x) ((x) & BIT(HDA_IDISP_ADDR)) -- cgit v1.2.3 From a749d744561ccc8658cebe23fc284034a57e6ceb Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 20 Jan 2022 17:15:30 -0600 Subject: ASoC: SOF: Intel: hda-loader: add SSP helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the SSP clock configuration to the hda_set_ssp_cbp_cfp() helper, to be used in follow-up patches Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120231532.196926-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 43de6f1d62a9..7f1b1d0f2422 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -25,6 +25,23 @@ #define HDA_CL_STREAM_FORMAT 0x40 +static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + int i; + + /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */ + for (i = 0; i < chip->ssp_count; i++) { + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + chip->ssp_base_offset + + i * SSP_DEV_MEM_SIZE + + SSP_SSC1_OFFSET, + SSP_SET_CBP_CFP, + SSP_SET_CBP_CFP); + } +} + static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, int direction) @@ -91,7 +108,6 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) char *dump_msg; u32 flags, j; int ret; - int i; /* step 1: power up corex */ ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask); @@ -101,15 +117,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) goto err; } - /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */ - for (i = 0; i < chip->ssp_count; i++) { - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - chip->ssp_base_offset - + i * SSP_DEV_MEM_SIZE - + SSP_SSC1_OFFSET, - SSP_SET_CBP_CFP, - SSP_SET_CBP_CFP); - } + hda_ssp_set_cbp_cfp(sdev); /* step 2: purge FW request */ snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, -- cgit v1.2.3 From 5fb5f51185126059e1d7eb4e452e08c7b17c3301 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 20 Jan 2022 17:15:31 -0600 Subject: ASoC: SOF: Intel: hda-loader: add IMR restore support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the firmware declares the IMR restore feature, we only need to do a simple powering up to resume from D3, no firmware re-downloading needed - the context is saved/restored to/from IMR without needing driver support. Add a hda_dsp_boot_imr() helper for this simple DSP reboot, and use it when it is available. Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120231532.196926-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 7f1b1d0f2422..baf2ff146f50 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -353,6 +353,38 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) return ret; } +static int hda_dsp_boot_imr(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + unsigned long mask; + u32 j; + int ret; + + /* power up & unstall/run the cores to run the firmware */ + ret = hda_dsp_enable_core(sdev, chip->init_core_mask); + if (ret < 0) { + dev_err(sdev->dev, "dsp core start failed %d\n", ret); + return -EIO; + } + + /* set enabled cores mask and increment ref count for cores in init_core_mask */ + sdev->enabled_cores_mask |= chip->init_core_mask; + mask = sdev->enabled_cores_mask; + for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) + sdev->dsp_core_ref_count[j]++; + + hda_ssp_set_cbp_cfp(sdev); + + /* enable IPC interrupts */ + hda_dsp_ipc_int_enable(sdev); + + /* process wakes */ + hda_sdw_process_wakeen(sdev); + + return ret; +} + int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -363,6 +395,12 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) struct firmware stripped_firmware; int ret, ret1, i; + if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) && + !sdev->first_boot) { + dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); + return hda_dsp_boot_imr(sdev); + } + chip_info = desc->chip_info; if (plat_data->fw->size <= plat_data->fw_offset) { -- cgit v1.2.3 From d7a8fbd17bfef174e85d81d94507b8015732a58e Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 20 Jan 2022 17:15:32 -0600 Subject: ASoC: SOF: add flag to disable IMR restore to sof_debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add flag _IGNORE_D3_PERSISTENT to disable IMR restore feature to the sof_debug module parameter. The IMR restore feature will be enabled for all Intel cAVS platforms by default, but setting the flag _IGNORE_D3_PERSISTENT can help to disable the feature for debug purpose, to rule out any possible regression introduced by the change of not re-downloading firmware to the DSP at resuming from suspended state. Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120231532.196926-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 2 ++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index baf2ff146f50..6af3325b7e40 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -21,6 +21,7 @@ #include #include "ext_manifest.h" #include "../ops.h" +#include "../sof-priv.h" #include "hda.h" #define HDA_CL_STREAM_FORMAT 0x40 @@ -396,6 +397,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) int ret, ret1, i; if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) && + !(sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) && !sdev->first_boot) { dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); return hda_dsp_boot_imr(sdev); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 087935192ce8..29bb56b7267a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -34,6 +34,9 @@ * on primary core */ #define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */ +#define SOF_DBG_IGNORE_D3_PERSISTENT BIT(7) /* ignore the DSP D3 persistent capability + * and always download firmware upon D3 exit + */ /* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) -- cgit v1.2.3 From 3ce57f22cb23eefdf485589bbf675a30e72aeb45 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Wed, 12 Jan 2022 18:00:28 +0100 Subject: ASoC: topology: Remove superfluous error prints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit soc_tplg_check_elem_count(), already prints an error when applicable, so there is no need to print another one. Also clean up alignment of arguments in if, so there is no confusion about what is checked and what is executed if condition is true. Signed-off-by: Amadeusz Sławiński Reviewed-by: Cezary Rojewski Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220112170030.569712-2-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 2630df024dff..a97c02c4ef6b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -685,12 +685,9 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, int err = 0; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_bytes_control), count, - size, "mixer bytes")) { - dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n", - count); + sizeof(struct snd_soc_tplg_bytes_control), + count, size, "mixer bytes")) return -EINVAL; - } for (i = 0; i < count; i++) { be = (struct snd_soc_tplg_bytes_control *)tplg->pos; @@ -763,13 +760,9 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, int err = 0; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_mixer_control), - count, size, "mixers")) { - - dev_err(tplg->dev, "ASoC: invalid count %d for controls\n", - count); + sizeof(struct snd_soc_tplg_mixer_control), + count, size, "mixers")) return -EINVAL; - } for (i = 0; i < count; i++) { mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; @@ -927,13 +920,9 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, int err = 0; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_enum_control), - count, size, "enums")) { - - dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n", - count); + sizeof(struct snd_soc_tplg_enum_control), + count, size, "enums")) return -EINVAL; - } for (i = 0; i < count; i++) { ec = (struct snd_soc_tplg_enum_control *)tplg->pos; @@ -1111,13 +1100,9 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, count = le32_to_cpu(hdr->count); if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_dapm_graph_elem), - count, le32_to_cpu(hdr->payload_size), "graph")) { - - dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n", - count); + sizeof(struct snd_soc_tplg_dapm_graph_elem), + count, le32_to_cpu(hdr->payload_size), "graph")) return -EINVAL; - } dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, hdr->index); @@ -1965,11 +1950,8 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, if (soc_tplg_check_elem_count(tplg, size, count, le32_to_cpu(hdr->payload_size), - "PCM DAI")) { - dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", - count); + "PCM DAI")) return -EINVAL; - } for (i = 0; i < count; i++) { pcm = (struct snd_soc_tplg_pcm *)tplg->pos; @@ -2243,14 +2225,10 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg, return -EINVAL; } - if (soc_tplg_check_elem_count(tplg, - size, count, + if (soc_tplg_check_elem_count(tplg, size, count, le32_to_cpu(hdr->payload_size), - "physical link config")) { - dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n", - count); + "physical link config")) return -EINVAL; - } /* config physical DAI links */ for (i = 0; i < count; i++) { -- cgit v1.2.3 From feb00b736af64875560f371fe7f58b0b7f239046 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Wed, 12 Jan 2022 18:00:29 +0100 Subject: ASoC: topology: Allow TLV control to be either read or write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason to force readwrite access on TLV controls. It can be either read, write or both. This is further evidenced in code where it performs following checks: if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) && !sbe->get) return -EINVAL; if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && !sbe->put) return -EINVAL; Fixes: 1a3232d2f61d ("ASoC: topology: Add support for TLV bytes controls") Signed-off-by: Amadeusz Sławiński Reviewed-by: Cezary Rojewski Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220112170030.569712-3-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index a97c02c4ef6b..a9a9c46d0316 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -512,7 +512,8 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, if (le32_to_cpu(hdr->ops.info) == SND_SOC_TPLG_CTL_BYTES && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER - && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE + && (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ + || k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { struct soc_bytes_ext *sbe; struct snd_soc_tplg_bytes_control *be; -- cgit v1.2.3 From cc44c7492bad811dcb89f3f33f5aaacb564e1dfc Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Wed, 12 Jan 2022 18:00:30 +0100 Subject: ASoC: topology: Optimize soc_tplg_dapm_graph_elems_load behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before commit "ASoC: topology: Change allocations to resource managed" soc_tplg_dapm_graph_elems_load() used to free routes on error. During migration to managed allocations the routes table was left as is, but looking at it again it is unnecessary, so remove routes table and just keep pointer to DAPM route currently being set up. Also remove outdated comments which keep describing old behavior of remove_route() freeing memory. While it still does some cleanup, it leaves freeing memory to framework. Fixes: ff9226224437 ("ASoC: topology: Change allocations to resource managed") Signed-off-by: Amadeusz Sławiński Reviewed-by: Cezary Rojewski Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220112170030.569712-4-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 56 ++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index a9a9c46d0316..72e50df7052c 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1094,7 +1094,7 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, { struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; struct snd_soc_tplg_dapm_graph_elem *elem; - struct snd_soc_dapm_route **routes; + struct snd_soc_dapm_route *route; int count, i; int ret = 0; @@ -1108,24 +1108,10 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, hdr->index); - /* allocate memory for pointer to array of dapm routes */ - routes = kcalloc(count, sizeof(struct snd_soc_dapm_route *), - GFP_KERNEL); - if (!routes) - return -ENOMEM; - - /* - * allocate memory for each dapm route in the array. - * This needs to be done individually so that - * each route can be freed when it is removed in remove_route(). - */ for (i = 0; i < count; i++) { - routes[i] = devm_kzalloc(tplg->dev, sizeof(*routes[i]), GFP_KERNEL); - if (!routes[i]) + route = devm_kzalloc(tplg->dev, sizeof(*route), GFP_KERNEL); + if (!route) return -ENOMEM; - } - - for (i = 0; i < count; i++) { elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); @@ -1146,46 +1132,32 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, break; } - routes[i]->source = elem->source; - routes[i]->sink = elem->sink; + route->source = elem->source; + route->sink = elem->sink; /* set to NULL atm for tplg users */ - routes[i]->connected = NULL; + route->connected = NULL; if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) - routes[i]->control = NULL; + route->control = NULL; else - routes[i]->control = elem->control; + route->control = elem->control; /* add route dobj to dobj_list */ - routes[i]->dobj.type = SND_SOC_DOBJ_GRAPH; - routes[i]->dobj.ops = tplg->ops; - routes[i]->dobj.index = tplg->index; - list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list); + route->dobj.type = SND_SOC_DOBJ_GRAPH; + route->dobj.ops = tplg->ops; + route->dobj.index = tplg->index; + list_add(&route->dobj.list, &tplg->comp->dobj_list); - ret = soc_tplg_add_route(tplg, routes[i]); + ret = soc_tplg_add_route(tplg, route); if (ret < 0) { dev_err(tplg->dev, "ASoC: topology: add_route failed: %d\n", ret); - /* - * this route was added to the list, it will - * be freed in remove_route() so increment the - * counter to skip it in the error handling - * below. - */ - i++; break; } /* add route, but keep going if some fail */ - snd_soc_dapm_add_routes(dapm, routes[i], 1); + snd_soc_dapm_add_routes(dapm, route, 1); } - /* - * free pointer to array of dapm routes as this is no longer needed. - * The memory allocated for each dapm route will be freed - * when it is removed in remove_route(). - */ - kfree(routes); - return ret; } -- cgit v1.2.3 From ec45268467f4c4a523fc4a8c212d25e3b85261e9 Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Sun, 16 Jan 2022 14:55:49 +1300 Subject: ASoC: add support for TAS5805M digital amplifier The Texas Instruments TAS5805M is a class D audio amplifier with an integrated DSP. DSP configuration is supplied in a firmware image specified through a device-tree attribute. These register writes set up application-specific DSP settings and are expected to be generated using TI's PPC3 tool. Signed-off-by: Daniel Beer Link: https://lore.kernel.org/r/b82fac1d21a33a5f57a5819eaf37c31f5c86eb65.1642298336.git.daniel.beer@igorinstitute.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tas5805m.c | 567 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 sound/soc/codecs/tas5805m.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d3e5ae8310ef..d6b8f5cb6ef8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1485,6 +1485,15 @@ config SND_SOC_TAS5720 Enable support for Texas Instruments TAS5720L/M high-efficiency mono Class-D audio power amplifiers. +config SND_SOC_TAS5805M + tristate "Texas Instruments TAS5805M speaker amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS5805M Class-D + amplifiers. This is a speaker amplifier with an integrated + DSP. DSP configuration for each instance needs to be supplied + via a device-tree attribute. + config SND_SOC_TAS6424 tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ac7f20972470..b4e11c3e4a08 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -236,6 +236,7 @@ snd-soc-sti-sas-objs := sti-sas.o snd-soc-tas5086-objs := tas5086.o snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o +snd-soc-tas5805m-objs := tas5805m.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o @@ -574,6 +575,7 @@ obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o +obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c new file mode 100644 index 000000000000..fa0e81ec875a --- /dev/null +++ b/sound/soc/codecs/tas5805m.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the TAS5805M Audio Amplifier +// +// Author: Andy Liu +// Author: Daniel Beer +// +// This is based on a driver originally written by Andy Liu at TI and +// posted here: +// +// https://e2e.ti.com/support/audio-group/audio/f/audio-forum/722027/linux-tas5825m-linux-drivers +// +// It has been simplified a little and reworked for the 5.x ALSA SoC API. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Datasheet-defined registers on page 0, book 0 */ +#define REG_PAGE 0x00 +#define REG_DEVICE_CTRL_1 0x02 +#define REG_DEVICE_CTRL_2 0x03 +#define REG_SIG_CH_CTRL 0x28 +#define REG_SAP_CTRL_1 0x33 +#define REG_FS_MON 0x37 +#define REG_BCK_MON 0x38 +#define REG_CLKDET_STATUS 0x39 +#define REG_VOL_CTL 0x4c +#define REG_AGAIN 0x54 +#define REG_ADR_PIN_CTRL 0x60 +#define REG_ADR_PIN_CONFIG 0x61 +#define REG_CHAN_FAULT 0x70 +#define REG_GLOBAL_FAULT1 0x71 +#define REG_GLOBAL_FAULT2 0x72 +#define REG_FAULT 0x78 +#define REG_BOOK 0x7f + +/* DEVICE_CTRL_2 register values */ +#define DCTRL2_MODE_DEEP_SLEEP 0x00 +#define DCTRL2_MODE_SLEEP 0x01 +#define DCTRL2_MODE_HIZ 0x02 +#define DCTRL2_MODE_PLAY 0x03 + +#define DCTRL2_MUTE 0x08 +#define DCTRL2_DIS_DSP 0x10 + +/* This sequence of register writes must always be sent, prior to the + * 5ms delay while we wait for the DSP to boot. + */ +static const uint8_t dsp_cfg_preboot[] = { + 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02, 0x01, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02, +}; + +static const uint32_t tas5805m_volume[] = { + 0x0000001B, /* 0, -110dB */ 0x0000001E, /* 1, -109dB */ + 0x00000021, /* 2, -108dB */ 0x00000025, /* 3, -107dB */ + 0x0000002A, /* 4, -106dB */ 0x0000002F, /* 5, -105dB */ + 0x00000035, /* 6, -104dB */ 0x0000003B, /* 7, -103dB */ + 0x00000043, /* 8, -102dB */ 0x0000004B, /* 9, -101dB */ + 0x00000054, /* 10, -100dB */ 0x0000005E, /* 11, -99dB */ + 0x0000006A, /* 12, -98dB */ 0x00000076, /* 13, -97dB */ + 0x00000085, /* 14, -96dB */ 0x00000095, /* 15, -95dB */ + 0x000000A7, /* 16, -94dB */ 0x000000BC, /* 17, -93dB */ + 0x000000D3, /* 18, -92dB */ 0x000000EC, /* 19, -91dB */ + 0x00000109, /* 20, -90dB */ 0x0000012A, /* 21, -89dB */ + 0x0000014E, /* 22, -88dB */ 0x00000177, /* 23, -87dB */ + 0x000001A4, /* 24, -86dB */ 0x000001D8, /* 25, -85dB */ + 0x00000211, /* 26, -84dB */ 0x00000252, /* 27, -83dB */ + 0x0000029A, /* 28, -82dB */ 0x000002EC, /* 29, -81dB */ + 0x00000347, /* 30, -80dB */ 0x000003AD, /* 31, -79dB */ + 0x00000420, /* 32, -78dB */ 0x000004A1, /* 33, -77dB */ + 0x00000532, /* 34, -76dB */ 0x000005D4, /* 35, -75dB */ + 0x0000068A, /* 36, -74dB */ 0x00000756, /* 37, -73dB */ + 0x0000083B, /* 38, -72dB */ 0x0000093C, /* 39, -71dB */ + 0x00000A5D, /* 40, -70dB */ 0x00000BA0, /* 41, -69dB */ + 0x00000D0C, /* 42, -68dB */ 0x00000EA3, /* 43, -67dB */ + 0x0000106C, /* 44, -66dB */ 0x0000126D, /* 45, -65dB */ + 0x000014AD, /* 46, -64dB */ 0x00001733, /* 47, -63dB */ + 0x00001A07, /* 48, -62dB */ 0x00001D34, /* 49, -61dB */ + 0x000020C5, /* 50, -60dB */ 0x000024C4, /* 51, -59dB */ + 0x00002941, /* 52, -58dB */ 0x00002E49, /* 53, -57dB */ + 0x000033EF, /* 54, -56dB */ 0x00003A45, /* 55, -55dB */ + 0x00004161, /* 56, -54dB */ 0x0000495C, /* 57, -53dB */ + 0x0000524F, /* 58, -52dB */ 0x00005C5A, /* 59, -51dB */ + 0x0000679F, /* 60, -50dB */ 0x00007444, /* 61, -49dB */ + 0x00008274, /* 62, -48dB */ 0x0000925F, /* 63, -47dB */ + 0x0000A43B, /* 64, -46dB */ 0x0000B845, /* 65, -45dB */ + 0x0000CEC1, /* 66, -44dB */ 0x0000E7FB, /* 67, -43dB */ + 0x00010449, /* 68, -42dB */ 0x0001240C, /* 69, -41dB */ + 0x000147AE, /* 70, -40dB */ 0x00016FAA, /* 71, -39dB */ + 0x00019C86, /* 72, -38dB */ 0x0001CEDC, /* 73, -37dB */ + 0x00020756, /* 74, -36dB */ 0x000246B5, /* 75, -35dB */ + 0x00028DCF, /* 76, -34dB */ 0x0002DD96, /* 77, -33dB */ + 0x00033718, /* 78, -32dB */ 0x00039B87, /* 79, -31dB */ + 0x00040C37, /* 80, -30dB */ 0x00048AA7, /* 81, -29dB */ + 0x00051884, /* 82, -28dB */ 0x0005B7B1, /* 83, -27dB */ + 0x00066A4A, /* 84, -26dB */ 0x000732AE, /* 85, -25dB */ + 0x00081385, /* 86, -24dB */ 0x00090FCC, /* 87, -23dB */ + 0x000A2ADB, /* 88, -22dB */ 0x000B6873, /* 89, -21dB */ + 0x000CCCCD, /* 90, -20dB */ 0x000E5CA1, /* 91, -19dB */ + 0x00101D3F, /* 92, -18dB */ 0x0012149A, /* 93, -17dB */ + 0x00144961, /* 94, -16dB */ 0x0016C311, /* 95, -15dB */ + 0x00198A13, /* 96, -14dB */ 0x001CA7D7, /* 97, -13dB */ + 0x002026F3, /* 98, -12dB */ 0x00241347, /* 99, -11dB */ + 0x00287A27, /* 100, -10dB */ 0x002D6A86, /* 101, -9dB */ + 0x0032F52D, /* 102, -8dB */ 0x00392CEE, /* 103, -7dB */ + 0x004026E7, /* 104, -6dB */ 0x0047FACD, /* 105, -5dB */ + 0x0050C336, /* 106, -4dB */ 0x005A9DF8, /* 107, -3dB */ + 0x0065AC8C, /* 108, -2dB */ 0x00721483, /* 109, -1dB */ + 0x00800000, /* 110, 0dB */ 0x008F9E4D, /* 111, 1dB */ + 0x00A12478, /* 112, 2dB */ 0x00B4CE08, /* 113, 3dB */ + 0x00CADDC8, /* 114, 4dB */ 0x00E39EA9, /* 115, 5dB */ + 0x00FF64C1, /* 116, 6dB */ 0x011E8E6A, /* 117, 7dB */ + 0x0141857F, /* 118, 8dB */ 0x0168C0C6, /* 119, 9dB */ + 0x0194C584, /* 120, 10dB */ 0x01C62940, /* 121, 11dB */ + 0x01FD93C2, /* 122, 12dB */ 0x023BC148, /* 123, 13dB */ + 0x02818508, /* 124, 14dB */ 0x02CFCC01, /* 125, 15dB */ + 0x0327A01A, /* 126, 16dB */ 0x038A2BAD, /* 127, 17dB */ + 0x03F8BD7A, /* 128, 18dB */ 0x0474CD1B, /* 129, 19dB */ + 0x05000000, /* 130, 20dB */ 0x059C2F02, /* 131, 21dB */ + 0x064B6CAE, /* 132, 22dB */ 0x07100C4D, /* 133, 23dB */ + 0x07ECA9CD, /* 134, 24dB */ 0x08E43299, /* 135, 25dB */ + 0x09F9EF8E, /* 136, 26dB */ 0x0B319025, /* 137, 27dB */ + 0x0C8F36F2, /* 138, 28dB */ 0x0E1787B8, /* 139, 29dB */ + 0x0FCFB725, /* 140, 30dB */ 0x11BD9C84, /* 141, 31dB */ + 0x13E7C594, /* 142, 32dB */ 0x16558CCB, /* 143, 33dB */ + 0x190F3254, /* 144, 34dB */ 0x1C1DF80E, /* 145, 35dB */ + 0x1F8C4107, /* 146, 36dB */ 0x2365B4BF, /* 147, 37dB */ + 0x27B766C2, /* 148, 38dB */ 0x2C900313, /* 149, 39dB */ + 0x32000000, /* 150, 40dB */ 0x3819D612, /* 151, 41dB */ + 0x3EF23ECA, /* 152, 42dB */ 0x46A07B07, /* 153, 43dB */ + 0x4F3EA203, /* 154, 44dB */ 0x58E9F9F9, /* 155, 45dB */ + 0x63C35B8E, /* 156, 46dB */ 0x6FEFA16D, /* 157, 47dB */ + 0x7D982575, /* 158, 48dB */ +}; + +#define TAS5805M_VOLUME_MAX ((int)ARRAY_SIZE(tas5805m_volume) - 1) +#define TAS5805M_VOLUME_MIN 0 + +struct tas5805m_priv { + struct regulator *pvdd; + struct gpio_desc *gpio_pdn_n; + + uint8_t *dsp_cfg_data; + int dsp_cfg_len; + + struct regmap *regmap; + + int vol[2]; + bool is_powered; + bool is_muted; +}; + +static void set_dsp_scale(struct regmap *rm, int offset, int vol) +{ + uint8_t v[4]; + uint32_t x = tas5805m_volume[vol]; + int i; + + for (i = 0; i < 4; i++) { + v[3 - i] = x; + x >>= 8; + } + + regmap_bulk_write(rm, offset, v, ARRAY_SIZE(v)); +} + +static void tas5805m_refresh(struct snd_soc_component *component) +{ + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + struct regmap *rm = tas5805m->regmap; + + dev_dbg(component->dev, "refresh: is_muted=%d, vol=%d/%d\n", + tas5805m->is_muted, tas5805m->vol[0], tas5805m->vol[1]); + + regmap_write(rm, REG_PAGE, 0x00); + regmap_write(rm, REG_BOOK, 0x8c); + regmap_write(rm, REG_PAGE, 0x2a); + + /* Refresh volume. The actual volume control documented in the + * datasheet doesn't seem to work correctly. This is a pair of + * DSP registers which are *not* documented in the datasheet. + */ + set_dsp_scale(rm, 0x24, tas5805m->vol[0]); + set_dsp_scale(rm, 0x28, tas5805m->vol[1]); + + /* Set/clear digital soft-mute */ + regmap_write(rm, REG_DEVICE_CTRL_2, + (tas5805m->is_muted ? DCTRL2_MUTE : 0) | + DCTRL2_MODE_PLAY); +} + +static int tas5805m_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + + uinfo->value.integer.min = TAS5805M_VOLUME_MIN; + uinfo->value.integer.max = TAS5805M_VOLUME_MAX; + return 0; +} + +static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = tas5805m->vol[0]; + ucontrol->value.integer.value[1] = tas5805m->vol[1]; + return 0; +} + +static inline int volume_is_valid(int v) +{ + return (v >= TAS5805M_VOLUME_MIN) && (v <= TAS5805M_VOLUME_MAX); +} + +static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + if (!(volume_is_valid(ucontrol->value.integer.value[0]) && + volume_is_valid(ucontrol->value.integer.value[1]))) + return -EINVAL; + + if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || + tas5805m->vol[1] != ucontrol->value.integer.value[1]) { + tas5805m->vol[0] = ucontrol->value.integer.value[0]; + tas5805m->vol[1] = ucontrol->value.integer.value[1]; + dev_dbg(component->dev, "set vol=%d/%d (is_powered=%d)\n", + tas5805m->vol[0], tas5805m->vol[1], + tas5805m->is_powered); + if (tas5805m->is_powered) + tas5805m_refresh(component); + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new tas5805m_snd_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas5805m_vol_info, + .get = tas5805m_vol_get, + .put = tas5805m_vol_put, + }, +}; + +static void send_cfg(struct regmap *rm, + const uint8_t *s, unsigned int len) +{ + unsigned int i; + + for (i = 0; i + 1 < len; i += 2) + regmap_write(rm, s[i], s[i + 1]); +} + +/* The TAS5805M DSP can't be configured until the I2S clock has been + * present and stable for 5ms, or else it won't boot and we get no + * sound. + */ +static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + struct regmap *rm = tas5805m->regmap; + unsigned int chan, global1, global2; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev_dbg(component->dev, "DSP startup\n"); + + /* We mustn't issue any I2C transactions until the I2S + * clock is stable. Furthermore, we must allow a 5ms + * delay after the first set of register writes to + * allow the DSP to boot before configuring it. + */ + usleep_range(5000, 10000); + send_cfg(rm, dsp_cfg_preboot, + ARRAY_SIZE(dsp_cfg_preboot)); + usleep_range(5000, 15000); + send_cfg(rm, tas5805m->dsp_cfg_data, + tas5805m->dsp_cfg_len); + + tas5805m->is_powered = true; + tas5805m_refresh(component); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev_dbg(component->dev, "DSP shutdown\n"); + + tas5805m->is_powered = false; + + regmap_write(rm, REG_PAGE, 0x00); + regmap_write(rm, REG_BOOK, 0x00); + + regmap_read(rm, REG_CHAN_FAULT, &chan); + regmap_read(rm, REG_GLOBAL_FAULT1, &global1); + regmap_read(rm, REG_GLOBAL_FAULT2, &global2); + + dev_dbg(component->dev, + "fault regs: CHAN=%02x, GLOBAL1=%02x, GLOBAL2=%02x\n", + chan, global1, global2); + + regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_route tas5805m_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_component_driver soc_codec_dev_tas5805m = { + .controls = tas5805m_snd_controls, + .num_controls = ARRAY_SIZE(tas5805m_snd_controls), + .dapm_widgets = tas5805m_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5805m_dapm_widgets), + .dapm_routes = tas5805m_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5805m_audio_map), + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", + mute, tas5805m->is_powered); + tas5805m->is_muted = mute; + if (tas5805m->is_powered) + tas5805m_refresh(component); + + return 0; +} + +static const struct snd_soc_dai_ops tas5805m_dai_ops = { + .trigger = tas5805m_trigger, + .mute_stream = tas5805m_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver tas5805m_dai = { + .name = "tas5805m-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tas5805m_dai_ops, +}; + +static const struct regmap_config tas5805m_regmap = { + .reg_bits = 8, + .val_bits = 8, + + /* We have quite a lot of multi-level bank switching and a + * relatively small number of register writes between bank + * switches. + */ + .cache_type = REGCACHE_NONE, +}; + +static int tas5805m_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct regmap *regmap; + struct tas5805m_priv *tas5805m; + char filename[128]; + const char *config_name; + const struct firmware *fw; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "unable to allocate register map: %d\n", ret); + return ret; + } + + tas5805m = devm_kzalloc(dev, sizeof(struct tas5805m_priv), GFP_KERNEL); + if (!tas5805m) + return -ENOMEM; + + tas5805m->pvdd = devm_regulator_get(dev, "pvdd"); + if (IS_ERR(tas5805m->pvdd)) { + dev_err(dev, "failed to get pvdd supply: %ld\n", + PTR_ERR(tas5805m->pvdd)); + return PTR_ERR(tas5805m->pvdd); + } + + dev_set_drvdata(dev, tas5805m); + tas5805m->regmap = regmap; + tas5805m->gpio_pdn_n = devm_gpiod_get(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(tas5805m->gpio_pdn_n)) { + dev_err(dev, "error requesting PDN gpio: %ld\n", + PTR_ERR(tas5805m->gpio_pdn_n)); + return PTR_ERR(tas5805m->gpio_pdn_n); + } + + /* This configuration must be generated by PPC3. The file loaded + * consists of a sequence of register writes, where bytes at + * even indices are register addresses and those at odd indices + * are register values. + * + * The fixed portion of PPC3's output prior to the 5ms delay + * should be omitted. + */ + if (device_property_read_string(dev, "ti,dsp-config-name", + &config_name)) + config_name = "default"; + + snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", + config_name); + ret = request_firmware(&fw, filename, dev); + if (ret) + return ret; + + if ((fw->size < 2) || (fw->size & 1)) { + dev_err(dev, "firmware is invalid\n"); + release_firmware(fw); + return -EINVAL; + } + + tas5805m->dsp_cfg_len = fw->size; + tas5805m->dsp_cfg_data = devm_kmalloc(dev, fw->size, GFP_KERNEL); + if (!tas5805m->dsp_cfg_data) { + release_firmware(fw); + return -ENOMEM; + } + memcpy(tas5805m->dsp_cfg_data, fw->data, fw->size); + + release_firmware(fw); + + /* Do the first part of the power-on here, while we can expect + * the I2S interface to be quiet. We must raise PDN# and then + * wait 5ms before any I2S clock is sent, or else the internal + * regulator apparently won't come on. + * + * Also, we must keep the device in power down for 100ms or so + * after PVDD is applied, or else the ADR pin is sampled + * incorrectly and the device comes up with an unpredictable I2C + * address. + */ + tas5805m->vol[0] = TAS5805M_VOLUME_MIN; + tas5805m->vol[1] = TAS5805M_VOLUME_MIN; + + ret = regulator_enable(tas5805m->pvdd); + if (ret < 0) { + dev_err(dev, "failed to enable pvdd: %d\n", ret); + return ret; + } + + usleep_range(100000, 150000); + gpiod_set_value(tas5805m->gpio_pdn_n, 1); + usleep_range(10000, 15000); + + /* Don't register through devm. We need to be able to unregister + * the component prior to deasserting PDN# + */ + ret = snd_soc_register_component(dev, &soc_codec_dev_tas5805m, + &tas5805m_dai, 1); + if (ret < 0) { + dev_err(dev, "unable to register codec: %d\n", ret); + gpiod_set_value(tas5805m->gpio_pdn_n, 0); + regulator_disable(tas5805m->pvdd); + return ret; + } + + return 0; +} + +static int tas5805m_i2c_remove(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct tas5805m_priv *tas5805m = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + gpiod_set_value(tas5805m->gpio_pdn_n, 0); + usleep_range(10000, 15000); + regulator_disable(tas5805m->pvdd); + return 0; +} + +static const struct i2c_device_id tas5805m_i2c_id[] = { + { "tas5805m", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas5805m_of_match[] = { + { .compatible = "ti,tas5805m", }, + { } +}; +MODULE_DEVICE_TABLE(of, tas5805m_of_match); +#endif + +static struct i2c_driver tas5805m_i2c_driver = { + .probe_new = tas5805m_i2c_probe, + .remove = tas5805m_i2c_remove, + .id_table = tas5805m_i2c_id, + .driver = { + .name = "tas5805m", + .of_match_table = of_match_ptr(tas5805m_of_match), + }, +}; + +module_i2c_driver(tas5805m_i2c_driver); + +MODULE_AUTHOR("Andy Liu "); +MODULE_AUTHOR("Daniel Beer "); +MODULE_DESCRIPTION("TAS5805M Audio Amplifier Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b8aec7a4a01b75973c22f004377a48593a3fef03 Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Sun, 16 Jan 2022 14:56:27 +1300 Subject: ASoC: dt-bindings: add bindings for TI TAS5805M. The TAS5805M is a class D speaker amplifier with integrated DSP. Configuration must be generated by TI's PPC3 tool and supplied as a firmware image. Signed-off-by: Daniel Beer Link: https://lore.kernel.org/r/e271d381dcf1c6036a2a22bab6ab72654455aa58.1642298336.git.daniel.beer@igorinstitute.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tas5805m.yaml | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tas5805m.yaml diff --git a/Documentation/devicetree/bindings/sound/tas5805m.yaml b/Documentation/devicetree/bindings/sound/tas5805m.yaml new file mode 100644 index 000000000000..3aade02d8a96 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas5805m.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/tas5805m.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TAS5805M audio amplifier + +maintainers: + - Daniel Beer + +description: | + The TAS5805M is a class D audio amplifier with a built-in DSP. + +properties: + compatible: + enum: + - ti,tas5805m + + reg: + maxItems: 1 + description: | + I2C address of the amplifier. See the datasheet for possible values. + + pvdd-supply: + description: | + Regulator for audio power supply (PVDD in the datasheet). + + pdn-gpios: + description: | + Power-down control GPIO (PDN pin in the datasheet). + + ti,dsp-config-name: + description: | + The name of the DSP configuration that should be loaded for this + instance. Configuration blobs are sequences of register writes + generated from TI's PPC3 tool. + $ref: /schemas/types.yaml#/definitions/string + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + tas5805m: tas5805m@2c { + reg = <0x2c>; + compatible = "ti,tas5805m"; + + pvdd-supply = <&audiopwr>; + pdn-gpios = <&tlmm 160 0>; + + ti,dsp-config-name = "mono_pbtl_48khz"; + }; + }; + +additionalProperties: true -- cgit v1.2.3 From 6570f991582e32b7992601d0497c61962a2c5dcc Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 10 Jan 2022 09:47:07 +0000 Subject: ASoC: sh: rz-ssi: Drop calling rz_ssi_pio_recv() recursively Instead of recursively calling rz_ssi_pio_recv() use a while loop to read the samples from RX fifo. This also fixes an issue where the return value of rz_ssi_pio_recv() was ignored when called recursively. Fixes: 03e786bd4341 ("ASoC: sh: Add RZ/G2L SSIF-2 driver") Reported-by: Pavel Machek Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220110094711.8574-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 68 ++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index e8d98b362f9d..2ac2c9722b3b 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -411,54 +411,56 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { struct snd_pcm_substream *substream = strm->substream; struct snd_pcm_runtime *runtime; + bool done = false; u16 *buf; int fifo_samples; int frames_left; - int samples = 0; + int samples; int i; if (!rz_ssi_stream_is_valid(ssi, strm)) return -EINVAL; runtime = substream->runtime; - /* frames left in this period */ - frames_left = runtime->period_size - (strm->buffer_pos % - runtime->period_size); - if (frames_left == 0) - frames_left = runtime->period_size; - /* Samples in RX FIFO */ - fifo_samples = (rz_ssi_reg_readl(ssi, SSIFSR) >> - SSIFSR_RDC_SHIFT) & SSIFSR_RDC_MASK; - - /* Only read full frames at a time */ - while (frames_left && (fifo_samples >= runtime->channels)) { - samples += runtime->channels; - fifo_samples -= runtime->channels; - frames_left--; - } + while (!done) { + /* frames left in this period */ + frames_left = runtime->period_size - + (strm->buffer_pos % runtime->period_size); + if (!frames_left) + frames_left = runtime->period_size; + + /* Samples in RX FIFO */ + fifo_samples = (rz_ssi_reg_readl(ssi, SSIFSR) >> + SSIFSR_RDC_SHIFT) & SSIFSR_RDC_MASK; + + /* Only read full frames at a time */ + samples = 0; + while (frames_left && (fifo_samples >= runtime->channels)) { + samples += runtime->channels; + fifo_samples -= runtime->channels; + frames_left--; + } - /* not enough samples yet */ - if (samples == 0) - return 0; + /* not enough samples yet */ + if (!samples) + break; - /* calculate new buffer index */ - buf = (u16 *)(runtime->dma_area); - buf += strm->buffer_pos * runtime->channels; + /* calculate new buffer index */ + buf = (u16 *)(runtime->dma_area); + buf += strm->buffer_pos * runtime->channels; - /* Note, only supports 16-bit samples */ - for (i = 0; i < samples; i++) - *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); + /* Note, only supports 16-bit samples */ + for (i = 0; i < samples; i++) + *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); - rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); - rz_ssi_pointer_update(strm, samples / runtime->channels); + rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); + rz_ssi_pointer_update(strm, samples / runtime->channels); - /* - * If we finished this period, but there are more samples in - * the RX FIFO, call this function again - */ - if (frames_left == 0 && fifo_samples >= runtime->channels) - rz_ssi_pio_recv(ssi, strm); + /* check if there are no more samples in the RX FIFO */ + if (!(!frames_left && fifo_samples >= runtime->channels)) + done = true; + } return 0; } -- cgit v1.2.3 From 0788785c78342d422f93b1c9831c2b2b7f137937 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 10 Jan 2022 09:47:08 +0000 Subject: ASoC: sh: rz-ssi: Make the data structures available before registering the handlers Initialize the spinlock and make the data structures available before registering the interrupt handlers. Reported-by: Pavel Machek Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220110094711.8574-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 2ac2c9722b3b..7379b1489e35 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -977,6 +977,9 @@ static int rz_ssi_probe(struct platform_device *pdev) ssi->playback.priv = ssi; ssi->capture.priv = ssi; + spin_lock_init(&ssi->lock); + dev_set_drvdata(&pdev->dev, ssi); + /* Error Interrupt */ ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); if (ssi->irq_int < 0) @@ -1029,8 +1032,6 @@ static int rz_ssi_probe(struct platform_device *pdev) return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); } - spin_lock_init(&ssi->lock); - dev_set_drvdata(&pdev->dev, ssi); ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component, rz_ssi_soc_dai, ARRAY_SIZE(rz_ssi_soc_dai)); -- cgit v1.2.3 From 4f78f3c970f131a179fd135806a9b693fa606beb Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 10 Jan 2022 09:47:09 +0000 Subject: ASoC: sh: rz-ssi: Drop ssi parameter from rz_ssi_stream_init() ssi parameter is unused in rz_ssi_stream_init() so just drop it. While at it, change the return type of rz_ssi_stream_init() to void instead of int. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220110094711.8574-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 7379b1489e35..cbd8e1f3ac2e 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -201,9 +201,8 @@ static int rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, return ret; } -static int rz_ssi_stream_init(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm, - struct snd_pcm_substream *substream) +static void rz_ssi_stream_init(struct rz_ssi_stream *strm, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -219,8 +218,6 @@ static int rz_ssi_stream_init(struct rz_ssi_priv *ssi, /* fifo init */ strm->fifo_sample_size = SSI_FIFO_DEPTH; - - return 0; } static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, @@ -728,9 +725,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); udelay(5); - ret = rz_ssi_stream_init(ssi, strm, substream); - if (ret) - goto done; + rz_ssi_stream_init(strm, substream); if (ssi->dma_rt) { bool is_playback; -- cgit v1.2.3 From e42c903e8bf400728c4ae1f922169b4d28b72efa Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 10 Jan 2022 09:47:10 +0000 Subject: ASoC: sh: rz-ssi: Make return type of rz_ssi_stream_is_valid() to bool rz_ssi_stream_is_valid() never returns an int, it returns the result of a condition which is either true or false. While at it, drop "!!" as the expression is boolean. Reported-by: Pavel Machek Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220110094711.8574-5-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index cbd8e1f3ac2e..81e1786b827d 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -188,14 +188,14 @@ static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch)); } -static int rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm) +static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, + struct rz_ssi_stream *strm) { unsigned long flags; - int ret; + bool ret; spin_lock_irqsave(&ssi->lock, flags); - ret = !!(strm->substream && strm->substream->runtime); + ret = strm->substream && strm->substream->runtime; spin_unlock_irqrestore(&ssi->lock, flags); return ret; -- cgit v1.2.3 From 8d06f797f844d04a961f201f886f7f9985edc9bf Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 21 Jan 2022 12:04:10 +0000 Subject: ASoC: cs42l42: Report full jack status when plug is detected When a plug event is detect report the full state of all status bits, don't assume that there will have been a previous unplug event to clear all the bits. Report the state of both HEADPHONE and MICROPHONE bits according to detected type, and clear all the button status bits. The current button status is already checked and reported at the end of the function. During a system suspend the jack could be unplugged and plugged, possibly changing the jack type. On resume the interrupt status will indicate a plug event - there will not be an unplug event to clear the bits. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220121120412.672284-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 43d98bdb5b5b..2c294868008e 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1637,7 +1637,11 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) mutex_lock(&cs42l42->jack_detect_mutex); - /* Check auto-detect status */ + /* + * Check auto-detect status. Don't assume a previous unplug event has + * cleared the flags. If the jack is unplugged and plugged during + * system suspend there won't have been an unplug event. + */ if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { cs42l42_process_hs_type_detect(cs42l42); @@ -1645,11 +1649,15 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) case CS42L42_PLUG_CTIA: case CS42L42_PLUG_OMTP: snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET, - SND_JACK_HEADSET); + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); break; case CS42L42_PLUG_HEADPHONE: snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADPHONE, - SND_JACK_HEADPHONE); + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); break; default: break; -- cgit v1.2.3 From 5982b5a8ec7ddb076e774bdd0b17d74681ab0943 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 21 Jan 2022 12:04:11 +0000 Subject: ASoC: cs42l42: Change jack_detect_mutex to a lock of all IRQ handling Rename jack_detect_mutex to irq_lock and make it lock the entire IRQ handling. The jack_detect_mutex was introduced to synchronize registering an ALSA jack handler, via cs42l42_set_jack(), with the jack state processing in the IRQ handler, and was taken only around the relevant part of the IRQ handling code. System suspend will need to synchronize with the IRQ handler thread so will need a similar mutex that surrounds all of the IRQ handling. Repurposing the existing jack_detect_mutex is the simplest option. It does no harm for a call to cs42l42_set_jack() to additionally block the first few lines of IRQ handling, and the only interrupts used by the driver are all for jack handling. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220121120412.672284-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 11 +++++------ sound/soc/codecs/cs42l42.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2c294868008e..f1b95d45af4a 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -550,7 +550,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); /* Prevent race with interrupt handler */ - mutex_lock(&cs42l42->jack_detect_mutex); + mutex_lock(&cs42l42->irq_lock); cs42l42->jack = jk; if (jk) { @@ -566,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ break; } } - mutex_unlock(&cs42l42->jack_detect_mutex); + mutex_unlock(&cs42l42->irq_lock); return 0; } @@ -1613,6 +1613,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) unsigned int i; int report = 0; + mutex_lock(&cs42l42->irq_lock); /* Read sticky registers to clear interurpt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { @@ -1635,8 +1636,6 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) CS42L42_M_DETECT_FT_MASK | CS42L42_M_HSBIAS_HIZ_MASK); - mutex_lock(&cs42l42->jack_detect_mutex); - /* * Check auto-detect status. Don't assume a previous unplug event has * cleared the flags. If the jack is unplugged and plugged during @@ -1713,7 +1712,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) } } - mutex_unlock(&cs42l42->jack_detect_mutex); + mutex_unlock(&cs42l42->irq_lock); return IRQ_HANDLED; } @@ -2062,7 +2061,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, cs42l42->dev = &i2c_client->dev; i2c_set_clientdata(i2c_client, cs42l42); - mutex_init(&cs42l42->jack_detect_mutex); + mutex_init(&cs42l42->irq_lock); cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); if (IS_ERR(cs42l42->regmap)) { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 9fff183dce8e..53d96287abba 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -842,7 +842,7 @@ struct cs42l42_private { struct gpio_desc *reset_gpio; struct completion pdn_done; struct snd_soc_jack *jack; - struct mutex jack_detect_mutex; + struct mutex irq_lock; int pll_config; int bclk; u32 sclk; -- cgit v1.2.3 From f8593e88540052b3feaf1fb36f2c1c0484c9dc14 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 21 Jan 2022 12:04:12 +0000 Subject: ASoC: cs42l42: Handle system suspend Add system suspend functions to handle clean power-down on suspend and restoring registers on resume. The jack state could change during suspend. Plug->unplug and unplug->plug are straightforward because this looks no different from any other plug state change - there will be a plugged or unplugged interrupt pending. The jack could be unplugged and a different type of jack plugged, and on resume the plug state would not have changed. Setting plug_state back to TS_TRANS (transitioning) will make the next plug interrupt after resume run a type detection. During system suspend any jack plug/unplug and button events will not be reported or generate a system wakeup. If the plug state or headset type has changed it will be reported after resume. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220121120412.672284-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs42l42.h | 5 ++ 2 files changed, 146 insertions(+) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index f1b95d45af4a..db6ef6cdce15 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1614,6 +1614,10 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) int report = 0; mutex_lock(&cs42l42->irq_lock); + if (cs42l42->suspended) { + mutex_unlock(&cs42l42->irq_lock); + return IRQ_NONE; + } /* Read sticky registers to clear interurpt */ for (i = 0; i < ARRAY_SIZE(stickies); i++) { @@ -2047,6 +2051,138 @@ static int cs42l42_handle_device_data(struct device *dev, return 0; } +/* Datasheet suspend sequence */ +static const struct reg_sequence __maybe_unused cs42l42_shutdown_seq[] = { + REG_SEQ0(CS42L42_MIC_DET_CTL1, 0x9F), + REG_SEQ0(CS42L42_ADC_OVFL_INT_MASK, 0x01), + REG_SEQ0(CS42L42_MIXER_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_SRC_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_ASP_RX_INT_MASK, 0x1F), + REG_SEQ0(CS42L42_ASP_TX_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_CODEC_INT_MASK, 0x03), + REG_SEQ0(CS42L42_SRCPL_INT_MASK, 0x7F), + REG_SEQ0(CS42L42_VPMON_INT_MASK, 0x01), + REG_SEQ0(CS42L42_PLL_LOCK_INT_MASK, 0x01), + REG_SEQ0(CS42L42_TSRS_PLUG_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_WAKE_CTL, 0xE1), + REG_SEQ0(CS42L42_DET_INT1_MASK, 0xE0), + REG_SEQ0(CS42L42_DET_INT2_MASK, 0xFF), + REG_SEQ0(CS42L42_MIXER_CHA_VOL, 0x3F), + REG_SEQ0(CS42L42_MIXER_ADC_VOL, 0x3F), + REG_SEQ0(CS42L42_MIXER_CHB_VOL, 0x3F), + REG_SEQ0(CS42L42_HP_CTL, 0x0F), + REG_SEQ0(CS42L42_ASP_RX_DAI0_EN, 0x00), + REG_SEQ0(CS42L42_ASP_CLK_CFG, 0x00), + REG_SEQ0(CS42L42_HSDET_CTL2, 0x00), + REG_SEQ0(CS42L42_PWR_CTL1, 0xFE), + REG_SEQ0(CS42L42_PWR_CTL2, 0x8C), + REG_SEQ0(CS42L42_DAC_CTL2, 0x02), + REG_SEQ0(CS42L42_HS_CLAMP_DISABLE, 0x00), + REG_SEQ0(CS42L42_MISC_DET_CTL, 0x03), + REG_SEQ0(CS42L42_TIPSENSE_CTL, 0x02), + REG_SEQ0(CS42L42_HSBIAS_SC_AUTOCTL, 0x03), + REG_SEQ0(CS42L42_PWR_CTL1, 0xFF) +}; + +static int __maybe_unused cs42l42_suspend(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + unsigned int reg; + u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)]; + int i, ret; + + /* + * Wait for threaded irq handler to be idle and stop it processing + * future interrupts. This ensures a safe disable if the interrupt + * is shared. + */ + mutex_lock(&cs42l42->irq_lock); + cs42l42->suspended = true; + + /* Save register values that will be overwritten by shutdown sequence */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { + regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); + save_regs[i] = (u8)reg; + } + + /* Shutdown codec */ + regmap_multi_reg_write(cs42l42->regmap, + cs42l42_shutdown_seq, + ARRAY_SIZE(cs42l42_shutdown_seq)); + + /* All interrupt sources are now disabled */ + mutex_unlock(&cs42l42->irq_lock); + + /* Wait for power-down complete */ + msleep(CS42L42_PDN_DONE_TIME_MS); + ret = regmap_read_poll_timeout(cs42l42->regmap, + CS42L42_CODEC_STATUS, reg, + (reg & CS42L42_PDN_DONE_MASK), + CS42L42_PDN_DONE_POLL_US, + CS42L42_PDN_DONE_TIMEOUT_US); + if (ret) + dev_warn(dev, "Failed to get PDN_DONE: %d\n", ret); + + /* Discharge FILT+ */ + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, + CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK); + msleep(CS42L42_FILT_DISCHARGE_TIME_MS); + + regcache_cache_only(cs42l42->regmap, true); + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + + /* Restore register values to the regmap cache */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) + regmap_write(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, save_regs[i]); + + /* The cached address page register value is now stale */ + regcache_drop_region(cs42l42->regmap, CS42L42_PAGE_REGISTER, CS42L42_PAGE_REGISTER); + + dev_dbg(dev, "System suspended\n"); + + return 0; + +} + +static int __maybe_unused cs42l42_resume(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + int ret; + + /* + * If jack was unplugged and re-plugged during suspend it could + * have changed type but the tip-sense state hasn't changed. + * Force a plugged state to be re-evaluated. + */ + if (cs42l42->plug_state != CS42L42_TS_UNPLUG) + cs42l42->plug_state = CS42L42_TS_TRANS; + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); + + regcache_cache_only(cs42l42->regmap, false); + regcache_mark_dirty(cs42l42->regmap); + + mutex_lock(&cs42l42->irq_lock); + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); + + cs42l42->suspended = false; + mutex_unlock(&cs42l42->irq_lock); + + dev_dbg(dev, "System resumed\n"); + + return 0; +} + static int cs42l42_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { @@ -2217,6 +2353,10 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client) return 0; } +static const struct dev_pm_ops cs42l42_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_resume) +}; + #ifdef CONFIG_OF static const struct of_device_id cs42l42_of_match[] = { { .compatible = "cirrus,cs42l42", }, @@ -2243,6 +2383,7 @@ MODULE_DEVICE_TABLE(i2c, cs42l42_id); static struct i2c_driver cs42l42_i2c_driver = { .driver = { .name = "cs42l42", + .pm = &cs42l42_pm_ops, .of_match_table = of_match_ptr(cs42l42_of_match), .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 53d96287abba..244b24d1f5e9 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -826,6 +826,10 @@ #define CS42L42_PLL_LOCK_POLL_US 250 #define CS42L42_PLL_LOCK_TIMEOUT_US 1250 #define CS42L42_HP_ADC_EN_TIME_US 20000 +#define CS42L42_PDN_DONE_POLL_US 1000 +#define CS42L42_PDN_DONE_TIMEOUT_US 200000 +#define CS42L42_PDN_DONE_TIME_MS 100 +#define CS42L42_FILT_DISCHARGE_TIME_MS 46 static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { "VA", @@ -860,6 +864,7 @@ struct cs42l42_private { u8 hs_bias_sense_en; u8 stream_use; bool hp_adc_up_pending; + bool suspended; }; #endif /* __CS42L42_H__ */ -- cgit v1.2.3 From f67c0c0d3b9048d86ea6ae52e36a2b78c48f265d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 20 Jan 2022 17:21:56 -0600 Subject: ASoC: SOF: Intel: match sdw version on link_slaves_found Codecs with the same part id, manufacturer id and part id, but different sdw version should be treated as different codecs. For example, rt711 and rt711-sdca are different. So, we should match sdw version as well. Reported-by: Reddy Muralidhar Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120232157.199919-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c8fb082209ce..67936be54ef7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1187,7 +1187,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, struct hdac_bus *bus = sof_to_bus(sdev); struct sdw_intel_slave_id *ids = sdw->ids; int num_slaves = sdw->num_slaves; - unsigned int part_id, link_id, unique_id, mfg_id; + unsigned int part_id, link_id, unique_id, mfg_id, version; int i, j, k; for (i = 0; i < link->num_adr; i++) { @@ -1197,12 +1197,14 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, mfg_id = SDW_MFG_ID(adr); part_id = SDW_PART_ID(adr); link_id = SDW_DISCO_LINK_ID(adr); + version = SDW_VERSION(adr); for (j = 0; j < num_slaves; j++) { /* find out how many identical parts were reported on that link */ if (ids[j].link_id == link_id && ids[j].id.part_id == part_id && - ids[j].id.mfg_id == mfg_id) + ids[j].id.mfg_id == mfg_id && + ids[j].id.sdw_version == version) reported_part_count++; } @@ -1211,21 +1213,24 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, if (ids[j].link_id != link_id || ids[j].id.part_id != part_id || - ids[j].id.mfg_id != mfg_id) + ids[j].id.mfg_id != mfg_id || + ids[j].id.sdw_version != version) continue; /* find out how many identical parts are expected */ for (k = 0; k < link->num_adr; k++) { u64 adr2 = link->adr_d[k].adr; - unsigned int part_id2, link_id2, mfg_id2; + unsigned int part_id2, link_id2, mfg_id2, version2; mfg_id2 = SDW_MFG_ID(adr2); part_id2 = SDW_PART_ID(adr2); link_id2 = SDW_DISCO_LINK_ID(adr2); + version2 = SDW_VERSION(adr2); if (link_id2 == link_id && part_id2 == part_id && - mfg_id2 == mfg_id) + mfg_id2 == mfg_id && + version2 == version) expected_part_count++; } -- cgit v1.2.3 From 7afed13b582b0c3c2a283642fcd87e0db0134f39 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 20 Jan 2022 17:21:57 -0600 Subject: ASoC: SOF: Intel: Compare sdw adr directly We can exclude the sdw unique id and compare the sdw adr directly when we are finding out identical parts. Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120232157.199919-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 67936be54ef7..8f6765317cfa 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1179,6 +1179,10 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ + SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) + /* Check if all Slaves defined on the link can be found */ static bool link_slaves_found(struct snd_sof_dev *sdev, const struct snd_soc_acpi_link_adr *link, @@ -1220,17 +1224,8 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, /* find out how many identical parts are expected */ for (k = 0; k < link->num_adr; k++) { u64 adr2 = link->adr_d[k].adr; - unsigned int part_id2, link_id2, mfg_id2, version2; - - mfg_id2 = SDW_MFG_ID(adr2); - part_id2 = SDW_PART_ID(adr2); - link_id2 = SDW_DISCO_LINK_ID(adr2); - version2 = SDW_VERSION(adr2); - if (link_id2 == link_id && - part_id2 == part_id && - mfg_id2 == mfg_id && - version2 == version) + if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) expected_part_count++; } -- cgit v1.2.3 From 22cefca393ea3256fa7afb97cca39d5a088053f4 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Thu, 20 Jan 2022 17:02:25 -0600 Subject: ASoC: Intel: sof_rt5682: add support for systems without i915 audio Add support to systems where iDisp HDMI/DP audio codec is disabled for some reason, switch codecs to SoC dummy in the affected DAI links. This allows to reuse existing topologies as hdmi_num is 3 by default. Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Signed-off-by: Yong Zhi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120230226.175906-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index bd6d2e7dea53..8391bea8ec79 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -81,6 +81,7 @@ struct sof_card_private { struct snd_soc_jack sof_headset; struct list_head hdmi_pcm_list; bool common_hdmi_codec_drv; + bool idisp_codec; }; static int sof_rt5682_quirk_cb(const struct dmi_system_id *id) @@ -417,7 +418,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) int i = 0; /* HDMI is not supported by SOF on Baytrail/CherryTrail */ - if (is_legacy_cpu) + if (is_legacy_cpu || !ctx->idisp_codec) return 0; if (list_empty(&ctx->hdmi_pcm_list)) @@ -558,11 +559,14 @@ static struct snd_soc_dai_link_component dummy_component[] = { } }; +#define IDISP_CODEC_MASK 0x4 + static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, int ssp_codec, int ssp_amp, int dmic_be_num, - int hdmi_num) + int hdmi_num, + bool idisp_codec) { struct snd_soc_dai_link_component *idisp_components; struct snd_soc_dai_link_component *cpus; @@ -676,13 +680,18 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (!links[id].cpus->dai_name) goto devm_err; - idisp_components[i - 1].name = "ehdaudio0D2"; - idisp_components[i - 1].dai_name = devm_kasprintf(dev, - GFP_KERNEL, - "intel-hdmi-hifi%d", - i); - if (!idisp_components[i - 1].dai_name) - goto devm_err; + if (idisp_codec) { + idisp_components[i - 1].name = "ehdaudio0D2"; + idisp_components[i - 1].dai_name = devm_kasprintf(dev, + GFP_KERNEL, + "intel-hdmi-hifi%d", + i); + if (!idisp_components[i - 1].dai_name) + goto devm_err; + } else { + idisp_components[i - 1].name = "snd-soc-dummy"; + idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + } links[id].codecs = &idisp_components[i - 1]; links[id].num_codecs = 1; @@ -838,6 +847,9 @@ static int sof_audio_probe(struct platform_device *pdev) /* default number of HDMI DAI's */ if (!hdmi_num) hdmi_num = 3; + + if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) + ctx->idisp_codec = true; } /* need to get main clock from pmc */ @@ -892,7 +904,7 @@ static int sof_audio_probe(struct platform_device *pdev) sof_audio_card_rt5682.num_links++; dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, - dmic_be_num, hdmi_num); + dmic_be_num, hdmi_num, ctx->idisp_codec); if (!dai_links) return -ENOMEM; -- cgit v1.2.3 From c8e98eaf2bcb91291b309f7f703dea345cae1411 Mon Sep 17 00:00:00 2001 From: Ajye Huang Date: Thu, 20 Jan 2022 17:02:26 -0600 Subject: ASoC: Intel: sof_rt5682: Add support for platform without amplifier Add a board config adl_rt5682 to support alc5682 headset codec without speaker amplifier. Follow Intel BT offload design by connecting alc5682 to SSP0. Reviewed-by: Ranjani Sridharan Signed-off-by: Ajye Huang Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120230226.175906-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 8 ++++++++ sound/soc/intel/common/soc-acpi-intel-adl-match.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 8391bea8ec79..70541caa7237 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -1045,6 +1045,14 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .name = "adl_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index f32bcb2b2e09..20257f547275 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -447,6 +447,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_rt5682", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); -- cgit v1.2.3 From 55915f20ad9ae92015bf7b2c4ac854e5b720d63f Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 17 Jan 2022 10:21:07 -0300 Subject: ASoC: bindings: fsl-asoc-card: Add mclk-id optional property Support setting the sound card main clock input from the device-tree using the mclk-id property. Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20220117132109.283365-4-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index 23d83fa7609f..b219626a5403 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -82,6 +82,7 @@ Optional properties: - dai-format : audio format, for details see simple-card.yaml. - frame-inversion : dai-link uses frame clock inversion, for details see simple-card.yaml. - bitclock-inversion : dai-link uses bit clock inversion, for details see simple-card.yaml. + - mclk-id : main clock id, specific for each card configuration. Optional unless SSI is selected as a CPU DAI: -- cgit v1.2.3 From 91e4e40b59bac246c4b4f2c9baf57c30337d5c71 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 17 Jan 2022 10:21:08 -0300 Subject: ASoC: fsl-asoc-card: Add optional dt property for setting mclk-id Sound cards may allow using different main clock inputs. In the generic fsl-asoc-card driver, these values are hardcoded for each specific card configuration. Let's make it more flexible, allowing setting mclk-id from the device-tree node. Otherwise, the default value for each card configuration is used. Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20220117132109.283365-5-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5ee945505281..156d3c669274 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -693,6 +693,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } + /* + * Allow setting mclk-id from the device-tree node. Otherwise, the + * default value for each card configuration is used. + */ + of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id); + /* Format info from DT is optional. */ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider); if (bitclkprovider || frameprovider) { -- cgit v1.2.3 From d4c4e2861560ab1cbf540bbda5bcdf4c92b17110 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 17 Jan 2022 10:21:09 -0300 Subject: ASoC: fsl-asoc-card: Remove BCLK default value for tlv320aic31xx card Now that fsl-asoc-card support setting mclk-id through the device-tree mclk-id property, let's remove the default BCLK configuration for this card. Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20220117132109.283365-6-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 156d3c669274..370bc790c6ba 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -637,7 +637,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[2].dpcm_capture = 0; priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; - priv->codec_priv.mclk_id = AIC31XX_PLL_CLKIN_BCLK; priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { -- cgit v1.2.3 From 85f856f790b5fd427cb31b3f62755713174da0aa Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 17 Jan 2022 10:21:05 -0300 Subject: ASoC: Rename tlv320aic31xx-micbias.h as tlv320aic31xx.h Let's use a more generic name, so other definitions for tlv320aic31xx can be included. Signed-off-by: Ariel D'Alessandro Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220117132109.283365-2-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tlv320aic31xx.txt | 2 +- arch/arm/boot/dts/am43x-epos-evm.dts | 2 +- include/dt-bindings/sound/tlv320aic31xx-micbias.h | 9 --------- include/dt-bindings/sound/tlv320aic31xx.h | 9 +++++++++ sound/soc/codecs/tlv320aic31xx.c | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 include/dt-bindings/sound/tlv320aic31xx-micbias.h create mode 100644 include/dt-bindings/sound/tlv320aic31xx.h diff --git a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt index e372303697dc..bbad98d5b986 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt @@ -58,7 +58,7 @@ The pins can be used in referring sound node's audio-routing property. Example: #include -#include +#include tlv320aic31xx: tlv320aic31xx@18 { compatible = "ti,tlv320aic311x"; diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts index 2f4d2e4e9b3e..4f9a7251a107 100644 --- a/arch/arm/boot/dts/am43x-epos-evm.dts +++ b/arch/arm/boot/dts/am43x-epos-evm.dts @@ -11,7 +11,7 @@ #include #include #include -#include +#include / { model = "TI AM43x EPOS EVM"; diff --git a/include/dt-bindings/sound/tlv320aic31xx-micbias.h b/include/dt-bindings/sound/tlv320aic31xx-micbias.h deleted file mode 100644 index c6895a18a455..000000000000 --- a/include/dt-bindings/sound/tlv320aic31xx-micbias.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DT_TLV320AIC31XX_MICBIAS_H -#define __DT_TLV320AIC31XX_MICBIAS_H - -#define MICBIAS_2_0V 1 -#define MICBIAS_2_5V 2 -#define MICBIAS_AVDDV 3 - -#endif /* __DT_TLV320AIC31XX_MICBIAS_H */ diff --git a/include/dt-bindings/sound/tlv320aic31xx.h b/include/dt-bindings/sound/tlv320aic31xx.h new file mode 100644 index 000000000000..3a845fbba992 --- /dev/null +++ b/include/dt-bindings/sound/tlv320aic31xx.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_TLV320AIC31XX_H +#define __DT_TLV320AIC31XX_H + +#define MICBIAS_2_0V 1 +#define MICBIAS_2_5V 2 +#define MICBIAS_AVDDV 3 + +#endif /* __DT_TLV320AIC31XX_H */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index e77342aff46d..8331dc26bcd2 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "tlv320aic31xx.h" -- cgit v1.2.3 From 6045ffd366283236f0de79c8a0e98ae766e9a8f9 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Mon, 17 Jan 2022 10:21:06 -0300 Subject: ASoC: tlv320aic31xx: Define PLL clock inputs Add constants for the different PLL clock inputs in tlv320aic31xx. Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20220117132109.283365-3-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/tlv320aic31xx.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dt-bindings/sound/tlv320aic31xx.h b/include/dt-bindings/sound/tlv320aic31xx.h index 3a845fbba992..4a80238ab250 100644 --- a/include/dt-bindings/sound/tlv320aic31xx.h +++ b/include/dt-bindings/sound/tlv320aic31xx.h @@ -6,4 +6,9 @@ #define MICBIAS_2_5V 2 #define MICBIAS_AVDDV 3 +#define PLL_CLKIN_MCLK 0x00 +#define PLL_CLKIN_BCLK 0x01 +#define PLL_CLKIN_GPIO1 0x02 +#define PLL_CLKIN_DIN 0x03 + #endif /* __DT_TLV320AIC31XX_H */ -- cgit v1.2.3 From 4ec19deec7ffae843c3445ac7d2cfcc78c56c145 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Mon, 10 Jan 2022 01:28:33 +0000 Subject: ASoC: codecs: remove redundant ret variable Return value from devm_snd_soc_register_component() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: CGEL ZTE Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220110012833.643994-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm8971.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 21ae55c32a6d..ddf0e2f5e66a 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -676,7 +676,6 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8971_priv *wm8971; - int ret; wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), GFP_KERNEL); @@ -689,10 +688,8 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8971); - ret = devm_snd_soc_register_component(&i2c->dev, + return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm8971, &wm8971_dai, 1); - - return ret; } static const struct i2c_device_id wm8971_i2c_id[] = { -- cgit v1.2.3 From de531908ca4251918f3aff4b21440a8f7b96b0b7 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Mon, 17 Jan 2022 11:03:57 +0000 Subject: ASoC: samsung: remove unneeded ret variable Return value from io_remap_pfn_range() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: CGEL ZTE Link: https://lore.kernel.org/r/20220117110357.863990-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/samsung/idma.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index c3f1b054e238..402ccadad46c 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -244,17 +244,14 @@ static int idma_mmap(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; unsigned long size, offset; - int ret; /* From snd_pcm_lib_mmap_iomem */ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); size = vma->vm_end - vma->vm_start; offset = vma->vm_pgoff << PAGE_SHIFT; - ret = io_remap_pfn_range(vma, vma->vm_start, + return io_remap_pfn_range(vma, vma->vm_start, (runtime->dma_addr + offset) >> PAGE_SHIFT, size, vma->vm_page_prot); - - return ret; } static irqreturn_t iis_irq(int irqno, void *dev_id) -- cgit v1.2.3 From 88c62b16281e5fe748f22f44da3def8a91fb1c34 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 20 Jan 2022 10:44:02 +0800 Subject: ASoC: soc-generic-dmaengine-pcm: separate max_buffer_size assignment The config->pcm_hardware may be NULL when config->prealloc_buffer_size is not zero, so it is better to move max_buffer_size assignment under a separate condition. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1642646642-15908-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index c54c8ca8d715..8659cb1794f1 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -237,13 +237,15 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, size_t max_buffer_size; unsigned int i; - if (config && config->prealloc_buffer_size) { + if (config && config->prealloc_buffer_size) prealloc_buffer_size = config->prealloc_buffer_size; - max_buffer_size = config->pcm_hardware->buffer_bytes_max; - } else { + else prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; + + if (config && config->pcm_hardware && config->pcm_hardware->buffer_bytes_max) + max_buffer_size = config->pcm_hardware->buffer_bytes_max; + else max_buffer_size = SIZE_MAX; - } for_each_pcm_streams(i) { struct snd_pcm_substream *substream = rtd->pcm->streams[i].substream; -- cgit v1.2.3 From 330dc18356e697eaf9796732b6acbdf948022136 Mon Sep 17 00:00:00 2001 From: V sujith kumar Reddy Date: Sun, 23 Jan 2022 01:16:59 +0530 Subject: ASoC: amd: sof-mach: Add support for RT5682S and RT1019 card We have new platform with rt5682s as a primary codec and rt1019 as an amp codec. Add machine struct to register sof audio based sound card on such Chrome machine. Signed-off-by: Ajit Kumar Pandey Signed-off-by: V sujith kumar Reddy Link: https://lore.kernel.org/r/20220122194707.2661026-1-vsujithkumar.reddy@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 9 +++++++++ sound/soc/amd/acp/acp-sof-mach.c | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index c9e1c08364f3..5cbc82eca4c9 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -110,6 +110,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { .fw_filename = "sof-rn.ri", .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg", }, + { + .id = "RTL5682", + .drv_name = "rt5682s-rt1019", + .pdata = (void *)&acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + .fw_filename = "sof-rn.ri", + .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg", + }, { .id = "AMDI1019", .drv_name = "renoir-dsp", diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 07de46142655..c1d9650fc222 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -40,6 +40,15 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .gpio_spkr_en = EN_SPKR_GPIO_NK, }; +static struct acp_card_drvdata sof_rt5682s_rt1019_data = { + .hs_cpu_id = I2S_SP, + .amp_cpu_id = I2S_SP, + .dmic_cpu_id = DMIC, + .hs_codec_id = RT5682S, + .amp_codec_id = RT1019, + .dmic_codec_id = DMIC, +}; + static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_cpu_id = I2S_SP, .amp_cpu_id = I2S_SP, @@ -126,6 +135,10 @@ static const struct platform_device_id board_ids[] = { .name = "rt5682s-max", .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data }, + { + .name = "rt5682s-rt1019", + .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data + }, { } }; static struct platform_driver acp_asoc_audio = { @@ -143,4 +156,5 @@ MODULE_DESCRIPTION("ACP chrome SOF audio support"); MODULE_ALIAS("platform:rt5682-rt1019"); MODULE_ALIAS("platform:rt5682-max"); MODULE_ALIAS("platform:rt5682s-max"); +MODULE_ALIAS("platform:rt5682s-rt1019"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From dbf2f8e3fecd5b2197f406f26fed26042664044e Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Thu, 20 Jan 2022 00:40:12 -0500 Subject: ASoC: Intel: sof_rt5682: add 512FS MCLK clock configuration codec system clock source support 512FS MCLK synchronous directly, so no need to set PLL configuration when MCLK 24.576MHz. Suggested-by: Shuming Fan Signed-off-by: Mac Chiang Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220120054012.15849-1-mac.chiang@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index bd6d2e7dea53..f4e833cbffe1 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -369,11 +369,16 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, pll_out = params_rate(params) * 512; - /* Configure pll for codec */ - ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, pll_in, - pll_out); - if (ret < 0) - dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret); + /* when MCLK is 512FS, no need to set PLL configuration additionally. */ + if (pll_in == pll_out) + clk_id = RT5682S_SCLK_S_MCLK; + else { + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, pll_in, + pll_out); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret); + } /* Configure sysclk for codec */ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, -- cgit v1.2.3 From aa505ecccf2ae7546e0e262d574e18a9241f3005 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Sat, 22 Jan 2022 01:10:31 +0800 Subject: ASoC: codecs: Check for error pointer after calling devm_regmap_init_mmio Since the potential failure of the devm_regmap_init_mmio(), it will return error pointer and be assigned to the regmap. Then the error pointer will be dereferenced. For example rx->regmap will be used in rx_macro_mclk_enable(). Therefore, it should be better to check it. Fixes: af3d54b99764 ("ASoC: codecs: lpass-rx-macro: add support for lpass rx macro") Fixes: c39667ddcfc5 ("ASoC: codecs: lpass-tx-macro: add support for lpass tx macro") Fixes: 809bcbcecebf ("ASoC: codecs: lpass-wsa-macro: Add support to WSA Macro") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220121171031.2826198-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 2 ++ sound/soc/codecs/lpass-tx-macro.c | 2 ++ sound/soc/codecs/lpass-wsa-macro.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index aec5127260fd..29d214f784d1 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3542,6 +3542,8 @@ static int rx_macro_probe(struct platform_device *pdev) return PTR_ERR(base); rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); + if (IS_ERR(rx->regmap)) + return PTR_ERR(rx->regmap); dev_set_drvdata(dev, rx); diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index a4c0a155af56..9c96ab1bf84f 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1821,6 +1821,8 @@ static int tx_macro_probe(struct platform_device *pdev) } tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config); + if (IS_ERR(tx->regmap)) + return PTR_ERR(tx->regmap); dev_set_drvdata(dev, tx); diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 75baf8eb7029..69d2915f40d8 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2405,6 +2405,8 @@ static int wsa_macro_probe(struct platform_device *pdev) return PTR_ERR(base); wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config); + if (IS_ERR(wsa->regmap)) + return PTR_ERR(wsa->regmap); dev_set_drvdata(dev, wsa); -- cgit v1.2.3 From 1c5091fbe7e0d0804158200b7feac5123f7b4fbd Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:27 -0600 Subject: ASoC: xilinx: xlnx_formatter_pcm: Handle sysclk setting This driver did not set the MM2S Fs Multiplier Register to the proper value for playback streams. This needs to be set to the sample rate to MCLK multiplier, or random stream underflows can occur on the downstream I2S transmitter. Store the sysclk value provided via the set_sysclk callback and use that in conjunction with the sample rate in the hw_params callback to calculate the proper value to set for this register. Fixes: 6f6c3c36f091 ("ASoC: xlnx: add pcm formatter platform driver") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20220120195832.1742271-2-robert.hancock@calian.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index ce19a6058b27..5c4158069a5a 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -84,6 +84,7 @@ struct xlnx_pcm_drv_data { struct snd_pcm_substream *play_stream; struct snd_pcm_substream *capture_stream; struct clk *axi_clk; + unsigned int sysclk; }; /* @@ -314,6 +315,15 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) return IRQ_NONE; } +static int xlnx_formatter_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev); + + adata->sysclk = freq; + return 0; +} + static int xlnx_formatter_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -450,11 +460,25 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, u64 size; struct snd_pcm_runtime *runtime = substream->runtime; struct xlnx_pcm_stream_param *stream_data = runtime->private_data; + struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev); active_ch = params_channels(params); if (active_ch > stream_data->ch_limit) return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + adata->sysclk) { + unsigned int mclk_fs = adata->sysclk / params_rate(params); + + if (adata->sysclk % params_rate(params) != 0) { + dev_warn(component->dev, "sysclk %u not divisible by rate %u\n", + adata->sysclk, params_rate(params)); + return -EINVAL; + } + + writel(mclk_fs, stream_data->mmio + XLNX_AUD_FS_MULTIPLIER); + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && stream_data->xfer_mode == AES_TO_PCM) { val = readl(stream_data->mmio + XLNX_AUD_STS); @@ -552,6 +576,7 @@ static int xlnx_formatter_pcm_new(struct snd_soc_component *component, static const struct snd_soc_component_driver xlnx_asoc_component = { .name = DRV_NAME, + .set_sysclk = xlnx_formatter_set_sysclk, .open = xlnx_formatter_pcm_open, .close = xlnx_formatter_pcm_close, .hw_params = xlnx_formatter_pcm_hw_params, -- cgit v1.2.3 From 5e46c63ca22278fe363dfd9f5360c2e2ad082087 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:28 -0600 Subject: ASoC: xilinx: xlnx_i2s: create drvdata structure An upcoming change will require storing additional driver data other than the memory base address. Create a drvdata structure and use that rather than storing the raw base address pointer. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20220120195832.1742271-3-robert.hancock@calian.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_i2s.c | 66 ++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c index cc641e582c82..3bafa34b789a 100644 --- a/sound/soc/xilinx/xlnx_i2s.c +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -22,15 +22,20 @@ #define I2S_CH0_OFFSET 0x30 #define I2S_I2STIM_VALID_MASK GENMASK(7, 0) +struct xlnx_i2s_drv_data { + struct snd_soc_dai_driver dai_drv; + void __iomem *base; +}; + static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai, int div_id, int div) { - void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai); + struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(cpu_dai); if (!div || (div & ~I2S_I2STIM_VALID_MASK)) return -EINVAL; - writel(div, base + I2S_I2STIM_OFFSET); + writel(div, drv_data->base + I2S_I2STIM_OFFSET); return 0; } @@ -40,13 +45,13 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *i2s_dai) { u32 reg_off, chan_id; - void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai); chan_id = params_channels(params) / 2; while (chan_id > 0) { reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4); - writel(chan_id, base + reg_off); + writel(chan_id, drv_data->base + reg_off); chan_id--; } @@ -56,18 +61,18 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *i2s_dai) { - void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - writel(1, base + I2S_CORE_CTRL_OFFSET); + writel(1, drv_data->base + I2S_CORE_CTRL_OFFSET); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - writel(0, base + I2S_CORE_CTRL_OFFSET); + writel(0, drv_data->base + I2S_CORE_CTRL_OFFSET); break; default: return -EINVAL; @@ -95,20 +100,19 @@ MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match); static int xlnx_i2s_probe(struct platform_device *pdev) { - void __iomem *base; - struct snd_soc_dai_driver *dai_drv; + struct xlnx_i2s_drv_data *drv_data; int ret; u32 ch, format, data_width; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); - if (!dai_drv) + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) return -ENOMEM; - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); + drv_data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drv_data->base)) + return PTR_ERR(drv_data->base); ret = of_property_read_u32(node, "xlnx,num-channels", &ch); if (ret < 0) { @@ -134,35 +138,35 @@ static int xlnx_i2s_probe(struct platform_device *pdev) } if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { - dai_drv->name = "xlnx_i2s_playback"; - dai_drv->playback.stream_name = "Playback"; - dai_drv->playback.formats = format; - dai_drv->playback.channels_min = ch; - dai_drv->playback.channels_max = ch; - dai_drv->playback.rates = SNDRV_PCM_RATE_8000_192000; - dai_drv->ops = &xlnx_i2s_dai_ops; + drv_data->dai_drv.name = "xlnx_i2s_playback"; + drv_data->dai_drv.playback.stream_name = "Playback"; + drv_data->dai_drv.playback.formats = format; + drv_data->dai_drv.playback.channels_min = ch; + drv_data->dai_drv.playback.channels_max = ch; + drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; + drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { - dai_drv->name = "xlnx_i2s_capture"; - dai_drv->capture.stream_name = "Capture"; - dai_drv->capture.formats = format; - dai_drv->capture.channels_min = ch; - dai_drv->capture.channels_max = ch; - dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000; - dai_drv->ops = &xlnx_i2s_dai_ops; + drv_data->dai_drv.name = "xlnx_i2s_capture"; + drv_data->dai_drv.capture.stream_name = "Capture"; + drv_data->dai_drv.capture.formats = format; + drv_data->dai_drv.capture.channels_min = ch; + drv_data->dai_drv.capture.channels_max = ch; + drv_data->dai_drv.capture.rates = SNDRV_PCM_RATE_8000_192000; + drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; } else { return -ENODEV; } - dev_set_drvdata(&pdev->dev, base); + dev_set_drvdata(&pdev->dev, drv_data); ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, - dai_drv, 1); + &drv_data->dai_drv, 1); if (ret) { dev_err(&pdev->dev, "i2s component registration failed\n"); return ret; } - dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name); + dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); return ret; } -- cgit v1.2.3 From c47aef899c1bb0cbda48808356e7c040d95ca612 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:29 -0600 Subject: ASoC: xilinx: xlnx_i2s: Handle sysclk setting This driver previously only handled the set_clkdiv divider callback when setting the SCLK Out Divider field in the I2S Timing Control register. However, when using the simple-audio-card driver, the set_sysclk function is called but not set_clkdiv. This caused the divider not to be set, leaving it at an invalid value of 0 and resulting in a very low SCLK output rate. Handle set_clkdiv and store the sysclk (MCLK) value for later use in hw_params to set the SCLK Out Divider such that: MCLK/SCLK = divider * 2 Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20220120195832.1742271-4-robert.hancock@calian.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_i2s.c | 91 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c index 3bafa34b789a..4cc6ee7c81a3 100644 --- a/sound/soc/xilinx/xlnx_i2s.c +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -18,6 +18,8 @@ #define DRV_NAME "xlnx_i2s" #define I2S_CORE_CTRL_OFFSET 0x08 +#define I2S_CORE_CTRL_32BIT_LRCLK BIT(3) +#define I2S_CORE_CTRL_ENABLE BIT(0) #define I2S_I2STIM_OFFSET 0x20 #define I2S_CH0_OFFSET 0x30 #define I2S_I2STIM_VALID_MASK GENMASK(7, 0) @@ -25,6 +27,12 @@ struct xlnx_i2s_drv_data { struct snd_soc_dai_driver dai_drv; void __iomem *base; + unsigned int sysclk; + u32 data_width; + u32 channels; + bool is_32bit_lrclk; + struct snd_ratnum ratnum; + struct snd_pcm_hw_constraint_ratnums rate_constraints; }; static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai, @@ -35,11 +43,50 @@ static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai, if (!div || (div & ~I2S_I2STIM_VALID_MASK)) return -EINVAL; + drv_data->sysclk = 0; + writel(div, drv_data->base + I2S_I2STIM_OFFSET); return 0; } +static int xlnx_i2s_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(dai); + + drv_data->sysclk = freq; + if (freq) { + unsigned int bits_per_sample; + + if (drv_data->is_32bit_lrclk) + bits_per_sample = 32; + else + bits_per_sample = drv_data->data_width; + + drv_data->ratnum.num = freq / (bits_per_sample * drv_data->channels) / 2; + drv_data->ratnum.den_step = 1; + drv_data->ratnum.den_min = 1; + drv_data->ratnum.den_max = 255; + drv_data->rate_constraints.rats = &drv_data->ratnum; + drv_data->rate_constraints.nrats = 1; + } + return 0; +} + +static int xlnx_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(dai); + + if (drv_data->sysclk) + return snd_pcm_hw_constraint_ratnums(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &drv_data->rate_constraints); + + return 0; +} + static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *i2s_dai) @@ -47,6 +94,26 @@ static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, u32 reg_off, chan_id; struct xlnx_i2s_drv_data *drv_data = snd_soc_dai_get_drvdata(i2s_dai); + if (drv_data->sysclk) { + unsigned int bits_per_sample, sclk, sclk_div; + + if (drv_data->is_32bit_lrclk) + bits_per_sample = 32; + else + bits_per_sample = drv_data->data_width; + + sclk = params_rate(params) * bits_per_sample * params_channels(params); + sclk_div = drv_data->sysclk / sclk / 2; + + if ((drv_data->sysclk % sclk != 0) || + !sclk_div || (sclk_div & ~I2S_I2STIM_VALID_MASK)) { + dev_warn(i2s_dai->dev, "invalid SCLK divisor for sysclk %u and sclk %u\n", + drv_data->sysclk, sclk); + return -EINVAL; + } + writel(sclk_div, drv_data->base + I2S_I2STIM_OFFSET); + } + chan_id = params_channels(params) / 2; while (chan_id > 0) { @@ -67,7 +134,7 @@ static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - writel(1, drv_data->base + I2S_CORE_CTRL_OFFSET); + writel(I2S_CORE_CTRL_ENABLE, drv_data->base + I2S_CORE_CTRL_OFFSET); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -83,7 +150,9 @@ static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops xlnx_i2s_dai_ops = { .trigger = xlnx_i2s_trigger, + .set_sysclk = xlnx_i2s_set_sysclk, .set_clkdiv = xlnx_i2s_set_sclkout_div, + .startup = xlnx_i2s_startup, .hw_params = xlnx_i2s_hw_params }; @@ -102,7 +171,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) { struct xlnx_i2s_drv_data *drv_data; int ret; - u32 ch, format, data_width; + u32 format; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; @@ -114,19 +183,19 @@ static int xlnx_i2s_probe(struct platform_device *pdev) if (IS_ERR(drv_data->base)) return PTR_ERR(drv_data->base); - ret = of_property_read_u32(node, "xlnx,num-channels", &ch); + ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); if (ret < 0) { dev_err(dev, "cannot get supported channels\n"); return ret; } - ch = ch * 2; + drv_data->channels *= 2; - ret = of_property_read_u32(node, "xlnx,dwidth", &data_width); + ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); if (ret < 0) { dev_err(dev, "cannot get data width\n"); return ret; } - switch (data_width) { + switch (drv_data->data_width) { case 16: format = SNDRV_PCM_FMTBIT_S16_LE; break; @@ -141,21 +210,23 @@ static int xlnx_i2s_probe(struct platform_device *pdev) drv_data->dai_drv.name = "xlnx_i2s_playback"; drv_data->dai_drv.playback.stream_name = "Playback"; drv_data->dai_drv.playback.formats = format; - drv_data->dai_drv.playback.channels_min = ch; - drv_data->dai_drv.playback.channels_max = ch; + drv_data->dai_drv.playback.channels_min = drv_data->channels; + drv_data->dai_drv.playback.channels_max = drv_data->channels; drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { drv_data->dai_drv.name = "xlnx_i2s_capture"; drv_data->dai_drv.capture.stream_name = "Capture"; drv_data->dai_drv.capture.formats = format; - drv_data->dai_drv.capture.channels_min = ch; - drv_data->dai_drv.capture.channels_max = ch; + drv_data->dai_drv.capture.channels_min = drv_data->channels; + drv_data->dai_drv.capture.channels_max = drv_data->channels; drv_data->dai_drv.capture.rates = SNDRV_PCM_RATE_8000_192000; drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; } else { return -ENODEV; } + drv_data->is_32bit_lrclk = readl(drv_data->base + I2S_CORE_CTRL_OFFSET) & + I2S_CORE_CTRL_32BIT_LRCLK; dev_set_drvdata(&pdev->dev, drv_data); -- cgit v1.2.3 From ce2f7b8d4290c22e462e465d1da38a1c113ae66a Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:30 -0600 Subject: ASoC: simple-card-utils: Set sysclk on all components If an mclk-fs value was provided in the device tree configuration, the calculated MCLK was fed into the downstream codec DAI and CPU DAI, however set_sysclk was not being called on the platform device. Some platform devices such as the Xilinx Audio Formatter need to know the MCLK as well. Call snd_soc_component_set_sysclk on each component in the stream to set the proper sysclk value in addition to the existing call of snd_soc_dai_set_sysclk on the codec DAI and CPU DAI. This may end up resulting in redundant calls if one of the snd_soc_dai_set_sysclk calls ends up calling snd_soc_component_set_sysclk itself, but that isn't expected to cause any significant harm. Fixes: f48dcbb6d47d ("ASoC: simple-card-utils: share asoc_simple_hw_param()") Signed-off-by: Robert Hancock Reviewed-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20220120195832.1742271-5-robert.hancock@calian.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a81323d1691d..9736102e6808 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -275,6 +275,7 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, mclk_fs = props->mclk_fs; if (mclk_fs) { + struct snd_soc_component *component; mclk = params_rate(params) * mclk_fs; for_each_prop_dai_codec(props, i, pdai) { @@ -282,16 +283,30 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; } + for_each_prop_dai_cpu(props, i, pdai) { ret = asoc_simple_set_clk_rate(pdai, mclk); if (ret < 0) return ret; } + + /* Ensure sysclk is set on all components in case any + * (such as platform components) are missed by calls to + * snd_soc_dai_set_sysclk. + */ + for_each_rtd_components(rtd, i, component) { + ret = snd_soc_component_set_sysclk(component, 0, 0, + mclk, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + for_each_rtd_codec_dais(rtd, i, sdai) { ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN); if (ret && ret != -ENOTSUPP) return ret; } + for_each_rtd_cpu_dais(rtd, i, sdai) { ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT); if (ret && ret != -ENOTSUPP) -- cgit v1.2.3 From e9fed03aebacb8873dee8e2edfbce96f27f6c730 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:31 -0600 Subject: ASoC: dt-bindings: simple-card: document new system-clock-fixed flag Document the new system-clock-fixed flag, which can be used to specify that the driver cannot or should not allow the clock frequency of the mapped clock to be modified. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20220120195832.1742271-6-robert.hancock@calian.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-card.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml index 45fd9fd9eb54..00597dc4f396 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.yaml +++ b/Documentation/devicetree/bindings/sound/simple-card.yaml @@ -48,6 +48,15 @@ definitions: It is useful for some aCPUs with fixed clocks. $ref: /schemas/types.yaml#/definitions/flag + system-clock-fixed: + description: | + Specifies that the clock frequency should not be modified. + Implied when system-clock-frequency is specified, but can be used when + a clock is mapped to the device whose frequency cannot or should not be + changed. When mclk-fs is also specified, this restricts the device to a + single fixed sampling rate. + $ref: /schemas/types.yaml#/definitions/flag + mclk-fs: description: | Multiplication factor between stream rate and codec mclk. @@ -134,6 +143,8 @@ definitions: $ref: "#/definitions/system-clock-frequency" system-clock-direction-out: $ref: "#/definitions/system-clock-direction-out" + system-clock-fixed: + $ref: "#/definitions/system-clock-fixed" required: - sound-dai -- cgit v1.2.3 From 5ca2ab4598179a2690a38420f3fde9f2ad79d55c Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 20 Jan 2022 13:58:32 -0600 Subject: ASoC: simple-card-utils: Add new system-clock-fixed flag Add a new system-clock-fixed flag, which can be used to specify that the driver cannot or should not allow the clock frequency of the mapped clock to be modified. This behavior is also implied if the system-clock-frequency parameter is set explicitly - the flag is meant for cases where a clock is mapped to the DAI but which is, or should be treated as, fixed. When mclk-fs is also specified, this causes a PCM constraint to be added which enforces that only the corresponding valid sample rate can be used. Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20220120195832.1742271-7-robert.hancock@calian.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 1 + sound/soc/generic/simple-card-utils.c | 71 +++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index df430f1c2a10..5ee269c59aac 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -25,6 +25,7 @@ struct asoc_simple_dai { unsigned int tx_slot_mask; unsigned int rx_slot_mask; struct clk *clk; + bool clk_fixed; }; struct asoc_simple_data { diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 9736102e6808..a4babfb63175 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -165,12 +165,15 @@ int asoc_simple_parse_clk(struct device *dev, * or device's module clock. */ clk = devm_get_clk_from_child(dev, node, NULL); + simple_dai->clk_fixed = of_property_read_bool( + node, "system-clock-fixed"); if (!IS_ERR(clk)) { simple_dai->sysclk = clk_get_rate(clk); simple_dai->clk = clk; } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { simple_dai->sysclk = val; + simple_dai->clk_fixed = true; } else { clk = devm_get_clk_from_child(dev, dlc->of_node, NULL); if (!IS_ERR(clk)) @@ -184,12 +187,29 @@ int asoc_simple_parse_clk(struct device *dev, } EXPORT_SYMBOL_GPL(asoc_simple_parse_clk); +static int asoc_simple_check_fixed_sysclk(struct device *dev, + struct asoc_simple_dai *dai, + unsigned int *fixed_sysclk) +{ + if (dai->clk_fixed) { + if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) { + dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n", + *fixed_sysclk, dai->sysclk); + return -EINVAL; + } + *fixed_sysclk = dai->sysclk; + } + + return 0; +} + int asoc_simple_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); struct asoc_simple_dai *dai; + unsigned int fixed_sysclk = 0; int i1, i2, i; int ret; @@ -197,12 +217,32 @@ int asoc_simple_startup(struct snd_pcm_substream *substream) ret = asoc_simple_clk_enable(dai); if (ret) goto cpu_err; + ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk); + if (ret) + goto cpu_err; } for_each_prop_dai_codec(props, i2, dai) { ret = asoc_simple_clk_enable(dai); if (ret) goto codec_err; + ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk); + if (ret) + goto codec_err; + } + + if (fixed_sysclk && props->mclk_fs) { + unsigned int fixed_rate = fixed_sysclk / props->mclk_fs; + + if (fixed_sysclk % props->mclk_fs) { + dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n", + fixed_sysclk, props->mclk_fs); + return -EINVAL; + } + ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, + fixed_rate, fixed_rate); + if (ret) + goto codec_err; } return 0; @@ -226,31 +266,40 @@ EXPORT_SYMBOL_GPL(asoc_simple_startup); void asoc_simple_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); struct asoc_simple_dai *dai; int i; - if (props->mclk_fs) { - snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); - snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); - } + for_each_prop_dai_cpu(props, i, dai) { + if (props->mclk_fs && !dai->clk_fixed) + snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, i), + 0, 0, SND_SOC_CLOCK_IN); - for_each_prop_dai_cpu(props, i, dai) asoc_simple_clk_disable(dai); - for_each_prop_dai_codec(props, i, dai) + } + for_each_prop_dai_codec(props, i, dai) { + if (props->mclk_fs && !dai->clk_fixed) + snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, i), + 0, 0, SND_SOC_CLOCK_IN); + asoc_simple_clk_disable(dai); + } } EXPORT_SYMBOL_GPL(asoc_simple_shutdown); -static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, +static int asoc_simple_set_clk_rate(struct device *dev, + struct asoc_simple_dai *simple_dai, unsigned long rate) { if (!simple_dai) return 0; + if (simple_dai->clk_fixed && rate != simple_dai->sysclk) { + dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate); + return -EINVAL; + } + if (!simple_dai->clk) return 0; @@ -279,13 +328,13 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, mclk = params_rate(params) * mclk_fs; for_each_prop_dai_codec(props, i, pdai) { - ret = asoc_simple_set_clk_rate(pdai, mclk); + ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk); if (ret < 0) return ret; } for_each_prop_dai_cpu(props, i, pdai) { - ret = asoc_simple_set_clk_rate(pdai, mclk); + ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk); if (ret < 0) return ret; } -- cgit v1.2.3 From 7276d3f329c633340f3c539ce35ed254d2fe467b Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 25 Jan 2022 13:24:55 +0000 Subject: ASoC: sh: rz-ssi: Use a do-while loop in rz_ssi_pio_recv() Use a do-while loop while reading the samples from RX FIFO. The "done" flag was only changed as an outcome of the last if-statement (last step) in this entire procedure. This patch moves the condition from if statement to while and drops the "done" variable for readability. While at it, also drop the unneeded parentheses around runtime->dma_area. Signed-off-by: Lad Prabhakar Link: https://lore.kernel.org/r/20220125132457.14984-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 81e1786b827d..2c8775d37f50 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -408,7 +408,6 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { struct snd_pcm_substream *substream = strm->substream; struct snd_pcm_runtime *runtime; - bool done = false; u16 *buf; int fifo_samples; int frames_left; @@ -420,7 +419,7 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) runtime = substream->runtime; - while (!done) { + do { /* frames left in this period */ frames_left = runtime->period_size - (strm->buffer_pos % runtime->period_size); @@ -444,7 +443,7 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) break; /* calculate new buffer index */ - buf = (u16 *)(runtime->dma_area); + buf = (u16 *)runtime->dma_area; buf += strm->buffer_pos * runtime->channels; /* Note, only supports 16-bit samples */ @@ -453,11 +452,7 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); rz_ssi_pointer_update(strm, samples / runtime->channels); - - /* check if there are no more samples in the RX FIFO */ - if (!(!frames_left && fifo_samples >= runtime->channels)) - done = true; - } + } while (!frames_left && fifo_samples >= runtime->channels); return 0; } -- cgit v1.2.3 From 962ff7ecb60b684fe15b135ccbe07628b8bb522a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 25 Jan 2022 13:24:56 +0000 Subject: ASoC: sh: rz-ssi: Add rz_ssi_set_substream() helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A copy of substream pointer is stored in priv structure during rz_ssi_dai_trigger() callback ie in SNDRV_PCM_TRIGGER_START case and the pointer is assigned to NULL in case of SNDRV_PCM_TRIGGER_STOP. The driver used the locks only in rz_ssi_stream_is_valid() and assigned the local substream pointer to NULL in rz_ssi_dai_trigger() callback but never locked it while making a local copy. This patch adds the rz_ssi_set_substream() helper function to set the substream pointer with locks acquired and replaces the instances of setting the local substream pointer with the rz_ssi_set_substream() function. Reported-by: Pavel Machek Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220125132457.14984-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 2c8775d37f50..1a46c9f3c4e5 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -188,6 +188,17 @@ static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch)); } +static void rz_ssi_set_substream(struct rz_ssi_stream *strm, + struct snd_pcm_substream *substream) +{ + struct rz_ssi_priv *ssi = strm->priv; + unsigned long flags; + + spin_lock_irqsave(&ssi->lock, flags); + strm->substream = substream; + spin_unlock_irqrestore(&ssi->lock, flags); +} + static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { @@ -206,7 +217,7 @@ static void rz_ssi_stream_init(struct rz_ssi_stream *strm, { struct snd_pcm_runtime *runtime = substream->runtime; - strm->substream = substream; + rz_ssi_set_substream(strm, substream); strm->sample_width = samples_to_bytes(runtime, 1); strm->dma_buffer_pos = 0; strm->period_counter = 0; @@ -224,11 +235,8 @@ static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { struct snd_soc_dai *dai = rz_ssi_get_dai(strm->substream); - unsigned long flags; - spin_lock_irqsave(&ssi->lock, flags); - strm->substream = NULL; - spin_unlock_irqrestore(&ssi->lock, flags); + rz_ssi_set_substream(strm, NULL); if (strm->oerr_num > 0) dev_info(dai->dev, "overrun = %d\n", strm->oerr_num); -- cgit v1.2.3 From acfa1e2c2ff5cd7fb7948b0c5c2057acd9dceb14 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 25 Jan 2022 13:24:57 +0000 Subject: ASoC: sh: rz-ssi: Remove duplicate macros Remove SSICR_MST and SSICR_CKDV macros which are defined more than once. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20220125132457.14984-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 1a46c9f3c4e5..e8edaed05d4c 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -28,8 +28,6 @@ /* SSI REGISTER BITS */ #define SSICR_DWL(x) (((x) & 0x7) << 19) #define SSICR_SWL(x) (((x) & 0x7) << 16) -#define SSICR_MST BIT(14) -#define SSICR_CKDV(x) (((x) & 0xf) << 4) #define SSICR_CKS BIT(30) #define SSICR_TUIEN BIT(29) -- cgit v1.2.3 From 2b101256fd552bf2e29980d51394ebd88737159c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 20 Jan 2022 15:16:00 -0600 Subject: ALSA: usb-audio: scarlett2: Use struct_size() helper in scarlett2_usb() Make use of the struct_size() helper instead of an open-coded version, in order to avoid any potential type mistakes or integer overflows that, in the worst scenario, could lead to heap overflows. Also, address the following sparse warnings: sound/usb/mixer_scarlett_gen2.c:1064:28: warning: using sizeof on a flexible structure sound/usb/mixer_scarlett_gen2.c:1065:29: warning: using sizeof on a flexible structure Link: https://github.com/KSPP/linux/issues/174 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20220120211600.GA28841@embeddedor Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 53ebabf42472..7ff8a4817c67 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1061,9 +1061,9 @@ static int scarlett2_usb( { struct scarlett2_data *private = mixer->private_data; struct usb_device *dev = mixer->chip->dev; - u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size; - u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size; struct scarlett2_usb_packet *req, *resp = NULL; + size_t req_buf_size = struct_size(req, data, req_size); + size_t resp_buf_size = struct_size(resp, data, resp_size); int err; req = kmalloc(req_buf_size, GFP_KERNEL); @@ -1111,7 +1111,7 @@ static int scarlett2_usb( usb_audio_err( mixer->chip, "Scarlett Gen 2/3 USB response result cmd %x was %d " - "expected %d\n", + "expected %zu\n", cmd, err, resp_buf_size); err = -EINVAL; goto unlock; -- cgit v1.2.3 From 88b6132248940954b958cd4e6d0432c640a9abd2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 25 Jan 2022 16:28:24 +0000 Subject: kselftest: alsa: Add test case for writing invalid values Attempt to write various invalid values for control types we know about and check that something sensible happens. The ABI isn't quite as clearly defined as one might like, rather than generating an error when an invalid value is written many devices will silently rewrite the value into one that is valid for the control. The exact value chosen is not predictable so in the case the write succeeds we just check that the value we read back is one that is valid for the control. Reviewed-by: Shuah Khan Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220125162824.3816659-1-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 17f158d7a767..0e88f4f3d802 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ #include "../kselftest.h" -#define TESTS_PER_CONTROL 3 +#define TESTS_PER_CONTROL 4 struct card_data { snd_ctl_t *handle; @@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } +bool test_ctl_write_invalid_value(struct ctl_data *ctl, + snd_ctl_elem_value_t *val) +{ + int err; + long val_read; + + /* Ideally this will fail... */ + err = snd_ctl_elem_write(ctl->card->handle, val); + if (err < 0) + return false; + + /* ...but some devices will clamp to an in range value */ + err = snd_ctl_elem_read(ctl->card->handle, val); + if (err < 0) { + ksft_print_msg("%s failed to read: %s\n", + ctl->name, snd_strerror(err)); + return true; + } + + return !ctl_value_valid(ctl, val); +} + +bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) +{ + int err, i; + long val_read; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_boolean(val, i, 2); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + return !fail; +} + +bool test_ctl_write_invalid_integer(struct ctl_data *ctl) +{ + int i; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) { + /* Just under range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, + snd_ctl_elem_info_get_min(ctl->info) - 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Minimum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, LONG_MIN); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) { + /* Just over range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, + snd_ctl_elem_info_get_max(ctl->info) + 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, LONG_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) +{ + int i; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) { + /* Just under range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, + snd_ctl_elem_info_get_min64(ctl->info) - 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Minimum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) { + /* Just over range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, + snd_ctl_elem_info_get_max64(ctl->info) + 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) +{ + int err, i; + unsigned int val_read; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + /* One beyond maximum */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_enumerated(val, i, + snd_ctl_elem_info_get_items(ctl->info)); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + } + + return !fail; +} + + +void test_ctl_write_invalid(struct ctl_data *ctl) +{ + bool pass; + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + pass = test_ctl_write_invalid_boolean(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + pass = test_ctl_write_invalid_integer(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + pass = test_ctl_write_invalid_integer64(ctl); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + pass = test_ctl_write_invalid_enumerated(ctl); + break; + + default: + /* No tests for this yet */ + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Restore the default value to minimise disruption */ + err = write_and_verify(ctl, ctl->def_val, NULL); + if (err < 0) + pass = false; + + ksft_test_result(pass, "write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); +} + int main(void) { struct ctl_data *ctl; @@ -697,6 +916,7 @@ int main(void) test_ctl_get_value(ctl); test_ctl_write_default(ctl); test_ctl_write_valid(ctl); + test_ctl_write_invalid(ctl); } ksft_exit_pass(); -- cgit v1.2.3 From 4fcc8710fdd91b37760ccd99bbfbe10352df7600 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Fri, 28 Jan 2022 18:07:52 +0530 Subject: ASoC: tegra: Update AHUB driver for Tegra234 The register offsets of switches connecting various AHUB internal modules have changed from previous chip. Address this variation by making use of Tegra234 based compatible. Signed-off-by: Mohan Kumar Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1643373476-8538-2-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_ahub.c | 146 +++++++++++++++++++++++++++++++++++++++- sound/soc/tegra/tegra210_ahub.h | 4 +- 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 388b815443c7..bccf8b8ca714 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -2,7 +2,7 @@ // // tegra210_ahub.c - Tegra210 AHUB driver // -// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. +// Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. #include #include @@ -624,6 +624,34 @@ MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27); MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28); MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29); +/* Controls for t234 */ +MUX_ENUM_CTRL_DECL_234(t234_mvc1_tx, 0x44); +MUX_ENUM_CTRL_DECL_234(t234_mvc2_tx, 0x45); +MUX_ENUM_CTRL_DECL_234(t234_amx11_tx, 0x48); +MUX_ENUM_CTRL_DECL_234(t234_amx12_tx, 0x49); +MUX_ENUM_CTRL_DECL_234(t234_amx13_tx, 0x4a); +MUX_ENUM_CTRL_DECL_234(t234_amx14_tx, 0x4b); +MUX_ENUM_CTRL_DECL_234(t234_amx21_tx, 0x4c); +MUX_ENUM_CTRL_DECL_234(t234_amx22_tx, 0x4d); +MUX_ENUM_CTRL_DECL_234(t234_amx23_tx, 0x4e); +MUX_ENUM_CTRL_DECL_234(t234_amx24_tx, 0x4f); +MUX_ENUM_CTRL_DECL_234(t234_amx31_tx, 0x50); +MUX_ENUM_CTRL_DECL_234(t234_amx32_tx, 0x51); +MUX_ENUM_CTRL_DECL_234(t234_amx33_tx, 0x52); +MUX_ENUM_CTRL_DECL_234(t234_amx34_tx, 0x53); +MUX_ENUM_CTRL_DECL_234(t234_adx1_tx, 0x58); +MUX_ENUM_CTRL_DECL_234(t234_adx2_tx, 0x59); +MUX_ENUM_CTRL_DECL_234(t234_adx3_tx, 0x5a); +MUX_ENUM_CTRL_DECL_234(t234_adx4_tx, 0x5b); +MUX_ENUM_CTRL_DECL_234(t234_amx41_tx, 0x5c); +MUX_ENUM_CTRL_DECL_234(t234_amx42_tx, 0x5d); +MUX_ENUM_CTRL_DECL_234(t234_amx43_tx, 0x5e); +MUX_ENUM_CTRL_DECL_234(t234_amx44_tx, 0x5f); +MUX_ENUM_CTRL_DECL_234(t234_admaif17_tx, 0x60); +MUX_ENUM_CTRL_DECL_234(t234_admaif18_tx, 0x61); +MUX_ENUM_CTRL_DECL_234(t234_admaif19_tx, 0x62); +MUX_ENUM_CTRL_DECL_234(t234_admaif20_tx, 0x63); + /* * The number of entries in, and order of, this array is closely tied to the * calculation of tegra210_ahub_codec.num_dapm_widgets near the end of @@ -787,6 +815,102 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { TX_WIDGETS("MIXER1 TX5"), }; +static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { + WIDGETS("ADMAIF1", t186_admaif1_tx), + WIDGETS("ADMAIF2", t186_admaif2_tx), + WIDGETS("ADMAIF3", t186_admaif3_tx), + WIDGETS("ADMAIF4", t186_admaif4_tx), + WIDGETS("ADMAIF5", t186_admaif5_tx), + WIDGETS("ADMAIF6", t186_admaif6_tx), + WIDGETS("ADMAIF7", t186_admaif7_tx), + WIDGETS("ADMAIF8", t186_admaif8_tx), + WIDGETS("ADMAIF9", t186_admaif9_tx), + WIDGETS("ADMAIF10", t186_admaif10_tx), + WIDGETS("ADMAIF11", t186_admaif11_tx), + WIDGETS("ADMAIF12", t186_admaif12_tx), + WIDGETS("ADMAIF13", t186_admaif13_tx), + WIDGETS("ADMAIF14", t186_admaif14_tx), + WIDGETS("ADMAIF15", t186_admaif15_tx), + WIDGETS("ADMAIF16", t186_admaif16_tx), + WIDGETS("ADMAIF17", t234_admaif17_tx), + WIDGETS("ADMAIF18", t234_admaif18_tx), + WIDGETS("ADMAIF19", t234_admaif19_tx), + WIDGETS("ADMAIF20", t234_admaif20_tx), + WIDGETS("I2S1", t186_i2s1_tx), + WIDGETS("I2S2", t186_i2s2_tx), + WIDGETS("I2S3", t186_i2s3_tx), + WIDGETS("I2S4", t186_i2s4_tx), + WIDGETS("I2S5", t186_i2s5_tx), + WIDGETS("I2S6", t186_i2s6_tx), + TX_WIDGETS("DMIC1"), + TX_WIDGETS("DMIC2"), + TX_WIDGETS("DMIC3"), + TX_WIDGETS("DMIC4"), + WIDGETS("DSPK1", t186_dspk1_tx), + WIDGETS("DSPK2", t186_dspk2_tx), + WIDGETS("SFC1", t186_sfc1_tx), + WIDGETS("SFC2", t186_sfc2_tx), + WIDGETS("SFC3", t186_sfc3_tx), + WIDGETS("SFC4", t186_sfc4_tx), + WIDGETS("MVC1", t234_mvc1_tx), + WIDGETS("MVC2", t234_mvc2_tx), + WIDGETS("AMX1 RX1", t234_amx11_tx), + WIDGETS("AMX1 RX2", t234_amx12_tx), + WIDGETS("AMX1 RX3", t234_amx13_tx), + WIDGETS("AMX1 RX4", t234_amx14_tx), + WIDGETS("AMX2 RX1", t234_amx21_tx), + WIDGETS("AMX2 RX2", t234_amx22_tx), + WIDGETS("AMX2 RX3", t234_amx23_tx), + WIDGETS("AMX2 RX4", t234_amx24_tx), + WIDGETS("AMX3 RX1", t234_amx31_tx), + WIDGETS("AMX3 RX2", t234_amx32_tx), + WIDGETS("AMX3 RX3", t234_amx33_tx), + WIDGETS("AMX3 RX4", t234_amx34_tx), + WIDGETS("AMX4 RX1", t234_amx41_tx), + WIDGETS("AMX4 RX2", t234_amx42_tx), + WIDGETS("AMX4 RX3", t234_amx43_tx), + WIDGETS("AMX4 RX4", t234_amx44_tx), + TX_WIDGETS("AMX1"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("AMX3"), + TX_WIDGETS("AMX4"), + WIDGETS("ADX1", t234_adx1_tx), + WIDGETS("ADX2", t234_adx2_tx), + WIDGETS("ADX3", t234_adx3_tx), + WIDGETS("ADX4", t234_adx4_tx), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + TX_WIDGETS("ADX3 TX1"), + TX_WIDGETS("ADX3 TX2"), + TX_WIDGETS("ADX3 TX3"), + TX_WIDGETS("ADX3 TX4"), + TX_WIDGETS("ADX4 TX1"), + TX_WIDGETS("ADX4 TX2"), + TX_WIDGETS("ADX4 TX3"), + TX_WIDGETS("ADX4 TX4"), + WIDGETS("MIXER1 RX1", t186_mixer11_tx), + WIDGETS("MIXER1 RX2", t186_mixer12_tx), + WIDGETS("MIXER1 RX3", t186_mixer13_tx), + WIDGETS("MIXER1 RX4", t186_mixer14_tx), + WIDGETS("MIXER1 RX5", t186_mixer15_tx), + WIDGETS("MIXER1 RX6", t186_mixer16_tx), + WIDGETS("MIXER1 RX7", t186_mixer17_tx), + WIDGETS("MIXER1 RX8", t186_mixer18_tx), + WIDGETS("MIXER1 RX9", t186_mixer19_tx), + WIDGETS("MIXER1 RX10", t186_mixer110_tx), + TX_WIDGETS("MIXER1 TX1"), + TX_WIDGETS("MIXER1 TX2"), + TX_WIDGETS("MIXER1 TX3"), + TX_WIDGETS("MIXER1 TX4"), + TX_WIDGETS("MIXER1 TX5"), +}; + #define TEGRA_COMMON_MUX_ROUTES(name) \ { name " XBAR-TX", NULL, name " Mux" }, \ { name " Mux", "ADMAIF1", "ADMAIF1 XBAR-RX" }, \ @@ -1027,6 +1151,13 @@ static const struct snd_soc_component_driver tegra186_ahub_component = { .num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes), }; +static const struct snd_soc_component_driver tegra234_ahub_component = { + .dapm_widgets = tegra234_ahub_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra234_ahub_widgets), + .dapm_routes = tegra186_ahub_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes), +}; + static const struct regmap_config tegra210_ahub_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -1067,9 +1198,22 @@ static const struct tegra_ahub_soc_data soc_data_tegra186 = { .reg_count = TEGRA186_XBAR_UPDATE_MAX_REG, }; +static const struct tegra_ahub_soc_data soc_data_tegra234 = { + .cmpnt_drv = &tegra234_ahub_component, + .dai_drv = tegra186_ahub_dais, + .num_dais = ARRAY_SIZE(tegra186_ahub_dais), + .regmap_config = &tegra186_ahub_regmap_config, + .mask[0] = TEGRA186_XBAR_REG_MASK_0, + .mask[1] = TEGRA186_XBAR_REG_MASK_1, + .mask[2] = TEGRA186_XBAR_REG_MASK_2, + .mask[3] = TEGRA186_XBAR_REG_MASK_3, + .reg_count = TEGRA186_XBAR_UPDATE_MAX_REG, +}; + static const struct of_device_id tegra_ahub_of_match[] = { { .compatible = "nvidia,tegra210-ahub", .data = &soc_data_tegra210 }, { .compatible = "nvidia,tegra186-ahub", .data = &soc_data_tegra186 }, + { .compatible = "nvidia,tegra234-ahub", .data = &soc_data_tegra234 }, {}, }; MODULE_DEVICE_TABLE(of, tegra_ahub_of_match); diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h index 47802bbe17a9..2728db4d24f2 100644 --- a/sound/soc/tegra/tegra210_ahub.h +++ b/sound/soc/tegra/tegra210_ahub.h @@ -2,7 +2,7 @@ /* * tegra210_ahub.h - TEGRA210 AHUB * - * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * */ @@ -74,6 +74,8 @@ tegra_ahub_get_value_enum, \ tegra_ahub_put_value_enum) +#define MUX_ENUM_CTRL_DECL_234(ename, id) MUX_ENUM_CTRL_DECL_186(ename, id) + #define WIDGETS(sname, ename) \ SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \ SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0), \ -- cgit v1.2.3 From fed44d6c3bcdb11ed77bc681f1cf80cbe8cfd9a5 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 28 Jan 2022 18:07:54 +0530 Subject: ASoC: Document Tegra234 APE support Update binding docs for devices which are part of APE subsystem on Tegra234 chip. Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1643373476-8538-4-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.yaml | 1 + Documentation/devicetree/bindings/dma/nvidia,tegra210-adma.yaml | 4 +++- Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml | 4 +++- Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml | 4 +++- Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml | 3 +++ Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml | 1 + Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml | 1 + 13 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.yaml b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.yaml index 7b1a08c62aef..d3ed048c9521 100644 --- a/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.yaml +++ b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.yaml @@ -21,6 +21,7 @@ properties: - const: nvidia,tegra210-aconnect - items: - enum: + - nvidia,tegra234-aconnect - nvidia,tegra186-aconnect - nvidia,tegra194-aconnect - const: nvidia,tegra210-aconnect diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra210-adma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra210-adma.yaml index 5c2e2f156e31..fef804565b88 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra210-adma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra210-adma.yaml @@ -23,7 +23,9 @@ properties: - nvidia,tegra210-adma - nvidia,tegra186-adma - items: - - const: nvidia,tegra194-adma + - enum: + - nvidia,tegra234-adma + - nvidia,tegra194-adma - const: nvidia,tegra186-adma reg: diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml index ba282f4c9fd0..62219a5c21c5 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml @@ -58,6 +58,7 @@ properties: - enum: - nvidia,tegra186-agic - nvidia,tegra194-agic + - nvidia,tegra234-agic - const: nvidia,tegra210-agic interrupt-controller: true diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml index 0912d3e3fd8e..73b98b2f3543 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml @@ -28,7 +28,9 @@ properties: oneOf: - const: nvidia,tegra186-dspk - items: - - const: nvidia,tegra194-dspk + - enum: + - nvidia,tegra234-dspk + - nvidia,tegra194-dspk - const: nvidia,tegra186-dspk reg: diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml index 19eaacc3f12a..372043edd98f 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml @@ -27,7 +27,9 @@ properties: - nvidia,tegra210-admaif - nvidia,tegra186-admaif - items: - - const: nvidia,tegra194-admaif + - enum: + - nvidia,tegra234-admaif + - nvidia,tegra194-admaif - const: nvidia,tegra186-admaif reg: diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml index c4ba12ea3611..8d8dc7fb3f0c 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml @@ -30,6 +30,7 @@ properties: - const: nvidia,tegra210-adx - items: - enum: + - nvidia,tegra234-adx - nvidia,tegra194-adx - nvidia,tegra186-adx - const: nvidia,tegra210-adx diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml index df81d208184a..4727f1e42c53 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml @@ -26,6 +26,7 @@ properties: - enum: - nvidia,tegra210-ahub - nvidia,tegra186-ahub + - nvidia,tegra234-ahub - items: - const: nvidia,tegra194-ahub - const: nvidia,tegra186-ahub diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml index bb2111afe5a8..f9e4fc6e0c47 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml @@ -31,6 +31,9 @@ properties: - const: nvidia,tegra186-amx - const: nvidia,tegra210-amx - const: nvidia,tegra194-amx + - items: + - const: nvidia,tegra234-amx + - const: nvidia,tegra194-amx reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml index 62db982bb01d..bcb496d3ace5 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml @@ -28,6 +28,7 @@ properties: - const: nvidia,tegra210-dmic - items: - enum: + - nvidia,tegra234-dmic - nvidia,tegra194-dmic - nvidia,tegra186-dmic - const: nvidia,tegra210-dmic diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml index f954be636697..6188f561f878 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml @@ -28,6 +28,7 @@ properties: - const: nvidia,tegra210-i2s - items: - enum: + - nvidia,tegra234-i2s - nvidia,tegra194-i2s - nvidia,tegra186-i2s - const: nvidia,tegra210-i2s diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml index 428f3c851941..ee1e1d2da79a 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml @@ -28,6 +28,7 @@ properties: - const: nvidia,tegra210-amixer - items: - enum: + - nvidia,tegra234-amixer - nvidia,tegra194-amixer - nvidia,tegra186-amixer - const: nvidia,tegra210-amixer diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml index e2f5a8591d8f..c9888c553e78 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml @@ -31,6 +31,7 @@ properties: - const: nvidia,tegra210-mvc - items: - enum: + - nvidia,tegra234-mvc - nvidia,tegra194-mvc - nvidia,tegra186-mvc - const: nvidia,tegra210-mvc diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml index 41ad65173548..8579306fc56f 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml @@ -28,6 +28,7 @@ properties: - const: nvidia,tegra210-sfc - items: - enum: + - nvidia,tegra234-sfc - nvidia,tegra194-sfc - nvidia,tegra186-sfc - const: nvidia,tegra210-sfc -- cgit v1.2.3 From 2ce0d008dcc59f9c01f43277b9f9743af7b01dad Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 28 Jan 2022 15:00:17 +0200 Subject: ASoC: SOF: Intel: hda: Remove link assignment limitation The limitation to assign a link DMA channel for a BE iff the corresponding host DMA channel is assigned to a connected FE is only applicable if the PROCEN_FMT_QUIRK is set. So, remove it for platforms that do not enable the quirk. Complements: a792bfc1c2bc ("ASoC: SOF: Intel: hda-stream: limit PROCEN workaround") Signed-off-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Kai Vehmanen Reviewed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220128130017.28508-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index cd12589355ef..28a54145c150 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -59,6 +59,8 @@ static struct hdac_ext_stream * { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sof_intel_hda_stream *hda_stream; + const struct sof_intel_dsp_desc *chip; + struct snd_sof_dev *sdev; struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; @@ -77,9 +79,20 @@ static struct hdac_ext_stream * continue; hda_stream = hstream_to_sof_hda_stream(hstream); + sdev = hda_stream->sdev; + chip = get_chip_info(sdev->pdata); /* check if link is available */ if (!hstream->link_locked) { + /* + * choose the first available link for platforms that do not have the + * PROCEN_FMT_QUIRK set. + */ + if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) { + res = hstream; + break; + } + if (stream->opened) { /* * check if the stream tag matches the stream -- cgit v1.2.3 From 0cfe76156cc1c7f8a707969c03ed2242db8f0292 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 26 Jan 2022 17:13:58 -0600 Subject: ASoC: dt-bindings: realtek,rt5682s: Drop Tegra specifics from example There's no need to complicate examples with a platform specific macro. It also complicates example parsing to figure out the number of interrupt cells in examples (based on bracketing). Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20220126231358.1637174-1-robh@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml index d65c0ed5060c..ca5b8987b749 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt5682s.yaml @@ -21,6 +21,7 @@ properties: description: I2C address of the device. interrupts: + maxItems: 1 description: The CODEC's interrupt output. realtek,dmic1-data-pin: @@ -94,7 +95,7 @@ required: examples: - | - #include + #include #include i2c { @@ -104,10 +105,9 @@ examples: codec@1a { compatible = "realtek,rt5682s"; reg = <0x1a>; - interrupt-parent = <&gpio>; - interrupts = ; + interrupts = <6 IRQ_TYPE_LEVEL_HIGH>; realtek,ldo1-en-gpios = - <&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>; + <&gpio 2 GPIO_ACTIVE_HIGH>; realtek,dmic1-data-pin = <1>; realtek,dmic1-clk-pin = <1>; realtek,jd-src = <1>; -- cgit v1.2.3 From 7bd04b8d46b9362fb1ade63b99cd6ddee0740af4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 14:06:27 +0200 Subject: ASoC: SOF: trace: Simplify count adjustment in trace_read The first count check and fixup against "buffer - lpos" can be removed as we will do the adjustment later against the "avail" in sof_dfsentry_trace_read() Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220128120627.18443-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/trace.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index f13024c8ebf2..9b505c4fe794 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -308,9 +308,6 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, lpos_64 = lpos; lpos = do_div(lpos_64, buffer_size); - if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */ - count = buffer_size - lpos; - /* get available count based on current host offset */ avail = sof_wait_trace_avail(sdev, lpos, buffer_size); if (sdev->dtrace_error) { @@ -319,7 +316,8 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, } /* make sure count is <= avail */ - count = avail > count ? count : avail; + if (count > avail) + count = avail; /* copy available trace data to debugfs */ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); -- cgit v1.2.3 From 9da1467b49ad6c02840e8f331c5da69f6a5bdb2e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Jan 2022 15:02:08 -0800 Subject: drm/rockchip: cdn-dp: Support HDMI codec plug-change callback Some audio servers like to monitor a jack device (perhaps combined with EDID, for audio-presence info) to determine DP/HDMI audio presence. Signed-off-by: Brian Norris Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20220114150129.v2.2.I20d754a1228aa5c51a18c8eb15a2c60dec25b639@changeid Signed-off-by: Mark Brown --- drivers/gpu/drm/rockchip/cdn-dp-core.c | 28 ++++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 16497c31d9f9..edd6a1fc46cd 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -586,6 +586,13 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) return drm_dp_channel_eq_ok(link_status, min(port->lanes, sink_lanes)); } +static void cdn_dp_audio_handle_plugged_change(struct cdn_dp_device *dp, + bool plugged) +{ + if (dp->codec_dev) + dp->plugged_cb(dp->codec_dev, plugged); +} + static void cdn_dp_encoder_enable(struct drm_encoder *encoder) { struct cdn_dp_device *dp = encoder_to_dp(encoder); @@ -641,6 +648,9 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); goto out; } + + cdn_dp_audio_handle_plugged_change(dp, true); + out: mutex_unlock(&dp->lock); } @@ -651,6 +661,8 @@ static void cdn_dp_encoder_disable(struct drm_encoder *encoder) int ret; mutex_lock(&dp->lock); + cdn_dp_audio_handle_plugged_change(dp, false); + if (dp->active) { ret = cdn_dp_disable(dp); if (ret) { @@ -846,11 +858,27 @@ static int cdn_dp_audio_get_eld(struct device *dev, void *data, return 0; } +static int cdn_dp_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + + mutex_lock(&dp->lock); + dp->plugged_cb = fn; + dp->codec_dev = codec_dev; + cdn_dp_audio_handle_plugged_change(dp, dp->connected); + mutex_unlock(&dp->lock); + + return 0; +} + static const struct hdmi_codec_ops audio_codec_ops = { .hw_params = cdn_dp_audio_hw_params, .audio_shutdown = cdn_dp_audio_shutdown, .mute_stream = cdn_dp_audio_mute_stream, .get_eld = cdn_dp_audio_get_eld, + .hook_plugged_cb = cdn_dp_audio_hook_plugged_cb, .no_capture_mute = 1, }; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 81ac9b658a70..d808a9de45ed 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "rockchip_drm_drv.h" @@ -101,5 +102,8 @@ struct cdn_dp_device { u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; + + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; }; #endif /* _CDN_DP_CORE_H */ -- cgit v1.2.3 From 6a8bc4b68ca0c6ef73518b692c00b7e1e010d056 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Jan 2022 15:02:09 -0800 Subject: ASoC: rk3399_gru_sound: Wire up DP jack detection Now that the cdn-dp driver supports plug-change callbacks, let's wire it up. Signed-off-by: Brian Norris Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20220114150129.v2.3.I3c79b1466c14b02980071221e5b99283cd26ec77@changeid Signed-off-by: Mark Brown --- sound/soc/rockchip/rk3399_gru_sound.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index e2d52d8d0ff9..eeef3ed70037 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -164,6 +164,25 @@ static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream, return 0; } +static struct snd_soc_jack cdn_dp_card_jack; + +static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_card *card = rtd->card; + int ret; + + /* Enable jack detection. */ + ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT, + &cdn_dp_card_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't create DP Jack %d\n", ret); + return ret; + } + + return snd_soc_component_set_jack(component, &cdn_dp_card_jack, NULL); +} + static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; @@ -315,6 +334,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_CDNDP] = { .name = "DP", .stream_name = "DP PCM", + .init = rockchip_sound_cdndp_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAILINK_REG(cdndp), -- cgit v1.2.3 From c32bd332ce5c9eda087dedae2cf5f98bb008e841 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:49 +0530 Subject: ASoC: amd: acp: Add generic support for PDM controller on ACP Add driver module for PDM controller on ACP IP block. Expose dai ops to configure ACP_WOV_PDM_BLOCK registers on ACP. Such dai ops will be used by platform specific driver module to register dmic related dai with ASoC. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-2-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 3 + sound/soc/amd/acp/Makefile | 2 + sound/soc/amd/acp/acp-pdm.c | 193 +++++++++++++++++++++++++++++++++++ sound/soc/amd/acp/amd.h | 9 +- sound/soc/amd/acp/chip_offset_byte.h | 20 ++++ 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 sound/soc/amd/acp/acp-pdm.c diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index d5838df3064b..2e6d0259f2e9 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -15,6 +15,9 @@ config SND_SOC_AMD_ACP_COMMON if SND_SOC_AMD_ACP_COMMON +config SND_SOC_AMD_ACP_PDM + tristate + config SND_SOC_AMD_ACP_I2S tristate diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index 16c144c2965c..66cac95432f6 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -7,6 +7,7 @@ #common acp driver snd-acp-pcm-objs := acp-platform.o snd-acp-i2s-objs := acp-i2s.o +snd-acp-pdm-objs := acp-pdm.o #platform specific driver snd-acp-renoir-objs := acp-renoir.o @@ -18,6 +19,7 @@ snd-acp-sof-mach-objs := acp-sof-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o +obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c new file mode 100644 index 000000000000..424c6e0bb9d6 --- /dev/null +++ b/sound/soc/amd/acp/acp-pdm.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Advanced Micro Devices, Inc. +// +// Authors: Ajit Kumar Pandey +// Vijendar Mukunda +// + +/* + * Generic Hardware interface for ACP Audio PDM controller + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "amd.h" + +#define DRV_NAME "acp-pdm" + +#define PDM_DMA_STAT 0x10 +#define PDM_DMA_INTR_MASK 0x10000 +#define PDM_DEC_64 0x2 +#define PDM_CLK_FREQ_MASK 0x07 +#define PDM_MISC_CTRL_MASK 0x10 +#define PDM_ENABLE 0x01 +#define PDM_DISABLE 0x00 +#define DMA_EN_MASK 0x02 +#define DELAY_US 5 +#define PDM_TIMEOUT 1000 +#define ACP_REGION2_OFFSET 0x02000000 + +static int acp_dmic_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct acp_stream *stream = substream->runtime->private_data; + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + u32 physical_addr, size_dmic, period_bytes; + unsigned int dmic_ctrl; + + /* Enable default DMIC clk */ + writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); + dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); + dmic_ctrl |= PDM_MISC_CTRL_MASK; + writel(dmic_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); + + period_bytes = frames_to_bytes(substream->runtime, + substream->runtime->period_size); + size_dmic = frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + + physical_addr = stream->reg_offset + MEM_WINDOW_START; + + /* Init DMIC Ring buffer */ + writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); + writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); + writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); + writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); + + return 0; +} + +static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + unsigned int dma_enable; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + if (!(dma_enable & DMA_EN_MASK)) { + writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); + writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + } + + ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, + dma_enable, (dma_enable & DMA_EN_MASK), + DELAY_US, PDM_TIMEOUT); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + if ((dma_enable & DMA_EN_MASK)) { + writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); + writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + + } + + ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, + dma_enable, !(dma_enable & DMA_EN_MASK), + DELAY_US, PDM_TIMEOUT); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int acp_dmic_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) +{ + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + unsigned int channels, ch_mask; + + channels = params_channels(hwparams); + switch (channels) { + case 2: + ch_mask = 0; + break; + case 4: + ch_mask = 1; + break; + case 6: + ch_mask = 2; + break; + default: + dev_err(dev, "Invalid channels %d\n", channels); + return -EINVAL; + } + + if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) { + dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); + return -EINVAL; + } + + writel(ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); + + return 0; +} + +static int acp_dmic_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct acp_stream *stream = substream->runtime->private_data; + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + u32 ext_int_ctrl; + + stream->dai_id = DMIC_INSTANCE; + stream->irq_bit = BIT(PDM_DMA_STAT); + stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET; + stream->reg_offset = ACP_REGION2_OFFSET; + + /* Enable DMIC Interrupts */ + ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_int_ctrl |= PDM_DMA_INTR_MASK; + writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL); + + return 0; +} + +static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + u32 ext_int_ctrl; + + /* Disable DMIC interrrupts */ + ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_int_ctrl |= ~PDM_DMA_INTR_MASK; + writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL); +} + +const struct snd_soc_dai_ops acp_dmic_dai_ops = { + .prepare = acp_dmic_prepare, + .hw_params = acp_dmic_hwparams, + .trigger = acp_dmic_dai_trigger, + .startup = acp_dmic_dai_startup, + .shutdown = acp_dmic_dai_shutdown, +}; +EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 8eee3d34774b..567355209a5c 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -17,8 +17,9 @@ #define I2S_SP_INSTANCE 0x00 #define I2S_BT_INSTANCE 0x01 +#define DMIC_INSTANCE 0x02 -#define MEM_WINDOW_START 0x4000000 +#define MEM_WINDOW_START 0x4080000 #define ACP_I2S_REG_START 0x1242400 #define ACP_I2S_REG_END 0x1242810 @@ -38,6 +39,7 @@ #define ACP_SRAM_SP_CP_PTE_OFFSET 0x100 #define ACP_SRAM_BT_PB_PTE_OFFSET 0x200 #define ACP_SRAM_BT_CP_PTE_OFFSET 0x300 +#define ACP_SRAM_PDM_PTE_OFFSET 0x400 #define PAGE_SIZE_4K_ENABLE 0x2 #define I2S_SP_TX_MEM_WINDOW_START 0x4000000 @@ -96,6 +98,7 @@ struct acp_dev_data { }; extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; +extern const struct snd_soc_dai_ops acp_dmic_dai_ops; int asoc_acp_i2s_probe(struct snd_soc_dai *dai); int acp_platform_register(struct device *dev); @@ -131,6 +134,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH); low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW); break; + case DMIC_INSTANCE: + high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH); + low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW); + break; default: dev_err(adata->dev, "Invalid dai id %x\n", dai_id); return -EINVAL; diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h index c7f77e975dc7..e38589a142e9 100644 --- a/sound/soc/amd/acp/chip_offset_byte.h +++ b/sound/soc/amd/acp/chip_offset_byte.h @@ -73,4 +73,24 @@ #define ACP_BTTDM_ITER 0x280C #define ACP_BTTDM_TXFRMT 0x2810 +/* Registers from ACP_WOV_PDM block */ + +#define ACP_WOV_PDM_ENABLE 0x2C04 +#define ACP_WOV_PDM_DMA_ENABLE 0x2C08 +#define ACP_WOV_RX_RINGBUFADDR 0x2C0C +#define ACP_WOV_RX_RINGBUFSIZE 0x2C10 +#define ACP_WOV_RX_LINKPOSITIONCNTR 0x2C14 +#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x2C18 +#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x2C1C +#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x2C20 +#define ACP_WOV_PDM_FIFO_FLUSH 0x2C24 +#define ACP_WOV_PDM_NO_OF_CHANNELS 0x2C28 +#define ACP_WOV_PDM_DECIMATION_FACTOR 0x2C2C +#define ACP_WOV_PDM_VAD_CTRL 0x2C30 +#define ACP_WOV_BUFFER_STATUS 0x2C58 +#define ACP_WOV_MISC_CTRL 0x2C5C +#define ACP_WOV_CLK_CTRL 0x2C60 +#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64 +#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68 + #endif -- cgit v1.2.3 From def6dc25070342be2eb220cb1650a286ee29734d Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:50 +0530 Subject: ASoC: amd: acp: Add PDM controller based dmic dai for Renoir Renoir ACP IP has a PDM controller block. Add DMIC dai instance in dai_driver struct to enable dmic capture support on Renoir platform. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-3-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 1 + sound/soc/amd/acp/acp-renoir.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 2e6d0259f2e9..f4ca7843391b 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -29,6 +29,7 @@ config SND_AMD_ASOC_RENOIR tristate "AMD ACP ASOC Renoir Support" select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S + select SND_SOC_AMD_ACP_PDM depends on X86 && PCI help This option enables Renoir I2S support on AMD platform. diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 9b321a055b52..770a57a0677b 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -97,6 +97,19 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { .ops = &asoc_acp_cpu_dai_ops, .probe = &asoc_acp_i2s_probe, }, +{ + .name = "acp-pdm-dmic", + .id = DMIC_INSTANCE, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &acp_dmic_dai_ops, +}, }; static int renoir_audio_probe(struct platform_device *pdev) -- cgit v1.2.3 From 5a9f07a41522e1d16f2a43b1843e266434df0866 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:51 +0530 Subject: ASoC: amd: acp: Add generic PCI driver module for ACP device Audio Co-processor or ACP IP block on AMD's SOC is connected via PCI bus interface, hence needs to be register as a PCI device. We have same PCI device ID across multiple SOC's but with different revision id for PCI hw. Add a generic PCI driver module for ACP that registers ACP as a PCI device and also register a platform device based on pci revision id. Any SOC's specific configuration for ACP block will be done in platform driver probe. We have added an initial support for ACP revision id 3 or ACP3X device. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-4-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 6 ++ sound/soc/amd/acp/Makefile | 2 + sound/soc/amd/acp/acp-pci.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/amd/acp/amd.h | 14 ++++ sound/soc/amd/mach-config.h | 1 + 5 files changed, 183 insertions(+) create mode 100644 sound/soc/amd/acp/acp-pci.c diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index f4ca7843391b..626e4a5cb06a 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -25,6 +25,12 @@ config SND_SOC_AMD_ACP_PCM tristate select SND_SOC_ACPI if ACPI +config SND_SOC_AMD_ACP_PCI + tristate "AMD ACP PCI Driver Support" + depends on X86 && PCI + help + This options enables generic PCI driver for ACP device. + config SND_AMD_ASOC_RENOIR tristate "AMD ACP ASOC Renoir Support" select SND_SOC_AMD_ACP_PCM diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index 66cac95432f6..657ddfadf0bb 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -8,6 +8,7 @@ snd-acp-pcm-objs := acp-platform.o snd-acp-i2s-objs := acp-i2s.o snd-acp-pdm-objs := acp-pdm.o +snd-acp-pci-objs := acp-pci.o #platform specific driver snd-acp-renoir-objs := acp-renoir.o @@ -20,6 +21,7 @@ snd-acp-sof-mach-objs := acp-sof-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o +obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c new file mode 100644 index 000000000000..340e39d7f420 --- /dev/null +++ b/sound/soc/amd/acp/acp-pci.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Ajit Kumar Pandey + +/* + * Generic PCI interface for ACP device + */ + +#include +#include +#include +#include +#include + +#include "amd.h" +#include "../mach-config.h" + +#define DRV_NAME "acp_pci" + +#define ACP3x_REG_START 0x1240000 +#define ACP3x_REG_END 0x125C000 + +static struct platform_device *dmic_dev; +static struct platform_device *pdev; + +static const struct resource acp3x_res[] = { + { + .start = 0, + .end = ACP3x_REG_END - ACP3x_REG_START, + .name = "acp_mem", + .flags = IORESOURCE_MEM, + }, + { + .start = 0, + .end = 0, + .name = "acp_dai_irq", + .flags = IORESOURCE_IRQ, + }, +}; + +static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + struct platform_device_info pdevinfo; + struct device *dev = &pci->dev; + const struct resource *res_acp; + struct acp_chip_info *chip; + struct resource *res; + unsigned int flag, addr, num_res, i; + int ret; + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_LEGACY) + return -ENODEV; + + chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (pci_enable_device(pci)) { + dev_err(&pci->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + + ret = pci_request_regions(pci, "AMD ACP3x audio"); + if (ret < 0) { + dev_err(&pci->dev, "pci_request_regions failed\n"); + return -ENOMEM; + } + + pci_set_master(pci); + + switch (pci->revision) { + case 0x01: + res_acp = acp3x_res; + num_res = ARRAY_SIZE(acp3x_res); + chip->name = "acp_asoc_renoir"; + chip->acp_rev = ACP3X_DEV; + break; + default: + dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); + return -EINVAL; + } + + dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(dmic_dev)) { + dev_err(dev, "failed to create DMIC device\n"); + return PTR_ERR(dmic_dev); + } + + addr = pci_resource_start(pci, 0); + chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); + + res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL); + if (!res) { + platform_device_unregister(dmic_dev); + return -ENOMEM; + } + + for (i = 0; i < num_res; i++, res_acp++) { + res[i].name = res_acp->name; + res[i].flags = res_acp->flags; + res[i].start = addr + res_acp->start; + res[i].end = addr + res_acp->end; + if (res_acp->flags == IORESOURCE_IRQ) { + res[i].start = pci->irq; + res[i].end = res[i].start; + } + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + + pdevinfo.name = chip->name; + pdevinfo.id = 0; + pdevinfo.parent = &pci->dev; + pdevinfo.num_res = num_res; + pdevinfo.res = &res[0]; + pdevinfo.data = chip; + pdevinfo.size_data = sizeof(*chip); + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name); + platform_device_unregister(dmic_dev); + ret = PTR_ERR(pdev); + } + + return ret; +}; + +static void acp_pci_remove(struct pci_dev *pci) +{ + if (dmic_dev) + platform_device_unregister(dmic_dev); + if (pdev) + platform_device_unregister(pdev); +} + +/* PCI IDs */ +static const struct pci_device_id acp_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, acp_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_amd_acp_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = acp_pci_ids, + .probe = acp_pci_probe, + .remove = acp_pci_remove, +}; +module_pci_driver(snd_amd_acp_pci_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 567355209a5c..8fd38bf4d3bd 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -12,9 +12,14 @@ #define __AMD_ACP_H #include +#include #include +#include + #include "chip_offset_byte.h" +#define ACP3X_DEV 3 + #define I2S_SP_INSTANCE 0x00 #define I2S_BT_INSTANCE 0x01 #define DMIC_INSTANCE 0x02 @@ -70,6 +75,12 @@ #define ACP_MAX_STREAM 6 +struct acp_chip_info { + char *name; /* Platform name */ + unsigned int acp_rev; /* ACP Revision id */ + void __iomem *base; /* ACP memory PCI base */ +}; + struct acp_stream { struct snd_pcm_substream *substream; int irq_bit; @@ -106,6 +117,9 @@ int acp_platform_unregister(struct device *dev); int acp_machine_select(struct acp_dev_data *adata); +/* Machine configuration */ +int snd_amd_acp_find_config(struct pci_dev *pci); + static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction) { u64 byte_count, low = 0, high = 0; diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index feb3756d9ac4..0a54567a2841 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -14,6 +14,7 @@ #define FLAG_AMD_SOF BIT(1) #define FLAG_AMD_SOF_ONLY_DMIC BIT(2) +#define FLAG_AMD_LEGACY BIT(3) #define ACP_PCI_DEV_ID 0x15E2 -- cgit v1.2.3 From 6a75585a3d4bc86e7f5f95b131c4e34125c871ba Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:52 +0530 Subject: ASoC: amd: acp: Add ACP init()/deinit() callback for Renoir. ACP hardware has PGFSM control registers that can be configured to power On/Off the ACP IP block. Add acp init()/de_init() callbacks in renoir platform driver probe()/remove() respectively to power on and off ACP IP block on ACP3X device. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-5-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-renoir.c | 147 +++++++++++++++++++++++++++++++++++ sound/soc/amd/acp/chip_offset_byte.h | 6 ++ 2 files changed, 153 insertions(+) diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 770a57a0677b..d06ad5ce7fec 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -25,6 +25,20 @@ #define DRV_NAME "acp_asoc_renoir" +#define ACP_SOFT_RST_DONE_MASK 0x00010001 + +#define ACP_PWR_ON_MASK 0x01 +#define ACP_PWR_OFF_MASK 0x00 +#define ACP_PGFSM_STAT_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_PWR_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 +#define DELAY_US 5 +#define ACP_TIMEOUT 500 + +#define ACP_ERROR_MASK 0x20000000 +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF + static struct snd_soc_acpi_codecs amp_rt1019 = { .num_codecs = 1, .codecs = {"10EC1019"} @@ -112,11 +126,130 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { }, }; +static int acp3x_power_on(void __iomem *base) +{ + u32 val; + + val = readl(base + ACP_PGFSM_STATUS); + + if (val == ACP_POWERED_ON) + return 0; + + if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) + writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); + + return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); +} + +static int acp3x_power_off(void __iomem *base) +{ + u32 val; + + writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); + + return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, + (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF, + DELAY_US, ACP_TIMEOUT); +} + +static int acp3x_reset(void __iomem *base) +{ + u32 val; + int ret; + + writel(1, base + ACP_SOFT_RESET); + + ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, + DELAY_US, ACP_TIMEOUT); + if (ret) + return ret; + + writel(0, base + ACP_SOFT_RESET); + + return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); +} + +static void acp3x_enable_interrupts(void __iomem *base) +{ + u32 ext_intr_ctrl; + + writel(0x01, base + ACP_EXTERNAL_INTR_ENB); + ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_ctrl |= ACP_ERROR_MASK; + writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL); +} + +static void acp3x_disable_interrupts(void __iomem *base) +{ + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT); + writel(0x00, base + ACP_EXTERNAL_INTR_ENB); +} + +static int rn_acp_init(void __iomem *base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(base); + if (ret) + return ret; + + writel(0x01, base + ACP_CONTROL); + + /* Reset */ + ret = acp3x_reset(base); + if (ret) + return ret; + + acp3x_enable_interrupts(base); + + return 0; +} + +static int rn_acp_deinit(void __iomem *base) +{ + int ret = 0; + + acp3x_disable_interrupts(base); + + /* Reset */ + ret = acp3x_reset(base); + if (ret) + return ret; + + writel(0x00, base + ACP_CONTROL); + + /* power off */ + ret = acp3x_power_off(base); + if (ret) + return ret; + + return 0; +} static int renoir_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct acp_chip_info *chip; struct acp_dev_data *adata; struct resource *res; + int ret; + + chip = dev_get_platdata(&pdev->dev); + if (!chip || !chip->base) { + dev_err(&pdev->dev, "ACP chip data is NULL\n"); + return -ENODEV; + } + + if (chip->acp_rev != ACP3X_DEV) { + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); + return -ENODEV; + } + + ret = rn_acp_init(chip->base); + if (ret) { + dev_err(&pdev->dev, "ACP Init failed\n"); + return -EINVAL; + } adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); if (!adata) @@ -155,6 +288,20 @@ static int renoir_audio_probe(struct platform_device *pdev) static int renoir_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct acp_chip_info *chip; + int ret; + + chip = dev_get_platdata(&pdev->dev); + if (!chip || !chip->base) { + dev_err(&pdev->dev, "ACP chip data is NULL\n"); + return -ENODEV; + } + + ret = rn_acp_deinit(chip->base); + if (ret) { + dev_err(&pdev->dev, "ACP de-init Failed\n"); + return -EINVAL; + } acp_platform_unregister(dev); return 0; diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h index e38589a142e9..88f6fa597cd6 100644 --- a/sound/soc/amd/acp/chip_offset_byte.h +++ b/sound/soc/amd/acp/chip_offset_byte.h @@ -14,6 +14,12 @@ #define ACPAXI2AXI_ATU_CTRL 0xC40 #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 + +#define ACP_PGFSM_CONTROL 0x141C +#define ACP_PGFSM_STATUS 0x1420 +#define ACP_SOFT_RESET 0x1000 +#define ACP_CONTROL 0x1004 + #define ACP_EXTERNAL_INTR_ENB 0x1800 #define ACP_EXTERNAL_INTR_CNTL 0x1804 #define ACP_EXTERNAL_INTR_STAT 0x1808 -- cgit v1.2.3 From 611ba05e8bc55b35690e90bcc6710f422dd72587 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:53 +0530 Subject: ASoC: amd: acp: acp-legacy: Add DMIC dai link support for Renoir Add DMIC related dai link for pdm-dmic dai on Renoir platform with generic dmic codec dai. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-6-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-legacy-mach.c | 4 ++-- sound/soc/amd/acp/acp-mach-common.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 0ad1cf41b308..91140d15691b 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -23,10 +23,10 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .hs_cpu_id = I2S_SP, .amp_cpu_id = I2S_SP, - .dmic_cpu_id = NONE, + .dmic_cpu_id = DMIC, .hs_codec_id = RT5682, .amp_codec_id = RT1019, - .dmic_codec_id = NONE, + .dmic_codec_id = DMIC, .gpio_spkr_en = EN_SPKR_GPIO_GB, }; diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index c9caade5cb74..b45442a56c40 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -438,6 +438,8 @@ SND_SOC_DAILINK_DEF(sof_sp, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp"))); SND_SOC_DAILINK_DEF(sof_dmic, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic"))); +SND_SOC_DAILINK_DEF(pdm_dmic, + DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic"))); int acp_sofdsp_dai_links_create(struct snd_soc_card *card) { @@ -613,6 +615,25 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].ops = &acp_card_maxim_ops; links[i].init = acp_card_maxim_init; } + i++; + } + + if (drv_data->dmic_cpu_id == DMIC) { + links[i].name = "acp-dmic-codec"; + links[i].id = DMIC_BE_ID; + if (drv_data->dmic_codec_id == DMIC) { + links[i].codecs = dmic_codec; + links[i].num_codecs = ARRAY_SIZE(dmic_codec); + } else { + /* Use dummy codec if codec id not specified */ + links[i].codecs = dummy_codec; + links[i].num_codecs = ARRAY_SIZE(dummy_codec); + } + links[i].cpus = pdm_dmic; + links[i].num_cpus = ARRAY_SIZE(pdm_dmic); + links[i].platforms = platform_component; + links[i].num_platforms = ARRAY_SIZE(platform_component); + links[i].dpcm_capture = 1; } card->dai_link = links; -- cgit v1.2.3 From 2d7d9f36b567ec44c9a758e1ee6e599b4db3cad8 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Mon, 17 Jan 2022 17:28:54 +0530 Subject: ASoC: amd: renoir: Add check for acp configuration flags We have SOF and generic ACP support enabled for Renoir platforms on some machines. Since we have same PCI id used for probing, add check for machine configuration flag to avoid conflict with newer pci drivers. Such machine flag has been initialized via dmi match on few Chrome machines. If no flag is specified probe and register older platform device. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220117115854.455995-7-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 1 + sound/soc/amd/renoir/rn-pci-acp3x.c | 7 ++++++- sound/soc/amd/renoir/rn_acp3x.h | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 7a9e45094f37..1381aec23048 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -44,6 +44,7 @@ config SND_SOC_AMD_RV_RT5682_MACH config SND_SOC_AMD_RENOIR tristate "AMD Audio Coprocessor - Renoir support" + select SND_AMD_ACP_CONFIG depends on X86 && PCI help This option enables ACP support for Renoir platform diff --git a/sound/soc/amd/renoir/rn-pci-acp3x.c b/sound/soc/amd/renoir/rn-pci-acp3x.c index 7b8040e812a1..b3812b70f5f9 100644 --- a/sound/soc/amd/renoir/rn-pci-acp3x.c +++ b/sound/soc/amd/renoir/rn-pci-acp3x.c @@ -212,10 +212,15 @@ static int snd_rn_acp_probe(struct pci_dev *pci, acpi_integer dmic_status; #endif const struct dmi_system_id *dmi_id; - unsigned int irqflags; + unsigned int irqflags, flag; int ret, index; u32 addr; + /* Return if acp config flag is defined */ + flag = snd_amd_acp_find_config(pci); + if (flag) + return -ENODEV; + /* Renoir device check */ if (pci->revision != 0x01) return -ENODEV; diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h index 14620399d766..ca586603d720 100644 --- a/sound/soc/amd/renoir/rn_acp3x.h +++ b/sound/soc/amd/renoir/rn_acp3x.h @@ -88,3 +88,6 @@ static inline void rn_writel(u32 val, void __iomem *base_addr) { writel(val, base_addr - ACP_PHY_BASE_ADDRESS); } + +/* Machine configuration */ +int snd_amd_acp_find_config(struct pci_dev *pci); -- cgit v1.2.3 From 5b6988fe844a298263821beef5fcc41286a048dc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 15:36:18 +0200 Subject: ASoC: SOF: Intel: cnl: Use pm_gate->hdr.cmd in cnl_compact_ipc_compress() Instead of first checking the msg->header (which is the hdr.cmd), use directly the cmd from the message itself. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Daniel Baluta Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220128133620.9411-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/cnl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index e615125d575e..1911e104f113 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -161,11 +161,9 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev) static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, u32 *dr, u32 *dd) { - struct sof_ipc_pm_gate *pm_gate; - - if (msg->header == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { - pm_gate = msg->msg_data; + struct sof_ipc_pm_gate *pm_gate = msg->msg_data; + if (pm_gate->hdr.cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { /* send the compact message via the primary register */ *dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE; -- cgit v1.2.3 From 73a548bd1fa3cbe5d18026230a34c1f058257536 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 15:36:19 +0200 Subject: ASoC: SOF: ipc: Drop header parameter from sof_ipc_tx_message_unlocked() The snd_sof_ipc_msg.header is not used by platform code, there is no need to update it and the 'header' parameter for sof_ipc_tx_message_unlocked() can be dropped at the same time. Instead of using the header parameter passed by the caller (which does by setting it to the hdr->cmd) use the hdr->cmd directly when logging. At the same time make sure that there is a message passed to the tx_message function. All instances of the tx_message passes an IPC message, this check is placed to make sure the future users can not introduce bugs. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Daniel Baluta Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220128133620.9411-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 5bcf906d90af..ec51daed8b31 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -294,14 +294,20 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, } /* send IPC message from host to DSP */ -static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, +static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes) { + struct sof_ipc_cmd_hdr *hdr = msg_data; struct snd_sof_dev *sdev = ipc->sdev; struct snd_sof_ipc_msg *msg; int ret; + if (!msg_data || msg_bytes < sizeof(*hdr)) { + dev_err_ratelimited(sdev->dev, "No IPC message to send\n"); + return -EINVAL; + } + if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE) return -ENODEV; @@ -314,15 +320,13 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, /* initialise the message */ msg = &ipc->msg; - msg->header = header; + /* attach message data */ + memcpy(msg->msg_data, msg_data, msg_bytes); msg->msg_size = msg_bytes; + msg->reply_size = reply_bytes; msg->reply_error = 0; - /* attach any data */ - if (msg_bytes) - memcpy(msg->msg_data, msg_data, msg_bytes); - sdev->msg = msg; ret = snd_sof_dsp_send_msg(sdev, msg); @@ -339,7 +343,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, return ret; } - ipc_log_header(sdev->dev, "ipc tx", msg->header); + ipc_log_header(sdev->dev, "ipc tx", hdr->cmd); /* now wait for completion */ return tx_wait_done(ipc, msg, reply_data); @@ -385,7 +389,7 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, /* Serialise IPC TX */ mutex_lock(&ipc->tx_mutex); - ret = sof_ipc_tx_message_unlocked(ipc, header, msg_data, msg_bytes, + ret = sof_ipc_tx_message_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); mutex_unlock(&ipc->tx_mutex); @@ -789,7 +793,6 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, memcpy(sparams->dst, sparams->src + offset, send_bytes); err = sof_ipc_tx_message_unlocked(sdev->ipc, - partdata->rhdr.hdr.cmd, partdata, partdata->rhdr.hdr.size, partdata, -- cgit v1.2.3 From 2acfab7101140e93928a61ca48d7e442aa538dd7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 15:36:20 +0200 Subject: ASoC: SOF: ipc: Do not allocate buffer for msg_data The sof_ipc_tx_message does not have support for async operations. There is no need to allocate a buffer and copy each message to it to be sent to the DSP, we can use the passed message data pointer directly. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Daniel Baluta Reviewed-by: Rander Wang Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220128133620.9411-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index ec51daed8b31..16a0d7a059f3 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -321,7 +321,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, msg = &ipc->msg; /* attach message data */ - memcpy(msg->msg_data, msg_data, msg_bytes); + msg->msg_data = msg_data; msg->msg_size = msg_bytes; msg->reply_size = reply_bytes; @@ -1003,9 +1003,6 @@ int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev) struct snd_sof_ipc_msg *msg; msg = &sdev->ipc->msg; - msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!msg->msg_data) - return -ENOMEM; msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); if (!msg->reply_data) -- cgit v1.2.3 From 73d4c3135b2aa2308fe058f58ddbf658436aa385 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 Jan 2022 08:18:32 +0100 Subject: ASoC: cs42l51: Improve error handling in cs42l51_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When disabling a regulator fails while the device goes away, there is little we can do and the machine is probably in enough trouble that any action we'd want to take fails anyhow. The return value used to be passed on in cs42l51_i2c_remove() (i.e. the i2c device remove callback). But the i2c core ignores the error code (apart from emitting a generic warning) and removes the device anyhow. So return 0 unconditionally in cs42l51_i2c_remove(), and instead of returning the error code to the upper layer emit a more helpful warning message. After that nobody is interested any more in the actual error code, so let cs42l51_remove() return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220110071832.306185-1-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l51-i2c.c | 4 +++- sound/soc/codecs/cs42l51.c | 11 ++++++++--- sound/soc/codecs/cs42l51.h | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 70260e0a8f09..3cb21a2ba29f 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -31,7 +31,9 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, static int cs42l51_i2c_remove(struct i2c_client *i2c) { - return cs42l51_remove(&i2c->dev); + cs42l51_remove(&i2c->dev); + + return 0; } static const struct dev_pm_ops cs42l51_pm_ops = { diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index c61b17dc2af8..e9c3cb4e2bfc 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -793,14 +793,19 @@ error: } EXPORT_SYMBOL_GPL(cs42l51_probe); -int cs42l51_remove(struct device *dev) +void cs42l51_remove(struct device *dev) { struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + int ret; gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); - return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), - cs42l51->supplies); + ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret) + dev_warn(dev, "Failed to disable all regulators (%pe)\n", + ERR_PTR(ret)); + } EXPORT_SYMBOL_GPL(cs42l51_remove); diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h index 9d06cf7f8876..a79343e8a54e 100644 --- a/sound/soc/codecs/cs42l51.h +++ b/sound/soc/codecs/cs42l51.h @@ -13,7 +13,7 @@ struct device; extern const struct regmap_config cs42l51_regmap; int cs42l51_probe(struct device *dev, struct regmap *regmap); -int cs42l51_remove(struct device *dev); +void cs42l51_remove(struct device *dev); int __maybe_unused cs42l51_suspend(struct device *dev); int __maybe_unused cs42l51_resume(struct device *dev); extern const struct of_device_id cs42l51_of_match[]; -- cgit v1.2.3 From bb45f689fa62110c263c86070bfcb9ecbb6e1e23 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 29 Jan 2022 00:02:59 -0800 Subject: ASoC: max98927: add missing header file Add a header file that provides the missing function prototypes and macro to fix these build errors (seen on arch/alpha/): ../sound/soc/codecs/max98927.c: In function 'max98927_i2c_probe': ../sound/soc/codecs/max98927.c:902:19: error: implicit declaration of function 'devm_gpiod_get_optional'; did you mean 'devm_regulator_get_optional'? [-Werror=implicit-function-declaration] 902 | = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); | ^~~~~~~~~~~~~~~~~~~~~~~ ../sound/soc/codecs/max98927.c:902:63: error: 'GPIOD_OUT_HIGH' undeclared (first use in this function); did you mean 'GPIOF_INIT_HIGH'? 902 | = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); | ^~~~~~~~~~~~~~ ../sound/soc/codecs/max98927.c:909:17: error: implicit declaration of function 'gpiod_set_value_cansleep'; did you mean 'gpio_set_value_cansleep'? [-Werror=implicit-function-declaration] 909 | gpiod_set_value_cansleep(max98927->reset_gpio, 0); | ^~~~~~~~~~~~~~~~~~~~~~~~ Fixes: 4d67dc1998f1 ("ASoC: max98927: Handle reset gpio when probing i2c") Signed-off-by: Randy Dunlap Reported-by: kernel test robot Cc: Alejandro Tafalla Cc: Mark Brown Cc: Liam Girdwood Cc: Takashi Iwai Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20220129080259.19964-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98927.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 5ba5f876eab8..fd84780bf689 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "max98927.h" -- cgit v1.2.3 From 5e63b2ea3dfbab9307e197004a3c5ee4e1442582 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:27 +0000 Subject: platform/x86: i2c-multi-instantiate: Rename it for a generic serial driver name Rename I2C multi instantiate driver to serial-multi-instantiate for upcoming addition of SPI support Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-6-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 4 +- drivers/acpi/scan.c | 13 +- drivers/platform/x86/Kconfig | 10 +- drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/i2c-multi-instantiate.c | 174 ------------------------ drivers/platform/x86/serial-multi-instantiate.c | 173 +++++++++++++++++++++++ 6 files changed, 188 insertions(+), 188 deletions(-) delete mode 100644 drivers/platform/x86/i2c-multi-instantiate.c create mode 100644 drivers/platform/x86/serial-multi-instantiate.c diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..f51ff0a45c8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -388,11 +388,11 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/acpi/arm64 -ACPI I2C MULTI INSTANTIATE DRIVER +ACPI SERIAL MULTI INSTANTIATE DRIVER M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/platform/x86/i2c-multi-instantiate.c +F: drivers/platform/x86/serial-multi-instantiate.c ACPI PCC(Platform Communication Channel) MAILBOX DRIVER M: Sudeep Holla diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1331756d4cfc..48db5e80c2dc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1734,12 +1734,13 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) bool is_serial_bus_slave = false; static const struct acpi_device_id ignore_serial_bus_ids[] = { /* - * These devices have multiple I2cSerialBus resources and an i2c-client - * must be instantiated for each, each with its own i2c_device_id. - * Normally we only instantiate an i2c-client for the first resource, - * using the ACPI HID as id. These special cases are handled by the - * drivers/platform/x86/i2c-multi-instantiate.c driver, which knows - * which i2c_device_id to use for each resource. + * These devices have multiple SerialBus resources and a client + * device must be instantiated for each of them, each with + * its own device id. + * Normally we only instantiate one client device for the first + * resource, using the ACPI HID as id. These special cases are handled + * by the drivers/platform/x86/serial-multi-instantiate.c driver, which + * knows which client device id to use for each resource. */ {"BSG1160", }, {"BSG2150", }, diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 24deeeb29af2..2e656909a866 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -990,16 +990,16 @@ config TOPSTAR_LAPTOP If you have a Topstar laptop, say Y or M here. -config I2C_MULTI_INSTANTIATE - tristate "I2C multi instantiate pseudo device driver" +config SERIAL_MULTI_INSTANTIATE + tristate "Serial bus multi instantiate pseudo device driver" depends on I2C && ACPI help - Some ACPI-based systems list multiple i2c-devices in a single ACPI - firmware-node. This driver will instantiate separate i2c-clients + Some ACPI-based systems list multiple devices in a single ACPI + firmware-node. This driver will instantiate separate clients for each device in the firmware-node. To compile this driver as a module, choose M here: the module - will be called i2c-multi-instantiate. + will be called serial-multi-instantiate. config MLX_PLATFORM tristate "Mellanox Technologies platform support" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c12a9b044fd8..9527088bba7f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -110,7 +110,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o # Platform drivers obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o -obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c deleted file mode 100644 index 4956a1df5b90..000000000000 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * I2C multi-instantiate driver, pseudo driver to instantiate multiple - * i2c-clients from a single fwnode. - * - * Copyright 2018 Hans de Goede - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IRQ_RESOURCE_TYPE GENMASK(1, 0) -#define IRQ_RESOURCE_NONE 0 -#define IRQ_RESOURCE_GPIO 1 -#define IRQ_RESOURCE_APIC 2 - -struct i2c_inst_data { - const char *type; - unsigned int flags; - int irq_idx; -}; - -struct i2c_multi_inst_data { - int num_clients; - struct i2c_client *clients[]; -}; - -static int i2c_multi_inst_probe(struct platform_device *pdev) -{ - struct i2c_multi_inst_data *multi; - const struct i2c_inst_data *inst_data; - struct i2c_board_info board_info = {}; - struct device *dev = &pdev->dev; - struct acpi_device *adev; - char name[32]; - int i, ret; - - inst_data = device_get_match_data(dev); - if (!inst_data) { - dev_err(dev, "Error ACPI match data is missing\n"); - return -ENODEV; - } - - adev = ACPI_COMPANION(dev); - - /* Count number of clients to instantiate */ - ret = i2c_acpi_client_count(adev); - if (ret < 0) - return ret; - - multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL); - if (!multi) - return -ENOMEM; - - multi->num_clients = ret; - - for (i = 0; i < multi->num_clients && inst_data[i].type; i++) { - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE); - snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), - inst_data[i].type, i); - board_info.dev_name = name; - switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) { - case IRQ_RESOURCE_GPIO: - ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx); - if (ret < 0) { - dev_err(dev, "Error requesting irq at index %d: %d\n", - inst_data[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - case IRQ_RESOURCE_APIC: - ret = platform_get_irq(pdev, inst_data[i].irq_idx); - if (ret < 0) { - dev_dbg(dev, "Error requesting irq at index %d: %d\n", - inst_data[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - default: - board_info.irq = 0; - break; - } - multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info); - if (IS_ERR(multi->clients[i])) { - ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]), - "Error creating i2c-client, idx %d\n", i); - goto error; - } - } - if (i < multi->num_clients) { - dev_err(dev, "Error finding driver, idx %d\n", i); - ret = -ENODEV; - goto error; - } - - platform_set_drvdata(pdev, multi); - return 0; - -error: - while (--i >= 0) - i2c_unregister_device(multi->clients[i]); - - return ret; -} - -static int i2c_multi_inst_remove(struct platform_device *pdev) -{ - struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < multi->num_clients; i++) - i2c_unregister_device(multi->clients[i]); - - return 0; -} - -static const struct i2c_inst_data bsg1160_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - { "bmg160" }, - {} -}; - -static const struct i2c_inst_data bsg2150_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - /* The resources describe a 3th client, but it is not really there. */ - { "bsg2150_dummy_dev" }, - {} -}; - -static const struct i2c_inst_data int3515_data[] = { - { "tps6598x", IRQ_RESOURCE_APIC, 0 }, - { "tps6598x", IRQ_RESOURCE_APIC, 1 }, - { "tps6598x", IRQ_RESOURCE_APIC, 2 }, - { "tps6598x", IRQ_RESOURCE_APIC, 3 }, - {} -}; - -/* - * Note new device-ids must also be added to i2c_multi_instantiate_ids in - * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). - */ -static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { - { "BSG1160", (unsigned long)bsg1160_data }, - { "BSG2150", (unsigned long)bsg2150_data }, - { "INT3515", (unsigned long)int3515_data }, - { } -}; -MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); - -static struct platform_driver i2c_multi_inst_driver = { - .driver = { - .name = "I2C multi instantiate pseudo device driver", - .acpi_match_table = i2c_multi_inst_acpi_ids, - }, - .probe = i2c_multi_inst_probe, - .remove = i2c_multi_inst_remove, -}; -module_platform_driver(i2c_multi_inst_driver); - -MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver"); -MODULE_AUTHOR("Hans de Goede "); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c new file mode 100644 index 000000000000..9af5bcd466d1 --- /dev/null +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Serial multi-instantiate driver, pseudo driver to instantiate multiple + * client devices from a single fwnode. + * + * Copyright 2018 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_RESOURCE_TYPE GENMASK(1, 0) +#define IRQ_RESOURCE_NONE 0 +#define IRQ_RESOURCE_GPIO 1 +#define IRQ_RESOURCE_APIC 2 + +struct smi_instance { + const char *type; + unsigned int flags; + int irq_idx; +}; + +struct smi { + int i2c_num; + struct i2c_client *i2c_devs[]; +}; + +static int smi_probe(struct platform_device *pdev) +{ + struct i2c_board_info board_info = {}; + const struct smi_instance *inst; + struct device *dev = &pdev->dev; + struct acpi_device *adev; + struct smi *smi; + char name[32]; + int i, ret; + + inst = device_get_match_data(dev); + if (!inst) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + adev = ACPI_COMPANION(dev); + + /* Count number of clients to instantiate */ + ret = i2c_acpi_client_count(adev); + if (ret < 0) + return ret; + + smi = devm_kmalloc(dev, struct_size(smi, i2c_devs, ret), GFP_KERNEL); + if (!smi) + return -ENOMEM; + + smi->i2c_num = ret; + + for (i = 0; i < smi->i2c_num && inst[i].type; i++) { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, inst[i].type, I2C_NAME_SIZE); + snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst[i].type, i); + board_info.dev_name = name; + switch (inst[i].flags & IRQ_RESOURCE_TYPE) { + case IRQ_RESOURCE_GPIO: + ret = acpi_dev_gpio_irq_get(adev, inst[i].irq_idx); + if (ret < 0) { + dev_err(dev, "Error requesting irq at index %d: %d\n", + inst[i].irq_idx, ret); + goto error; + } + board_info.irq = ret; + break; + case IRQ_RESOURCE_APIC: + ret = platform_get_irq(pdev, inst[i].irq_idx); + if (ret < 0) { + dev_dbg(dev, "Error requesting irq at index %d: %d\n", + inst[i].irq_idx, ret); + goto error; + } + board_info.irq = ret; + break; + default: + board_info.irq = 0; + break; + } + smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info); + if (IS_ERR(smi->i2c_devs[i])) { + ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]), + "Error creating i2c-client, idx %d\n", i); + goto error; + } + } + if (i < smi->i2c_num) { + dev_err(dev, "Error finding driver, idx %d\n", i); + ret = -ENODEV; + goto error; + } + + platform_set_drvdata(pdev, smi); + return 0; + +error: + while (--i >= 0) + i2c_unregister_device(smi->i2c_devs[i]); + + return ret; +} + +static int smi_remove(struct platform_device *pdev) +{ + struct smi *smi = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < smi->i2c_num; i++) + i2c_unregister_device(smi->i2c_devs[i]); + + return 0; +} + +static const struct smi_instance bsg1160_data[] = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + { "bmg160" }, + {} +}; + +static const struct smi_instance bsg2150_data[] = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + /* The resources describe a 3th client, but it is not really there. */ + { "bsg2150_dummy_dev" }, + {} +}; + +static const struct smi_instance int3515_data[] = { + { "tps6598x", IRQ_RESOURCE_APIC, 0 }, + { "tps6598x", IRQ_RESOURCE_APIC, 1 }, + { "tps6598x", IRQ_RESOURCE_APIC, 2 }, + { "tps6598x", IRQ_RESOURCE_APIC, 3 }, + {} +}; + +/* + * Note new device-ids must also be added to ignore_serial_bus_ids in + * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). + */ +static const struct acpi_device_id smi_acpi_ids[] = { + { "BSG1160", (unsigned long)bsg1160_data }, + { "BSG2150", (unsigned long)bsg2150_data }, + { "INT3515", (unsigned long)int3515_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); + +static struct platform_driver smi_driver = { + .driver = { + .name = "Serial bus multi instantiate pseudo device driver", + .acpi_match_table = smi_acpi_ids, + }, + .probe = smi_probe, + .remove = smi_remove, +}; +module_platform_driver(smi_driver); + +MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 35a36cbb7b1ce7596fc03962f7ce261b2e908d1f Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:28 +0000 Subject: platform/x86: serial-multi-instantiate: Reorganize I2C functions Reorganize I2C functions to accommodate SPI support Split the probe and factor out parts of the code that will be used in the SPI support Also switched from strlcpy() to strscpy() Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-7-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/serial-multi-instantiate.c | 144 +++++++++++++++--------- 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 9af5bcd466d1..1eb29bec405f 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -29,96 +29,132 @@ struct smi_instance { struct smi { int i2c_num; - struct i2c_client *i2c_devs[]; + struct i2c_client **i2c_devs; }; -static int smi_probe(struct platform_device *pdev) +static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, + const struct smi_instance *inst) +{ + int ret; + + switch (inst->flags & IRQ_RESOURCE_TYPE) { + case IRQ_RESOURCE_GPIO: + ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx); + break; + case IRQ_RESOURCE_APIC: + ret = platform_get_irq(pdev, inst->irq_idx); + break; + default: + return 0; + } + + if (ret < 0) + dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n", + inst->irq_idx, ret); + + return ret; +} + +static void smi_devs_unregister(struct smi *smi) +{ + while (smi->i2c_num > 0) + i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]); +} + +/** + * smi_i2c_probe - Instantiate multiple I2C devices from inst array + * @pdev: Platform device + * @adev: ACPI device + * @smi: Internal struct for Serial multi instantiate driver + * @inst_array: Array of instances to probe + * + * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code. + */ +static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, + const struct smi_instance *inst_array) { struct i2c_board_info board_info = {}; - const struct smi_instance *inst; struct device *dev = &pdev->dev; - struct acpi_device *adev; - struct smi *smi; char name[32]; - int i, ret; + int i, ret, count; - inst = device_get_match_data(dev); - if (!inst) { - dev_err(dev, "Error ACPI match data is missing\n"); - return -ENODEV; - } - - adev = ACPI_COMPANION(dev); - - /* Count number of clients to instantiate */ ret = i2c_acpi_client_count(adev); if (ret < 0) return ret; + else if (!ret) + return -ENODEV; - smi = devm_kmalloc(dev, struct_size(smi, i2c_devs, ret), GFP_KERNEL); - if (!smi) - return -ENOMEM; + count = ret; - smi->i2c_num = ret; + smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL); + if (!smi->i2c_devs) + return -ENOMEM; - for (i = 0; i < smi->i2c_num && inst[i].type; i++) { + for (i = 0; i < count && inst_array[i].type; i++) { memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, inst[i].type, I2C_NAME_SIZE); - snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst[i].type, i); + strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE); + snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i); board_info.dev_name = name; - switch (inst[i].flags & IRQ_RESOURCE_TYPE) { - case IRQ_RESOURCE_GPIO: - ret = acpi_dev_gpio_irq_get(adev, inst[i].irq_idx); - if (ret < 0) { - dev_err(dev, "Error requesting irq at index %d: %d\n", - inst[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - case IRQ_RESOURCE_APIC: - ret = platform_get_irq(pdev, inst[i].irq_idx); - if (ret < 0) { - dev_dbg(dev, "Error requesting irq at index %d: %d\n", - inst[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - default: - board_info.irq = 0; - break; - } + + ret = smi_get_irq(pdev, adev, &inst_array[i]); + if (ret < 0) + goto error; + board_info.irq = ret; + smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info); if (IS_ERR(smi->i2c_devs[i])) { ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]), "Error creating i2c-client, idx %d\n", i); goto error; } + smi->i2c_num++; } - if (i < smi->i2c_num) { - dev_err(dev, "Error finding driver, idx %d\n", i); + if (smi->i2c_num < count) { + dev_dbg(dev, "Error finding driver, idx %d\n", i); ret = -ENODEV; goto error; } - platform_set_drvdata(pdev, smi); - return 0; + dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num); + return 0; error: - while (--i >= 0) - i2c_unregister_device(smi->i2c_devs[i]); + smi_devs_unregister(smi); return ret; } +static int smi_probe(struct platform_device *pdev) +{ + const struct smi_instance *inst_array; + struct device *dev = &pdev->dev; + struct acpi_device *adev; + struct smi *smi; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + inst_array = device_get_match_data(dev); + if (!inst_array) { + dev_dbg(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL); + if (!smi) + return -ENOMEM; + + platform_set_drvdata(pdev, smi); + + return smi_i2c_probe(pdev, adev, smi, inst_array); +} + static int smi_remove(struct platform_device *pdev) { struct smi *smi = platform_get_drvdata(pdev); - int i; - for (i = 0; i < smi->i2c_num; i++) - i2c_unregister_device(smi->i2c_devs[i]); + smi_devs_unregister(smi); return 0; } -- cgit v1.2.3 From 68f201f9061c000d7a4a9f359f021b1cd535d62b Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:29 +0000 Subject: platform/x86: serial-multi-instantiate: Add SPI support Add support for spi bus in serial-multi-instantiate driver Some peripherals can have either a I2C or a SPI connection to the host (but not both) but use the same HID for both types. So it is not possible to use the HID to determine whether it is I2C or SPI. The driver must check the node to see if it contains I2cSerialBus or SpiSerialBus entries. For backwards-compatibility with the existing nodes I2C is checked first and if such entries are found ONLY I2C devices are created. Since some existing nodes that were already handled by this driver could also contain unrelated SpiSerialBus nodes that were previously ignored, and this preserves that behavior. If there is ever a need to handle a node where both I2C and SPI devices must be instantiated this can be added in future. Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-8-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 2 +- drivers/platform/x86/serial-multi-instantiate.c | 173 ++++++++++++++++++++---- 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2e656909a866..8d1eec208854 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -992,7 +992,7 @@ config TOPSTAR_LAPTOP config SERIAL_MULTI_INSTANTIATE tristate "Serial bus multi instantiate pseudo device driver" - depends on I2C && ACPI + depends on I2C && SPI && ACPI help Some ACPI-based systems list multiple devices in a single ACPI firmware-node. This driver will instantiate separate clients diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 1eb29bec405f..e2fd73905312 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define IRQ_RESOURCE_TYPE GENMASK(1, 0) @@ -21,15 +22,28 @@ #define IRQ_RESOURCE_GPIO 1 #define IRQ_RESOURCE_APIC 2 +enum smi_bus_type { + SMI_I2C, + SMI_SPI, + SMI_AUTO_DETECT, +}; + struct smi_instance { const char *type; unsigned int flags; int irq_idx; }; +struct smi_node { + enum smi_bus_type bus_type; + struct smi_instance instances[]; +}; + struct smi { int i2c_num; + int spi_num; struct i2c_client **i2c_devs; + struct spi_device **spi_devs; }; static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, @@ -59,6 +73,94 @@ static void smi_devs_unregister(struct smi *smi) { while (smi->i2c_num > 0) i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]); + + while (smi->spi_num > 0) + spi_unregister_device(smi->spi_devs[--smi->spi_num]); +} + +/** + * smi_spi_probe - Instantiate multiple SPI devices from inst array + * @pdev: Platform device + * @adev: ACPI device + * @smi: Internal struct for Serial multi instantiate driver + * @inst_array: Array of instances to probe + * + * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code. + */ +static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, + const struct smi_instance *inst_array) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctlr; + struct spi_device *spi_dev; + char name[50]; + int i, ret, count; + + ret = acpi_spi_count_resources(adev); + if (ret < 0) + return ret; + else if (!ret) + return -ENODEV; + + count = ret; + + smi->spi_devs = devm_kcalloc(dev, count, sizeof(*smi->spi_devs), GFP_KERNEL); + if (!smi->spi_devs) + return -ENOMEM; + + for (i = 0; i < count && inst_array[i].type; i++) { + + spi_dev = acpi_spi_device_alloc(NULL, adev, i); + if (IS_ERR(spi_dev)) { + ret = PTR_ERR(spi_dev); + dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n", + dev_name(&adev->dev), ret); + goto error; + } + + ctlr = spi_dev->controller; + + strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias)); + + ret = smi_get_irq(pdev, adev, &inst_array[i]); + if (ret < 0) { + spi_dev_put(spi_dev); + goto error; + } + spi_dev->irq = ret; + + snprintf(name, sizeof(name), "%s-%s-%s.%d", dev_name(&ctlr->dev), dev_name(dev), + inst_array[i].type, i); + spi_dev->dev.init_name = name; + + ret = spi_add_device(spi_dev); + if (ret) { + dev_err_probe(&ctlr->dev, ret, + "failed to add SPI device %s from ACPI: %d\n", + dev_name(&adev->dev), ret); + spi_dev_put(spi_dev); + goto error; + } + + dev_dbg(dev, "SPI device %s using chip select %u", name, spi_dev->chip_select); + + smi->spi_devs[i] = spi_dev; + smi->spi_num++; + } + + if (smi->spi_num < count) { + dev_dbg(dev, "Error finding driver, idx %d\n", i); + ret = -ENODEV; + goto error; + } + + dev_info(dev, "Instantiated %d SPI devices.\n", smi->spi_num); + + return 0; +error: + smi_devs_unregister(smi); + + return ret; } /** @@ -126,8 +228,8 @@ error: static int smi_probe(struct platform_device *pdev) { - const struct smi_instance *inst_array; struct device *dev = &pdev->dev; + const struct smi_node *node; struct acpi_device *adev; struct smi *smi; @@ -135,8 +237,8 @@ static int smi_probe(struct platform_device *pdev) if (!adev) return -ENODEV; - inst_array = device_get_match_data(dev); - if (!inst_array) { + node = device_get_match_data(dev); + if (!node) { dev_dbg(dev, "Error ACPI match data is missing\n"); return -ENODEV; } @@ -147,7 +249,21 @@ static int smi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, smi); - return smi_i2c_probe(pdev, adev, smi, inst_array); + switch (node->bus_type) { + case SMI_I2C: + return smi_i2c_probe(pdev, adev, smi, node->instances); + case SMI_SPI: + return smi_spi_probe(pdev, adev, smi, node->instances); + case SMI_AUTO_DETECT: + if (i2c_acpi_client_count(adev) > 0) + return smi_i2c_probe(pdev, adev, smi, node->instances); + else + return smi_spi_probe(pdev, adev, smi, node->instances); + default: + return -EINVAL; + } + + return 0; /* never reached */ } static int smi_remove(struct platform_device *pdev) @@ -159,27 +275,36 @@ static int smi_remove(struct platform_device *pdev) return 0; } -static const struct smi_instance bsg1160_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - { "bmg160" }, - {} +static const struct smi_node bsg1160_data = { + .instances = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + { "bmg160" }, + {} + }, + .bus_type = SMI_I2C, }; -static const struct smi_instance bsg2150_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - /* The resources describe a 3th client, but it is not really there. */ - { "bsg2150_dummy_dev" }, - {} +static const struct smi_node bsg2150_data = { + .instances = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + /* The resources describe a 3th client, but it is not really there. */ + { "bsg2150_dummy_dev" }, + {} + }, + .bus_type = SMI_I2C, }; -static const struct smi_instance int3515_data[] = { - { "tps6598x", IRQ_RESOURCE_APIC, 0 }, - { "tps6598x", IRQ_RESOURCE_APIC, 1 }, - { "tps6598x", IRQ_RESOURCE_APIC, 2 }, - { "tps6598x", IRQ_RESOURCE_APIC, 3 }, - {} +static const struct smi_node int3515_data = { + .instances = { + { "tps6598x", IRQ_RESOURCE_APIC, 0 }, + { "tps6598x", IRQ_RESOURCE_APIC, 1 }, + { "tps6598x", IRQ_RESOURCE_APIC, 2 }, + { "tps6598x", IRQ_RESOURCE_APIC, 3 }, + {} + }, + .bus_type = SMI_I2C, }; /* @@ -187,9 +312,9 @@ static const struct smi_instance int3515_data[] = { * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). */ static const struct acpi_device_id smi_acpi_ids[] = { - { "BSG1160", (unsigned long)bsg1160_data }, - { "BSG2150", (unsigned long)bsg2150_data }, - { "INT3515", (unsigned long)int3515_data }, + { "BSG1160", (unsigned long)&bsg1160_data }, + { "BSG2150", (unsigned long)&bsg2150_data }, + { "INT3515", (unsigned long)&int3515_data }, { } }; MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); -- cgit v1.2.3 From 07bcab93946cd9980f8eafe89afe991cd918acb0 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:30 +0000 Subject: ALSA: hda/realtek: Add support for HP Laptops Add support for two and four CS35L41 using the component binding method Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20220121172431.6876-9-sbinding@opensource.cirrus.com Signed-off-by: Hans de Goede --- sound/pci/hda/patch_realtek.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 668274e52674..db0fce42887c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6611,6 +6611,16 @@ static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); } +static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 2); +} + +static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 4); +} + static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, struct snd_pcm_substream *sub, int action) { @@ -6948,6 +6958,9 @@ enum { ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, ALC287_FIXUP_LEGION_16ACHG6, ALC287_FIXUP_CS35L41_I2C_2, + ALC245_FIXUP_CS35L41_SPI_2, + ALC245_FIXUP_CS35L41_SPI_4, + ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, }; @@ -8699,6 +8712,20 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC245_FIXUP_CS35L41_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_two, + }, + [ALC245_FIXUP_CS35L41_SPI_4] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_four, + }, + [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_gpio_led, + .chained = true, + .chain_id = ALC245_FIXUP_CS35L41_SPI_4, + }, [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -8926,7 +8953,21 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), -- cgit v1.2.3 From d9c01c530cc5e3b6d5bdfeae12c3d0f33fae7498 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:31 +0000 Subject: ACPI / scan: Create platform device for CS35L41 The ACPI device with CSC3551 or CLSA0100 are sound cards with multiple instances of CS35L41 connected by I2C or SPI to the main CPU. We add an ID to the ignore_serial_bus_ids list to enumerate all I2C or SPI devices correctly. The same IDs are also added into serial-multi-instantiate so that the driver can correctly enumerate the ACPI. Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-10-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/acpi/scan.c | 3 +++ drivers/platform/x86/serial-multi-instantiate.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 48db5e80c2dc..4463c2eda61e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1744,8 +1744,11 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) */ {"BSG1160", }, {"BSG2150", }, + {"CSC3551", }, {"INT33FE", }, {"INT3515", }, + /* Non-conforming _HID for Cirrus Logic already released */ + {"CLSA0100", }, /* * HIDs of device with an UartSerialBusV2 resource for which userspace * expects a regular tty cdev to be created (instead of the in kernel diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index e2fd73905312..1e8063b7c169 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -307,6 +307,17 @@ static const struct smi_node int3515_data = { .bus_type = SMI_I2C, }; +static const struct smi_node cs35l41_hda = { + .instances = { + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + /* * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). @@ -315,6 +326,9 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "INT3515", (unsigned long)&int3515_data }, + { "CSC3551", (unsigned long)&cs35l41_hda }, + /* Non-conforming _HID for Cirrus Logic already released */ + { "CLSA0100", (unsigned long)&cs35l41_hda }, { } }; MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); -- cgit v1.2.3 From 7f97b2ad948343c3be543d12c2965f74bddc34c7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 2 Feb 2022 20:01:13 -0600 Subject: ASoC: dt-bindings: sun4i-i2s: Add compatibles for R329 and D1 R329 contains I2S controllers which are similar to, but are incompatible with, the H6 variant, because they change the layout of the RX channel mapping registers. The D1 contains I2S controllers which appear to be identical to those in the R329. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20220203020116.12279-2-samuel@sholland.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml index 7d48ea094c66..c21c807b667c 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml @@ -31,6 +31,10 @@ properties: - const: allwinner,sun50i-a64-i2s - const: allwinner,sun8i-h3-i2s - const: allwinner,sun50i-h6-i2s + - const: allwinner,sun50i-r329-i2s + - items: + - const: allwinner,sun20i-d1-i2s + - const: allwinner,sun50i-r329-i2s reg: maxItems: 1 @@ -67,6 +71,7 @@ allOf: - allwinner,sun8i-h3-i2s - allwinner,sun50i-a64-codec-i2s - allwinner,sun50i-h6-i2s + - allwinner,sun50i-r329-i2s then: required: -- cgit v1.2.3 From c8bbc1de9088fedb5d71db7d185c37db18feb2e1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 2 Feb 2022 20:01:14 -0600 Subject: ASoC: sun4i-i2s: Update registers for more channels H6 expands the number of channels in each direction to 16, so the slot number fields need to be expanded from 3 to 4 bits each. R329/D1 expand that further by allowing each of the 16 slots to map to any of 4 data pins. For TX, the configuration of each pin is independent, so there is a copy of the mapping registers for each pin. For RX, each of the 16 slots can map to only one pin, so the registers were changed to add the pin selection inline with the channel mapping. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20220203020116.12279-3-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 1e9116cd365e..7da8a16955a1 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -115,9 +115,9 @@ #define SUN8I_I2S_FIFO_TX_REG 0x20 #define SUN8I_I2S_CHAN_CFG_REG 0x30 -#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4) +#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(7, 4) #define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) ((chan - 1) << 4) -#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0) +#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(3, 0) #define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1) #define SUN8I_I2S_TX_CHAN_MAP_REG 0x44 @@ -138,13 +138,19 @@ #define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0) #define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1)) -#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44 -#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48 +#define SUN50I_H6_I2S_TX_CHAN_SEL_REG(pin) (0x34 + 4 * (pin)) +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG(pin) (0x44 + 8 * (pin)) +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG(pin) (0x48 + 8 * (pin)) #define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64 #define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68 #define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C +#define SUN50I_R329_I2S_RX_CHAN_MAP0_REG 0x68 +#define SUN50I_R329_I2S_RX_CHAN_MAP1_REG 0x6c +#define SUN50I_R329_I2S_RX_CHAN_MAP2_REG 0x70 +#define SUN50I_R329_I2S_RX_CHAN_MAP3_REG 0x74 + struct sun4i_i2s; /** @@ -523,13 +529,13 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, unsigned int lrck_period; /* Map the channels for playback and capture */ - regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0xFEDCBA98); - regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x76543210); + regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98); + regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210); regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98); regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210); /* Configure the channels */ - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, + regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), SUN50I_H6_I2S_TX_CHAN_SEL_MASK, SUN50I_H6_I2S_TX_CHAN_SEL(channels)); regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG, @@ -563,7 +569,7 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period)); - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, + regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), SUN50I_H6_I2S_TX_CHAN_EN_MASK, SUN50I_H6_I2S_TX_CHAN_EN(channels)); @@ -1210,9 +1216,9 @@ static const struct reg_default sun50i_h6_i2s_reg_defaults[] = { { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 }, { SUN4I_I2S_CLK_DIV_REG, 0x00000000 }, { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 }, - { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 }, - { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 }, - { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 }, -- cgit v1.2.3 From e2ce580f1fffc009807da73adf7dc86912ab6a19 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 2 Feb 2022 20:01:15 -0600 Subject: ASoC: sun4i-i2s: Add support for the R329/D1 variant This adds a new set of quirks to set the right RX channel map. Since that is the only change to the register layout, reuse the H6 regmap config by extending its last register. R329 support is added by its compatible string. D1 uses R329 as its fallback compatible, so no additional code change is needed for it. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20220203020116.12279-4-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-i2s.c | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 7da8a16955a1..7047f71629ab 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -181,6 +181,9 @@ struct sun4i_i2s_quirks { struct reg_field field_fmt_wss; struct reg_field field_fmt_sr; + unsigned int num_din_pins; + unsigned int num_dout_pins; + const struct sun4i_i2s_clk_div *bclk_dividers; unsigned int num_bclk_dividers; const struct sun4i_i2s_clk_div *mclk_dividers; @@ -531,8 +534,15 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, /* Map the channels for playback and capture */ regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98); regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210); - regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98); - regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210); + if (i2s->variant->num_din_pins > 1) { + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP0_REG, 0x0F0E0D0C); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP1_REG, 0x0B0A0908); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP2_REG, 0x07060504); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP3_REG, 0x03020100); + } else { + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98); + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210); + } /* Configure the channels */ regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), @@ -1255,7 +1265,7 @@ static const struct regmap_config sun50i_h6_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG, + .max_register = SUN50I_R329_I2S_RX_CHAN_MAP3_REG, .cache_type = REGCACHE_FLAT, .reg_defaults = sun50i_h6_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_h6_i2s_reg_defaults), @@ -1440,6 +1450,26 @@ static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = { .set_fmt = sun50i_h6_i2s_set_soc_fmt, }; +static const struct sun4i_i2s_quirks sun50i_r329_i2s_quirks = { + .has_reset = true, + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, + .sun4i_i2s_regmap = &sun50i_h6_i2s_regmap_config, + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2), + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), + .num_din_pins = 4, + .num_dout_pins = 4, + .bclk_dividers = sun8i_i2s_clk_div, + .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), + .mclk_dividers = sun8i_i2s_clk_div, + .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), + .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate, + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, + .set_chan_cfg = sun50i_h6_i2s_set_chan_cfg, + .set_fmt = sun50i_h6_i2s_set_soc_fmt, +}; + static int sun4i_i2s_init_regmap_fields(struct device *dev, struct sun4i_i2s *i2s) { @@ -1612,6 +1642,10 @@ static const struct of_device_id sun4i_i2s_match[] = { .compatible = "allwinner,sun50i-h6-i2s", .data = &sun50i_h6_i2s_quirks, }, + { + .compatible = "allwinner,sun50i-r329-i2s", + .data = &sun50i_r329_i2s_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_i2s_match); -- cgit v1.2.3 From b5083c0c948ac7f52ca700af219cb491735ecd4b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 2 Feb 2022 16:45:45 +0000 Subject: ASoC: wm8962: Allow switching between analog and digital inputs When the DMIC_ENA bit is set the analogue inputs are disconnected from the digital core of the chip, in favour of the digital microphones. Currently the driver will always enable DMIC_ENA whilst the GPIOs are configured for the DMIC function, this means the user can't currently use both the analog inputs and the digital inputs in one system. Add an additional DAPM mutex that allows switching between analog and digital inputs into the digital core. Reported-by: Martin Kepplinger Signed-off-by: Charles Keepax Reported-and-tested-by: Martin Kepplinger Link: https://lore.kernel.org/r/20220202164545.30457-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index a5584ba962dc..2c41d31956aa 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2049,6 +2049,13 @@ static SOC_ENUM_SINGLE_DECL(hpoutl_enum, static const struct snd_kcontrol_new hpoutl_mux = SOC_DAPM_ENUM("HPOUTL Mux", hpoutl_enum); +static const char * const input_mode_text[] = { "Analog", "Digital" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(input_mode_enum, input_mode_text); + +static const struct snd_kcontrol_new input_mode_mux = + SOC_DAPM_ENUM("Input Mode", input_mode_enum); + static const struct snd_kcontrol_new inpgal[] = { SOC_DAPM_SINGLE("IN1L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 3, 1, 0), SOC_DAPM_SINGLE("IN2L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 2, 1, 0), @@ -2147,6 +2154,9 @@ SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0, SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0), +SND_SOC_DAPM_MUX("Input Mode L", SND_SOC_NOPM, 0, 0, &input_mode_mux), +SND_SOC_DAPM_MUX("Input Mode R", SND_SOC_NOPM, 0, 0, &input_mode_mux), + SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0), SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0), @@ -2226,16 +2236,19 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = { { "DMIC_ENA", NULL, "DMICDAT" }, + { "Input Mode L", "Analog", "MIXINL" }, + { "Input Mode L", "Digital", "DMIC_ENA" }, + { "Input Mode R", "Analog", "MIXINR" }, + { "Input Mode R", "Digital", "DMIC_ENA" }, + { "ADCL", NULL, "SYSCLK" }, { "ADCL", NULL, "TOCLK" }, - { "ADCL", NULL, "MIXINL" }, - { "ADCL", NULL, "DMIC_ENA" }, + { "ADCL", NULL, "Input Mode L" }, { "ADCL", NULL, "DSP2" }, { "ADCR", NULL, "SYSCLK" }, { "ADCR", NULL, "TOCLK" }, - { "ADCR", NULL, "MIXINR" }, - { "ADCR", NULL, "DMIC_ENA" }, + { "ADCR", NULL, "Input Mode R" }, { "ADCR", NULL, "DSP2" }, { "STL", "Left", "ADCL" }, -- cgit v1.2.3 From ed482dc8c76db7613c08f39b09c6b98718c92940 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:13:22 +0000 Subject: ASoC: samsung: Explicitly include gpiolib header midas_wm811 uses gpiolib but relies on the header being implicitly included which can lead to build failures in some configurations, explicitly pull the header in to avoid problems. Signed-off-by: Mark Brown Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220202191322.3650708-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index a2019535a0b1..5e9dc18687cc 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -6,6 +6,7 @@ // Copyright (C) 2020 Samsung Electronics Co., Ltd. #include +#include #include #include #include -- cgit v1.2.3 From ec29170c724ca30305fc3a19ba2ee73ecac65509 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 3 Feb 2022 11:50:25 +0000 Subject: ASoC: madera: Add dependencies on MFD The Madera CODECs use regmap_irq functions but nothing ensures that regmap_irq is built into the kernel. Add dependencies on the ASoC symbols for the relevant MFD component. There is no point in building the ASoC driver if the MFD doesn't support it and the MFD part contains the necessary dependencies to ensure everything is built into the kernel. Reported-by: Mark Brown Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220203115025.16464-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d6b8f5cb6ef8..68ad04ab3349 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -733,6 +733,7 @@ config SND_SOC_CS4349 config SND_SOC_CS47L15 tristate + depends on MFD_CS47L15 config SND_SOC_CS47L24 tristate @@ -740,15 +741,19 @@ config SND_SOC_CS47L24 config SND_SOC_CS47L35 tristate + depends on MFD_CS47L35 config SND_SOC_CS47L85 tristate + depends on MFD_CS47L85 config SND_SOC_CS47L90 tristate + depends on MFD_CS47L90 config SND_SOC_CS47L92 tristate + depends on MFD_CS47L92 # Cirrus Logic Quad-Channel ADC config SND_SOC_CS53L30 -- cgit v1.2.3 From b1446bda56456887f8d439938163164fe916bc0d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 15:09:01 +0000 Subject: kselftest: alsa: Check for event generation when we write to controls Add some coverage of event generation to mixer-test. Rather than doing a separate set of writes designed to trigger events we add a step to the existing write_and_verify() which checks to see if the value we read back from non-volatile controls matches the value before writing and that an event is or isn't generated as appropriate. The "tests" for events then simply check that no spurious or missing events were detected. This avoids needing further logic to generate appropriate values for each control type and maximises coverage. When checking for events we use a timeout of 0. This relies on the kernel generating any event prior to returning to userspace when setting a control. That is currently the case and it is difficult to see it changing, if it does the test will need to be updated. Using a delay of 0 means that we don't slow things down unduly when checking for no event or when events fail to be generated. We don't check behaviour for volatile controls since we can't tell what the behaviour is supposed to be for any given control. Signed-off-by: Mark Brown Reviewed-by: Shuah Khan Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220202150902.19563-1-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 154 +++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 0e88f4f3d802..6edb7dca32af 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -3,7 +3,7 @@ // kselftest for the ALSA mixer API // // Original author: Mark Brown -// Copyright (c) 2021 Arm Limited +// Copyright (c) 2021-2 Arm Limited // This test will iterate over all cards detected in the system, exercising // every mixer control it can find. This may conflict with other system @@ -27,11 +27,12 @@ #include "../kselftest.h" -#define TESTS_PER_CONTROL 4 +#define TESTS_PER_CONTROL 6 struct card_data { snd_ctl_t *handle; int card; + struct pollfd pollfd; int num_ctls; snd_ctl_elem_list_t *ctls; struct card_data *next; @@ -43,6 +44,8 @@ struct ctl_data { snd_ctl_elem_info_t *info; snd_ctl_elem_value_t *def_val; int elem; + int event_missing; + int event_spurious; struct card_data *card; struct ctl_data *next; }; @@ -149,6 +152,7 @@ void find_controls(void) if (!ctl_data) ksft_exit_fail_msg("Out of memory\n"); + memset(ctl_data, 0, sizeof(*ctl_data)); ctl_data->card = card_data; ctl_data->elem = ctl; ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, @@ -184,6 +188,26 @@ void find_controls(void) ctl_list = ctl_data; } + /* Set up for events */ + err = snd_ctl_subscribe_events(card_data->handle, true); + if (err < 0) { + ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n", + card, err); + } + + err = snd_ctl_poll_descriptors_count(card_data->handle); + if (err != 1) { + ksft_exit_fail_msg("Unexpected desciptor count %d for card %d\n", + err, card); + } + + err = snd_ctl_poll_descriptors(card_data->handle, + &card_data->pollfd, 1); + if (err != 1) { + ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %d\n", + card, err); + } + next_card: if (snd_card_next(&card) < 0) { ksft_print_msg("snd_card_next"); @@ -194,6 +218,79 @@ void find_controls(void) snd_config_delete(config); } +/* + * Block for up to timeout ms for an event, returns a negative value + * on error, 0 for no event and 1 for an event. + */ +int wait_for_event(struct ctl_data *ctl, int timeout) +{ + unsigned short revents; + snd_ctl_event_t *event; + int count, err; + unsigned int mask = 0; + unsigned int ev_id; + + snd_ctl_event_alloca(&event); + + do { + err = poll(&(ctl->card->pollfd), 1, timeout); + if (err < 0) { + ksft_print_msg("poll() failed for %s: %s (%d)\n", + ctl->name, strerror(errno), errno); + return -1; + } + /* Timeout */ + if (err == 0) + return 0; + + err = snd_ctl_poll_descriptors_revents(ctl->card->handle, + &(ctl->card->pollfd), + 1, &revents); + if (err < 0) { + ksft_print_msg("snd_ctl_poll_desciptors_revents() failed for %s: %d\n", + ctl->name, err); + return err; + } + if (revents & POLLERR) { + ksft_print_msg("snd_ctl_poll_desciptors_revents() reported POLLERR for %s\n", + ctl->name); + return -1; + } + /* No read events */ + if (!(revents & POLLIN)) { + ksft_print_msg("No POLLIN\n"); + continue; + } + + err = snd_ctl_read(ctl->card->handle, event); + if (err < 0) { + ksft_print_msg("snd_ctl_read() failed for %s: %d\n", + ctl->name, err); + return err; + } + + if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) + continue; + + /* The ID returned from the event is 1 less than numid */ + mask = snd_ctl_event_elem_get_mask(event); + ev_id = snd_ctl_event_elem_get_numid(event); + if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) { + ksft_print_msg("Event for unexpected ctl %s\n", + snd_ctl_event_elem_get_name(event)); + continue; + } + + if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) { + ksft_print_msg("Removal event for %s\n", + ctl->name); + return -1; + } + } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE); + + return 1; +} + bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, int index) { @@ -428,7 +525,8 @@ int write_and_verify(struct ctl_data *ctl, { int err, i; bool error_expected, mismatch_shown; - snd_ctl_elem_value_t *read_val, *w_val; + snd_ctl_elem_value_t *initial_val, *read_val, *w_val; + snd_ctl_elem_value_alloca(&initial_val); snd_ctl_elem_value_alloca(&read_val); snd_ctl_elem_value_alloca(&w_val); @@ -446,6 +544,18 @@ int write_and_verify(struct ctl_data *ctl, snd_ctl_elem_value_copy(expected_val, write_val); } + /* Store the value before we write */ + if (snd_ctl_elem_info_is_readable(ctl->info)) { + snd_ctl_elem_value_set_id(initial_val, ctl->id); + + err = snd_ctl_elem_read(ctl->card->handle, initial_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + return err; + } + } + /* * Do the write, if we have an expected value ignore the error * and carry on to validate the expected value. @@ -470,6 +580,30 @@ int write_and_verify(struct ctl_data *ctl, return err; } + /* + * Check for an event if the value changed, or confirm that + * there was none if it didn't. We rely on the kernel + * generating the notification before it returns from the + * write, this is currently true, should that ever change this + * will most likely break and need updating. + */ + if (!snd_ctl_elem_info_is_volatile(ctl->info)) { + err = wait_for_event(ctl, 0); + if (snd_ctl_elem_value_compare(initial_val, read_val)) { + if (err < 1) { + ksft_print_msg("No event generated for %s\n", + ctl->name); + ctl->event_missing++; + } + } else { + if (err != 0) { + ksft_print_msg("Spurious event generated for %s\n", + ctl->name); + ctl->event_spurious++; + } + } + } + /* * Use the libray to compare values, if there's a mismatch * carry on and try to provide a more useful diagnostic than @@ -898,6 +1032,18 @@ void test_ctl_write_invalid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } +void test_ctl_event_missing(struct ctl_data *ctl) +{ + ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", + ctl->card->card, ctl->elem); +} + +void test_ctl_event_spurious(struct ctl_data *ctl) +{ + ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", + ctl->card->card, ctl->elem); +} + int main(void) { struct ctl_data *ctl; @@ -917,6 +1063,8 @@ int main(void) test_ctl_write_default(ctl); test_ctl_write_valid(ctl); test_ctl_write_invalid(ctl); + test_ctl_event_missing(ctl); + test_ctl_event_spurious(ctl); } ksft_exit_pass(); -- cgit v1.2.3 From 9d73d1928eb861cb90ddad08724c5ceb83c9a13b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 15:09:02 +0000 Subject: kselftest: alsa: Declare most functions static This program has only one file so most functions can be static. Signed-off-by: Mark Brown Reviewed-by: Shuah Khan Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220202150902.19563-2-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 58 ++++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 6edb7dca32af..d0b788b8d287 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -71,7 +71,8 @@ struct ctl_data *ctl_list = NULL; #endif #ifndef LIB_HAS_LOAD_STRING -int snd_config_load_string(snd_config_t **config, const char *s, size_t size) +static int snd_config_load_string(snd_config_t **config, const char *s, + size_t size) { snd_input_t *input; snd_config_t *dst; @@ -99,7 +100,7 @@ int snd_config_load_string(snd_config_t **config, const char *s, size_t size) } #endif -void find_controls(void) +static void find_controls(void) { char name[32]; int card, ctl, err; @@ -222,7 +223,7 @@ void find_controls(void) * Block for up to timeout ms for an event, returns a negative value * on error, 0 for no event and 1 for an event. */ -int wait_for_event(struct ctl_data *ctl, int timeout) +static int wait_for_event(struct ctl_data *ctl, int timeout) { unsigned short revents; snd_ctl_event_t *event; @@ -291,8 +292,9 @@ int wait_for_event(struct ctl_data *ctl, int timeout) return 1; } -bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, - int index) +static bool ctl_value_index_valid(struct ctl_data *ctl, + snd_ctl_elem_value_t *val, + int index) { long int_val; long long int64_val; @@ -403,7 +405,7 @@ bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, * Check that the provided value meets the constraints for the * provided control. */ -bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) +static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) { int i; bool valid = true; @@ -419,7 +421,7 @@ bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) * Check that we can read the default value and it is valid. Write * tests use the read value to restore the default. */ -void test_ctl_get_value(struct ctl_data *ctl) +static void test_ctl_get_value(struct ctl_data *ctl) { int err; @@ -454,9 +456,9 @@ out: ctl->card->card, ctl->elem); } -bool show_mismatch(struct ctl_data *ctl, int index, - snd_ctl_elem_value_t *read_val, - snd_ctl_elem_value_t *expected_val) +static bool show_mismatch(struct ctl_data *ctl, int index, + snd_ctl_elem_value_t *read_val, + snd_ctl_elem_value_t *expected_val) { long long expected_int, read_int; @@ -519,9 +521,9 @@ bool show_mismatch(struct ctl_data *ctl, int index, * the write to fail, for verifying that invalid writes don't corrupt * anything. */ -int write_and_verify(struct ctl_data *ctl, - snd_ctl_elem_value_t *write_val, - snd_ctl_elem_value_t *expected_val) +static int write_and_verify(struct ctl_data *ctl, + snd_ctl_elem_value_t *write_val, + snd_ctl_elem_value_t *expected_val) { int err, i; bool error_expected, mismatch_shown; @@ -628,7 +630,7 @@ int write_and_verify(struct ctl_data *ctl, * Make sure we can write the default value back to the control, this * should validate that at least some write works. */ -void test_ctl_write_default(struct ctl_data *ctl) +static void test_ctl_write_default(struct ctl_data *ctl) { int err; @@ -661,7 +663,7 @@ void test_ctl_write_default(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -bool test_ctl_write_valid_boolean(struct ctl_data *ctl) +static bool test_ctl_write_valid_boolean(struct ctl_data *ctl) { int err, i, j; bool fail = false; @@ -682,7 +684,7 @@ bool test_ctl_write_valid_boolean(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_integer(struct ctl_data *ctl) +static bool test_ctl_write_valid_integer(struct ctl_data *ctl) { int err; int i; @@ -712,7 +714,7 @@ bool test_ctl_write_valid_integer(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_integer64(struct ctl_data *ctl) +static bool test_ctl_write_valid_integer64(struct ctl_data *ctl) { int err, i; long long j, step; @@ -740,7 +742,7 @@ bool test_ctl_write_valid_integer64(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) +static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) { int err, i, j; bool fail = false; @@ -761,7 +763,7 @@ bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) return !fail; } -void test_ctl_write_valid(struct ctl_data *ctl) +static void test_ctl_write_valid(struct ctl_data *ctl) { bool pass; int err; @@ -814,8 +816,8 @@ void test_ctl_write_valid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -bool test_ctl_write_invalid_value(struct ctl_data *ctl, - snd_ctl_elem_value_t *val) +static bool test_ctl_write_invalid_value(struct ctl_data *ctl, + snd_ctl_elem_value_t *val) { int err; long val_read; @@ -836,7 +838,7 @@ bool test_ctl_write_invalid_value(struct ctl_data *ctl, return !ctl_value_valid(ctl, val); } -bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) +static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) { int err, i; long val_read; @@ -855,7 +857,7 @@ bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_integer(struct ctl_data *ctl) +static bool test_ctl_write_invalid_integer(struct ctl_data *ctl) { int i; bool fail = false; @@ -901,7 +903,7 @@ bool test_ctl_write_invalid_integer(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) +static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) { int i; bool fail = false; @@ -947,7 +949,7 @@ bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) +static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) { int err, i; unsigned int val_read; @@ -979,7 +981,7 @@ bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) } -void test_ctl_write_invalid(struct ctl_data *ctl) +static void test_ctl_write_invalid(struct ctl_data *ctl) { bool pass; int err; @@ -1032,13 +1034,13 @@ void test_ctl_write_invalid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -void test_ctl_event_missing(struct ctl_data *ctl) +static void test_ctl_event_missing(struct ctl_data *ctl) { ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", ctl->card->card, ctl->elem); } -void test_ctl_event_spurious(struct ctl_data *ctl) +static void test_ctl_event_spurious(struct ctl_data *ctl) { ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", ctl->card->card, ctl->elem); -- cgit v1.2.3 From 3db3d859441b93cefe715ac5bd90d02873fe65d3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Feb 2022 14:06:17 +0000 Subject: ALSA: usb-audio: remove redundant assignment to variable c The variable c is being initialized in an outer for-loop and also re-initialized inside an inner for-loop. The first initialization is redundant and can be removed. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20220207140617.341172-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_s1810c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 0255089c9efb..fac4bbc6b275 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -221,7 +221,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) e = 0xbc; for (n = 0; n < 2; n++) { off = n * 18; - for (b = off, c = 0; b < 18 + off; b++) { + for (b = off; b < 18 + off; b++) { /* This channel to all outputs ? */ for (c = 0; c <= 8; c++) { snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); -- cgit v1.2.3 From 8f85b4da579e006547c8cf3660220c54b99451da Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Feb 2022 09:22:35 +0000 Subject: kselftest: alsa: fix spelling mistake "desciptor" -> "descriptor" There are some spelling mistakes in some ksft messages. Fix them. Signed-off-by: Colin Ian King Reviewed-by: Shuah Khan Link: https://lore.kernel.org/r/20220207092235.240284-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index d0b788b8d287..eb2213540fe3 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -198,7 +198,7 @@ static void find_controls(void) err = snd_ctl_poll_descriptors_count(card_data->handle); if (err != 1) { - ksft_exit_fail_msg("Unexpected desciptor count %d for card %d\n", + ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n", err, card); } @@ -248,12 +248,12 @@ static int wait_for_event(struct ctl_data *ctl, int timeout) &(ctl->card->pollfd), 1, &revents); if (err < 0) { - ksft_print_msg("snd_ctl_poll_desciptors_revents() failed for %s: %d\n", + ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n", ctl->name, err); return err; } if (revents & POLLERR) { - ksft_print_msg("snd_ctl_poll_desciptors_revents() reported POLLERR for %s\n", + ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n", ctl->name); return -1; } -- cgit v1.2.3 From 823868f59ff4f1a8889120bc71a216542b79d909 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:28 +0000 Subject: ASoC: dmic: Remove spurious gpiolib select The usage of GPIOs is optional in the code so don't force on gpiolib when building it, avoiding warnings in randconfigs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-2-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d3e5ae8310ef..18159d560c74 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -842,7 +842,6 @@ config SND_SOC_DA9055 config SND_SOC_DMIC tristate "Generic Digital Microphone CODEC" - depends on GPIOLIB help Enable support for the Generic Digital Microphone CODEC. Select this if your sound card has DMICs. -- cgit v1.2.3 From 2cc12ef489a39d22230d4029c9890d27902a855b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:29 +0000 Subject: ASoC: rt9120: Remove spurious gpiolib select The usage of GPIOs is optional in the code so don't force on gpiolib when building it, avoiding warnings in randconfigs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-3-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 18159d560c74..196b5d9b08c3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1348,7 +1348,6 @@ config SND_SOC_RT9120 tristate "Richtek RT9120 Stereo Class-D Amplifier" depends on I2C select REGMAP_I2C - select GPIOLIB help Enable support for Richtek RT9120 20W, stereo, inductor-less, high-efficiency Class-D audio amplifier. -- cgit v1.2.3 From 44bd27c42a1c9a00f1fbcb58301a7f3e6f5cdd0f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:30 +0000 Subject: ASoC: simple-amplifier: Remove spurious gpiolib select The usage of GPIOs is optional in the code so don't force on gpiolib when building it, avoiding warnings in randconfigs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-4-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 196b5d9b08c3..54eb6005f99c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1391,7 +1391,6 @@ config SND_SOC_SIGMADSP_REGMAP config SND_SOC_SIMPLE_AMPLIFIER tristate "Simple Audio Amplifier" - select GPIOLIB config SND_SOC_SIMPLE_MUX tristate "Simple Audio Mux" -- cgit v1.2.3 From c29744876071c3186871515b3f849a884dc47241 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:31 +0000 Subject: ASoC: max9759: Remove spurious gpiolib select The usage of GPIOs is optional in the code so don't force on gpiolib when building it, avoiding warnings in randconfigs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-5-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 54eb6005f99c..cee4330fce93 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1912,7 +1912,7 @@ config SND_SOC_LM4857 config SND_SOC_MAX9759 tristate "Maxim MAX9759 speaker Amplifier" - select GPIOLIB + depends on GPIOLIB config SND_SOC_MAX9768 tristate -- cgit v1.2.3 From 8e70aaae32b72d3088d18a3447b67112b3f5979a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:32 +0000 Subject: ASoC: zl38060: Remove spurious gpiolib select The usage of GPIOs is optional in the code so don't force on gpiolib when building it, avoiding warnings in randconfigs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-6-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cee4330fce93..8a2e1c61f616 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1898,7 +1898,6 @@ config SND_SOC_WSA881X config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER - select GPIOLIB select REGMAP help Support for ZL38060 Connected Home Audio Processor from Microsemi, -- cgit v1.2.3 From 805fff750107bce59f769a54f77205a8c4c37705 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 19:23:33 +0000 Subject: ASoC: simple-mux: Depend on gpiolib rather than selecting it The simple-mux driver requires gpiolib. Currently it selects GPIOLIB but since the use of select can lead to issues with randconfig let's instead depend on GPIOLIB, select is more idiomatically used for Kconfig symbols that are not user selectable but GPIOLIB is user selectable. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220202192333.3655269-7-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8a2e1c61f616..0ef2cfe40723 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1394,7 +1394,7 @@ config SND_SOC_SIMPLE_AMPLIFIER config SND_SOC_SIMPLE_MUX tristate "Simple Audio Mux" - select GPIOLIB + depends on GPIOLIB config SND_SOC_SPDIF tristate "S/PDIF CODEC" -- cgit v1.2.3 From 91e716b2a4f997cafb017c04351c2751fc820637 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 14:36:22 +0200 Subject: ASoC: SOF: intel: hda-trace: Pass the dma buffer pointer to hda_dsp_trace_prepare Pass the snd_dma_buffer pointer as parameter to hda_dsp_trace_prepare() function. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220128123623.23569-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-trace.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 29e3da3c63db..c5dc833b57b8 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -19,16 +19,15 @@ #include "../ops.h" #include "hda.h" -static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_ext_stream *stream = hda->dtrace_stream; struct hdac_stream *hstream = &stream->hstream; - struct snd_dma_buffer *dmab = &sdev->dmatb; int ret; hstream->period_bytes = 0;/* initialize period_bytes */ - hstream->bufsize = sdev->dmatb.bytes; + hstream->bufsize = dmab->bytes; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) @@ -57,7 +56,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev); + ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); -- cgit v1.2.3 From bab05b508ebfde32a14880696a13820d54510fcb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 28 Jan 2022 14:36:23 +0200 Subject: ASoC: SOF: dma-trace: Pass pointer to params_ext struct in trace_init() Instead of passing a pointer to the stream_tag within the struct sof_ipc_dma_trace_params_ext, pass the pointer to the containing struct. AMD needs to update buffer.phy_addr (and don't really use the stream_tag) for the trace implementation. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220128123623.23569-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-trace.c | 38 +++++++++----------------------------- sound/soc/sof/amd/acp.h | 3 ++- sound/soc/sof/intel/hda-trace.c | 10 ++++++---- sound/soc/sof/intel/hda.h | 3 ++- sound/soc/sof/ops.h | 4 ++-- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/trace.c | 2 +- 7 files changed, 23 insertions(+), 39 deletions(-) diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c index fa4da8947186..903b6cc3dda3 100644 --- a/sound/soc/sof/amd/acp-trace.c +++ b/sound/soc/sof/amd/acp-trace.c @@ -34,51 +34,31 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON); -static int acp_sof_trace_prepare(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_params_ext *params) +int acp_sof_trace_init(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct acp_dsp_stream *stream; struct acp_dev_data *adata; int ret; adata = sdev->pdata->hw_pdata; - stream = adata->dtrace_stream; + stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM); + if (!stream) + return -ENODEV; + stream->dmab = &sdev->dmatb; stream->num_pages = NUM_PAGES; ret = acp_dsp_stream_config(sdev, stream); if (ret < 0) { - dev_err(sdev->dev, "Failed to configure trace stream\n"); + acp_dsp_stream_put(sdev, stream); return ret; } - params->buffer.phy_addr = stream->reg_offset; - params->stream_tag = stream->stream_tag; - - return 0; -} - -int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) -{ - struct sof_ipc_dma_trace_params_ext *params; - struct acp_dsp_stream *stream; - struct acp_dev_data *adata; - int ret; - - adata = sdev->pdata->hw_pdata; - stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM); - if (!stream) - return -ENODEV; - adata->dtrace_stream = stream; - params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag); - ret = acp_sof_trace_prepare(sdev, params); - if (ret < 0) { - acp_dsp_stream_put(sdev, stream); - return ret; - } + dtrace_params->stream_tag = stream->stream_tag; + dtrace_params->buffer.phy_addr = stream->reg_offset; - *stream_tag = stream->stream_tag; return 0; } EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index a2f8e4219066..7ceb8bee0d8f 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -210,7 +210,8 @@ extern const struct snd_sof_dsp_ops sof_renoir_ops; int snd_amd_acp_find_config(struct pci_dev *pci); /* Trace */ -int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int acp_sof_trace_init(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *dtrace_params); int acp_sof_trace_release(struct snd_sof_dev *sdev); struct sof_amd_acp_desc { diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index c5dc833b57b8..1791ec045a54 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -36,7 +36,8 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer return ret; } -int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) +int hda_dsp_trace_init(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; int ret; @@ -50,7 +51,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) return -ENODEV; } - *stream_tag = hda->dtrace_stream->hstream.stream_tag; + dtrace_params->stream_tag = hda->dtrace_stream->hstream.stream_tag; /* * initialize capture stream, set BDL address and return corresponding @@ -59,9 +60,10 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); - hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); + hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, + dtrace_params->stream_tag); hda->dtrace_stream = NULL; - *stream_tag = 0; + dtrace_params->stream_tag = 0; } return ret; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 7838a998ea95..2390561906dd 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -670,7 +670,8 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } /* * Trace Control. */ -int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int hda_dsp_trace_init(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *dtrace_params); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index ffe7456e7713..1f84d30296cf 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -369,10 +369,10 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, /* host DMA trace */ static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, - u32 *stream_tag) + struct sof_ipc_dma_trace_params_ext *dtrace_params) { if (sof_ops(sdev)->trace_init) - return sof_ops(sdev)->trace_init(sdev, stream_tag); + return sof_ops(sdev)->trace_init(sdev, dtrace_params); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 29bb56b7267a..e48402ce4bdb 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -281,7 +281,7 @@ struct snd_sof_dsp_ops { /* host DMA trace initialization */ int (*trace_init)(struct snd_sof_dev *sdev, - u32 *stream_tag); /* optional */ + struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */ int (*trace_release)(struct snd_sof_dev *sdev); /* optional */ int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); /* optional */ diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 9b505c4fe794..2335d0f06d42 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -409,7 +409,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) sdev->host_offset = 0; sdev->dtrace_draining = false; - ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); + ret = snd_sof_dma_trace_init(sdev, ¶ms); if (ret < 0) { dev_err(sdev->dev, "error: fail in snd_sof_dma_trace_init %d\n", ret); -- cgit v1.2.3 From 6324cf901e14c6662be508f30485e0f09c54694d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 20 Jan 2022 16:37:41 +0200 Subject: ASoC: SOF: compr: Add compress ops implementation Implement snd_compress_ops. There are a lot of similarities with PCM implementation. For now we use sof_ipc_pcm_params to transfer compress parameters to SOF firmware. This will be changed in the future once we either add new compress parameters to SOF or enhance existing sof_ipc_pcm_params structure to support all native compress params. Note that get_caps and get_codec_caps are missing and will be added later. This is because we need to find a way to advertise DSP capabilities depending on supported platforms. Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20220120143741.492634-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/compress.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 01ca85f0b87f..91a9c95929cd 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -10,6 +10,22 @@ #include "sof-audio.h" #include "sof-priv.h" +static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp, + u64 host_pos, u64 buffer_size) +{ + u64 prev_pos; + unsigned int copied; + + div64_u64_rem(tstamp->copied_total, buffer_size, &prev_pos); + + if (host_pos < prev_pos) + copied = (buffer_size - prev_pos) + host_pos; + else + copied = host_pos - prev_pos; + + tstamp->copied_total += copied; +} + static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work) { struct snd_sof_pcm_stream *sps = @@ -29,14 +45,16 @@ void snd_sof_compr_init_elapsed_work(struct work_struct *work) */ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_compr_runtime *crtd = cstream->runtime; struct snd_soc_component *component; - struct snd_soc_pcm_runtime *rtd; + struct snd_compr_tstamp *tstamp; struct snd_sof_pcm *spcm; if (!cstream) return; - rtd = cstream->private_data; + tstamp = crtd->private_data; component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); spcm = snd_sof_find_spcm_dai(component, rtd); @@ -46,6 +64,257 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) return; } + sof_set_transferred_bytes(tstamp, spcm->stream[cstream->direction].posn.host_posn, + crtd->buffer_size); + /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); } + +static int create_page_table(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + unsigned char *dma_area, size_t size) +{ + struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + int dir = cstream->direction; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + return snd_sof_create_page_table(component->dev, dmab, + spcm->stream[dir].page_table.area, size); +} + +int sof_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_compr_runtime *crtd = cstream->runtime; + struct snd_compr_tstamp *tstamp; + struct snd_sof_pcm *spcm; + int dir; + + tstamp = kzalloc(sizeof(*tstamp), GFP_KERNEL); + if (!tstamp) + return -ENOMEM; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + kfree(tstamp); + return -EINVAL; + } + + dir = cstream->direction; + + if (spcm->stream[dir].cstream) { + kfree(tstamp); + return -EBUSY; + } + + spcm->stream[dir].cstream = cstream; + spcm->stream[dir].posn.host_posn = 0; + spcm->stream[dir].posn.dai_posn = 0; + spcm->prepared[dir] = false; + + crtd->private_data = tstamp; + + return 0; +} + +int sof_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_compr_tstamp *tstamp = cstream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + int ret = 0; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[cstream->direction].comp_id; + + if (spcm->prepared[cstream->direction]) { + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, + &stream, sizeof(stream), + &reply, sizeof(reply)); + if (!ret) + spcm->prepared[cstream->direction] = false; + } + + cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work); + spcm->stream[cstream->direction].cstream = NULL; + kfree(tstamp); + + return ret; +} + +int sof_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_compr_params *params) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_compr_runtime *crtd = cstream->runtime; + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct snd_compr_tstamp *tstamp; + struct sof_ipc_pcm_params pcm; + struct snd_sof_pcm *spcm; + int ret; + + tstamp = crtd->private_data; + + spcm = snd_sof_find_spcm_dai(component, rtd); + + if (!spcm) + return -EINVAL; + + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = sdev->dev; + ret = snd_compr_malloc_pages(cstream, crtd->buffer_size); + if (ret < 0) + return ret; + + ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes); + if (ret < 0) + return ret; + + memset(&pcm, 0, sizeof(pcm)); + + pcm.params.buffer.pages = PFN_UP(crtd->dma_bytes); + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + + pcm.comp_id = spcm->stream[cstream->direction].comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.buffer.phy_addr = spcm->stream[cstream->direction].page_table.addr; + pcm.params.buffer.size = crtd->dma_bytes; + pcm.params.direction = cstream->direction; + pcm.params.channels = params->codec.ch_out; + pcm.params.rate = params->codec.sample_rate; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + pcm.params.sample_container_bytes = + snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3; + pcm.params.host_period_bytes = params->buffer.fragment_size; + + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + if (ret < 0) { + dev_err(component->dev, "error ipc failed\n"); + return ret; + } + + tstamp->byte_offset = sdev->stream_box.offset + ipc_params_reply.posn_offset; + tstamp->sampling_rate = params->codec.sample_rate; + + spcm->prepared[cstream->direction] = true; + + return 0; +} + +int sof_compr_get_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_codec *params) +{ + /* TODO: we don't query the supported codecs for now, if the + * application asks for an unsupported codec the set_params() will fail. + */ + return 0; +} + +int sof_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[cstream->direction].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + default: + dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd); + break; + } + + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, + &stream, sizeof(stream), + &reply, sizeof(reply)); +} + +int sof_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + unsigned int offset, n; + void *ptr; + int ret; + + if (count > rtd->buffer_size) + count = rtd->buffer_size; + + div_u64_rem(rtd->total_bytes_available, rtd->buffer_size, &offset); + ptr = rtd->dma_area + offset; + n = rtd->buffer_size - offset; + + if (count < n) { + ret = copy_from_user(ptr, buf, count); + } else { + ret = copy_from_user(ptr, buf, n); + ret += copy_from_user(rtd->dma_area, buf + n, count - n); + } + + return count - ret; +} + +static int sof_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_compr_tstamp *pstamp = cstream->runtime->private_data; + + tstamp->sampling_rate = pstamp->sampling_rate; + tstamp->copied_total = pstamp->copied_total; + + return 0; +} + +struct snd_compress_ops sof_compressed_ops = { + .open = sof_compr_open, + .free = sof_compr_free, + .set_params = sof_compr_set_params, + .get_params = sof_compr_get_params, + .trigger = sof_compr_trigger, + .pointer = sof_compr_pointer, + .copy = sof_compr_copy, +}; +EXPORT_SYMBOL(sof_compressed_ops); -- cgit v1.2.3 From eba0f007751986ee401f2a1bcbdd3bdc845cb606 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 11 Jan 2022 09:15:18 +0100 Subject: ASoC: fsl_sai: Enable combine mode soft The fsl_sai driver calculates the number of pins used and enables multiple channels if necessary. This means the SAI expects data in one FIFO per pin. The SDMA engine only services a single FIFO, so multi pin support doesn't work at all. This patch enables the software combine mode in chips that support it. With this the SAI presents only a single FIFO to the outside and distributes the data into the different FIFOs internally. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220111081518.982437-1-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 9 +++++++++ sound/soc/fsl/fsl_sai.h | 1 + 2 files changed, 10 insertions(+) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 10544fa27dc0..cab015d96889 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -517,6 +517,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, FSL_SAI_CR5_FBT_MASK, val_cr5); } + if (sai->soc_data->pins > 1) + regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), + FSL_SAI_CR4_FCOMB_MASK, FSL_SAI_CR4_FCOMB_SOFT); + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, FSL_SAI_CR3_TRCE((1 << pins) - 1)); @@ -1195,6 +1199,7 @@ static const struct fsl_sai_soc_data fsl_sai_vf610_data = { .use_imx_pcm = false, .use_edma = false, .fifo_depth = 32, + .pins = 1, .reg_offset = 0, .mclk0_is_mclk1 = false, .flags = 0, @@ -1204,6 +1209,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { .use_imx_pcm = true, .use_edma = false, .fifo_depth = 32, + .pins = 1, .reg_offset = 0, .mclk0_is_mclk1 = true, .flags = 0, @@ -1213,6 +1219,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { .use_imx_pcm = true, .use_edma = false, .fifo_depth = 16, + .pins = 2, .reg_offset = 8, .mclk0_is_mclk1 = false, .flags = PMQOS_CPU_LATENCY, @@ -1222,6 +1229,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { .use_imx_pcm = true, .use_edma = false, .fifo_depth = 128, + .pins = 8, .reg_offset = 8, .mclk0_is_mclk1 = false, .flags = 0, @@ -1231,6 +1239,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { .use_imx_pcm = true, .use_edma = true, .fifo_depth = 64, + .pins = 1, .reg_offset = 0, .mclk0_is_mclk1 = false, .flags = 0, diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 9aaf231bc024..410f6e6a9137 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -223,6 +223,7 @@ struct fsl_sai_soc_data { bool use_edma; bool mclk0_is_mclk1; unsigned int fifo_depth; + unsigned int pins; unsigned int reg_offset; unsigned int flags; }; -- cgit v1.2.3 From 69458e2c27800da7697c87ed908b65323ef3f3bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Feb 2022 09:19:12 +0100 Subject: ALSA: hda: Fix driver index handling at re-binding HD-audio driver handles the multiple instances and keeps the static index that is incremented at each probe. This becomes a problem when user tries to re-bind the device via sysfs multiple times; as the device index isn't cleared unlike rmmod case, it points to the next element at re-binding, and eventually later you can't probe any more when it reaches to SNDRV_CARDS_MAX (usually 32). This patch is an attempt to improve the handling at rebinding. Instead of a static device index, now we keep a bitmap and assigns to the first zero bit position. At the driver remove, in return, the bitmap slot is cleared again, so that it'll be available for the next probe. Reported-by: Alexander Sergeyev Link: https://lore.kernel.org/r/20220209081912.20687-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4b0338c4c543..a2922233e85f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2064,14 +2064,16 @@ static const struct hda_controller_ops pci_hda_ops = { .position_check = azx_position_check, }; +static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); + static int azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { - static int dev; struct snd_card *card; struct hda_intel *hda; struct azx *chip; bool schedule_probe; + int dev; int err; if (pci_match_id(driver_denylist, pci)) { @@ -2079,10 +2081,11 @@ static int azx_probe(struct pci_dev *pci, return -ENODEV; } + dev = find_first_zero_bit(probed_devs, SNDRV_CARDS); if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { - dev++; + set_bit(dev, probed_devs); return -ENOENT; } @@ -2149,7 +2152,7 @@ static int azx_probe(struct pci_dev *pci, if (schedule_probe) schedule_delayed_work(&hda->probe_work, 0); - dev++; + set_bit(dev, probed_devs); if (chip->disabled) complete_all(&hda->probe_wait); return 0; @@ -2372,6 +2375,7 @@ static void azx_remove(struct pci_dev *pci) cancel_delayed_work_sync(&hda->probe_work); device_lock(&pci->dev); + clear_bit(chip->dev_index, probed_devs); pci_set_drvdata(pci, NULL); snd_card_free(card); } -- cgit v1.2.3 From 7d88b9608142f95ccdd3dfb190da4a5faddb1cc7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 9 Feb 2022 08:31:04 +0200 Subject: ASoC: SOF: Intel: hdac_ext_stream: consistent prefixes for variables/members The existing code maximizes confusion by using 'stream' and 'hstream' variables of different types, e.g: struct hdac_stream *stream; struct hdac_ext_stream *stream; struct hdac_stream *hstream; struct hdac_ext_stream *hstream; This confusion is partly inherited from legacy code but SOF contributors added their own creative spin, e.g. struct hdac_ext_stream *link_dev; struct hdac_ext_stream *dsp_stream; struct hdac_ext_stream hda_stream; and my personal favorite: stream = &hda_stream->hda_stream; This patch suggests a consistent naming across all Intel code related to HDAudio stream management. The convention is - by hierarchical order: struct sof_intel_hda_stream *hda_stream; struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; No functionality change - just renaming of variables/members. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220209063104.9971-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 92 ++++++++++++++--------------- sound/soc/sof/intel/hda-dsp.c | 14 ++--- sound/soc/sof/intel/hda-ipc.c | 10 ++-- sound/soc/sof/intel/hda-loader.c | 50 ++++++++-------- sound/soc/sof/intel/hda-pcm.c | 12 ++-- sound/soc/sof/intel/hda-probes.c | 34 +++++------ sound/soc/sof/intel/hda-stream.c | 122 +++++++++++++++++++-------------------- sound/soc/sof/intel/hda-trace.c | 6 +- sound/soc/sof/intel/hda.h | 17 +++--- 9 files changed, 179 insertions(+), 178 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 28a54145c150..af0c85e4e299 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -62,7 +62,7 @@ static struct hdac_ext_stream * const struct sof_intel_dsp_desc *chip; struct snd_sof_dev *sdev; struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; int stream_dir = substream->stream; @@ -72,39 +72,39 @@ static struct hdac_ext_stream * } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = - stream_to_hdac_ext_stream(stream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = + stream_to_hdac_ext_stream(hstream); + if (hstream->direction != substream->stream) continue; - hda_stream = hstream_to_sof_hda_stream(hstream); + hda_stream = hstream_to_sof_hda_stream(hext_stream); sdev = hda_stream->sdev; chip = get_chip_info(sdev->pdata); /* check if link is available */ - if (!hstream->link_locked) { + if (!hext_stream->link_locked) { /* * choose the first available link for platforms that do not have the * PROCEN_FMT_QUIRK set. */ if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) { - res = hstream; + res = hext_stream; break; } - if (stream->opened) { + if (hstream->opened) { /* * check if the stream tag matches the stream * tag of one of the connected FEs */ if (hda_check_fes(rtd, stream_dir, - stream->stream_tag)) { - res = hstream; + hstream->stream_tag)) { + res = hext_stream; break; } } else { - res = hstream; + res = hext_stream; /* * This must be a hostless stream. @@ -132,17 +132,17 @@ static struct hdac_ext_stream * return res; } -static int hda_link_dma_params(struct hdac_ext_stream *stream, +static int hda_link_dma_params(struct hdac_ext_stream *hext_stream, struct hda_pipe_params *params) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; unsigned char stream_tag = hstream->stream_tag; struct hdac_bus *bus = hstream->bus; struct hdac_ext_link *link; unsigned int format_val; - snd_hdac_ext_stream_decouple(bus, stream, true); - snd_hdac_ext_link_stream_reset(stream); + snd_hdac_ext_stream_decouple(bus, hext_stream, true); + snd_hdac_ext_link_stream_reset(hext_stream); format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, params->format, @@ -151,9 +151,9 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream, dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", format_val, params->s_freq, params->ch, params->format); - snd_hdac_ext_link_stream_setup(stream, format_val); + snd_hdac_ext_link_stream_setup(hext_stream, format_val); - if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { list_for_each_entry(link, &bus->hlink_list, list) { if (link->index == params->link_index) snd_hdac_ext_link_set_stream_id(link, @@ -161,7 +161,7 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream, } } - stream->link_prepared = 1; + hext_stream->link_prepared = 1; return 0; } @@ -218,7 +218,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, { struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_bus *bus = hstream->bus; - struct hdac_ext_stream *link_dev; + struct hdac_ext_stream *hext_stream; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct sof_intel_hda_stream *hda_stream; @@ -229,18 +229,18 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, int ret; /* get stored dma data if resuming from system suspend */ - link_dev = snd_soc_dai_get_dma_data(dai, substream); - if (!link_dev) { - link_dev = hda_link_stream_assign(bus, substream); - if (!link_dev) + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream) { + hext_stream = hda_link_stream_assign(bus, substream); + if (!hext_stream) return -EBUSY; - snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); + snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream); } - stream_tag = hdac_stream(link_dev)->stream_tag; + stream_tag = hdac_stream(hext_stream)->stream_tag; - hda_stream = hstream_to_sof_hda_stream(link_dev); + hda_stream = hstream_to_sof_hda_stream(hext_stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -257,7 +257,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, return -EINVAL; /* set the hdac_stream in the codec dai */ - snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); + snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -271,20 +271,20 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, else p_params.link_bps = codec_dai->driver->capture.sig_bits; - return hda_link_dma_params(link_dev, &p_params); + return hda_link_dma_params(hext_stream, &p_params); } static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_stream *link_dev = + struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); int stream = substream->stream; - if (link_dev->link_prepared) + if (hext_stream->link_prepared) return 0; dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); @@ -326,7 +326,7 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct hdac_ext_stream *link_dev = + struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream); struct sof_intel_hda_stream *hda_stream; struct snd_soc_pcm_runtime *rtd; @@ -345,7 +345,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - hda_stream = hstream_to_sof_hda_stream(link_dev); + hda_stream = hstream_to_sof_hda_stream(hext_stream); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); @@ -354,11 +354,11 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_hdac_ext_link_stream_start(link_dev); + snd_hdac_ext_link_stream_start(hext_stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - snd_hdac_ext_link_stream_clear(link_dev); + snd_hdac_ext_link_stream_clear(hext_stream); /* * free DAI widget during stop/suspend to keep widget use_count's balanced. @@ -368,14 +368,14 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_tag = hdac_stream(link_dev)->stream_tag; + stream_tag = hdac_stream(hext_stream)->stream_tag; snd_hdac_ext_link_clear_stream_id(link, stream_tag); } - link_dev->link_prepared = 0; + hext_stream->link_prepared = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_hdac_ext_link_stream_clear(link_dev); + snd_hdac_ext_link_stream_clear(hext_stream); ret = hda_link_dai_config_pause_push_ipc(w); if (ret < 0) @@ -396,22 +396,22 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, struct hdac_ext_link *link; struct hdac_stream *hstream; struct snd_soc_pcm_runtime *rtd; - struct hdac_ext_stream *link_dev; + struct hdac_ext_stream *hext_stream; struct snd_soc_dapm_widget *w; int ret; hstream = substream->runtime->private_data; bus = hstream->bus; rtd = asoc_substream_to_rtd(substream); - link_dev = snd_soc_dai_get_dma_data(dai, substream); + hext_stream = snd_soc_dai_get_dma_data(dai, substream); - if (!link_dev) { + if (!hext_stream) { dev_dbg(dai->dev, - "%s: link_dev is not assigned\n", __func__); + "%s: hext_stream is not assigned\n", __func__); return -EINVAL; } - hda_stream = hstream_to_sof_hda_stream(link_dev); + hda_stream = hstream_to_sof_hda_stream(hext_stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -428,13 +428,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_tag = hdac_stream(link_dev)->stream_tag; + stream_tag = hdac_stream(hext_stream)->stream_tag; snd_hdac_ext_link_clear_stream_id(link, stream_tag); } snd_soc_dai_set_dma_data(dai, substream, NULL); - snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); - link_dev->link_prepared = 0; + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); + hext_stream->link_prepared = 0; /* free the host DMA channel reserved by hostless streams */ hda_stream->host_reserved = 0; diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 916a257ea96b..0fe522549c91 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -904,7 +904,7 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); struct snd_soc_pcm_runtime *rtd; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; struct hdac_ext_link *link; struct hdac_stream *s; const char *name; @@ -912,7 +912,7 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) /* set internal flag for BE */ list_for_each_entry(s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); + hext_stream = stream_to_hdac_ext_stream(s); /* * clear stream. This should already be taken care for running @@ -920,20 +920,20 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) * streams do not get suspended, so this needs to be done * explicitly during suspend. */ - if (stream->link_substream) { - rtd = asoc_substream_to_rtd(stream->link_substream); + if (hext_stream->link_substream) { + rtd = asoc_substream_to_rtd(hext_stream->link_substream); name = asoc_rtd_to_codec(rtd, 0)->component->name; link = snd_hdac_ext_bus_get_link(bus, name); if (!link) return -EINVAL; - stream->link_prepared = 0; + hext_stream->link_prepared = 0; - if (hdac_stream(stream)->direction == + if (hdac_stream(hext_stream)->direction == SNDRV_PCM_STREAM_CAPTURE) continue; - stream_tag = hdac_stream(stream)->stream_tag; + stream_tag = hdac_stream(hext_stream)->stream_tag; snd_hdac_ext_link_clear_stream_id(link, stream_tag); } } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index f0cf8019d72d..a8c452144168 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -255,13 +255,13 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev, hda_stream = container_of(hstream, struct sof_intel_hda_stream, - hda_stream.hstream); + hext_stream.hstream); /* The stream might already be closed */ if (!hstream) return -ESTRPIPE; - sof_mailbox_read(sdev, hda_stream->stream.posn_offset, p, sz); + sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz); } return 0; @@ -277,17 +277,17 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev, size_t posn_offset = reply->posn_offset; hda_stream = container_of(hstream, struct sof_intel_hda_stream, - hda_stream.hstream); + hext_stream.hstream); /* check for unaligned offset or overflow */ if (posn_offset > sdev->stream_box.size || posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) return -EINVAL; - hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset; + hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset; dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", - substream->stream, hda_stream->stream.posn_offset); + substream->stream, hda_stream->sof_intel_stream.posn_offset); return 0; } diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 6af3325b7e40..3c0d7cbdf174 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -47,18 +47,18 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig unsigned int size, struct snd_dma_buffer *dmab, int direction) { - struct hdac_ext_stream *dsp_stream; + struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct pci_dev *pci = to_pci_dev(sdev->dev); int ret; - dsp_stream = hda_dsp_stream_get(sdev, direction, 0); + hext_stream = hda_dsp_stream_get(sdev, direction, 0); - if (!dsp_stream) { + if (!hext_stream) { dev_err(sdev->dev, "error: no stream available\n"); return ERR_PTR(-ENODEV); } - hstream = &dsp_stream->hstream; + hstream = &hext_stream->hstream; hstream->substream = NULL; /* allocate DMA buffer */ @@ -73,21 +73,21 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig hstream->bufsize = size; if (direction == SNDRV_PCM_STREAM_CAPTURE) { - ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL); + ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); if (ret < 0) { dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); goto error; } } else { - ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); goto error; } - hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); } - return dsp_stream; + return hext_stream; error: hda_dsp_stream_put(sdev, direction, hstream->stream_tag); @@ -209,9 +209,9 @@ err: } static int cl_trigger(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, int cmd) + struct hdac_ext_stream *hext_stream, int cmd) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); /* code loader is special case that reuses stream ops */ @@ -231,19 +231,19 @@ static int cl_trigger(struct snd_sof_dev *sdev, hstream->running = true; return 0; default: - return hda_dsp_stream_trigger(sdev, stream, cmd); + return hda_dsp_stream_trigger(sdev, hext_stream, cmd); } } static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_ext_stream *stream) + struct hdac_ext_stream *hext_stream) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret = 0; if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); else snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_SD_CTL_DMA_START, 0); @@ -267,12 +267,12 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, return ret; } -static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) +static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) { unsigned int reg; int ret, status; - ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START); + ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); if (ret < 0) { dev_err(sdev->dev, "error: DMA trigger start failed\n"); return ret; @@ -296,7 +296,7 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) __func__); } - ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); + ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); if (ret < 0) { dev_err(sdev->dev, "error: DMA trigger stop failed\n"); if (!status) @@ -392,7 +392,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) struct snd_sof_pdata *plat_data = sdev->pdata; const struct sof_dev_desc *desc = plat_data->desc; const struct sof_intel_dsp_desc *chip_info; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; struct firmware stripped_firmware; int ret, ret1, i; @@ -417,11 +417,11 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); /* prepare DMA for code loader stream */ - stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, - &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); - if (IS_ERR(stream)) { + hext_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); + if (IS_ERR(hext_stream)) { dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); - return PTR_ERR(stream); + return PTR_ERR(hext_stream); } memcpy(sdev->dmab.area, stripped_firmware.data, @@ -433,7 +433,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) "Attempting iteration %d of Core En/ROM load...\n", i); hda->boot_iteration = i + 1; - ret = cl_dsp_init(sdev, stream->hstream.stream_tag); + ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag); /* don't retry anymore if successful */ if (!ret) @@ -472,7 +472,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) * Continue with code loading and firmware boot */ hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; - ret = cl_copy_fw(sdev, stream); + ret = cl_copy_fw(sdev, hext_stream); if (!ret) dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); else @@ -485,7 +485,7 @@ cleanup: * This should be done even if firmware loading fails. * If the cleanup also fails, we return the initial error */ - ret1 = cl_cleanup(sdev, &sdev->dmab, stream); + ret1 = cl_cleanup(sdev, &sdev->dmab, hext_stream); if (ret1 < 0) { dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index d78aa5d8552d..eec83ca557a1 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -96,7 +96,7 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct sof_ipc_stream_params *ipc_params) { struct hdac_stream *hstream = substream->runtime->private_data; - struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_dma_buffer *dmab; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; @@ -118,7 +118,7 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, params); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); return ret; @@ -126,9 +126,9 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* enable SPIB when rewinds are disabled */ if (hda_disable_rewinds) - hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0); + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, 0); else - hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); /* update no_stream_position flag for ipc params */ if (hda && hda->no_ipc_position) { @@ -174,9 +174,9 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd) { struct hdac_stream *hstream = substream->runtime->private_data; - struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); - return hda_dsp_stream_trigger(sdev, stream, cmd); + return hda_dsp_stream_trigger(sdev, hext_stream, cmd); } snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/hda-probes.c b/sound/soc/sof/intel/hda-probes.c index fe2f3f7d236b..35e548da3609 100644 --- a/sound/soc/sof/intel/hda-probes.c +++ b/sound/soc/sof/intel/hda-probes.c @@ -23,34 +23,34 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, struct snd_soc_dai *dai) { - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; - stream = hda_dsp_stream_get(sdev, cstream->direction, 0); - if (!stream) + hext_stream = hda_dsp_stream_get(sdev, cstream->direction, 0); + if (!hext_stream) return -EBUSY; - hdac_stream(stream)->curr_pos = 0; - hdac_stream(stream)->cstream = cstream; - cstream->runtime->private_data = stream; + hdac_stream(hext_stream)->curr_pos = 0; + hdac_stream(hext_stream)->cstream = cstream; + cstream->runtime->private_data = hext_stream; - return hdac_stream(stream)->stream_tag; + return hdac_stream(hext_stream)->stream_tag; } int hda_probe_compr_free(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, struct snd_soc_dai *dai) { - struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); int ret; ret = hda_dsp_stream_put(sdev, cstream->direction, - hdac_stream(stream)->stream_tag); + hdac_stream(hext_stream)->stream_tag); if (ret < 0) { dev_dbg(sdev->dev, "stream put failed: %d\n", ret); return ret; } - hdac_stream(stream)->cstream = NULL; + hdac_stream(hext_stream)->cstream = NULL; cstream->runtime->private_data = NULL; return 0; @@ -61,8 +61,8 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev, struct snd_compr_params *params, struct snd_soc_dai *dai) { - struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); - struct hdac_stream *hstream = hdac_stream(stream); + struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); + struct hdac_stream *hstream = hdac_stream(hext_stream); struct snd_dma_buffer *dmab; u32 bits, rate; int bps, ret; @@ -80,7 +80,7 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev, hstream->period_bytes = cstream->runtime->fragment_size; hstream->no_period_wakeup = 0; - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); return ret; @@ -93,9 +93,9 @@ int hda_probe_compr_trigger(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, int cmd, struct snd_soc_dai *dai) { - struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); - return hda_dsp_stream_trigger(sdev, stream, cmd); + return hda_dsp_stream_trigger(sdev, hext_stream, cmd); } int hda_probe_compr_pointer(struct snd_sof_dev *sdev, @@ -103,11 +103,11 @@ int hda_probe_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) { - struct hdac_ext_stream *stream = hda_compr_get_stream(cstream); + struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); struct snd_soc_pcm_stream *pstream; pstream = &dai->driver->capture; - tstamp->copied_total = hdac_stream(stream)->curr_pos; + tstamp->copied_total = hdac_stream(hext_stream)->curr_pos; tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); return 0; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index ba60807fbd8f..daeb64c495e4 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -57,7 +57,7 @@ static char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream) */ static int hda_setup_bdle(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream, + struct hdac_stream *hstream, struct sof_intel_dsp_bdl **bdlp, int offset, int size, int ioc) { @@ -68,7 +68,7 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev, dma_addr_t addr; int chunk; - if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { + if (hstream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { dev_err(sdev->dev, "error: stream frags exceeded\n"); return -EINVAL; } @@ -91,11 +91,11 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev, size -= chunk; bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01); bdl++; - stream->frags++; + hstream->frags++; offset += chunk; dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n", - stream->frags, chunk); + hstream->frags, chunk); } *bdlp = bdl; @@ -108,47 +108,47 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev, */ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream) + struct hdac_stream *hstream) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct sof_intel_dsp_bdl *bdl; int i, offset, period_bytes, periods; int remain, ioc; - period_bytes = stream->period_bytes; + period_bytes = hstream->period_bytes; dev_dbg(sdev->dev, "%s: period_bytes:0x%x\n", __func__, period_bytes); if (!period_bytes) - period_bytes = stream->bufsize; + period_bytes = hstream->bufsize; - periods = stream->bufsize / period_bytes; + periods = hstream->bufsize / period_bytes; dev_dbg(sdev->dev, "%s: periods:%d\n", __func__, periods); - remain = stream->bufsize % period_bytes; + remain = hstream->bufsize % period_bytes; if (remain) periods++; /* program the initial BDL entries */ - bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; + bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area; offset = 0; - stream->frags = 0; + hstream->frags = 0; /* * set IOC if don't use position IPC * and period_wakeup needed. */ ioc = hda->no_ipc_position ? - !stream->no_period_wakeup : 0; + !hstream->no_period_wakeup : 0; for (i = 0; i < periods; i++) { if (i == (periods - 1) && remain) /* set the last small entry */ offset = hda_setup_bdle(sdev, dmab, - stream, &bdl, offset, + hstream, &bdl, offset, remain, 0); else offset = hda_setup_bdle(sdev, dmab, - stream, &bdl, offset, + hstream, &bdl, offset, period_bytes, ioc); } @@ -156,10 +156,10 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, } int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, int enable, u32 size) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; u32 mask; if (!sdev->bar[HDA_DSP_SPIB_BAR]) { @@ -175,7 +175,7 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, enable << hstream->index); /* set the SPIB value */ - sof_io_write(sdev, stream->spib_addr, size); + sof_io_write(sdev, hext_stream->spib_addr, size); return 0; } @@ -186,7 +186,7 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) { struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *hda_stream; - struct hdac_ext_stream *stream = NULL; + struct hdac_ext_stream *hext_stream = NULL; struct hdac_stream *s; spin_lock_irq(&bus->reg_lock); @@ -194,10 +194,10 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) /* get an unused stream */ list_for_each_entry(s, &bus->stream_list, list) { if (s->direction == direction && !s->opened) { - stream = stream_to_hdac_ext_stream(s); - hda_stream = container_of(stream, + hext_stream = stream_to_hdac_ext_stream(s); + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, - hda_stream); + hext_stream); /* check if the host DMA channel is reserved */ if (hda_stream->host_reserved) continue; @@ -210,11 +210,11 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) spin_unlock_irq(&bus->reg_lock); /* stream found ? */ - if (!stream) { + if (!hext_stream) { dev_err(sdev->dev, "error: no free %s streams\n", direction == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); - return stream; + return hext_stream; } hda_stream->flags = flags; @@ -229,7 +229,7 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) HDA_VS_INTEL_EM2, HDA_VS_INTEL_EM2_L1SEN, 0); - return stream; + return hext_stream; } /* free a stream */ @@ -237,7 +237,7 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) { struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *hda_stream; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; struct hdac_stream *s; bool dmi_l1_enable = true; bool found = false; @@ -249,8 +249,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) * that are DMI L1 incompatible. */ list_for_each_entry(s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); - hda_stream = container_of(stream, struct sof_intel_hda_stream, hda_stream); + hext_stream = stream_to_hdac_ext_stream(s); + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, hext_stream); if (!s->opened) continue; @@ -319,9 +319,9 @@ static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hs } int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, int cmd) + struct hdac_ext_stream *hext_stream, int cmd) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); u32 dma_start = SOF_HDA_SD_CTL_DMA_START; int ret = 0; @@ -396,17 +396,17 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, } /* minimal recommended programming for ICCMAX stream */ -int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret; u32 mask = 0x1 << hstream->index; - if (!stream) { + if (!hext_stream) { dev_err(sdev->dev, "error: no stream available\n"); return -ENODEV; } @@ -467,20 +467,20 @@ int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_st * and normal stream. */ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; u32 mask; u32 run; - if (!stream) { + if (!hext_stream) { dev_err(sdev->dev, "error: no stream available\n"); return -ENODEV; } @@ -659,28 +659,28 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { - struct hdac_stream *stream = substream->runtime->private_data; - struct hdac_ext_stream *link_dev = container_of(stream, - struct hdac_ext_stream, - hstream); + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); struct hdac_bus *bus = sof_to_bus(sdev); - u32 mask = 0x1 << stream->index; + u32 mask = 0x1 << hstream->index; int ret; - ret = hda_dsp_stream_reset(sdev, stream); + ret = hda_dsp_stream_reset(sdev, hstream); if (ret < 0) return ret; spin_lock_irq(&bus->reg_lock); /* couple host and link DMA if link DMA channel is idle */ - if (!link_dev->link_locked) + if (!hext_stream->link_locked) snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0); spin_unlock_irq(&bus->reg_lock); - hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0); + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - stream->substream = NULL; + hstream->substream = NULL; return 0; } @@ -808,7 +808,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) int hda_dsp_stream_init(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct pci_dev *pci = to_pci_dev(sdev->dev); struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); @@ -872,27 +872,27 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) hda_stream->sdev = sdev; - stream = &hda_stream->hda_stream; + hext_stream = &hda_stream->hext_stream; - stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; - stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + SOF_HDA_PPLC_INTERVAL * i; /* do we support SPIB */ if (sdev->bar[HDA_DSP_SPIB_BAR]) { - stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + hext_stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + SOF_HDA_SPIB_SPIB; - stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + hext_stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + SOF_HDA_SPIB_MAXFIFO; } - hstream = &stream->hstream; + hstream = &hext_stream->hstream; hstream->bus = bus; hstream->sd_int_sta_mask = 1 << i; hstream->index = i; @@ -927,28 +927,28 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) hda_stream->sdev = sdev; - stream = &hda_stream->hda_stream; + hext_stream = &hda_stream->hext_stream; /* we always have DSP support */ - stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; - stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + SOF_HDA_PPLC_INTERVAL * i; /* do we support SPIB */ if (sdev->bar[HDA_DSP_SPIB_BAR]) { - stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + hext_stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + SOF_HDA_SPIB_SPIB; - stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + hext_stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + SOF_HDA_SPIB_MAXFIFO; } - hstream = &stream->hstream; + hstream = &hext_stream->hstream; hstream->bus = bus; hstream->sd_int_sta_mask = 1 << i; hstream->index = i; @@ -983,7 +983,7 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *s, *_s; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; struct sof_intel_hda_stream *hda_stream; /* free position buffer */ @@ -1003,9 +1003,9 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev) if (s->bdl.area) snd_dma_free_pages(&s->bdl); list_del(&s->list); - stream = stream_to_hdac_ext_stream(s); - hda_stream = container_of(stream, struct sof_intel_hda_stream, - hda_stream); + hext_stream = stream_to_hdac_ext_stream(s); + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, + hext_stream); devm_kfree(sdev->dev, hda_stream); } } diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 1791ec045a54..755ef1d835e0 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -22,14 +22,14 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - struct hdac_ext_stream *stream = hda->dtrace_stream; - struct hdac_stream *hstream = &stream->hstream; + struct hdac_ext_stream *hext_stream = hda->dtrace_stream; + struct hdac_stream *hstream = &hext_stream->hstream; int ret; hstream->period_bytes = 0;/* initialize period_bytes */ hstream->bufsize = dmab->bytes; - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); if (ret < 0) dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2390561906dd..324418582044 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -471,14 +471,14 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s) struct sof_intel_hda_stream { struct snd_sof_dev *sdev; - struct hdac_ext_stream hda_stream; - struct sof_intel_stream stream; + struct hdac_ext_stream hext_stream; + struct sof_intel_stream sof_intel_stream; int host_reserved; /* reserve host DMA channel */ u32 flags; }; #define hstream_to_sof_hda_stream(hstream) \ - container_of(hstream, struct sof_intel_hda_stream, hda_stream) + container_of(hstream, struct sof_intel_hda_stream, hext_stream) #define bus_to_sof_hda(bus) \ container_of(bus, struct sof_intel_hda_dev, hbus.core) @@ -545,18 +545,19 @@ int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substrea int hda_dsp_stream_init(struct snd_sof_dev *sdev); void hda_dsp_stream_free(struct snd_sof_dev *sdev); int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params); -int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, + struct hdac_ext_stream *hext_stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, int cmd); + struct hdac_ext_stream *hext_stream, int cmd); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream); + struct hdac_stream *hstream); bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev); bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); @@ -564,7 +565,7 @@ struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, int enable, u32 size); int hda_ipc_msg_data(struct snd_sof_dev *sdev, -- cgit v1.2.3 From 8be90641a0bbd9a3606547aa6a0f70b020e74c8f Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Wed, 9 Feb 2022 12:00:08 +0530 Subject: ASoC: dt-bindings: davinci-mcasp: convert McASP bindings to yaml schema Convert the bindings for McASP controllers for TI SoCs from txt to YAML schema. Adds additional properties 'clocks', 'clock-names', 'power-domains', '#sound-dai-cells' and 'port' which were missing from the txt file. Removes properties 'sram-size-playback' and 'sram-size-capture' since they are not used. Adds 'dmas' and 'dma-names' in the example which were missing from the txt file. Changes 'interrupts' and 'interrupt-names' from optional to required properties. Changes 'op-modes', 'serial-dir' to optional properties as they are not needed if the McASP is used only as GPIO. Changes 'tdm-slots' to required property only for I2S operation mode. Adds the yaml file in the 'MAINTAINERS' under the heading 'TEXAS INSTRUMENTS ASoC DRIVERS' Signed-off-by: Jayesh Choudhary Reviewed-by: Rob Herring Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220209063008.2928-1-j-choudhary@ti.com Signed-off-by: Mark Brown --- .../bindings/sound/davinci-mcasp-audio.txt | 86 --------- .../bindings/sound/davinci-mcasp-audio.yaml | 201 +++++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 202 insertions(+), 86 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt create mode 100644 Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt deleted file mode 100644 index bd863bd69501..000000000000 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ /dev/null @@ -1,86 +0,0 @@ -Texas Instruments McASP controller - -Required properties: -- compatible : - "ti,dm646x-mcasp-audio" : for DM646x platforms - "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms - "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, AM43xx, TI81xx) - "ti,dra7-mcasp-audio" : for DRA7xx platforms - "ti,omap4-mcasp-audio" : for OMAP4 - -- reg : Should contain reg specifiers for the entries in the reg-names property. -- reg-names : Should contain: - * "mpu" for the main registers (required). For compatibility with - existing software, it is recommended this is the first entry. - * "dat" for separate data port register access (optional). -- op-mode : I2S/DIT ops mode. 0 for I2S mode. 1 for DIT mode used for S/PDIF, - IEC60958-1, and AES-3 formats. -- tdm-slots : Slots for TDM operation. Indicates number of channels transmitted - or received over one serializer. -- serial-dir : A list of serializer configuration. Each entry is a number - indication for serializer pin direction. - (0 - INACTIVE, 1 - TX, 2 - RX) -- dmas: two element list of DMA controller phandles and DMA request line - ordered pairs. -- dma-names: identifier string for each DMA request line in the dmas property. - These strings correspond 1:1 with the ordered pairs in dmas. The dma - identifiers must be "rx" and "tx". - -Optional properties: - -- ti,hwmods : Must be "mcasp", n is controller instance starting 0 -- tx-num-evt : FIFO levels. -- rx-num-evt : FIFO levels. -- dismod : Specify the drive on TX pin during inactive slots - 0 : 3-state - 2 : logic low - 3 : logic high - Defaults to 'logic low' when the property is not present -- sram-size-playback : size of sram to be allocated during playback -- sram-size-capture : size of sram to be allocated during capture -- interrupts : Interrupt numbers for McASP -- interrupt-names : Known interrupt names are "tx" and "rx" -- pinctrl-0: Should specify pin control group used for this controller. -- pinctrl-names: Should contain only one value - "default", for more details - please refer to pinctrl-bindings.txt -- fck_parent : Should contain a valid clock name which will be used as parent - for the McASP fck -- auxclk-fs-ratio: When McASP is bus master indicates the ratio between AUCLK - and FS rate if applicable: - AUCLK rate = auxclk-fs-ratio * FS rate - -Optional GPIO support: -If any McASP pin need to be used as GPIO then the McASP node must have: -... - gpio-controller - #gpio-cells = <2>; -... - -When requesting a GPIO, the first parameter is the PIN index in McASP_P* -registers. -For example to request the AXR2 pin of mcasp8: -function-gpios = <&mcasp8 2 0>; - -Or to request the ACLKR pin of mcasp8: -function-gpios = <&mcasp8 29 0>; - -For generic gpio information, please refer to bindings/gpio/gpio.txt - -Example: - -mcasp0: mcasp0@1d00000 { - compatible = "ti,da830-mcasp-audio"; - reg = <0x100000 0x3000>; - reg-names "mpu"; - interrupts = <82>, <83>; - interrupt-names = "tx", "rx"; - op-mode = <0>; /* MCASP_IIS_MODE */ - tdm-slots = <2>; - serial-dir = < - 0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */ - 0 0 0 0 - 0 0 0 1 - 2 0 0 0 >; - tx-num-evt = <1>; - rx-num-evt = <1>; -}; diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml new file mode 100644 index 000000000000..f46c66bc6b2d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/davinci-mcasp-audio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: McASP Controller for TI SoCs + +maintainers: + - Jayesh Choudhary + +properties: + compatible: + enum: + - ti,dm646x-mcasp-audio + - ti,da830-mcasp-audio + - ti,am33xx-mcasp-audio + - ti,dra7-mcasp-audio + - ti,omap4-mcasp-audio + + reg: + minItems: 1 + items: + - description: CFG registers + - description: data registers + + reg-names: + minItems: 1 + items: + - const: mpu + - const: dat + + op-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: 0 - I2S or 1 - DIT operation mode + enum: + - 0 + - 1 + + tdm-slots: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + number of channels over one serializer + the property is ignored in DIT mode + minimum: 2 + maximum: 32 + + serial-dir: + description: + A list of serializer configuration + Entry is indication for serializer pin direction + 0 - Inactive, 1 - TX, 2 - RX + All AXR pins should be present in the array even if inactive + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 25 + items: + minimum: 0 + maximum: 2 + + dmas: + minItems: 1 + items: + - description: transmission DMA channel + - description: reception DMA channel + + dma-names: + minItems: 1 + items: + - const: tx + - const: rx + + ti,hwmods: + $ref: /schemas/types.yaml#/definitions/string + description: Name of hwmod associated with McASP + maxItems: 1 + deprecated: true + + tx-num-evt: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + configures WFIFO threshold + 0 disables the FIFO use + if property is missing, then also FIFO use is disabled + + rx-num-evt: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + configures RFIFO threshold + 0 disables the FIFO use + if property is missing, then also FIFO use is disabled + + dismod: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + specify the drive on TX pin during inactive time slots + 0 - 3-state, 2 - logic low, 3 - logic high + enum: + - 0 + - 2 + - 3 + default: 2 + + interrupts: + anyOf: + - minItems: 1 + items: + - description: TX interrupt + - description: RX interrupt + - items: + - description: common/combined interrupt + + interrupt-names: + oneOf: + - minItems: 1 + items: + - const: tx + - const: rx + - const: common + + fck_parent: + $ref: /schemas/types.yaml#/definitions/string + description: parent clock name for McASP fck + maxItems: 1 + + auxclk-fs-ratio: + $ref: /schemas/types.yaml#/definitions/uint32 + description: ratio of AUCLK and FS rate if applicable + + gpio-controller: true + + "#gpio-cells": + const: 2 + + clocks: + minItems: 1 + items: + - description: functional clock + - description: module specific optional ahclkx clock + - description: module specific optional ahclkr clock + + clock-names: + minItems: 1 + items: + - const: fck + - const: ahclkx + - const: ahclkr + + power-domains: + description: phandle to the corresponding power-domain + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + port: + description: connection for when McASP is used via graph card + type: object + +required: + - compatible + - reg + - reg-names + - dmas + - dma-names + - interrupts + - interrupt-names + +allOf: + - if: + properties: + opmode: + enum: + - 0 + + then: + required: + - tdm-slots + +additionalProperties: false + +examples: + - | + mcasp0: mcasp0@1d00000 { + compatible = "ti,da830-mcasp-audio"; + reg = <0x100000 0x3000>; + reg-names = "mpu"; + interrupts = <82>, <83>; + interrupt-names = "tx", "rx"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>; + dma-names = "tx", "rx"; + serial-dir = < + 0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 0 0 + 0 0 0 1 + 2 0 0 0 >; + tx-num-evt = <1>; + rx-num-evt = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..321e08ee8c5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19042,6 +19042,7 @@ TEXAS INSTRUMENTS ASoC DRIVERS M: Peter Ujfalusi L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml F: sound/soc/ti/ TEXAS INSTRUMENTS' DAC7612 DAC DRIVER -- cgit v1.2.3 From fdb1e56932a3b5ccba48b34a6f8ba843a2903445 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Feb 2022 13:42:27 +0100 Subject: ALSA: ca0106: Rename register macro names ca0106 driver code uses too generic names for its register definitions such as PTR, DATA, IPR, etc, which may eventually conflict with other code. This patch renames (some of) those register definitions with CA0106_ prefix to avoid the conflicts. Link: https://lore.kernel.org/r/20220210124227.11272-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106.h | 18 +++++------ sound/pci/ca0106/ca0106_main.c | 70 ++++++++++++++++++++--------------------- sound/pci/ca0106/ca0106_mixer.c | 16 +++++----- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index f246e6094289..991b1c5d41d5 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -59,15 +59,15 @@ /* PCI function 0 registers, address = + PCIBASE0 */ /************************************************************************************************/ -#define PTR 0x00 /* Indexed register set pointer register */ +#define CA0106_PTR 0x00 /* Indexed register set pointer register */ /* NOTE: The CHANNELNUM and ADDRESS words can */ /* be modified independently of each other. */ /* CNL[1:0], ADDR[27:16] */ -#define DATA 0x04 /* Indexed register set data register */ +#define CA0106_DATA 0x04 /* Indexed register set data register */ /* DATA[31:0] */ -#define IPR 0x08 /* Global interrupt pending register */ +#define CA0106_IPR 0x08 /* Global interrupt pending register */ /* Clear pending interrupts by writing a 1 to */ /* the relevant bits and zero to the other bits */ #define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ @@ -88,7 +88,7 @@ #define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ #define IPR_PCI 0x00000001 /* PCI Bus error */ -#define INTE 0x0c /* Interrupt enable register */ +#define CA0106_INTE 0x0c /* Interrupt enable register */ #define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ #define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */ @@ -108,8 +108,8 @@ #define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ #define INTE_PCI 0x00000001 /* PCI Bus error */ -#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */ -#define HCFG 0x14 /* Hardware config register */ +#define CA0106_UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */ +#define CA0106_HCFG 0x14 /* Hardware config register */ /* 0x1000 causes AC3 to fails. It adds a dither bit. */ #define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */ @@ -133,7 +133,7 @@ #define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ /* Should be set to 1 when the EMU10K1 is */ /* completely initialized. */ -#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */ +#define CA0106_GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */ /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */ /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */ /* SB Live 24bit: @@ -152,9 +152,9 @@ * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF) * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin. */ -#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ +#define CA0106_AC97DATA 0x1c /* AC97 register set data register (16 bit) */ -#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define CA0106_AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ /********************************************************************************************************/ /* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 36fb150b72fb..8577f9fa5ea6 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -338,8 +338,8 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); - outl(regptr, emu->port + PTR); - val = inl(emu->port + DATA); + outl(regptr, emu->port + CA0106_PTR); + val = inl(emu->port + CA0106_DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val; } @@ -355,8 +355,8 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu, regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); - outl(regptr, emu->port + PTR); - outl(data, emu->port + DATA); + outl(regptr, emu->port + CA0106_PTR); + outl(data, emu->port + CA0106_DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -455,8 +455,8 @@ static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb) unsigned int intr_enable; spin_lock_irqsave(&emu->emu_lock, flags); - intr_enable = inl(emu->port + INTE) | intrenb; - outl(intr_enable, emu->port + INTE); + intr_enable = inl(emu->port + CA0106_INTE) | intrenb; + outl(intr_enable, emu->port + CA0106_INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -466,8 +466,8 @@ static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb unsigned int intr_enable; spin_lock_irqsave(&emu->emu_lock, flags); - intr_enable = inl(emu->port + INTE) & ~intrenb; - outl(intr_enable, emu->port + INTE); + intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb; + outl(intr_enable, emu->port + CA0106_INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -786,9 +786,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream) hcfg_set = 0; break; } - hcfg = inl(emu->port + HCFG) ; + hcfg = inl(emu->port + CA0106_HCFG) ; hcfg = (hcfg & ~hcfg_mask) | hcfg_set; - outl(hcfg, emu->port + HCFG); + outl(hcfg, emu->port + CA0106_HCFG); reg40 = snd_ca0106_ptr_read(emu, 0x40, 0); reg40 = (reg40 & ~reg40_mask) | reg40_set; snd_ca0106_ptr_write(emu, 0x40, 0, reg40); @@ -888,9 +888,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream) hcfg_set = 0; break; } - hcfg = inl(emu->port + HCFG) ; + hcfg = inl(emu->port + CA0106_HCFG) ; hcfg = (hcfg & ~hcfg_mask) | hcfg_set; - outl(hcfg, emu->port + HCFG); + outl(hcfg, emu->port + CA0106_HCFG); reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); reg71 = (reg71 & ~reg71_mask) | reg71_set; snd_ca0106_ptr_write(emu, 0x71, 0, reg71); @@ -1142,8 +1142,8 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97, unsigned short val; spin_lock_irqsave(&emu->emu_lock, flags); - outb(reg, emu->port + AC97ADDRESS); - val = inw(emu->port + AC97DATA); + outb(reg, emu->port + CA0106_AC97ADDRESS); + val = inw(emu->port + CA0106_AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val; } @@ -1155,8 +1155,8 @@ static void snd_ca0106_ac97_write(struct snd_ac97 *ac97, unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); - outb(reg, emu->port + AC97ADDRESS); - outw(val, emu->port + AC97DATA); + outb(reg, emu->port + CA0106_AC97ADDRESS); + outw(val, emu->port + CA0106_AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -1200,7 +1200,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) unsigned int stat76; struct snd_ca0106_channel *pchannel; - status = inl(chip->port + IPR); + status = inl(chip->port + CA0106_IPR); if (! status) return IRQ_NONE; @@ -1255,7 +1255,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) } // acknowledge the interrupt if necessary - outl(status, chip->port+IPR); + outl(status, chip->port + CA0106_IPR); return IRQ_HANDLED; } @@ -1383,7 +1383,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) int ch; unsigned int def_bits; - outl(0, chip->port + INTE); + outl(0, chip->port + CA0106_INTE); /* * Init to 0x02109204 : @@ -1420,8 +1420,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000); /* Write 0x8000 to AC97_REC_GAIN to mute it. */ - outb(AC97_REC_GAIN, chip->port + AC97ADDRESS); - outw(0x8000, chip->port + AC97DATA); + outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS); + outw(0x8000, chip->port + CA0106_AC97DATA); #if 0 /* FIXME: what are these? */ snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006); snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006); @@ -1495,30 +1495,30 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) /* FIXME: Still need to find out what the other GPIO bits do. * E.g. For digital spdif out. */ - outl(0x0, chip->port+GPIO); - /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ - outl(0x005f5301, chip->port+GPIO); /* Analog */ + outl(0x0, chip->port + CA0106_GPIO); + /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */ + outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */ } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. * E.g. For digital spdif out. */ - outl(0x0, chip->port+GPIO); - /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ - outl(0x005f5301, chip->port+GPIO); /* Analog */ + outl(0x0, chip->port + CA0106_GPIO); + /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */ + outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */ } else { - outl(0x0, chip->port+GPIO); - outl(0x005f03a3, chip->port+GPIO); /* Analog */ - /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */ + outl(0x0, chip->port + CA0106_GPIO); + outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */ + /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */ } snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */ /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */ /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */ - /* outl(0x00001409, chip->port+HCFG); */ - /* outl(0x00000009, chip->port+HCFG); */ + /* outl(0x00001409, chip->port + CA0106_HCFG); */ + /* outl(0x00000009, chip->port + CA0106_HCFG); */ /* AC97 2.0, Enable outputs. */ - outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); + outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG); if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ @@ -1560,12 +1560,12 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip) { /* disable interrupts */ snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); - outl(0, chip->port + INTE); + outl(0, chip->port + CA0106_INTE); snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); udelay(1000); /* disable audio */ /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */ - outl(0, chip->port + HCFG); + outl(0, chip->port + CA0106_HCFG); /* FIXME: We need to stop and DMA transfers here. * But as I am not sure how yet, we cannot from the dma pages. * So we can fix: snd-malloc: Memory leak? pages not freed = 8 diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index c852c6a75b91..05f56015ddd8 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -70,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu) snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); - val = inl(emu->port + GPIO) & ~0x101; - outl(val, emu->port + GPIO); + val = inl(emu->port + CA0106_GPIO) & ~0x101; + outl(val, emu->port + CA0106_GPIO); } else { /* Analog */ @@ -79,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu) snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); - val = inl(emu->port + GPIO) | 0x101; - outl(val, emu->port + GPIO); + val = inl(emu->port + CA0106_GPIO) | 0x101; + outl(val, emu->port + CA0106_GPIO); } } @@ -119,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) if (emu->capture_mic_line_in) { /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ - tmp = inl(emu->port+GPIO) & ~0x400; + tmp = inl(emu->port + CA0106_GPIO) & ~0x400; tmp = tmp | 0x400; - outl(tmp, emu->port+GPIO); + outl(tmp, emu->port + CA0106_GPIO); /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ } else { /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ - tmp = inl(emu->port+GPIO) & ~0x400; - outl(tmp, emu->port+GPIO); + tmp = inl(emu->port + CA0106_GPIO) & ~0x400; + outl(tmp, emu->port + CA0106_GPIO); /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ } } -- cgit v1.2.3 From 7bd431486511482b6e789dd69d07654a1d8c5eba Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 10 Feb 2022 18:20:13 +0530 Subject: ASoC: google: dt-bindings: Add sc7280-herobrine machine bindings Add devicetree bindings documentation file for sc7280 sound card registration. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1644497415-25291-2-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- .../bindings/sound/google,sc7280-herobrine.yaml | 180 +++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml diff --git a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml new file mode 100644 index 000000000000..869b40363af8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/google,sc7280-herobrine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google SC7280-Herobrine ASoC sound card driver + +maintainers: + - Srinivasa Rao Mandadapu + - Judy Hsiao + +description: + This binding describes the SC7280 sound card which uses LPASS for audio. + +properties: + compatible: + enum: + - google,sc7280-herobrine + + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: + A list of the connections between audio components. Each entry is a + pair of strings, the first being the connection's sink, the second + being the connection's source. + + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^dai-link@[0-9a-f]$": + description: + Each subnode represents a dai link. Subnodes of each dai links would be + cpu/codec dais. + + type: object + + properties: + link-name: + description: Indicates dai-link name and PCM stream name. + $ref: /schemas/types.yaml#/definitions/string + maxItems: 1 + + reg: + maxItems: 1 + description: dai link address. + + cpu: + description: Holds subnode which indicates cpu dai. + type: object + properties: + sound-dai: true + + required: + - sound-dai + + additionalProperties: false + + codec: + description: Holds subnode which indicates codec dai. + type: object + properties: + sound-dai: true + + required: + - sound-dai + + additionalProperties: false + + required: + - link-name + - cpu + - codec + - reg + + additionalProperties: false + +required: + - compatible + - model + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + + - | + #include + sound { + compatible = "google,sc7280-herobrine"; + model = "sc7280-wcd938x-max98360a-4dmic"; + + audio-routing = + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC1", "MIC BIAS1", + "AMIC2", "MIC BIAS2", + "VA DMIC0", "MIC BIAS3", + "VA DMIC1", "MIC BIAS3", + "VA DMIC2", "MIC BIAS4", + "VA DMIC3", "MIC BIAS4", + "TX SWR_ADC0", "ADC1_OUTPUT", + "TX SWR_ADC1", "ADC2_OUTPUT", + "TX SWR_ADC2", "ADC3_OUTPUT", + "TX SWR_DMIC0", "DMIC1_OUTPUT", + "TX SWR_DMIC1", "DMIC2_OUTPUT", + "TX SWR_DMIC2", "DMIC3_OUTPUT", + "TX SWR_DMIC3", "DMIC4_OUTPUT"; + + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + link-name = "WCD Playback"; + reg = ; + cpu { + sound-dai = <&lpass_cpu LPASS_CDC_DMA_RX0>; + }; + + codec { + sound-dai = <&wcd938x 0>, <&swr0 0>, <&rxmacro 0>; + }; + }; + dai-link@1 { + link-name = "WCD Capture"; + reg = ; + cpu { + sound-dai = <&lpass_cpu LPASS_CDC_DMA_TX3>; + }; + + codec { + sound-dai = <&wcd938x 1>, <&swr1 0>, <&txmacro 0>; + }; + }; + + dai-link@2 { + link-name = "MI2S Playback"; + reg = ; + cpu { + sound-dai = <&lpass_cpu MI2S_SECONDARY>; + }; + + codec { + sound-dai = <&max98360a>; + }; + }; + + dai-link@3 { + link-name = "DMIC Capture"; + reg = ; + cpu { + sound-dai = <&lpass_cpu LPASS_CDC_DMA_VA_TX0>; + }; + + codec { + sound-dai = <&vamacro 0>; + }; + }; + + dai-link@5 { + link-name = "DP Playback"; + reg = ; + cpu { + sound-dai = <&lpass_cpu LPASS_DP_RX>; + }; + + codec { + sound-dai = <&mdss_dp>; + }; + }; + }; -- cgit v1.2.3 From 77d0ffef793da818741127f4905a3e3d45d05ac7 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 10 Feb 2022 18:20:14 +0530 Subject: ASoC: qcom: Add macro for lpass DAI id's max limit Add macro for lpass DAI id's max limit to create static arrays and for array boundary checks. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1644497415-25291-3-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 67ef497166af..c0f0247e6819 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -16,6 +16,7 @@ #include "lpass-hdmi.h" #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 +#define LPASS_MAX_PORTS (LPASS_CDC_DMA_VA_TX8 + 1) #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) #define LPASS_MAX_HDMI_DMA_CHANNELS (4) -- cgit v1.2.3 From 57350bd41c3ac01bcae1d94e2c85d47dd90b6a3f Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 10 Feb 2022 18:20:15 +0530 Subject: ASoC: qcom: SC7280: Add machine driver Add new machine driver to register sound card on sc7280 based targets and do the required configuration for lpass cpu dai and external codecs connected over MI2S and soundwire interfaces. Add support for audio jack detection, soundwire init and MBHC. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1644497415-25291-4-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 14 +++ sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7280.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 sound/soc/qcom/sc7280.c diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index cf3e151bb635..dd5949eb6b15 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -176,4 +176,18 @@ config SND_SOC_SC7180 SC7180 SoC-based systems. Say Y if you want to use audio device on this SoCs. +config SND_SOC_SC7280 + tristate "SoC Machine driver for SC7280 boards" + depends on I2C && SOUNDWIRE || COMPILE_TEST + select SND_SOC_QCOM_COMMON + select SND_SOC_LPASS_SC7280 + select SND_SOC_MAX98357A + select SND_SOC_WCD938X + select SND_SOC_LPASS_RX_MACRO + select SND_SOC_LPASS_TX_MACRO + help + Add support for audio on Qualcomm Technologies Inc. + SC7280 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 1600ae55bd34..625aec63b4ee 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -19,6 +19,7 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sc7180-objs := sc7180.o +snd-soc-sc7280-objs := sc7280.o snd-soc-sdm845-objs := sdm845.o snd-soc-sm8250-objs := sm8250.o snd-soc-qcom-common-objs := common.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o +obj-$(CONFIG_SND_SOC_SC7280) += snd-soc-sc7280.o 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 diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c new file mode 100644 index 000000000000..bd0bf9c8cb28 --- /dev/null +++ b/sound/soc/qcom/sc7280.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. +// +// ALSA SoC Machine driver for sc7280 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "lpass.h" + +struct sc7280_snd_data { + struct snd_soc_card card; + struct sdw_stream_runtime *sruntime[LPASS_MAX_PORTS]; + struct snd_soc_jack hs_jack; + struct snd_soc_jack hdmi_jack; + bool jack_setup; + bool stream_prepared[LPASS_MAX_PORTS]; +}; + +static void sc7280_jack_free(struct snd_jack *jack) +{ + struct snd_soc_component *component = jack->private_data; + + snd_soc_component_set_jack(component, NULL, NULL); +} + +static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + struct snd_jack *jack; + int rval, i; + + if (!pdata->jack_setup) { + rval = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &pdata->hs_jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add Headset Jack\n"); + return rval; + } + + jack = pdata->hs_jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + jack->private_data = component; + jack->private_free = sc7280_jack_free; + pdata->jack_setup = true; + } + switch (cpu_dai->id) { + case LPASS_CDC_DMA_RX0: + case LPASS_CDC_DMA_TX3: + for_each_rtd_codec_dais(rtd, i, codec_dai) { + rval = snd_soc_component_set_jack(component, &pdata->hs_jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_err(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + break; + default: + break; + } + + return 0; +} + +static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + struct snd_jack *jack; + int rval; + + rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &pdata->hdmi_jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add HDMI Jack\n"); + return rval; + } + + jack = pdata->hdmi_jack.jack; + jack->private_data = component; + jack->private_free = sc7280_jack_free; + + return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL); +} + +static int sc7280_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + switch (cpu_dai->id) { + case LPASS_CDC_DMA_TX3: + return sc7280_headset_init(rtd); + case LPASS_CDC_DMA_RX0: + case LPASS_CDC_DMA_VA_TX0: + case MI2S_SECONDARY: + return 0; + case LPASS_DP_RX: + return sc7280_hdmi_init(rtd); + default: + dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + } + + return -EINVAL; +} + +static int sc7280_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime; + int i; + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000); + + switch (cpu_dai->id) { + case LPASS_CDC_DMA_TX3: + case LPASS_CDC_DMA_RX0: + for_each_rtd_codec_dais(rtd, i, codec_dai) { + sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream); + if (sruntime != ERR_PTR(-ENOTSUPP)) + pdata->sruntime[cpu_dai->id] = sruntime; + } + break; + } + + return 0; +} + +static int sc7280_snd_swr_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + int ret; + + if (!sruntime) + return 0; + + if (data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + + ret = sdw_prepare_stream(sruntime); + if (ret) + return ret; + + ret = sdw_enable_stream(sruntime); + if (ret) { + sdw_deprepare_stream(sruntime); + return ret; + } + data->stream_prepared[cpu_dai->id] = true; + + return ret; +} + +static int sc7280_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + switch (cpu_dai->id) { + case LPASS_CDC_DMA_RX0: + case LPASS_CDC_DMA_TX3: + return sc7280_snd_swr_prepare(substream); + default: + break; + } + + return 0; +} + +static int sc7280_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + switch (cpu_dai->id) { + case LPASS_CDC_DMA_RX0: + case LPASS_CDC_DMA_TX3: + if (sruntime && data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + break; + default: + break; + } + return 0; +} + +static const struct snd_soc_ops sc7280_ops = { + .hw_params = sc7280_snd_hw_params, + .hw_free = sc7280_snd_hw_free, + .prepare = sc7280_snd_prepare, +}; + +static int sc7280_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sc7280_snd_data *data; + struct device *dev = &pdev->dev; + struct snd_soc_dai_link *link; + int ret, i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + card = &data->card; + snd_soc_card_set_drvdata(card, data); + + card->owner = THIS_MODULE; + card->driver_name = "SC7280"; + card->dev = dev; + + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + + for_each_card_prelinks(card, i, link) { + link->init = sc7280_init; + link->ops = &sc7280_ops; + } + + return devm_snd_soc_register_card(dev, card); +} + +static const struct of_device_id sc7280_snd_device_id[] = { + { .compatible = "google,sc7280-herobrine" }, + {} +}; +MODULE_DEVICE_TABLE(of, sc7280_snd_device_id); + +static struct platform_driver sc7280_snd_driver = { + .probe = sc7280_snd_platform_probe, + .driver = { + .name = "msm-snd-sc7280", + .of_match_table = sc7280_snd_device_id, + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(sc7280_snd_driver); + +MODULE_DESCRIPTION("sc7280 ASoC Machine Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d9c5996ab37fca71b4d97440798d54dd87540c8b Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 10 Feb 2022 15:19:00 +0800 Subject: ASoC: rt5640: Remove the sysclk and sysclk_src checking Remove the sysclk and sysclk_src checking in the function set_sysclk() to prevent the PLL power off. It is not getting re-programmed during subsequent runs after the first run (in BIAS_OFF stage). Signed-off-by: Oder Chiou Link: https://lore.kernel.org/r/20220210071900.17287-1-oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e7a82565b905..30c2e7cb7ed2 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1839,9 +1839,6 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, unsigned int reg_val = 0; unsigned int pll_bit = 0; - if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src) - return 0; - switch (clk_id) { case RT5640_SCLK_S_MCLK: reg_val |= RT5640_SCLK_SRC_MCLK; -- cgit v1.2.3 From 7f021b723ea51ae94329e6d76f68189e1696deca Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:23:52 +0100 Subject: ASoC: dt-bindings: samsung,aries-wm8994: require sound-dai property The cpu and codec nodes must provide sound-dai property. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220129122357.45545-2-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml index 5fff586dc802..afdacffc9a7a 100644 --- a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml @@ -31,6 +31,8 @@ properties: description: | phandles to the I2S controller and bluetooth codec, in that order + required: + - sound-dai codec: type: object @@ -38,6 +40,8 @@ properties: sound-dai: $ref: /schemas/types.yaml#/definitions/phandle-array description: phandle to the WM8994 CODEC + required: + - sound-dai samsung,audio-routing: $ref: /schemas/types.yaml#/definitions/non-unique-string-array -- cgit v1.2.3 From 0412539614a223817646150d910ab6fedbb80507 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:24:26 +0100 Subject: ASoC: dt-bindings: samsung,arndale: convert to dtschema Convert the audio complex on Arndale boards with Samsung Exynos SoC to DT schema format. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220129122430.45694-1-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/arndale.txt | 25 ------------ .../devicetree/bindings/sound/samsung,arndale.yaml | 44 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 25 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/arndale.txt create mode 100644 Documentation/devicetree/bindings/sound/samsung,arndale.yaml diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt deleted file mode 100644 index 17530120ccfc..000000000000 --- a/Documentation/devicetree/bindings/sound/arndale.txt +++ /dev/null @@ -1,25 +0,0 @@ -Audio Binding for Arndale boards - -Required properties: -- compatible : Can be one of the following: - "samsung,arndale-rt5631", - "samsung,arndale-wm1811" - -- samsung,audio-cpu: The phandle of the Samsung I2S controller -- samsung,audio-codec: The phandle of the audio codec - -Optional: -- samsung,model: The name of the sound-card - -Arndale Boards has many audio daughter cards, one of them is -rt5631/alc5631. Below example shows audio bindings for rt5631/ -alc5631 based codec. - -Example: - -sound { - compatible = "samsung,arndale-rt5631"; - - samsung,audio-cpu = <&i2s0> - samsung,audio-codec = <&rt5631>; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,arndale.yaml b/Documentation/devicetree/bindings/sound/samsung,arndale.yaml new file mode 100644 index 000000000000..e7dc65637f02 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,arndale.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung,arndale.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Insignal Arndale boards audio complex + +maintainers: + - Krzysztof Kozlowski + - Sylwester Nawrocki + +properties: + compatible: + enum: + - samsung,arndale-rt5631 + - samsung,arndale-wm1811 + + samsung,audio-codec: + description: Phandle to the audio codec. + $ref: /schemas/types.yaml#/definitions/phandle + + samsung,audio-cpu: + description: Phandle to the Samsung I2S controller. + $ref: /schemas/types.yaml#/definitions/phandle + + samsung,model: + description: The user-visible name of this sound complex. + $ref: /schemas/types.yaml#/definitions/string + +required: + - compatible + - samsung,audio-codec + - samsung,audio-cpu + +additionalProperties: false + +examples: + - | + sound { + compatible = "samsung,arndale-rt5631"; + samsung,audio-cpu = <&i2s0>; + samsung,audio-codec = <&rt5631>; + }; -- cgit v1.2.3 From b6145d8f0d6436a83a31024d4f9953d7088710b4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:24:27 +0100 Subject: ASoC: dt-bindings: samsung,arndale: document ALC5631 The Arndale audio complex might come with ALC5631 which is compatible with RT5631. Document the compatible since it is used in Linux kernel sources. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220129122430.45694-2-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/samsung,arndale.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/samsung,arndale.yaml b/Documentation/devicetree/bindings/sound/samsung,arndale.yaml index e7dc65637f02..cea2bf3544f0 100644 --- a/Documentation/devicetree/bindings/sound/samsung,arndale.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,arndale.yaml @@ -13,6 +13,7 @@ maintainers: properties: compatible: enum: + - samsung,arndale-alc5631 - samsung,arndale-rt5631 - samsung,arndale-wm1811 -- cgit v1.2.3 From 6752770d590594ff42fc19e74c30059d34f133af Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:24:28 +0100 Subject: ASoC: dt-bindings: samsung,smdk5250: convert to dtschema Convert the audio complex on SMDK5250 boards with Samsung Exynos SoC to DT schema format. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220129122430.45694-3-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- .../bindings/sound/samsung,smdk-wm8994.txt | 14 -------- .../bindings/sound/samsung,smdk5250.yaml | 38 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt create mode 100644 Documentation/devicetree/bindings/sound/samsung,smdk5250.yaml diff --git a/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt deleted file mode 100644 index 4686646fb122..000000000000 --- a/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt +++ /dev/null @@ -1,14 +0,0 @@ -Samsung SMDK audio complex - -Required properties: -- compatible : "samsung,smdk-wm8994" -- samsung,i2s-controller: The phandle of the Samsung I2S0 controller -- samsung,audio-codec: The phandle of the WM8994 audio codec -Example: - -sound { - compatible = "samsung,smdk-wm8994"; - - samsung,i2s-controller = <&i2s0>; - samsung,audio-codec = <&wm8994>; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,smdk5250.yaml b/Documentation/devicetree/bindings/sound/samsung,smdk5250.yaml new file mode 100644 index 000000000000..cb51af90435e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,smdk5250.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung,smdk5250.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SMDK5250 audio complex with WM8994 codec + +maintainers: + - Krzysztof Kozlowski + - Sylwester Nawrocki + +properties: + compatible: + const: samsung,smdk-wm8994 + + samsung,audio-codec: + description: Phandle to the audio codec. + $ref: /schemas/types.yaml#/definitions/phandle + + samsung,i2s-controller: + description: Phandle to the Samsung I2S controller. + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - samsung,audio-codec + - samsung,i2s-controller + +additionalProperties: false + +examples: + - | + sound { + compatible = "samsung,smdk-wm8994"; + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&wm8994>; + }; -- cgit v1.2.3 From a7e5305f7ab03cf3ae19ddd3f29919a7a2da0e5d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:24:29 +0100 Subject: ASoC: dt-bindings: samsung,snow: convert to dtschema Convert the audio complex on Google Snow boards with Samsung Exynos SoC to DT schema format. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220129122430.45694-4-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/samsung,snow.yaml | 74 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/snow.txt | 31 --------- 2 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/samsung,snow.yaml delete mode 100644 Documentation/devicetree/bindings/sound/snow.txt diff --git a/Documentation/devicetree/bindings/sound/samsung,snow.yaml b/Documentation/devicetree/bindings/sound/samsung,snow.yaml new file mode 100644 index 000000000000..0c3b3302b842 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,snow.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung,snow.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google Snow audio complex with MAX9809x codec + +maintainers: + - Krzysztof Kozlowski + - Sylwester Nawrocki + +properties: + compatible: + enum: + - google,snow-audio-max98090 + - google,snow-audio-max98091 + - google,snow-audio-max98095 + + codec: + type: object + properties: + sound-dai: + description: List of phandles to the CODEC and HDMI IP nodes. + items: + - description: Phandle to the MAX98090, MAX98091 or MAX98095 CODEC. + - description: Phandle to the HDMI IP block node. + required: + - sound-dai + + cpu: + type: object + properties: + sound-dai: + description: Phandle to the Samsung I2S controller. + maxItems: 1 + required: + - sound-dai + + samsung,audio-codec: + description: Phandle to the audio codec. + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + + samsung,i2s-controller: + description: Phandle to the Samsung I2S controller. + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + + samsung,model: + description: The user-visible name of this sound complex. + $ref: /schemas/types.yaml#/definitions/string + +required: + - compatible + - codec + - cpu + +additionalProperties: false + +examples: + - | + sound { + compatible = "google,snow-audio-max98095"; + samsung,model = "Snow-I2S-MAX98095"; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&max98095 0>, <&hdmi>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/snow.txt b/Documentation/devicetree/bindings/sound/snow.txt deleted file mode 100644 index 80fd9a87bb3f..000000000000 --- a/Documentation/devicetree/bindings/sound/snow.txt +++ /dev/null @@ -1,31 +0,0 @@ -Audio Binding for Snow boards - -Required properties: -- compatible : Can be one of the following, - "google,snow-audio-max98090" or - "google,snow-audio-max98091" or - "google,snow-audio-max98095" -- samsung,i2s-controller (deprecated): The phandle of the Samsung I2S controller -- samsung,audio-codec (deprecated): The phandle of the audio codec - -Required sub-nodes: - - - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S - controller - - 'codec' subnode with a 'sound-dai' property containing list of phandles - to the CODEC nodes, first entry must be the phandle of the MAX98090, - MAX98091 or MAX98095 CODEC (exact device type is indicated by the compatible - string) and the second entry must be the phandle of the HDMI IP block node - -Optional: -- samsung,model: The name of the sound-card - -Example: - -sound { - compatible = "google,snow-audio-max98095"; - - samsung,model = "Snow-I2S-MAX98095"; - samsung,i2s-controller = <&i2s0>; - samsung,audio-codec = <&max98095>; -}; -- cgit v1.2.3 From c1fc51ebb098cd43a68ebc82fde51364c207de32 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jan 2022 13:24:30 +0100 Subject: ASoC: dt-bindings: samsung,tm2: convert to dtschema Convert the audio complex on Samsung TM2 boards with Samsung Exynos SoC to DT schema format. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220129122430.45694-5-krzysztof.kozlowski@canonical.com Signed-off-by: Mark Brown --- .../bindings/sound/samsung,tm2-audio.txt | 42 ------------ .../devicetree/bindings/sound/samsung,tm2.yaml | 80 ++++++++++++++++++++++ 2 files changed, 80 insertions(+), 42 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt create mode 100644 Documentation/devicetree/bindings/sound/samsung,tm2.yaml diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt b/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt deleted file mode 100644 index f5ccc12ddc00..000000000000 --- a/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt +++ /dev/null @@ -1,42 +0,0 @@ -Samsung Exynos5433 TM2(E) audio complex with WM5110 codec - -Required properties: - - - compatible : "samsung,tm2-audio" - - model : the user-visible name of this sound complex - - audio-codec : the first entry should be phandle of the wm5110 audio - codec node, as described in ../mfd/arizona.txt; - the second entry should be phandle of the HDMI - transmitter node - - i2s-controller : the list of phandle and argument tuples pointing to - I2S controllers, the first entry should be I2S0 and - the second one I2S1 - - audio-amplifier : the phandle of the MAX98504 amplifier - - samsung,audio-routing : a list of the connections between audio components; - each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source; valid names for sources and sinks are the - WM5110's and MAX98504's pins and the jacks on the - board: HP, SPK, Main Mic, Sub Mic, Third Mic, - Headset Mic - - mic-bias-gpios : GPIO pin that enables the Main Mic bias regulator - - -Example: - -sound { - compatible = "samsung,tm2-audio"; - audio-codec = <&wm5110>, <&hdmi>; - i2s-controller = <&i2s0 0>, <&i2s1 0>; - audio-amplifier = <&max98504>; - mic-bias-gpios = <&gpr3 2 0>; - model = "wm5110"; - samsung,audio-routing = - "HP", "HPOUT1L", - "HP", "HPOUT1R", - "SPK", "SPKOUT", - "SPKOUT", "HPOUT2L", - "SPKOUT", "HPOUT2R", - "Main Mic", "MICBIAS2", - "IN1R", "Main Mic"; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml new file mode 100644 index 000000000000..74712d6f3ef4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung,tm2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung Exynos5433 TM2(E) audio complex with WM5110 codec + +maintainers: + - Krzysztof Kozlowski + - Sylwester Nawrocki + +properties: + compatible: + const: samsung,tm2-audio + + audio-amplifier: + description: Phandle to the MAX98504 amplifier. + $ref: /schemas/types.yaml#/definitions/phandle + + audio-codec: + description: Phandles to the codecs. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - description: Phandle to the WM5110 audio codec. + - description: Phandle to the HDMI transmitter node. + + samsung,audio-routing: + description: | + List of the connections between audio components; each entry is + a pair of strings, the first being the connection's sink, the second + being the connection's source; valid names for sources and sinks are the + WM5110's and MAX98504's pins and the jacks on the board: HP, SPK, Main + Mic, Sub Mic, Third Mic, Headset Mic. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + + i2s-controller: + description: Phandles to the I2S controllers. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - description: Phandle to I2S0. + - description: Phandle to I2S1. + + mic-bias-gpios: + description: GPIO pin that enables the Main Mic bias regulator. + + model: + description: The user-visible name of this sound complex. + $ref: /schemas/types.yaml#/definitions/string + +required: + - compatible + - audio-amplifier + - audio-codec + - samsung,audio-routing + - i2s-controller + - mic-bias-gpios + - model + +additionalProperties: false + +examples: + - | + #include + + sound { + compatible = "samsung,tm2-audio"; + audio-codec = <&wm5110>, <&hdmi>; + i2s-controller = <&i2s0 0>, <&i2s1 0>; + audio-amplifier = <&max98504>; + mic-bias-gpios = <&gpr3 2 GPIO_ACTIVE_HIGH>; + model = "wm5110"; + samsung,audio-routing = "HP", "HPOUT1L", + "HP", "HPOUT1R", + "SPK", "SPKOUT", + "SPKOUT", "HPOUT2L", + "SPKOUT", "HPOUT2R", + "RCV", "HPOUT3L", + "RCV", "HPOUT3R"; + }; -- cgit v1.2.3 From 23c2f1392f2f3a1c7a82c5d18f02d1a055ad6f16 Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Thu, 10 Feb 2022 10:40:48 -0300 Subject: ASoC: bindings: fsl-asoc-card: Add compatible for tlv320aic31xx codec Commit 8c9b9cfb7724 ("ASoC: fsl-asoc-card: Support fsl,imx-audio-tlv320aic31xx codec")' added support for tlv320aic31xx codec to fsl-asoc-card, but missed the related device-tree compatible string documentation. Fix this. Signed-off-by: Ariel D'Alessandro Link: https://lore.kernel.org/r/20220210134049.32576-1-ariel.dalessandro@collabora.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl-asoc-card.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index b219626a5403..8b4f4015cfe4 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -40,6 +40,8 @@ The compatible list for this generic sound card currently: "fsl,imx-audio-tlv320aic32x4" + "fsl,imx-audio-tlv320aic31xx" + "fsl,imx-audio-si476x" "fsl,imx-audio-wm8958" -- cgit v1.2.3 From 2439a35508277922ea116c99ff4d4a32c607464c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:17 +0200 Subject: ASoC: SOF: Drop unused DSP power states: D3_HOT and D3_COLD The only reference to D3_HOT and D3_COLD DSP power state is in intel/hda-dsp.c in form of a dev_dbg() print. Remove them as they are not used and even if they are they could be re-added via the substate. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 6 ------ sound/soc/sof/sof-priv.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 0fe522549c91..8ddde60c56b3 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -498,15 +498,9 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev) case SOF_DSP_PM_D2: dev_dbg(sdev->dev, "Current DSP power state: D2\n"); break; - case SOF_DSP_PM_D3_HOT: - dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n"); - break; case SOF_DSP_PM_D3: dev_dbg(sdev->dev, "Current DSP power state: D3\n"); break; - case SOF_DSP_PM_D3_COLD: - dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n"); - break; default: dev_dbg(sdev->dev, "Unknown DSP power state: %d\n", sdev->dsp_power_state.state); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e48402ce4bdb..6358f8c84cce 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -84,9 +84,7 @@ enum sof_dsp_power_states { SOF_DSP_PM_D0, SOF_DSP_PM_D1, SOF_DSP_PM_D2, - SOF_DSP_PM_D3_HOT, SOF_DSP_PM_D3, - SOF_DSP_PM_D3_COLD, }; struct sof_dsp_power_state { -- cgit v1.2.3 From 5fdc1242453e2ae88b2cdb607e4eda6b687f084c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:18 +0200 Subject: ASoC: SOF: Move the definition of enum sof_dsp_power_states to global header Move the enum sof_dsp_power_states to include/sound/sof.h to be accessible outside of the core SOF stack. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 8 ++++++++ sound/soc/sof/sof-priv.h | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 813680ab9aad..7cdfc954df12 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -39,6 +39,14 @@ enum sof_fw_state { SOF_FW_CRASHED, }; +/* DSP power states */ +enum sof_dsp_power_states { + SOF_DSP_PM_D0, + SOF_DSP_PM_D1, + SOF_DSP_PM_D2, + SOF_DSP_PM_D3, +}; + /* * SOF Platform data. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6358f8c84cce..2e474048d708 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -79,14 +79,6 @@ bool sof_debug_check_flag(int mask); /* max number of DSP cores */ #define SOF_MAX_DSP_NUM_CORES 8 -/* DSP power state */ -enum sof_dsp_power_states { - SOF_DSP_PM_D0, - SOF_DSP_PM_D1, - SOF_DSP_PM_D2, - SOF_DSP_PM_D3, -}; - struct sof_dsp_power_state { u32 state; u32 substate; /* platform-specific */ -- cgit v1.2.3 From ab3a2189a3744527f54ace1be19eb13e6c3d24df Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:19 +0200 Subject: ASoC: SOF: ipc: Read and pass the whole message to handlers for IPC events Change the parameter list for the firmware initiated message (IPC event) handler functions to: handler(struct snd_sof_dev *sdev, void *full_msg); Allocate memory and read the whole message in snd_sof_ipc_msgs_rx() then pass the pointer to the function handling the message. Do this only if we actually have a function which is tasked to process the given type. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 85 +++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 16a0d7a059f3..ee56d4fa4053 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -18,8 +18,10 @@ #include "sof-audio.h" #include "ops.h" -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type); -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); +typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); + +static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf); +static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf); /* * IPC message Tx/Rx message handling. @@ -477,44 +479,30 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -static void ipc_comp_notification(struct snd_sof_dev *sdev, - struct sof_ipc_cmd_hdr *hdr) +static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) { + struct sof_ipc_cmd_hdr *hdr = msg_buf; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - struct sof_ipc_ctrl_data *cdata; - int ret; switch (msg_type) { case SOF_IPC_COMP_GET_VALUE: case SOF_IPC_COMP_GET_DATA: - cdata = kmalloc(hdr->size, GFP_KERNEL); - if (!cdata) - return; - - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to read component event: %d\n", ret); - goto err; - } break; default: dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); return; } - snd_sof_control_notify(sdev, cdata); - -err: - kfree(cdata); + snd_sof_control_notify(sdev, msg_buf); } /* DSP firmware has sent host a message */ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) { + ipc_rx_callback rx_callback = NULL; struct sof_ipc_cmd_hdr hdr; - u32 cmd, type; + void *msg_buf; + u32 cmd; int err; /* read back header */ @@ -523,10 +511,15 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); return; } + + if (hdr.size < sizeof(hdr)) { + dev_err(sdev->dev, "The received message size is invalid\n"); + return; + } + ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - type = hdr.cmd & SOF_CMD_TYPE_MASK; /* check message type */ switch (cmd) { @@ -551,20 +544,35 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_PM_MSG: break; case SOF_IPC_GLB_COMP_MSG: - ipc_comp_notification(sdev, &hdr); + rx_callback = ipc_comp_notification; break; case SOF_IPC_GLB_STREAM_MSG: - /* need to pass msg id into the function */ - ipc_stream_message(sdev, hdr.cmd); + rx_callback = ipc_stream_message; break; case SOF_IPC_GLB_TRACE_MSG: - ipc_trace_message(sdev, type); + rx_callback = ipc_trace_message; break; default: - dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd); + dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd); break; } + if (rx_callback) { + /* read the full message as we have rx handler for it */ + msg_buf = kmalloc(hdr.size, GFP_KERNEL); + if (!msg_buf) + return; + + err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); + if (err < 0) + dev_err(sdev->dev, "%s: Failed to read message: %d\n", + __func__, err); + else + rx_callback(sdev, msg_buf); + + kfree(msg_buf); + } + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); @@ -573,19 +581,14 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); * IPC trace mechanism. */ -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type) +static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf) { - struct sof_ipc_dma_trace_posn posn; - int ret; + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); - if (ret < 0) - dev_warn(sdev->dev, "failed to read trace position: %d\n", ret); - else - snd_sof_trace_update_pos(sdev, &posn); + snd_sof_trace_update_pos(sdev, msg_buf); break; default: dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); @@ -667,11 +670,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) } /* stream notifications from DSP FW */ -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf) { - /* get msg cmd type and msd id */ - u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; - u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); switch (msg_type) { case SOF_IPC_STREAM_POSITION: -- cgit v1.2.3 From ee8443050b2bf06d80fdd2c78cc25cae2abdedcd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:20 +0200 Subject: ASoC: SOF: Split up utils.c into sof-utils and iomem-utils The utils.c contains wrappers and implementation for accessing iomem mapped regions and a single unrelated function to create a compressed page table from snd_dma_buffer for firmware use. The latter is used by the PCM and the dma trace code and it needs to be moved to a generic source/header for the client conversion to be possible. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 5 +- sound/soc/sof/compress.c | 1 + sound/soc/sof/iomem-utils.c | 127 ++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 1 + sound/soc/sof/sof-priv.h | 4 - sound/soc/sof/sof-utils.c | 77 ++++++++++++++++++ sound/soc/sof/sof-utils.h | 19 +++++ sound/soc/sof/trace.c | 1 + sound/soc/sof/utils.c | 186 -------------------------------------------- 9 files changed, 230 insertions(+), 191 deletions(-) create mode 100644 sound/soc/sof/iomem-utils.c create mode 100644 sound/soc/sof/sof-utils.c create mode 100644 sound/soc/sof/sof-utils.h delete mode 100644 sound/soc/sof/utils.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 964b429146be..4b9fccacc2b7 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # 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 utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o @@ -12,9 +12,12 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-nocodec-objs := nocodec.o +snd-sof-utils-objs := sof-utils.o + obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o +obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 91a9c95929cd..2137d1d65dd9 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -9,6 +9,7 @@ #include #include "sof-audio.h" #include "sof-priv.h" +#include "sof-utils.h" static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp, u64 host_pos, u64 buffer_size) diff --git a/sound/soc/sof/iomem-utils.c b/sound/soc/sof/iomem-utils.c new file mode 100644 index 000000000000..3f57f6cf6542 --- /dev/null +++ b/sound/soc/sof/iomem-utils.c @@ -0,0 +1,127 @@ +// 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) 2018-2022 Intel Corporation. All rights reserved. +// +// Author: Keyon Jie +// + +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* + * Register IO + * + * The sof_io_xyz() wrappers are typically referenced in snd_sof_dsp_ops + * structures and cannot be inlined. + */ + +void sof_io_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value) +{ + writel(value, addr); +} +EXPORT_SYMBOL(sof_io_write); + +u32 sof_io_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} +EXPORT_SYMBOL(sof_io_read); + +void sof_io_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value) +{ + writeq(value, addr); +} +EXPORT_SYMBOL(sof_io_write64); + +u64 sof_io_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readq(addr); +} +EXPORT_SYMBOL(sof_io_read64); + +/* + * IPC Mailbox IO + */ + +void sof_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} +EXPORT_SYMBOL(sof_mailbox_write); + +void sof_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} +EXPORT_SYMBOL(sof_mailbox_read); + +/* + * Memory copy. + */ + +int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size) +{ + int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); + const u8 *src_byte = src; + void __iomem *dest; + u32 affected_mask; + u32 tmp; + int m, n; + + if (bar < 0) + return bar; + + dest = sdev->bar[bar] + offset; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(dest, src, m); + + if (n) { + affected_mask = (1 << (8 * n)) - 1; + + /* first read the 32bit data of dest, then change affected + * bytes, and write back to dest. For unaffected bytes, it + * should not be changed + */ + tmp = ioread32(dest + m * 4); + tmp &= ~affected_mask; + + tmp |= *(u32 *)(src_byte + m * 4) & affected_mask; + iowrite32(tmp, dest + m * 4); + } + + return 0; +} +EXPORT_SYMBOL(sof_block_write); + +int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size) +{ + int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); + + if (bar < 0) + return bar; + + memcpy_fromio(dest, sdev->bar[bar] + offset, size); + + return 0; +} +EXPORT_SYMBOL(sof_block_read); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 37fb8e6cd493..62cb61655761 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -19,6 +19,7 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) #include "sof-probes.h" #endif +#include "sof-utils.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2e474048d708..27d2f3ca2f06 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -502,10 +502,6 @@ void snd_sof_complete(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size); - /* * Firmware loading. */ diff --git a/sound/soc/sof/sof-utils.c b/sound/soc/sof/sof-utils.c new file mode 100644 index 000000000000..a3300ecee062 --- /dev/null +++ b/sound/soc/sof/sof-utils.c @@ -0,0 +1,77 @@ +// 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) 2018-2022 Intel Corporation. All rights reserved. +// +// Author: Keyon Jie +// + +#include +#include +#include +#include +#include +#include "sof-utils.h" + +/* + * Generic buffer page table creation. + * Take the each physical page address and drop the least significant unused + * bits from each (based on PAGE_SIZE). Then pack valid page address bits + * into compressed page table. + */ + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + /* + * The number of valid address bits for each page is 20. + * idx determines the byte position within page_table + * where the current page's address is stored + * in the compressed page_table. + * This can be calculated by multiplying the page number by 2.5. + */ + u32 idx = (5 * i) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u8 *pg_table; + + dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u8 *)(page_table + idx); + + /* + * pagetable compression: + * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 + * ___________pfn 0__________ __________pfn 1___________ _pfn 2... + * .... .... .... .... .... .... .... .... .... .... .... + * It is created by: + * 1. set current location to 0, PFN index i to 0 + * 2. put pfn[i] at current location in Little Endian byte order + * 3. calculate an intermediate value as + * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) + * 4. put x at offset (current location + 2) in LE byte order + * 5. increment current location by 5 bytes, increment i by 2 + * 6. continue to (2) + */ + if (i & 1) + put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, + pg_table); + else + put_unaligned_le32(pfn, pg_table); + } + + return pages; +} +EXPORT_SYMBOL(snd_sof_create_page_table); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-utils.h b/sound/soc/sof/sof-utils.h new file mode 100644 index 000000000000..6f902893807e --- /dev/null +++ b/sound/soc/sof/sof-utils.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022 Intel Corporation. All rights reserved. + */ + +#ifndef __SOC_SOF_UTILS_H +#define __SOC_SOF_UTILS_H + +struct snd_dma_buffer; +struct device; + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +#endif diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 2335d0f06d42..104388c551cb 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -12,6 +12,7 @@ #include #include "sof-priv.h" #include "ops.h" +#include "sof-utils.h" #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c deleted file mode 100644 index 66fa6602fb67..000000000000 --- a/sound/soc/sof/utils.c +++ /dev/null @@ -1,186 +0,0 @@ -// 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) 2018 Intel Corporation. All rights reserved. -// -// Author: Keyon Jie -// - -#include -#include -#include -#include -#include -#include "sof-priv.h" -#include "ops.h" - -/* - * Register IO - * - * The sof_io_xyz() wrappers are typically referenced in snd_sof_dsp_ops - * structures and cannot be inlined. - */ - -void sof_io_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value) -{ - writel(value, addr); -} -EXPORT_SYMBOL(sof_io_write); - -u32 sof_io_read(struct snd_sof_dev *sdev, void __iomem *addr) -{ - return readl(addr); -} -EXPORT_SYMBOL(sof_io_read); - -void sof_io_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value) -{ - writeq(value, addr); -} -EXPORT_SYMBOL(sof_io_write64); - -u64 sof_io_read64(struct snd_sof_dev *sdev, void __iomem *addr) -{ - return readq(addr); -} -EXPORT_SYMBOL(sof_io_read64); - -/* - * IPC Mailbox IO - */ - -void sof_mailbox_write(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) -{ - void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; - - memcpy_toio(dest, message, bytes); -} -EXPORT_SYMBOL(sof_mailbox_write); - -void sof_mailbox_read(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) -{ - void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; - - memcpy_fromio(message, src, bytes); -} -EXPORT_SYMBOL(sof_mailbox_read); - -/* - * Memory copy. - */ - -int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, - u32 offset, void *src, size_t size) -{ - int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); - const u8 *src_byte = src; - void __iomem *dest; - u32 affected_mask; - u32 tmp; - int m, n; - - if (bar < 0) - return bar; - - dest = sdev->bar[bar] + offset; - - m = size / 4; - n = size % 4; - - /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy(dest, src, m); - - if (n) { - affected_mask = (1 << (8 * n)) - 1; - - /* first read the 32bit data of dest, then change affected - * bytes, and write back to dest. For unaffected bytes, it - * should not be changed - */ - tmp = ioread32(dest + m * 4); - tmp &= ~affected_mask; - - tmp |= *(u32 *)(src_byte + m * 4) & affected_mask; - iowrite32(tmp, dest + m * 4); - } - - return 0; -} -EXPORT_SYMBOL(sof_block_write); - -int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, - u32 offset, void *dest, size_t size) -{ - int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); - - if (bar < 0) - return bar; - - memcpy_fromio(dest, sdev->bar[bar] + offset, size); - - return 0; -} -EXPORT_SYMBOL(sof_block_read); - -/* - * Generic buffer page table creation. - * Take the each physical page address and drop the least significant unused - * bits from each (based on PAGE_SIZE). Then pack valid page address bits - * into compressed page table. - */ - -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size) -{ - int i, pages; - - pages = snd_sgbuf_aligned_pages(size); - - dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", - dmab->area, size, pages); - - for (i = 0; i < pages; i++) { - /* - * The number of valid address bits for each page is 20. - * idx determines the byte position within page_table - * where the current page's address is stored - * in the compressed page_table. - * This can be calculated by multiplying the page number by 2.5. - */ - u32 idx = (5 * i) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u8 *pg_table; - - dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u8 *)(page_table + idx); - - /* - * pagetable compression: - * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 - * ___________pfn 0__________ __________pfn 1___________ _pfn 2... - * .... .... .... .... .... .... .... .... .... .... .... - * It is created by: - * 1. set current location to 0, PFN index i to 0 - * 2. put pfn[i] at current location in Little Endian byte order - * 3. calculate an intermediate value as - * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) - * 4. put x at offset (current location + 2) in LE byte order - * 5. increment current location by 5 bytes, increment i by 2 - * 6. continue to (2) - */ - if (i & 1) - put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, - pg_table); - else - put_unaligned_le32(pfn, pg_table); - } - - return pages; -} -EXPORT_SYMBOL(snd_sof_create_page_table); -- cgit v1.2.3 From 6955d9512d0ea814f1c2761bef7ad7b3cedf4d68 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:21 +0200 Subject: ASoC: SOF: Introduce IPC SOF client support A client in the SOF (Sound Open Firmware) context is a driver that needs to communicate with the DSP via IPC messages. The SOF core is responsible for serializing the IPC messages to the DSP from the different clients. One example of an SOF client would be an IPC test client that floods the DSP with test IPC messages to validate if the serialization works as expected. Multi-client support will also add the ability to split the existing audio cards into multiple ones, so as to e.g. to deal with HDMI with a dedicated client instead of adding HDMI to all cards. This patch introduces descriptors for SOF client driver and SOF client device along with APIs for registering and unregistering a SOF client driver, sending IPCs from a client device and accessing the SOF core debugfs root entry. Along with this, add a couple of new members to struct snd_sof_dev that will be used for maintaining the list of clients. Signed-off-by: Ranjani Sridharan Co-developed-by: Fred Oh Signed-off-by: Fred Oh Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 7 + sound/soc/sof/Makefile | 1 + sound/soc/sof/core.c | 43 +++++- sound/soc/sof/ipc.c | 25 ++-- sound/soc/sof/sof-client.c | 340 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client.h | 67 +++++++++ sound/soc/sof/sof-priv.h | 78 +++++++++-- 7 files changed, 540 insertions(+), 21 deletions(-) create mode 100644 sound/soc/sof/sof-client.c create mode 100644 sound/soc/sof/sof-client.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 1a7d6cefd3b7..468d7c113dae 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -61,6 +61,13 @@ config SND_SOC_SOF_DEBUG_PROBES Say Y if you want to enable probes. If unsure, select "N". +config SND_SOC_SOF_CLIENT + tristate + select AUXILIARY_BUS + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" depends on EXPERT && SND_SOC_SOF diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 4b9fccacc2b7..38ce2fe376fa 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,6 +2,7 @@ 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 +snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 8f32b5b12b3e..2aabaac9007f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -122,6 +122,27 @@ out: } EXPORT_SYMBOL(sof_print_oops_and_stack); +/* Helper to manage DSP state */ +void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state) +{ + if (sdev->fw_state == new_state) + return; + + dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state); + sdev->fw_state = new_state; + + switch (new_state) { + case SOF_FW_BOOT_NOT_STARTED: + case SOF_FW_BOOT_COMPLETE: + case SOF_FW_CRASHED: + sof_client_fw_state_dispatcher(sdev); + fallthrough; + default: + break; + } +} +EXPORT_SYMBOL(sof_set_fw_state); + /* * FW Boot State Transition Diagram * @@ -266,6 +287,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_trace_err; } + ret = sof_register_clients(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to register clients %d\n", ret); + goto sof_machine_err; + } + /* * Some platforms in SOF, ex: BYT, may not have their platform PM * callbacks set. Increment the usage count so as to @@ -281,6 +308,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; +sof_machine_err: + snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: snd_sof_free_trace(sdev); fw_run_err: @@ -329,7 +358,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; - sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; #endif @@ -350,9 +378,14 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->ipc_client_list); + INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); + INIT_LIST_HEAD(&sdev->fw_state_handler_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->power_state_access); + mutex_init(&sdev->ipc_client_mutex); + mutex_init(&sdev->client_event_handler_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) @@ -364,6 +397,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) else sdev->boot_timeout = plat_data->desc->boot_timeout; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); + if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { INIT_WORK(&sdev->probe_work, sof_probe_work); schedule_work(&sdev->probe_work); @@ -391,6 +426,12 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister any registered client device first before IPC and debugfs + * to allow client drivers to be removed cleanly + */ + sof_unregister_clients(sdev); + /* * Unregister machine driver. This will unbind the snd_card which * will remove the component driver and unload the topology diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index ee56d4fa4053..c729bb7bf8c8 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -557,22 +557,25 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) break; } - if (rx_callback) { - /* read the full message as we have rx handler for it */ - msg_buf = kmalloc(hdr.size, GFP_KERNEL); - if (!msg_buf) - return; + /* read the full message */ + msg_buf = kmalloc(hdr.size, GFP_KERNEL); + if (!msg_buf) + return; - err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); - if (err < 0) - dev_err(sdev->dev, "%s: Failed to read message: %d\n", - __func__, err); - else + err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); + if (err < 0) { + dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); + } else { + /* Call local handler for the message */ + if (rx_callback) rx_callback(sdev, msg_buf); - kfree(msg_buf); + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, msg_buf); } + kfree(msg_buf); + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c new file mode 100644 index 000000000000..6f747d051b59 --- /dev/null +++ b/sound/soc/sof/sof-client.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Authors: Ranjani Sridharan +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include +#include "ops.h" +#include "sof-client.h" +#include "sof-priv.h" + +/** + * struct sof_ipc_event_entry - IPC client event description + * @ipc_msg_type: IPC msg type of the event the client is interested + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_ipc_event_entry { + u32 ipc_msg_type; + struct sof_client_dev *cdev; + sof_client_event_callback callback; + struct list_head list; +}; + +/** + * struct sof_state_event_entry - DSP panic event subscription entry + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_state_event_entry { + struct sof_client_dev *cdev; + sof_client_fw_state_callback callback; + struct list_head list; +}; + +static void sof_client_auxdev_release(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + + kfree(cdev->auxdev.dev.platform_data); + kfree(cdev); +} + +static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data, + size_t size) +{ + void *d = NULL; + + if (data) { + d = kmemdup(data, size, GFP_KERNEL); + if (!d) + return -ENOMEM; + } + + cdev->auxdev.dev.platform_data = d; + return 0; +} + +int sof_register_clients(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) + return sof_ops(sdev)->register_ipc_clients(sdev); + + return 0; +} + +void sof_unregister_clients(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) + sof_ops(sdev)->unregister_ipc_clients(sdev); +} + +int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, + const void *data, size_t size) +{ + struct auxiliary_device *auxdev; + struct sof_client_dev *cdev; + int ret; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->sdev = sdev; + auxdev = &cdev->auxdev; + auxdev->name = name; + auxdev->dev.parent = sdev->dev; + auxdev->dev.release = sof_client_auxdev_release; + auxdev->id = id; + + ret = sof_client_dev_add_data(cdev, data, size); + if (ret < 0) + goto err_dev_add_data; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id); + goto err_dev_init; + } + + ret = auxiliary_device_add(&cdev->auxdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id); + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + auxiliary_device_uninit(&cdev->auxdev); + return ret; + } + + /* add to list of SOF client devices */ + mutex_lock(&sdev->ipc_client_mutex); + list_add(&cdev->list, &sdev->ipc_client_list); + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; + +err_dev_init: + kfree(cdev->auxdev.dev.platform_data); + +err_dev_add_data: + kfree(cdev); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT); + +void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) +{ + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { + list_del(&cdev->list); + auxiliary_device_delete(&cdev->auxdev); + auxiliary_device_uninit(&cdev->auxdev); + break; + } + } + + mutex_unlock(&sdev->ipc_client_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); + +int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, + void *reply_data, size_t reply_bytes) +{ + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size, + reply_data, reply_bytes); +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); + +struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) +{ + return cdev->sdev->debugfs_root; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT); + +/* DMA buffer allocation in client drivers must use the core SOF device */ +struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) +{ + return cdev->sdev->dev; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); + +const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return &sdev->fw_ready.version; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); + +/* module refcount management of SOF core */ +int sof_client_core_module_get(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (!try_module_get(sdev->dev->driver->owner)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT); + +void sof_client_core_module_put(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + module_put(sdev->dev->driver->owner); +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); + +/* IPC event handling */ +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) +{ + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; + struct sof_ipc_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->ipc_msg_type == msg_type) + event->callback(event->cdev, msg_buf); + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} + +int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type, + sof_client_event_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK)) + return -EINVAL; + + event = kmalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->ipc_msg_type = ipc_msg_type; + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + mutex_lock(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->ipc_rx_handler_list); + mutex_unlock(&sdev->client_event_handler_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT); + +void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { + list_del(&event->list); + kfree(event); + break; + } + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT); + +/*DSP state notification and query */ +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ + struct sof_state_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) + event->callback(event->cdev, sdev->fw_state); + + mutex_unlock(&sdev->client_event_handler_mutex); +} + +int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, + sof_client_fw_state_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + if (!callback) + return -EINVAL; + + event = kmalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + mutex_lock(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->fw_state_handler_list); + mutex_unlock(&sdev->client_event_handler_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT); + +void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + mutex_lock(&sdev->client_event_handler_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) { + if (event->cdev == cdev) { + list_del(&event->list); + kfree(event); + break; + } + } + + mutex_unlock(&sdev->client_event_handler_mutex); +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT); + +enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->fw_state; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h new file mode 100644 index 000000000000..4b6394b4c694 --- /dev/null +++ b/sound/soc/sof/sof-client.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_SOF_CLIENT_H +#define __SOC_SOF_CLIENT_H + +#include +#include +#include +#include + +struct sof_ipc_fw_version; +struct sof_ipc_cmd_hdr; +struct snd_sof_dev; +struct dentry; + +/** + * struct sof_client_dev - SOF client device + * @auxdev: auxiliary device + * @sdev: pointer to SOF core device struct + * @list: item in SOF core client dev list + * @data: device specific data + */ +struct sof_client_dev { + struct auxiliary_device auxdev; + struct snd_sof_dev *sdev; + struct list_head list; + void *data; +}; + +#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev) + +#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \ + container_of(auxiliary_dev, struct sof_client_dev, auxdev) + +#define dev_to_sof_client_dev(dev) \ + container_of(to_auxiliary_dev(dev), struct sof_client_dev, auxdev) + +int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, + void *reply_data, size_t reply_bytes); + +struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); +struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); +const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); + +/* module refcount management of SOF core */ +int sof_client_core_module_get(struct sof_client_dev *cdev); +void sof_client_core_module_put(struct sof_client_dev *cdev); + +/* IPC notification */ +typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *msg_buf); + +int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type, + sof_client_event_callback callback); +void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type); + +/* DSP state notification and query */ +typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev, + enum sof_fw_state state); + +int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, + sof_client_fw_state_callback callback); +void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev); +enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev); + +#endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 27d2f3ca2f06..f641833f3ff9 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -292,6 +292,10 @@ struct snd_sof_dsp_ops { void (*set_mach_params)(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ + /* IPC client ops */ + int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ + void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ + /* DAI ops */ struct snd_soc_dai_driver *drv; int num_drv; @@ -479,6 +483,30 @@ struct snd_sof_dev { */ int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES]; + /* + * Used to keep track of registered IPC client devices so that they can + * be removed when the parent SOF module is removed. + */ + struct list_head ipc_client_list; + + /* mutex to protect client list */ + struct mutex ipc_client_mutex; + + /* + * Used for tracking the IPC client's RX registration for DSP initiated + * message handling. + */ + struct list_head ipc_rx_handler_list; + + /* + * Used for tracking the IPC client's registration for DSP state change + * notification + */ + struct list_head fw_state_handler_list; + + /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */ + struct mutex client_event_handler_mutex; + void *private; /* core does not touch this */ }; @@ -582,15 +610,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops; /* * Firmware state tracking */ -static inline void sof_set_fw_state(struct snd_sof_dev *sdev, - enum sof_fw_state new_state) -{ - if (sdev->fw_state == new_state) - return; - - dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state); - sdev->fw_state = new_state; -} +void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state); /* * Utilities @@ -623,4 +643,44 @@ 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, + const void *data, size_t size); +void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id); +int sof_register_clients(struct snd_sof_dev *sdev); +void sof_unregister_clients(struct snd_sof_dev *sdev); +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf); +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev); +#else /* CONFIG_SND_SOC_SOF_CLIENT */ +static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, + u32 id, const void *data, size_t size) +{ + return 0; +} + +static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev, + const char *name, u32 id) +{ +} + +static inline int sof_register_clients(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_clients(struct snd_sof_dev *sdev) +{ +} + +static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) +{ +} + +static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ +} +#endif /* CONFIG_SND_SOC_SOF_CLIENT */ + #endif -- cgit v1.2.3 From 1069967afe1e6b728061682ff99ec534a55a5613 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:22 +0200 Subject: ASoC: SOF: sof-client: Add support for clients not managed by pm framework Some SOF client can be of 'passive' type, meaning that they do not handle PM framework callbacks by themselves but rely on the auxiliary driver's suspend and resume callbacks to be notified about the core's suspend or resume event. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pm.c | 13 ++++++++++++- sound/soc/sof/sof-client.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 12 ++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 197a88695fef..7300ecadabd9 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -167,6 +167,9 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + /* Notify clients not managed by pm framework about core resume */ + sof_resume_clients(sdev); + /* notify DSP of system resume */ ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); if (ret < 0) @@ -180,6 +183,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + pm_message_t pm_state; u32 target_state = 0; int ret; @@ -205,16 +209,23 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) } target_state = snd_sof_dsp_power_target(sdev); + pm_state.event = target_state; /* Skip to platform-specific suspend if DSP is entering D0 */ - if (target_state == SOF_DSP_PM_D0) + if (target_state == SOF_DSP_PM_D0) { + /* Notify clients not managed by pm framework about core suspend */ + sof_suspend_clients(sdev, pm_state); goto suspend; + } sof_tear_down_pipelines(sdev, false); /* release trace */ snd_sof_release_trace(sdev); + /* Notify clients not managed by pm framework about core suspend */ + sof_suspend_clients(sdev, pm_state); + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 6f747d051b59..932bdea49c24 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -169,6 +169,52 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, } EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); +int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) +{ + struct auxiliary_driver *adrv; + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->suspend) + adrv->suspend(&cdev->auxdev, state); + } + + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT); + +int sof_resume_clients(struct snd_sof_dev *sdev) +{ + struct auxiliary_driver *adrv; + struct sof_client_dev *cdev; + + mutex_lock(&sdev->ipc_client_mutex); + + list_for_each_entry(cdev, &sdev->ipc_client_list, list) { + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->resume) + adrv->resume(&cdev->auxdev); + } + + mutex_unlock(&sdev->ipc_client_mutex); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT); + struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) { return cdev->sdev->debugfs_root; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f641833f3ff9..39bbba5aeab2 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -653,6 +653,8 @@ int sof_register_clients(struct snd_sof_dev *sdev); void sof_unregister_clients(struct snd_sof_dev *sdev); void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf); void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev); +int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state); +int sof_resume_clients(struct snd_sof_dev *sdev); #else /* CONFIG_SND_SOC_SOF_CLIENT */ static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, const void *data, size_t size) @@ -681,6 +683,16 @@ static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void * static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { } + +static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) +{ + return 0; +} + +static inline int sof_resume_clients(struct snd_sof_dev *sdev) +{ + return 0; +} #endif /* CONFIG_SND_SOC_SOF_CLIENT */ #endif -- cgit v1.2.3 From 6e9548cdb30e5d6724236dd7b89a79a270751485 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 10 Feb 2022 17:05:23 +0200 Subject: ASoC: SOF: Convert the generic IPC flood test into SOF client Move the IPC flood test code out from the debug file as separate SOF client driver. Based on the kernel configuration, the device registration for the new IPC flood test is going to happen in the core. With the separate client driver it is going to be possible to run multiple flood tests in parallel to increase the stress, the new Kconfig option can be used to select this (defaults to 1). In order to preserve backward compatibility with existing SW/scripts, the first IPC flood test's debugfs files have been linked to the old files. Signed-off-by: Ranjani Sridharan Co-developed-by: Fred Oh Signed-off-by: Fred Oh Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 16 +- sound/soc/sof/Makefile | 4 + sound/soc/sof/core.c | 1 + sound/soc/sof/debug.c | 231 ----------------- sound/soc/sof/sof-client-ipc-flood-test.c | 396 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-client.c | 56 ++++- sound/soc/sof/sof-priv.h | 6 +- 7 files changed, 469 insertions(+), 241 deletions(-) create mode 100644 sound/soc/sof/sof-client-ipc-flood-test.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 468d7c113dae..3f8a2cadd2f8 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -194,13 +194,23 @@ config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE If unsure, select "N". config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST - bool "SOF enable IPC flood test" + tristate "SOF enable IPC flood test" + select SND_SOC_SOF_CLIENT help - This option enables the IPC flood test which can be used to flood - the DSP with test IPCs and gather stats about response times. + This option enables a separate client device for IPC flood test + which can be used to flood the DSP with test IPCs and gather stats + about response times. Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM + int "Number of IPC flood test clients" + range 1 32 + default 2 + depends on SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST + help + Select the number of IPC flood test clients to be created. + config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR bool "SOF enable IPC message injector" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 38ce2fe376fa..964eff43c9ba 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -11,6 +11,8 @@ snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o snd-sof-of-objs := sof-of-dev.o +snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o + snd-sof-nocodec-objs := nocodec.o snd-sof-utils-objs := sof-utils.o @@ -24,6 +26,8 @@ obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o + obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2aabaac9007f..f29fe573ec3c 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -484,3 +484,4 @@ MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:sof-audio"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 6d6757075f7c..e3a5f77bbd4d 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -234,107 +234,6 @@ static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, } #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) -#define MAX_IPC_FLOOD_DURATION_MS 1000 -#define MAX_IPC_FLOOD_COUNT 10000 -#define IPC_FLOOD_TEST_RESULT_LEN 512 - -static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, - struct snd_sof_dfsentry *dfse, - bool flood_duration_test, - unsigned long ipc_duration_ms, - unsigned long ipc_count) -{ - struct sof_ipc_cmd_hdr hdr; - struct sof_ipc_reply reply; - u64 min_response_time = U64_MAX; - ktime_t start, end, test_end; - u64 avg_response_time = 0; - u64 max_response_time = 0; - u64 ipc_response_time; - int i = 0; - int ret; - - /* configure test IPC */ - hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD; - hdr.size = sizeof(hdr); - - /* set test end time for duration flood test */ - if (flood_duration_test) - test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC; - - /* send test IPC's */ - while (1) { - start = ktime_get(); - ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, - &reply, sizeof(reply)); - end = ktime_get(); - - if (ret < 0) - break; - - /* compute min and max response times */ - ipc_response_time = ktime_to_ns(ktime_sub(end, start)); - min_response_time = min(min_response_time, ipc_response_time); - max_response_time = max(max_response_time, ipc_response_time); - - /* sum up response times */ - avg_response_time += ipc_response_time; - i++; - - /* test complete? */ - if (flood_duration_test) { - if (ktime_to_ns(end) >= test_end) - break; - } else { - if (i == ipc_count) - break; - } - } - - if (ret < 0) - dev_err(sdev->dev, - "error: ipc flood test failed at %d iterations\n", i); - - /* return if the first IPC fails */ - if (!i) - return ret; - - /* compute average response time */ - do_div(avg_response_time, i); - - /* clear previous test output */ - memset(dfse->cache_buf, 0, IPC_FLOOD_TEST_RESULT_LEN); - - if (flood_duration_test) { - dev_dbg(sdev->dev, "IPC Flood test duration: %lums\n", - ipc_duration_ms); - snprintf(dfse->cache_buf, IPC_FLOOD_TEST_RESULT_LEN, - "IPC Flood test duration: %lums\n", ipc_duration_ms); - } - - dev_dbg(sdev->dev, - "IPC Flood count: %d, Avg response time: %lluns\n", - i, avg_response_time); - dev_dbg(sdev->dev, "Max response time: %lluns\n", - max_response_time); - dev_dbg(sdev->dev, "Min response time: %lluns\n", - min_response_time); - - /* format output string */ - snprintf(dfse->cache_buf + strlen(dfse->cache_buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), - "IPC Flood count: %d\nAvg response time: %lluns\n", - i, avg_response_time); - - snprintf(dfse->cache_buf + strlen(dfse->cache_buf), - IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), - "Max response time: %lluns\nMin response time: %lluns\n", - max_response_time, min_response_time); - - return ret; -} -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) static ssize_t msg_inject_read(struct file *file, char __user *buffer, @@ -437,15 +336,6 @@ static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - unsigned long ipc_duration_ms = 0; - bool flood_duration_test = false; - unsigned long ipc_count = 0; - struct dentry *dentry; - int err; -#endif size_t size; char *string; int ret; @@ -457,78 +347,6 @@ static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size = simple_write_to_buffer(string, count, ppos, buffer, count); ret = size; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - /* - * write op is only supported for ipc_flood_count or - * ipc_flood_duration_ms debugfs entries atm. - * ipc_flood_count floods the DSP with the number of IPC's specified. - * ipc_duration_ms test floods the DSP for the time specified - * in the debugfs entry. - */ - dentry = file->f_path.dentry; - if (strcmp(dentry->d_name.name, "ipc_flood_count") && - strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) { - ret = -EINVAL; - goto out; - } - - if (!strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) - flood_duration_test = true; - - /* test completion criterion */ - if (flood_duration_test) - ret = kstrtoul(string, 0, &ipc_duration_ms); - else - ret = kstrtoul(string, 0, &ipc_count); - if (ret < 0) - goto out; - - /* limit max duration/ipc count for flood test */ - if (flood_duration_test) { - if (!ipc_duration_ms) { - ret = size; - goto out; - } - - /* find the minimum. min() is not used to avoid warnings */ - if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS) - ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS; - } else { - if (!ipc_count) { - ret = size; - goto out; - } - - /* find the minimum. min() is not used to avoid warnings */ - if (ipc_count > MAX_IPC_FLOOD_COUNT) - ipc_count = MAX_IPC_FLOOD_COUNT; - } - - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(sdev->dev, - "error: debugfs write failed to resume %d\n", - ret); - pm_runtime_put_noidle(sdev->dev); - goto out; - } - - /* flood test */ - ret = sof_debug_ipc_flood_test(sdev, dfse, flood_duration_test, - ipc_duration_ms, ipc_count); - - pm_runtime_mark_last_busy(sdev->dev); - err = pm_runtime_put_autosuspend(sdev->dev); - if (err < 0) - dev_err_ratelimited(sdev->dev, - "error: debugfs write failed to idle %d\n", - err); - - /* return size if test is successful */ - if (ret >= 0) - ret = size; -out: -#endif kfree(string); return ret; } @@ -544,24 +362,6 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, int size; u8 *buf; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - struct dentry *dentry; - - dentry = file->f_path.dentry; - if ((!strcmp(dentry->d_name.name, "ipc_flood_count") || - !strcmp(dentry->d_name.name, "ipc_flood_duration_ms"))) { - if (*ppos) - return 0; - - count = strlen(dfse->cache_buf); - size_ret = copy_to_user(buffer, dfse->cache_buf, count); - if (size_ret) - return -EFAULT; - - *ppos += count; - return count; - } -#endif size = dfse->size; /* validate position & count */ @@ -719,19 +519,6 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, dfse->size = size; dfse->sdev = sdev; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - if (!strncmp(name, "ipc_flood", strlen("ipc_flood"))) { - /* - * cache_buf is unused for SOF_DFSENTRY_TYPE_BUF debugfs entries. - * So, use it to save the results of the last IPC flood test. - */ - dfse->cache_buf = devm_kzalloc(sdev->dev, IPC_FLOOD_TEST_RESULT_LEN, - GFP_KERNEL); - if (!dfse->cache_buf) - return -ENOMEM; - } -#endif - debugfs_create_file(name, mode, sdev->debugfs_root, dfse, &sof_dfs_fops); /* add to dfsentry list */ @@ -892,24 +679,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) - /* create read-write ipc_flood_count debugfs entry */ - err = snd_sof_debugfs_buf_item(sdev, NULL, 0, - "ipc_flood_count", 0666); - - /* errors are only due to memory allocation, not debugfs */ - if (err < 0) - return err; - - /* create read-write ipc_flood_duration_ms debugfs entry */ - err = snd_sof_debugfs_buf_item(sdev, NULL, 0, - "ipc_flood_duration_ms", 0666); - - /* errors are only due to memory allocation, not debugfs */ - if (err < 0) - return err; -#endif - #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, &msg_inject_fops); diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c new file mode 100644 index 000000000000..db3a052c5dd2 --- /dev/null +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Authors: Ranjani Sridharan +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-client.h" + +#define MAX_IPC_FLOOD_DURATION_MS 1000 +#define MAX_IPC_FLOOD_COUNT 10000 +#define IPC_FLOOD_TEST_RESULT_LEN 512 +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 + +#define DEBUGFS_IPC_FLOOD_COUNT "ipc_flood_count" +#define DEBUGFS_IPC_FLOOD_DURATION "ipc_flood_duration_ms" + +struct sof_ipc_flood_priv { + struct dentry *dfs_root; + struct dentry *dfs_link[2]; + char *buf; +}; + +static int sof_ipc_flood_dfs_open(struct inode *inode, struct file *file) +{ + struct sof_client_dev *cdev = inode->i_private; + int ret; + + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + + ret = debugfs_file_get(file->f_path.dentry); + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + +/* + * helper function to perform the flood test. Only one of the two params, ipc_duration_ms + * or ipc_count, will be non-zero and will determine the type of test + */ +static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev, + bool flood_duration_test, + unsigned long ipc_duration_ms, + unsigned long ipc_count) +{ + struct sof_ipc_flood_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply reply; + u64 min_response_time = U64_MAX; + ktime_t start, end, test_end; + u64 avg_response_time = 0; + u64 max_response_time = 0; + u64 ipc_response_time; + int i = 0; + int ret; + + /* configure test IPC */ + hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD; + hdr.size = sizeof(hdr); + + /* set test end time for duration flood test */ + if (flood_duration_test) + test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC; + + /* send test IPC's */ + while (1) { + start = ktime_get(); + ret = sof_client_ipc_tx_message(cdev, &hdr, &reply, sizeof(reply)); + end = ktime_get(); + + if (ret < 0) + break; + + /* compute min and max response times */ + ipc_response_time = ktime_to_ns(ktime_sub(end, start)); + min_response_time = min(min_response_time, ipc_response_time); + max_response_time = max(max_response_time, ipc_response_time); + + /* sum up response times */ + avg_response_time += ipc_response_time; + i++; + + /* test complete? */ + if (flood_duration_test) { + if (ktime_to_ns(end) >= test_end) + break; + } else { + if (i == ipc_count) + break; + } + } + + if (ret < 0) + dev_err(dev, "ipc flood test failed at %d iterations\n", i); + + /* return if the first IPC fails */ + if (!i) + return ret; + + /* compute average response time */ + do_div(avg_response_time, i); + + /* clear previous test output */ + memset(priv->buf, 0, IPC_FLOOD_TEST_RESULT_LEN); + + if (!ipc_count) { + dev_dbg(dev, "IPC Flood test duration: %lums\n", ipc_duration_ms); + snprintf(priv->buf, IPC_FLOOD_TEST_RESULT_LEN, + "IPC Flood test duration: %lums\n", ipc_duration_ms); + } + + dev_dbg(dev, "IPC Flood count: %d, Avg response time: %lluns\n", + i, avg_response_time); + dev_dbg(dev, "Max response time: %lluns\n", max_response_time); + dev_dbg(dev, "Min response time: %lluns\n", min_response_time); + + /* format output string and save test results */ + snprintf(priv->buf + strlen(priv->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf), + "IPC Flood count: %d\nAvg response time: %lluns\n", + i, avg_response_time); + + snprintf(priv->buf + strlen(priv->buf), + IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf), + "Max response time: %lluns\nMin response time: %lluns\n", + max_response_time, min_response_time); + + return ret; +} + +/* + * Writing to the debugfs entry initiates the IPC flood test based on + * the IPC count or the duration specified by the user. + */ +static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct device *dev = &cdev->auxdev.dev; + unsigned long ipc_duration_ms = 0; + bool flood_duration_test = false; + unsigned long ipc_count = 0; + struct dentry *dentry; + int err; + size_t size; + char *string; + int ret; + + string = kzalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + size = simple_write_to_buffer(string, count, ppos, buffer, count); + + /* + * write op is only supported for ipc_flood_count or + * ipc_flood_duration_ms debugfs entries atm. + * ipc_flood_count floods the DSP with the number of IPC's specified. + * ipc_duration_ms test floods the DSP for the time specified + * in the debugfs entry. + */ + dentry = file->f_path.dentry; + if (strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) && + strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { + ret = -EINVAL; + goto out; + } + + if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) + flood_duration_test = true; + + /* test completion criterion */ + if (flood_duration_test) + ret = kstrtoul(string, 0, &ipc_duration_ms); + else + ret = kstrtoul(string, 0, &ipc_count); + if (ret < 0) + goto out; + + /* limit max duration/ipc count for flood test */ + if (flood_duration_test) { + if (!ipc_duration_ms) { + ret = size; + goto out; + } + + /* find the minimum. min() is not used to avoid warnings */ + if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS) + ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS; + } else { + if (!ipc_count) { + ret = size; + goto out; + } + + /* find the minimum. min() is not used to avoid warnings */ + if (ipc_count > MAX_IPC_FLOOD_COUNT) + ipc_count = MAX_IPC_FLOOD_COUNT; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto out; + } + + /* flood test */ + ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, + ipc_duration_ms, ipc_count); + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; +out: + kfree(string); + return ret; +} + +/* return the result of the last IPC flood test */ +static ssize_t sof_ipc_flood_dfs_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_ipc_flood_priv *priv = cdev->data; + size_t size_ret; + + struct dentry *dentry; + + dentry = file->f_path.dentry; + if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) || + !strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { + if (*ppos) + return 0; + + count = min_t(size_t, count, strlen(priv->buf)); + size_ret = copy_to_user(buffer, priv->buf, count); + if (size_ret) + return -EFAULT; + + *ppos += count; + return count; + } + return count; +} + +static int sof_ipc_flood_dfs_release(struct inode *inode, struct file *file) +{ + debugfs_file_put(file->f_path.dentry); + + return 0; +} + +static const struct file_operations sof_ipc_flood_fops = { + .open = sof_ipc_flood_dfs_open, + .read = sof_ipc_flood_dfs_read, + .llseek = default_llseek, + .write = sof_ipc_flood_dfs_write, + .release = sof_ipc_flood_dfs_release, + + .owner = THIS_MODULE, +}; + +/* + * The IPC test client creates a couple of debugfs entries that will be used + * flood tests. Users can write to these entries to execute the IPC flood test + * by specifying either the number of IPCs to flood the DSP with or the duration + * (in ms) for which the DSP should be flooded with test IPCs. At the + * end of each test, the average, min and max response times are reported back. + * The results of the last flood test can be accessed by reading the debugfs + * entries. + */ +static int sof_ipc_flood_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct sof_ipc_flood_priv *priv; + + /* allocate memory for client data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->buf = devm_kmalloc(dev, IPC_FLOOD_TEST_RESULT_LEN, GFP_KERNEL); + if (!priv->buf) + return -ENOMEM; + + cdev->data = priv; + + /* create debugfs root folder with device name under parent SOF dir */ + priv->dfs_root = debugfs_create_dir(dev_name(dev), debugfs_root); + if (!IS_ERR_OR_NULL(priv->dfs_root)) { + /* create read-write ipc_flood_count debugfs entry */ + debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, priv->dfs_root, + cdev, &sof_ipc_flood_fops); + + /* create read-write ipc_flood_duration_ms debugfs entry */ + debugfs_create_file(DEBUGFS_IPC_FLOOD_DURATION, 0644, + priv->dfs_root, cdev, &sof_ipc_flood_fops); + + if (auxdev->id == 0) { + /* + * Create symlinks for backwards compatibility to the + * first IPC flood test instance + */ + char target[100]; + + snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_COUNT, + dev_name(dev)); + priv->dfs_link[0] = + debugfs_create_symlink(DEBUGFS_IPC_FLOOD_COUNT, + debugfs_root, target); + + snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_DURATION, + dev_name(dev)); + priv->dfs_link[1] = + debugfs_create_symlink(DEBUGFS_IPC_FLOOD_DURATION, + debugfs_root, target); + } + } + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_ipc_flood_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_ipc_flood_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + if (auxdev->id == 0) { + debugfs_remove(priv->dfs_link[0]); + debugfs_remove(priv->dfs_link[1]); + } + + debugfs_remove_recursive(priv->dfs_root); +} + +static const struct auxiliary_device_id sof_ipc_flood_client_id_table[] = { + { .name = "snd_sof.ipc_flood" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_ipc_flood_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_ipc_flood_client_drv = { + .probe = sof_ipc_flood_probe, + .remove = sof_ipc_flood_remove, + + .id_table = sof_ipc_flood_client_id_table, +}; + +module_auxiliary_driver(sof_ipc_flood_client_drv); + +MODULE_DESCRIPTION("SOF IPC Flood Test Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 932bdea49c24..0ffe7a26a19a 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -66,18 +66,70 @@ static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) +static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) +{ + int ret = 0; + int i; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { + ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0); + if (ret < 0) + break; + } + + if (ret) { + for (; i >= 0; --i) + sof_client_dev_unregister(sdev, "ipc_flood", i); + } + + return ret; +} + +static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) +{ + int i; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) + sof_client_dev_unregister(sdev, "ipc_flood", i); +} +#else +static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ + int sof_register_clients(struct snd_sof_dev *sdev) { + int ret; + + /* Register platform independent client devices */ + ret = sof_register_ipc_flood_test(sdev); + if (ret) { + dev_err(sdev->dev, "IPC flood test client registration failed\n"); + return ret; + } + + /* Platform depndent client device registration */ + if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) - return sof_ops(sdev)->register_ipc_clients(sdev); + ret = sof_ops(sdev)->register_ipc_clients(sdev); - return 0; + if (ret) + sof_unregister_ipc_flood_test(sdev); + + return ret; } void sof_unregister_clients(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) sof_ops(sdev)->unregister_ipc_clients(sdev); + + sof_unregister_ipc_flood_test(sdev); } int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 39bbba5aeab2..1af61ff89345 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -69,10 +69,6 @@ bool sof_debug_check_flag(int mask); #define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) -#define ENABLE_DEBUGFS_CACHEBUF \ - (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \ - IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)) - /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 @@ -326,7 +322,7 @@ struct snd_sof_dfsentry { * or if it is accessible only when the DSP is in D0. */ enum sof_debugfs_access_type access_type; -#if ENABLE_DEBUGFS_CACHEBUF +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) char *cache_buf; /* buffer to cache the contents of debugfs memory */ #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) -- cgit v1.2.3 From cac0b0887e5304bddfda91a4a7106f9328c31318 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:24 +0200 Subject: ASoC: SOF: Convert the generic IPC message injector into SOF client Move the IPC message injection code out from the debug file as separate SOF client driver. Based on the kernel configuration, the device registration for the new IPC message injector is going to happen in the core. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 3 +- sound/soc/sof/Makefile | 2 + sound/soc/sof/debug.c | 108 ---------------- sound/soc/sof/sof-client-ipc-msg-injector.c | 192 ++++++++++++++++++++++++++++ sound/soc/sof/sof-client.c | 35 ++++- sound/soc/sof/sof-priv.h | 4 - 6 files changed, 229 insertions(+), 115 deletions(-) create mode 100644 sound/soc/sof/sof-client-ipc-msg-injector.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 3f8a2cadd2f8..203b086ac22c 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -212,7 +212,8 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM Select the number of IPC flood test clients to be created. config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR - bool "SOF enable IPC message injector" + tristate "SOF enable IPC message injector" + select SND_SOC_SOF_CLIENT help This option enables the IPC message injector which can be used to send crafted IPC messages to the DSP to test its robustness. diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 964eff43c9ba..a2ae79ebf756 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -12,6 +12,7 @@ snd-sof-acpi-objs := sof-acpi-dev.o snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o +snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o snd-sof-nocodec-objs := nocodec.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index e3a5f77bbd4d..937fe6e11d0d 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -234,105 +234,6 @@ static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, } #endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) -static ssize_t msg_inject_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct sof_ipc_reply *rhdr = dfse->msg_inject_rx; - - if (!rhdr->hdr.size || !count || *ppos) - return 0; - - if (count > rhdr->hdr.size) - count = rhdr->hdr.size; - - if (copy_to_user(buffer, dfse->msg_inject_rx, count)) - return -EFAULT; - - *ppos += count; - return count; -} - -static ssize_t msg_inject_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx; - size_t size; - int ret, err; - - if (*ppos) - return 0; - - size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE, - ppos, buffer, count); - if (size != count) - return size > 0 ? -EFAULT : size; - - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n", - __func__, ret); - pm_runtime_put_noidle(sdev->dev); - goto out; - } - - /* send the message */ - memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE); - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count, - dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE); - - pm_runtime_mark_last_busy(sdev->dev); - err = pm_runtime_put_autosuspend(sdev->dev); - if (err < 0) - dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n", - __func__, err); - - /* return size if test is successful */ - if (ret >= 0) - ret = size; - -out: - return ret; -} - -static const struct file_operations msg_inject_fops = { - .open = simple_open, - .read = msg_inject_read, - .write = msg_inject_write, - .llseek = default_llseek, -}; - -static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, - const char *name, mode_t mode, - const struct file_operations *fops) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - /* pre allocate the tx and rx buffers */ - dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!dfse->msg_inject_tx || !dfse->msg_inject_rx) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->sdev = sdev; - - debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} -#endif - static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -679,15 +580,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) - err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, - &msg_inject_fops); - - /* errors are only due to memory allocation, not debugfs */ - if (err < 0) - return err; -#endif - return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c new file mode 100644 index 000000000000..bce103da4c49 --- /dev/null +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-client.h" + +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 + +struct sof_msg_inject_priv { + struct dentry *dfs_file; + + void *tx_buffer; + void *rx_buffer; +}; + +static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file) +{ + struct sof_client_dev *cdev = inode->i_private; + int ret; + + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + + ret = debugfs_file_get(file->f_path.dentry); + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + +static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + struct sof_ipc_reply *rhdr = priv->rx_buffer; + + if (!rhdr->hdr.size || !count || *ppos) + return 0; + + if (count > rhdr->hdr.size) + count = rhdr->hdr.size; + + if (copy_to_user(buffer, priv->rx_buffer, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + int ret, err; + size_t size; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE, + ppos, buffer, count); + if (size != count) + return size > 0 ? -EFAULT : size; + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + return ret; + } + + /* send the message */ + memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE); + ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer, + SOF_IPC_MSG_MAX_SIZE); + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; + + return ret; +}; + +static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file) +{ + debugfs_file_put(file->f_path.dentry); + + return 0; +} + +static const struct file_operations sof_msg_inject_fops = { + .open = sof_msg_inject_dfs_open, + .read = sof_msg_inject_dfs_read, + .write = sof_msg_inject_dfs_write, + .llseek = default_llseek, + .release = sof_msg_inject_dfs_release, + + .owner = THIS_MODULE, +}; + +static int sof_msg_inject_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct sof_msg_inject_priv *priv; + + /* allocate memory for client data */ + priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + priv->rx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!priv->tx_buffer || !priv->rx_buffer) + return -ENOMEM; + + cdev->data = priv; + + priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root, + cdev, &sof_msg_inject_fops); + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_msg_inject_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_msg_inject_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + debugfs_remove(priv->dfs_file); +} + +static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = { + { .name = "snd_sof.msg_injector" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_msg_inject_client_drv = { + .probe = sof_msg_inject_probe, + .remove = sof_msg_inject_remove, + + .id_table = sof_msg_inject_client_id_table, +}; + +module_auxiliary_driver(sof_msg_inject_client_drv); + +MODULE_DESCRIPTION("SOF IPC Message Injector Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 0ffe7a26a19a..686ad0c3bb61 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -102,6 +102,25 @@ static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) +static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0); +} + +static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "msg_injector", 0); +} +#else +static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ + int sof_register_clients(struct snd_sof_dev *sdev) { int ret; @@ -113,13 +132,24 @@ int sof_register_clients(struct snd_sof_dev *sdev) return ret; } + ret = sof_register_ipc_msg_injector(sdev); + if (ret) { + dev_err(sdev->dev, "IPC message injector client registration failed\n"); + goto err_msg_injector; + } + /* Platform depndent client device registration */ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) ret = sof_ops(sdev)->register_ipc_clients(sdev); - if (ret) - sof_unregister_ipc_flood_test(sdev); + if (!ret) + return 0; + + sof_unregister_ipc_msg_injector(sdev); + +err_msg_injector: + sof_unregister_ipc_flood_test(sdev); return ret; } @@ -129,6 +159,7 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) sof_ops(sdev)->unregister_ipc_clients(sdev); + sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 1af61ff89345..2529408a4e90 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -324,10 +324,6 @@ struct snd_sof_dfsentry { enum sof_debugfs_access_type access_type; #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) char *cache_buf; /* buffer to cache the contents of debugfs memory */ -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) - void *msg_inject_tx; - void *msg_inject_rx; #endif struct snd_sof_dev *sdev; struct list_head list; /* list in sdev dfsentry list */ -- cgit v1.2.3 From 3dc0d709177828a22dfc9d0072e3ac937ef90d06 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Feb 2022 17:05:25 +0200 Subject: ASoC: SOF: Convert the generic probe support to SOF client Add a new client driver for probes support and move all the probes-related code from the core to the client driver. The probes client driver registers a component driver with one CPU DAI driver for extraction and creates a new sound card with one DUMMY DAI link with a dummy codec that will be used for extracting audio data from specific points in the audio pipeline. The probes debugfs ops are based on the initial implementation by Cezary Rojewski and have been moved out of the SOF core into the client driver making it easier to maintain. This change will make it easier for the probes functionality to be added for all platforms without having the need to modify the existing(15+) machine drivers. Signed-off-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220210150525.30756-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Kconfig | 7 +- sound/soc/sof/Makefile | 3 +- sound/soc/sof/core.c | 6 - sound/soc/sof/debug.c | 226 ----------- sound/soc/sof/intel/Kconfig | 19 +- sound/soc/sof/intel/apl.c | 13 +- sound/soc/sof/intel/cnl.c | 13 +- sound/soc/sof/intel/hda-dai.c | 19 - sound/soc/sof/intel/hda-probes.c | 72 +++- sound/soc/sof/intel/hda.c | 10 + sound/soc/sof/intel/hda.h | 49 +-- sound/soc/sof/intel/icl.c | 13 +- sound/soc/sof/intel/tgl.c | 13 +- sound/soc/sof/ops.h | 43 -- sound/soc/sof/pcm.c | 8 +- sound/soc/sof/sof-client-probes.c | 821 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client-probes.h | 31 ++ sound/soc/sof/sof-priv.h | 25 -- sound/soc/sof/sof-probes.c | 364 ----------------- sound/soc/sof/sof-probes.h | 38 -- 20 files changed, 968 insertions(+), 825 deletions(-) create mode 100644 sound/soc/sof/sof-client-probes.c create mode 100644 sound/soc/sof/sof-client-probes.h delete mode 100644 sound/soc/sof/sof-probes.c delete mode 100644 sound/soc/sof/sof-probes.h diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 203b086ac22c..4542868cd730 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -53,13 +53,14 @@ config SND_SOC_SOF_COMPRESS select SND_SOC_COMPRESS config SND_SOC_SOF_DEBUG_PROBES - bool "SOF enable data probing" + tristate + select SND_SOC_SOF_CLIENT select SND_SOC_COMPRESS help This option enables the data probing feature that can be used to gather data directly from specific points of the audio pipeline. - Say Y if you want to enable probes. - If unsure, select "N". + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. config SND_SOC_SOF_CLIENT tristate diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index a2ae79ebf756..4d31282c847d 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -4,7 +4,6 @@ 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 snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o -snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o snd-sof-pci-objs := sof-pci-dev.o @@ -13,6 +12,7 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o +snd-sof-probes-objs := sof-client-probes.o snd-sof-nocodec-objs := nocodec.o @@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index f29fe573ec3c..d99ecbb4282d 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -14,9 +14,6 @@ #include #include "sof-priv.h" #include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" -#endif /* see SOF_DBG_ flags */ static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); @@ -358,9 +355,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; -#endif dev_set_drvdata(dev, sdev); /* check all mandatory ops */ diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 937fe6e11d0d..145fd0d1e166 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -19,221 +19,6 @@ #include "sof-priv.h" #include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" - -/** - * strsplit_u32 - Split string into sequence of u32 tokens - * @buf: String to split into tokens. - * @delim: String containing delimiter characters. - * @tkns: Returned u32 sequence pointer. - * @num_tkns: Returned number of tokens obtained. - */ -static int -strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns) -{ - char *s; - u32 *data, *tmp; - size_t count = 0; - size_t cap = 32; - int ret = 0; - - *tkns = NULL; - *num_tkns = 0; - data = kcalloc(cap, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - while ((s = strsep(buf, delim)) != NULL) { - ret = kstrtouint(s, 0, data + count); - if (ret) - goto exit; - if (++count >= cap) { - cap *= 2; - tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); - if (!tmp) { - ret = -ENOMEM; - goto exit; - } - data = tmp; - } - } - - if (!count) - goto exit; - *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); - if (*tkns == NULL) { - ret = -ENOMEM; - goto exit; - } - *num_tkns = count; - -exit: - kfree(data); - return ret; -} - -static int tokenize_input(const char __user *from, size_t count, - loff_t *ppos, u32 **tkns, size_t *num_tkns) -{ - char *buf; - int ret; - - buf = kmalloc(count + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = simple_write_to_buffer(buf, count, ppos, from, count); - if (ret != count) { - ret = ret >= 0 ? -EIO : ret; - goto exit; - } - - buf[count] = '\0'; - ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns); -exit: - kfree(buf); - return ret; -} - -static ssize_t probe_points_read(struct file *file, - char __user *to, size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - struct sof_probe_point_desc *desc; - size_t num_desc, len = 0; - char *buf; - int i, ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - buf = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); - if (ret < 0) - goto exit; - - for (i = 0; i < num_desc; i++) { - ret = snprintf(buf + len, PAGE_SIZE - len, - "Id: %#010x Purpose: %d Node id: %#x\n", - desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); - if (ret < 0) - goto free_desc; - len += ret; - } - - ret = simple_read_from_buffer(to, count, ppos, buf, len); -free_desc: - kfree(desc); -exit: - kfree(buf); - return ret; -} - -static ssize_t probe_points_write(struct file *file, - const char __user *from, size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - struct sof_probe_point_desc *desc; - size_t num_tkns, bytes; - u32 *tkns; - int ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); - if (ret < 0) - return ret; - bytes = sizeof(*tkns) * num_tkns; - if (!num_tkns || (bytes % sizeof(*desc))) { - ret = -EINVAL; - goto exit; - } - - desc = (struct sof_probe_point_desc *)tkns; - ret = sof_ipc_probe_points_add(sdev, - desc, bytes / sizeof(*desc)); - if (!ret) - ret = count; -exit: - kfree(tkns); - return ret; -} - -static const struct file_operations probe_points_fops = { - .open = simple_open, - .read = probe_points_read, - .write = probe_points_write, - .llseek = default_llseek, -}; - -static ssize_t probe_points_remove_write(struct file *file, - const char __user *from, size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - size_t num_tkns; - u32 *tkns; - int ret; - - if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { - dev_warn(sdev->dev, "no extractor stream running\n"); - return -ENOENT; - } - - ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); - if (ret < 0) - return ret; - if (!num_tkns) { - ret = -EINVAL; - goto exit; - } - - ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns); - if (!ret) - ret = count; -exit: - kfree(tkns); - return ret; -} - -static const struct file_operations probe_points_remove_fops = { - .open = simple_open, - .write = probe_points_remove_write, - .llseek = default_llseek, -}; - -static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, - const char *name, mode_t mode, - const struct file_operations *fops) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->sdev = sdev; - - debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} -#endif - static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -569,17 +354,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - err = snd_sof_debugfs_probe_item(sdev, "probe_points", - 0644, &probe_points_fops); - if (err < 0) - return err; - err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove", - 0200, &probe_points_remove_fops); - if (err < 0) - return err; -#endif - return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 88b6176af021..b53f216d4ecc 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -215,6 +215,7 @@ config SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_PCI_DEV select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE + select SND_SOC_SOF_HDA_PROBES help This option is not user-selectable but automagically handled by 'select' statements at a higher level. @@ -240,15 +241,6 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC Say Y if you want to enable HDAudio codecs with SOF. If unsure select "N". -config SND_SOC_SOF_HDA_PROBES - bool "SOF enable probes over HDA" - depends on SND_SOC_SOF_DEBUG_PROBES - help - This option enables the data probing for Intel(R) - Skylake and newer platforms. - Say Y if you want to enable probes. - If unsure, select "N". - endif ## SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK_BASELINE @@ -266,6 +258,15 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level. +config SND_SOC_SOF_HDA_PROBES + bool + select SND_SOC_SOF_DEBUG_PROBES + help + The option enables the data probing for Intel(R) Skylake and newer + (HDA) platforms. + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE tristate select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 810b8b6748a0..cd8d08c17561 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,15 +80,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -110,6 +101,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = hda_register_clients, + .unregister_ipc_clients = hda_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 1911e104f113..bef27e8751f2 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -298,15 +298,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -328,6 +319,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = hda_register_clients, + .unregister_ipc_clients = hda_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index af0c85e4e299..75063140ed0c 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -16,10 +16,6 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "../sof-probes.h" -#endif - struct hda_pipe_params { u32 ch; u32 s_freq; @@ -737,20 +733,5 @@ struct snd_soc_dai_driver skl_dai[] = { .channels_max = 16, }, }, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -{ - .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, - .cops = &sof_probe_compr_ops, - .capture = { - .stream_name = "Probe Extraction", - .channels_min = 1, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - }, -}, -#endif #endif }; diff --git a/sound/soc/sof/intel/hda-probes.c b/sound/soc/sof/intel/hda-probes.c index 35e548da3609..31e85d4aae8c 100644 --- a/sound/soc/sof/intel/hda-probes.c +++ b/sound/soc/sof/intel/hda-probes.c @@ -3,14 +3,20 @@ // 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) 2019-2020 Intel Corporation. All rights reserved. +// Copyright(c) 2019-2021 Intel Corporation. All rights reserved. // // Author: Cezary Rojewski +// Converted to SOF client: +// Ranjani Sridharan +// Peter Ujfalusi // +#include #include #include #include "../sof-priv.h" +#include "../sof-client-probes.h" +#include "../sof-client.h" #include "hda.h" static inline struct hdac_ext_stream * @@ -19,10 +25,11 @@ hda_compr_get_stream(struct snd_compr_stream *cstream) return cstream->runtime->private_data; } -int hda_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) +static int hda_probes_compr_assign(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai, u32 *stream_id) { + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct hdac_ext_stream *hext_stream; hext_stream = hda_dsp_stream_get(sdev, cstream->direction, 0); @@ -33,14 +40,17 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev, hdac_stream(hext_stream)->cstream = cstream; cstream->runtime->private_data = hext_stream; - return hdac_stream(hext_stream)->stream_tag; + *stream_id = hdac_stream(hext_stream)->stream_tag; + + return 0; } -int hda_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) +static int hda_probes_compr_free(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) { struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); int ret; ret = hda_dsp_stream_put(sdev, cstream->direction, @@ -56,12 +66,13 @@ int hda_probe_compr_free(struct snd_sof_dev *sdev, return 0; } -int hda_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai) +static int hda_probes_compr_set_params(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) { struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct hdac_stream *hstream = hdac_stream(hext_stream); struct snd_dma_buffer *dmab; u32 bits, rate; @@ -89,19 +100,20 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev, return 0; } -int hda_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) +static int hda_probes_compr_trigger(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + int cmd, struct snd_soc_dai *dai) { struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); return hda_dsp_stream_trigger(sdev, hext_stream, cmd); } -int hda_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai) +static int hda_probes_compr_pointer(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) { struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream); struct snd_soc_pcm_stream *pstream; @@ -112,3 +124,25 @@ int hda_probe_compr_pointer(struct snd_sof_dev *sdev, return 0; } + +/* SOF client implementation */ +static const struct sof_probes_host_ops hda_probes_ops = { + .assign = hda_probes_compr_assign, + .free = hda_probes_compr_free, + .set_params = hda_probes_compr_set_params, + .trigger = hda_probes_compr_trigger, + .pointer = hda_probes_compr_pointer, +}; + +int hda_probes_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "hda-probes", 0, &hda_probes_ops, + sizeof(hda_probes_ops)); +} + +void hda_probes_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "hda-probes", 0); +} + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 8f6765317cfa..659fe9d1a542 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1423,6 +1423,16 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) } EXPORT_SYMBOL_NS(hda_pci_intel_probe, SND_SOC_SOF_INTEL_HDA_COMMON); +int hda_register_clients(struct snd_sof_dev *sdev) +{ + return hda_probes_register(sdev); +} + +void hda_unregister_clients(struct snd_sof_dev *sdev) +{ + hda_probes_unregister(sdev); +} + MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 324418582044..21e34580a403 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -16,6 +16,7 @@ #include #include #include +#include "../sof-client-probes.h" #include "shim.h" /* PCI registers */ @@ -351,13 +352,7 @@ /* Number of DAIs */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -#define SOF_SKL_NUM_DAIS 16 -#else #define SOF_SKL_NUM_DAIS 15 -#endif - #else #define SOF_SKL_NUM_DAIS 8 #endif @@ -575,29 +570,6 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -/* - * Probe Compress Operations. - */ -int hda_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); -int hda_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); -int hda_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai); -int hda_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai); -int hda_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai); -#endif - /* * DSP IPC Operations. */ @@ -729,6 +701,25 @@ extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; +/* Probes support */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) +int hda_probes_register(struct snd_sof_dev *sdev); +void hda_probes_unregister(struct snd_sof_dev *sdev); +#else +static inline int hda_probes_register(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_probes_unregister(struct snd_sof_dev *sdev) +{ +} +#endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */ + +/* SOF client registration for HDA platforms */ +int hda_register_clients(struct snd_sof_dev *sdev); +void hda_unregister_clients(struct snd_sof_dev *sdev); + /* machine driver select */ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); void hda_set_mach_params(struct snd_soc_acpi_mach *mach, diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index f75e3983969f..f20ab60e8a52 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -142,15 +142,6 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -173,6 +164,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = hda_register_clients, + .unregister_ipc_clients = hda_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 7f7929c5cb88..c7d1c244bc48 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -115,15 +115,6 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) - /* probe callbacks */ - .probe_assign = hda_probe_compr_assign, - .probe_free = hda_probe_compr_free, - .probe_set_params = hda_probe_compr_set_params, - .probe_trigger = hda_probe_compr_trigger, - .probe_pointer = hda_probe_compr_pointer, -#endif - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -146,6 +137,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + /* client ops */ + .register_ipc_clients = hda_register_clients, + .unregister_ipc_clients = hda_unregister_clients, + /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 1f84d30296cf..999a36208b11 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -497,49 +497,6 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, return 0; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -static inline int -snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_assign(sdev, cstream, dai); -} - -static inline int -snd_sof_probe_compr_free(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_free(sdev, cstream, dai); -} - -static inline int -snd_sof_probe_compr_set_params(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_set_params(sdev, cstream, params, dai); -} - -static inline int -snd_sof_probe_compr_trigger(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) -{ - return sof_ops(sdev)->probe_trigger(sdev, cstream, cmd, dai); -} - -static inline int -snd_sof_probe_compr_pointer(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) -{ - if (sof_ops(sdev) && sof_ops(sdev)->probe_pointer) - return sof_ops(sdev)->probe_pointer(sdev, cstream, tstamp, dai); - - return 0; -} -#endif - /* machine driver */ static inline int snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 62cb61655761..137f8ed71677 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -15,11 +15,8 @@ #include #include "sof-priv.h" #include "sof-audio.h" -#include "ops.h" -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "sof-probes.h" -#endif #include "sof-utils.h" +#include "ops.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, @@ -925,9 +922,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->pointer = sof_pcm_pointer; pd->ack = sof_pcm_ack; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - pd->compress_ops = &sof_probe_compressed_ops; -#endif pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c new file mode 100644 index 000000000000..797dedb26163 --- /dev/null +++ b/sound/soc/sof/sof-client-probes.c @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2019-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// +// SOF client support: +// Ranjani Sridharan +// Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include "sof-client.h" +#include "sof-client-probes.h" + +#define SOF_PROBES_SUSPEND_DELAY_MS 3000 +/* only extraction supported for now */ +#define SOF_PROBES_NUM_DAI_LINKS 1 + +#define SOF_PROBES_INVALID_NODE_ID UINT_MAX + +static bool __read_mostly sof_probes_enabled; +module_param_named(enable, sof_probes_enabled, bool, 0444); +MODULE_PARM_DESC(enable, "Enable SOF probes support"); + +struct sof_probes_priv { + struct dentry *dfs_points; + struct dentry *dfs_points_remove; + u32 extractor_stream_tag; + struct snd_soc_card card; + + const struct sof_probes_host_ops *host_ops; +}; + +struct sof_probe_point_desc { + unsigned int buffer_id; + unsigned int purpose; + unsigned int stream_tag; +} __packed; + +struct sof_probe_dma { + unsigned int stream_tag; + unsigned int dma_buffer_size; +} __packed; + +struct sof_ipc_probe_dma_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_dma dma[]; +} __packed; + +struct sof_ipc_probe_info_params { + struct sof_ipc_reply rhdr; + unsigned int num_elems; + union { + struct sof_probe_dma dma[0]; + struct sof_probe_point_desc desc[0]; + }; +} __packed; + +struct sof_ipc_probe_point_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_point_desc desc[]; +} __packed; + +struct sof_ipc_probe_point_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int buffer_id[]; +} __packed; + +/** + * sof_probes_init - initialize data probing + * @cdev: SOF client device + * @stream_tag: Extractor stream tag + * @buffer_size: DMA buffer size to set for extractor + * + * Host chooses whether extraction is supported or not by providing + * valid stream tag to DSP. Once specified, stream described by that + * tag will be tied to DSP for extraction for the entire lifetime of + * probe. + * + * Probing is initialized only once and each INIT request must be + * matched by DEINIT call. + */ +static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag, + size_t buffer_size) +{ + struct sof_ipc_probe_dma_add_params *msg; + size_t size = struct_size(msg, dma, 1); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; + msg->num_elems = 1; + msg->dma[0].stream_tag = stream_tag; + msg->dma[0].dma_buffer_size = buffer_size; + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +/** + * sof_probes_deinit - cleanup after data probing + * @cdev: SOF client device + * + * Host sends DEINIT request to free previously initialized probe + * on DSP side once it is no longer needed. DEINIT only when there + * are no probes connected and with all injectors detached. + */ +static int sof_probes_deinit(struct sof_client_dev *cdev) +{ + struct sof_ipc_cmd_hdr msg; + struct sof_ipc_reply reply; + + msg.size = sizeof(msg); + msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; + + return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply)); +} + +static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, + void **params, size_t *num_params) +{ + struct sof_ipc_probe_info_params msg = {{{0}}}; + struct sof_ipc_probe_info_params *reply; + size_t bytes; + int ret; + + *params = NULL; + *num_params = 0; + + reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!reply) + return -ENOMEM; + msg.rhdr.hdr.size = sizeof(msg); + msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; + + ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE); + if (ret < 0 || reply->rhdr.error < 0) + goto exit; + + if (!reply->num_elems) + goto exit; + + if (cmd == SOF_IPC_PROBE_DMA_INFO) + bytes = sizeof(reply->dma[0]); + else + bytes = sizeof(reply->desc[0]); + bytes *= reply->num_elems; + *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); + if (!*params) { + ret = -ENOMEM; + goto exit; + } + *num_params = reply->num_elems; + +exit: + kfree(reply); + return ret; +} + +/** + * sof_probes_points_info - retrieve list of active probe points + * @cdev: SOF client device + * @desc: Returned list of active probes + * @num_desc: Returned count of active probes + * + * Host sends PROBE_POINT_INFO request to obtain list of active probe + * points, valid for disconnection when given probe is no longer + * required. + */ +static int sof_probes_points_info(struct sof_client_dev *cdev, + struct sof_probe_point_desc **desc, + size_t *num_desc) +{ + return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO, + (void **)desc, num_desc); +} + +/** + * sof_probes_points_add - connect specified probes + * @cdev: SOF client device + * @desc: List of probe points to connect + * @num_desc: Number of elements in @desc + * + * Dynamically connects to provided set of endpoints. Immediately + * after connection is established, host must be prepared to + * transfer data from or to target stream given the probing purpose. + * + * Each probe point should be removed using PROBE_POINT_REMOVE + * request when no longer needed. + */ +static int sof_probes_points_add(struct sof_client_dev *cdev, + struct sof_probe_point_desc *desc, + size_t num_desc) +{ + struct sof_ipc_probe_point_add_params *msg; + size_t size = struct_size(msg, desc, num_desc); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_desc; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; + memcpy(&msg->desc[0], desc, size - sizeof(*msg)); + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +/** + * sof_probes_points_remove - disconnect specified probes + * @cdev: SOF client device + * @buffer_id: List of probe points to disconnect + * @num_buffer_id: Number of elements in @desc + * + * Removes previously connected probes from list of active probe + * points and frees all resources on DSP side. + */ +static int sof_probes_points_remove(struct sof_client_dev *cdev, + unsigned int *buffer_id, size_t num_buffer_id) +{ + struct sof_ipc_probe_point_remove_params *msg; + size_t size = struct_size(msg, buffer_id, num_buffer_id); + struct sof_ipc_reply reply; + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_buffer_id; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; + memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); + + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); + kfree(msg); + return ret; +} + +static int sof_probes_compr_startup(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + int ret; + + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + + ret = sof_client_core_module_get(cdev); + if (ret) + return ret; + + ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag); + if (ret) { + dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + sof_client_core_module_put(cdev); + } + + return ret; +} + +static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + struct sof_probe_point_desc *desc; + size_t num_desc; + int i, ret; + + /* disconnect all probe points */ + ret = sof_probes_points_info(cdev, &desc, &num_desc); + if (ret < 0) { + dev_err(dai->dev, "Failed to get probe points: %d\n", ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) + sof_probes_points_remove(cdev, &desc[i].buffer_id, 1); + kfree(desc); + +exit: + ret = sof_probes_deinit(cdev); + if (ret < 0) + dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); + + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + snd_compr_free_pages(cstream); + + ret = ops->free(cdev, cstream, dai); + + sof_client_core_module_put(cdev); + + return ret; +} + +static int sof_probes_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct snd_compr_runtime *rtd = cstream->runtime; + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + int ret; + + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev); + ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); + if (ret < 0) + return ret; + + ret = ops->set_params(cdev, cstream, params, dai); + if (ret) + return ret; + + ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); + if (ret < 0) { + dev_err(dai->dev, "Failed to init probe: %d\n", ret); + return ret; + } + + return 0; +} + +static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + + return ops->trigger(cdev, cstream, cmd, dai); +} + +static int sof_probes_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); + struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); + struct sof_probes_priv *priv = cdev->data; + const struct sof_probes_host_ops *ops = priv->host_ops; + + return ops->pointer(cdev, cstream, tstamp, dai); +} + +static const struct snd_soc_cdai_ops sof_probes_compr_ops = { + .startup = sof_probes_compr_startup, + .shutdown = sof_probes_compr_shutdown, + .set_params = sof_probes_compr_set_params, + .trigger = sof_probes_compr_trigger, + .pointer = sof_probes_compr_pointer, +}; + +static int sof_probes_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + unsigned int offset, n; + void *ptr; + int ret; + + if (count > rtd->buffer_size) + count = rtd->buffer_size; + + div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); + ptr = rtd->dma_area + offset; + n = rtd->buffer_size - offset; + + if (count < n) { + ret = copy_to_user(buf, ptr, count); + } else { + ret = copy_to_user(buf, ptr, n); + ret += copy_to_user(buf + n, rtd->dma_area, count - n); + } + + if (ret) + return count - ret; + return count; +} + +static const struct snd_compress_ops sof_probes_compressed_ops = { + .copy = sof_probes_compr_copy, +}; + +/** + * strsplit_u32 - Split string into sequence of u32 tokens + * @buf: String to split into tokens. + * @delim: String containing delimiter characters. + * @tkns: Returned u32 sequence pointer. + * @num_tkns: Returned number of tokens obtained. + */ +static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns) +{ + char *s; + u32 *data, *tmp; + size_t count = 0; + size_t cap = 32; + int ret = 0; + + *tkns = NULL; + *num_tkns = 0; + data = kcalloc(cap, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + while ((s = strsep(&buf, delim)) != NULL) { + ret = kstrtouint(s, 0, data + count); + if (ret) + goto exit; + if (++count >= cap) { + cap *= 2; + tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto exit; + } + data = tmp; + } + } + + if (!count) + goto exit; + *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); + if (!(*tkns)) { + ret = -ENOMEM; + goto exit; + } + *num_tkns = count; + +exit: + kfree(data); + return ret; +} + +static int tokenize_input(const char __user *from, size_t count, + loff_t *ppos, u32 **tkns, size_t *num_tkns) +{ + char *buf; + int ret; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, ppos, from, count); + if (ret != count) { + ret = ret >= 0 ? -EIO : ret; + goto exit; + } + + buf[count] = '\0'; + ret = strsplit_u32(buf, ",", tkns, num_tkns); +exit: + kfree(buf); + return ret; +} + +static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_probe_point_desc *desc; + int remaining, offset; + size_t num_desc; + char *buf; + int i, ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_info(cdev, &desc, &num_desc); + if (ret < 0) + goto exit; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); + + for (i = 0; i < num_desc; i++) { + offset = strlen(buf); + remaining = PAGE_SIZE - offset; + ret = snprintf(buf + offset, remaining, + "Id: %#010x Purpose: %u Node id: %#x\n", + desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); + if (ret < 0 || ret >= remaining) { + /* truncate the output buffer at the last full line */ + buf[offset] = '\0'; + break; + } + } + + ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf)); + + kfree(desc); +exit: + kfree(buf); + return ret; +} + +static ssize_t +sof_probes_dfs_points_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + struct sof_probe_point_desc *desc; + size_t num_tkns, bytes; + u32 *tkns; + int ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + bytes = sizeof(*tkns) * num_tkns; + if (!num_tkns || (bytes % sizeof(*desc))) { + ret = -EINVAL; + goto exit; + } + + desc = (struct sof_probe_point_desc *)tkns; + + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc)); + if (!ret) + ret = count; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations sof_probes_points_fops = { + .open = simple_open, + .read = sof_probes_dfs_points_read, + .write = sof_probes_dfs_points_write, + .llseek = default_llseek, + + .owner = THIS_MODULE, +}; + +static ssize_t +sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_probes_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + size_t num_tkns; + u32 *tkns; + int ret, err; + + if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { + dev_warn(dev, "no extractor stream running\n"); + return -ENOENT; + } + + ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); + if (ret < 0) + return ret; + if (!num_tkns) { + ret = -EINVAL; + goto exit; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + pm_runtime_put_noidle(dev); + goto exit; + } + + ret = sof_probes_points_remove(cdev, tkns, num_tkns); + if (!ret) + ret = count; + + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); +exit: + kfree(tkns); + return ret; +} + +static const struct file_operations sof_probes_points_remove_fops = { + .open = simple_open, + .write = sof_probes_dfs_points_remove_write, + .llseek = default_llseek, + + .owner = THIS_MODULE, +}; + +static struct snd_soc_dai_driver sof_probes_dai_drv[] = { +{ + .name = "Probe Extraction CPU DAI", + .compress_new = snd_soc_new_compress, + .cops = &sof_probes_compr_ops, + .capture = { + .stream_name = "Probe Extraction", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + }, +}, +}; + +static const struct snd_soc_component_driver sof_probes_component = { + .name = "sof-probes-component", + .compress_ops = &sof_probes_compressed_ops, + .module_get_upon_open = 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) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *dfsroot = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct snd_soc_dai_link_component platform_component[] = { + { + .name = dev_name(dev), + } + }; + struct snd_soc_card *card; + struct sof_probes_priv *priv; + struct snd_soc_dai_link_component *cpus; + struct sof_probes_host_ops *ops; + struct snd_soc_dai_link *links; + int ret; + + /* do not set up the probes support if it is not enabled */ + if (!sof_probes_enabled) + return -ENXIO; + + if (!dev->platform_data) { + dev_err(dev, "missing platform data\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = dev->platform_data; + + if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger || + !ops->pointer) { + dev_err(dev, "missing platform callback(s)\n"); + return -ENODEV; + } + + priv->host_ops = ops; + cdev->data = priv; + + /* register probes component driver and dai */ + ret = devm_snd_soc_register_component(dev, &sof_probes_component, + sof_probes_dai_drv, + ARRAY_SIZE(sof_probes_dai_drv)); + if (ret < 0) { + dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret); + return ret; + } + + /* set client data */ + priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; + + /* create read-write probes_points debugfs entry */ + priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot, + cdev, &sof_probes_points_fops); + + /* create read-write probe_points_remove debugfs entry */ + priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644, + dfsroot, cdev, + &sof_probes_points_remove_fops); + + links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL); + cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL); + if (!links || !cpus) { + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); + return -ENOMEM; + } + + /* extraction DAI link */ + links[0].name = "Compress Probe Capture"; + links[0].id = 0; + 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].num_codecs = 1; + links[0].platforms = platform_component; + links[0].num_platforms = ARRAY_SIZE(platform_component); + links[0].nonatomic = 1; + + card = &priv->card; + + card->dev = dev; + card->name = "sof-probes"; + card->owner = THIS_MODULE; + card->num_links = SOF_PROBES_NUM_DAI_LINKS; + card->dai_link = links; + + /* set idle_bias_off to prevent the core from resuming the card->dev */ + card->dapm.idle_bias_off = true; + + snd_soc_card_set_drvdata(card, cdev); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) { + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); + dev_err(dev, "Probes card register failed %d\n", ret); + return ret; + } + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_probes_client_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_probes_priv *priv = cdev->data; + + if (!sof_probes_enabled) + return; + + pm_runtime_disable(&auxdev->dev); + debugfs_remove(priv->dfs_points); + debugfs_remove(priv->dfs_points_remove); +} + +static const struct auxiliary_device_id sof_probes_client_id_table[] = { + { .name = "snd_sof.hda-probes", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table); + +/* driver name will be set based on KBUILD_MODNAME */ +static struct auxiliary_driver sof_probes_client_drv = { + .probe = sof_probes_client_probe, + .remove = sof_probes_client_remove, + + .id_table = sof_probes_client_id_table, +}; + +module_auxiliary_driver(sof_probes_client_drv); + +MODULE_DESCRIPTION("SOF Probes Client Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client-probes.h b/sound/soc/sof/sof-client-probes.h new file mode 100644 index 000000000000..0f9ed4569fd3 --- /dev/null +++ b/sound/soc/sof/sof-client-probes.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_PROBES_H +#define __SOF_CLIENT_PROBES_H + +struct snd_compr_stream; +struct snd_compr_tstamp; +struct snd_compr_params; +struct sof_client_dev; +struct snd_soc_dai; + +/* + * Callbacks used on platforms where the control for audio is split between + * DSP and host, like HDA. + */ +struct sof_probes_host_ops { + int (*assign)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_soc_dai *dai, u32 *stream_id); + int (*free)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_soc_dai *dai); + int (*set_params)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai); + int (*trigger)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + int cmd, struct snd_soc_dai *dai); + int (*pointer)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai); +}; + +#endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2529408a4e90..2c8e556cd5cc 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -201,27 +201,6 @@ struct snd_sof_dsp_ops { /* pcm ack */ int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - /* Except for probe_pointer, all probe ops are mandatory */ - int (*probe_assign)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_free)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_set_params)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_trigger)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai); /* mandatory */ - int (*probe_pointer)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai); /* optional */ -#endif - /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, @@ -446,10 +425,6 @@ struct snd_sof_dev { int ipc_timeout; int boot_timeout; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - unsigned int extractor_stream_tag; -#endif - /* DMA for Trace */ struct snd_dma_buffer dmatb; struct snd_dma_buffer dmatp; diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c deleted file mode 100644 index c79026cdb8c7..000000000000 --- a/sound/soc/sof/sof-probes.c +++ /dev/null @@ -1,364 +0,0 @@ -// 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) 2019-2021 Intel Corporation. All rights reserved. -// Author: Cezary Rojewski -// - -#include -#include "ops.h" -#include "sof-priv.h" -#include "sof-probes.h" - -struct sof_probe_dma { - unsigned int stream_tag; - unsigned int dma_buffer_size; -} __packed; - -struct sof_ipc_probe_dma_add_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - struct sof_probe_dma dma[]; -} __packed; - -struct sof_ipc_probe_info_params { - struct sof_ipc_reply rhdr; - unsigned int num_elems; - union { - struct sof_probe_dma dma[0]; - struct sof_probe_point_desc desc[0]; - }; -} __packed; - -struct sof_ipc_probe_point_add_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - struct sof_probe_point_desc desc[]; -} __packed; - -struct sof_ipc_probe_point_remove_params { - struct sof_ipc_cmd_hdr hdr; - unsigned int num_elems; - unsigned int buffer_id[]; -} __packed; - -/** - * sof_ipc_probe_init - initialize data probing - * @sdev: SOF sound device - * @stream_tag: Extractor stream tag - * @buffer_size: DMA buffer size to set for extractor - * - * Host chooses whether extraction is supported or not by providing - * valid stream tag to DSP. Once specified, stream described by that - * tag will be tied to DSP for extraction for the entire lifetime of - * probe. - * - * Probing is initialized only once and each INIT request must be - * matched by DEINIT call. - */ -static int sof_ipc_probe_init(struct snd_sof_dev *sdev, u32 stream_tag, - size_t buffer_size) -{ - struct sof_ipc_probe_dma_add_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, dma, 1); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; - msg->num_elems = 1; - msg->dma[0].stream_tag = stream_tag; - msg->dma[0].dma_buffer_size = buffer_size; - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} - -/** - * sof_ipc_probe_deinit - cleanup after data probing - * @sdev: SOF sound device - * - * Host sends DEINIT request to free previously initialized probe - * on DSP side once it is no longer needed. DEINIT only when there - * are no probes connected and with all injectors detached. - */ -static int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) -{ - struct sof_ipc_cmd_hdr msg; - struct sof_ipc_reply reply; - - msg.size = sizeof(msg); - msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; - - return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, - &reply, sizeof(reply)); -} - -static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, - void **params, size_t *num_params) -{ - struct sof_ipc_probe_info_params msg = {{{0}}}; - struct sof_ipc_probe_info_params *reply; - size_t bytes; - int ret; - - *params = NULL; - *num_params = 0; - - reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!reply) - return -ENOMEM; - msg.rhdr.hdr.size = sizeof(msg); - msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; - - ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, - msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); - if (ret < 0 || reply->rhdr.error < 0) - goto exit; - - if (!reply->num_elems) - goto exit; - - if (cmd == SOF_IPC_PROBE_DMA_INFO) - bytes = sizeof(reply->dma[0]); - else - bytes = sizeof(reply->desc[0]); - bytes *= reply->num_elems; - *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); - if (!*params) { - ret = -ENOMEM; - goto exit; - } - *num_params = reply->num_elems; - -exit: - kfree(reply); - return ret; -} - -/** - * sof_ipc_probe_points_info - retrieve list of active probe points - * @sdev: SOF sound device - * @desc: Returned list of active probes - * @num_desc: Returned count of active probes - * - * Host sends PROBE_POINT_INFO request to obtain list of active probe - * points, valid for disconnection when given probe is no longer - * required. - */ -int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, - struct sof_probe_point_desc **desc, - size_t *num_desc) -{ - return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, - (void **)desc, num_desc); -} -EXPORT_SYMBOL(sof_ipc_probe_points_info); - -/** - * sof_ipc_probe_points_add - connect specified probes - * @sdev: SOF sound device - * @desc: List of probe points to connect - * @num_desc: Number of elements in @desc - * - * Dynamically connects to provided set of endpoints. Immediately - * after connection is established, host must be prepared to - * transfer data from or to target stream given the probing purpose. - * - * Each probe point should be removed using PROBE_POINT_REMOVE - * request when no longer needed. - */ -int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, - struct sof_probe_point_desc *desc, size_t num_desc) -{ - struct sof_ipc_probe_point_add_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, desc, num_desc); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->num_elems = num_desc; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; - memcpy(&msg->desc[0], desc, size - sizeof(*msg)); - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} -EXPORT_SYMBOL(sof_ipc_probe_points_add); - -/** - * sof_ipc_probe_points_remove - disconnect specified probes - * @sdev: SOF sound device - * @buffer_id: List of probe points to disconnect - * @num_buffer_id: Number of elements in @desc - * - * Removes previously connected probes from list of active probe - * points and frees all resources on DSP side. - */ -int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, - unsigned int *buffer_id, size_t num_buffer_id) -{ - struct sof_ipc_probe_point_remove_params *msg; - struct sof_ipc_reply reply; - size_t size = struct_size(msg, buffer_id, num_buffer_id); - int ret; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - msg->hdr.size = size; - msg->num_elems = num_buffer_id; - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; - memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); - - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - kfree(msg); - return ret; -} -EXPORT_SYMBOL(sof_ipc_probe_points_remove); - -static int sof_probe_compr_startup(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - int ret; - - ret = snd_sof_probe_compr_assign(sdev, cstream, dai); - if (ret < 0) { - dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); - return ret; - } - - sdev->extractor_stream_tag = ret; - return 0; -} - -static int sof_probe_compr_shutdown(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - struct sof_probe_point_desc *desc; - size_t num_desc; - int i, ret; - - /* disconnect all probe points */ - ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); - if (ret < 0) { - dev_err(dai->dev, "Failed to get probe points: %d\n", ret); - goto exit; - } - - for (i = 0; i < num_desc; i++) - sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1); - kfree(desc); - -exit: - ret = sof_ipc_probe_deinit(sdev); - if (ret < 0) - dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); - - sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; - snd_compr_free_pages(cstream); - - return snd_sof_probe_compr_free(sdev, cstream, dai); -} - -static int sof_probe_compr_set_params(struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - struct snd_compr_runtime *rtd = cstream->runtime; - int ret; - - cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; - cstream->dma_buffer.dev.dev = sdev->dev; - ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); - if (ret < 0) - return ret; - - ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai); - if (ret < 0) - return ret; - - ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag, - rtd->dma_bytes); - if (ret < 0) { - dev_err(dai->dev, "Failed to init probe: %d\n", ret); - return ret; - } - - return 0; -} - -static int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai); -} - -static int sof_probe_compr_pointer(struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); -} - -const struct snd_soc_cdai_ops sof_probe_compr_ops = { - .startup = sof_probe_compr_startup, - .shutdown = sof_probe_compr_shutdown, - .set_params = sof_probe_compr_set_params, - .trigger = sof_probe_compr_trigger, - .pointer = sof_probe_compr_pointer, -}; -EXPORT_SYMBOL(sof_probe_compr_ops); - -static int sof_probe_compr_copy(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - char __user *buf, size_t count) -{ - struct snd_compr_runtime *rtd = cstream->runtime; - unsigned int offset, n; - void *ptr; - int ret; - - if (count > rtd->buffer_size) - count = rtd->buffer_size; - - div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); - ptr = rtd->dma_area + offset; - n = rtd->buffer_size - offset; - - if (count < n) { - ret = copy_to_user(buf, ptr, count); - } else { - ret = copy_to_user(buf, ptr, n); - ret += copy_to_user(buf + n, rtd->dma_area, count - n); - } - - if (ret) - return count - ret; - return count; -} - -const struct snd_compress_ops sof_probe_compressed_ops = { - .copy = sof_probe_compr_copy, -}; -EXPORT_SYMBOL(sof_probe_compressed_ops); diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h deleted file mode 100644 index 4a1ed2942d28..000000000000 --- a/sound/soc/sof/sof-probes.h +++ /dev/null @@ -1,38 +0,0 @@ -/* 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) 2019-2021 Intel Corporation. All rights reserved. - * Author: Cezary Rojewski - */ - -#ifndef __SOF_PROBES_H -#define __SOF_PROBES_H - -#include -#include - -struct snd_sof_dev; - -#define SOF_PROBE_INVALID_NODE_ID UINT_MAX - -struct sof_probe_point_desc { - unsigned int buffer_id; - unsigned int purpose; - unsigned int stream_tag; -} __packed; - -int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, - struct sof_probe_point_desc **desc, - size_t *num_desc); -int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, - struct sof_probe_point_desc *desc, - size_t num_desc); -int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, - unsigned int *buffer_id, size_t num_buffer_id); - -extern const struct snd_soc_cdai_ops sof_probe_compr_ops; -extern const struct snd_compress_ops sof_probe_compressed_ops; - -#endif -- cgit v1.2.3 From 9193bc0558d1812343039b510797b669f054efc5 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Thu, 10 Feb 2022 18:07:36 +0100 Subject: ASoC: tlv320adc3xxx: Add IIR filter configuration The TLV320ADC3001/3101 have an internal DSP, which can either be used in various preset configurations (called "Processing Blocks" in the data sheet), or as a freely programmable (using the "PurePath Studio" graphical programming tool from TI) but rather small DSP ("miniDSP"). Using the default configuration (PRB_R1) it's possible to set up filtering using a first-order IIR, which can be useful for adding a digital high pass filter to the signal chain, for instance. This patch adds support for configuring the IIR filter coefficients. The filter itself is always enabled; the default coefficients implement a pass-through function. Signed-off-by: Ricard Wanderlof Link: https://lore.kernel.org/r/alpine.DEB.2.21.2202101805360.7068@lnxricardw1.se.axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 143 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 4baf3d881633..1d12a3f7074a 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -169,6 +169,23 @@ #define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62) /* 63-127 Reserved */ +/* + * Page 4 registers. First page of coefficient memory for the miniDSP. + */ +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 8) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 9) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 10) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 11) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 12) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 13) + +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 72) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 73) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 74) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 75) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 76) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 77) + /* * Register bits. */ @@ -373,6 +390,40 @@ static const struct reg_default adc3xxx_defaults[] = { { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff }, { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 }, { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 }, + + /* Page 4 */ + { 1024, 0x00 }, { 1026, 0x01 }, { 1027, 0x17 }, + { 1028, 0x01 }, { 1029, 0x17 }, { 1030, 0x7d }, { 1031, 0xd3 }, + { 1032, 0x7f }, { 1033, 0xff }, { 1034, 0x00 }, { 1035, 0x00 }, + { 1036, 0x00 }, { 1037, 0x00 }, { 1038, 0x7f }, { 1039, 0xff }, + { 1040, 0x00 }, { 1041, 0x00 }, { 1042, 0x00 }, { 1043, 0x00 }, + { 1044, 0x00 }, { 1045, 0x00 }, { 1046, 0x00 }, { 1047, 0x00 }, + { 1048, 0x7f }, { 1049, 0xff }, { 1050, 0x00 }, { 1051, 0x00 }, + { 1052, 0x00 }, { 1053, 0x00 }, { 1054, 0x00 }, { 1055, 0x00 }, + { 1056, 0x00 }, { 1057, 0x00 }, { 1058, 0x7f }, { 1059, 0xff }, + { 1060, 0x00 }, { 1061, 0x00 }, { 1062, 0x00 }, { 1063, 0x00 }, + { 1064, 0x00 }, { 1065, 0x00 }, { 1066, 0x00 }, { 1067, 0x00 }, + { 1068, 0x7f }, { 1069, 0xff }, { 1070, 0x00 }, { 1071, 0x00 }, + { 1072, 0x00 }, { 1073, 0x00 }, { 1074, 0x00 }, { 1075, 0x00 }, + { 1076, 0x00 }, { 1077, 0x00 }, { 1078, 0x7f }, { 1079, 0xff }, + { 1080, 0x00 }, { 1081, 0x00 }, { 1082, 0x00 }, { 1083, 0x00 }, + { 1084, 0x00 }, { 1085, 0x00 }, { 1086, 0x00 }, { 1087, 0x00 }, + { 1088, 0x00 }, { 1089, 0x00 }, { 1090, 0x00 }, { 1091, 0x00 }, + { 1092, 0x00 }, { 1093, 0x00 }, { 1094, 0x00 }, { 1095, 0x00 }, + { 1096, 0x00 }, { 1097, 0x00 }, { 1098, 0x00 }, { 1099, 0x00 }, + { 1100, 0x00 }, { 1101, 0x00 }, { 1102, 0x00 }, { 1103, 0x00 }, + { 1104, 0x00 }, { 1105, 0x00 }, { 1106, 0x00 }, { 1107, 0x00 }, + { 1108, 0x00 }, { 1109, 0x00 }, { 1110, 0x00 }, { 1111, 0x00 }, + { 1112, 0x00 }, { 1113, 0x00 }, { 1114, 0x00 }, { 1115, 0x00 }, + { 1116, 0x00 }, { 1117, 0x00 }, { 1118, 0x00 }, { 1119, 0x00 }, + { 1120, 0x00 }, { 1121, 0x00 }, { 1122, 0x00 }, { 1123, 0x00 }, + { 1124, 0x00 }, { 1125, 0x00 }, { 1126, 0x00 }, { 1127, 0x00 }, + { 1128, 0x00 }, { 1129, 0x00 }, { 1130, 0x00 }, { 1131, 0x00 }, + { 1132, 0x00 }, { 1133, 0x00 }, { 1134, 0x00 }, { 1135, 0x00 }, + { 1136, 0x00 }, { 1137, 0x00 }, { 1138, 0x00 }, { 1139, 0x00 }, + { 1140, 0x00 }, { 1141, 0x00 }, { 1142, 0x00 }, { 1143, 0x00 }, + { 1144, 0x00 }, { 1145, 0x00 }, { 1146, 0x00 }, { 1147, 0x00 }, + { 1148, 0x00 }, { 1149, 0x00 }, { 1150, 0x00 }, { 1151, 0x00 }, }; static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg) @@ -388,7 +439,7 @@ static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_range_cfg adc3xxx_ranges[] = { { .range_min = 0, - .range_max = 2 * ADC3XXX_PAGE_SIZE, + .range_max = 5 * ADC3XXX_PAGE_SIZE, .selector_reg = ADC3XXX_PAGE_SELECT, .selector_mask = 0xff, .selector_shift = 0, @@ -410,7 +461,7 @@ static const struct regmap_config adc3xxx_regmap = { .ranges = adc3xxx_ranges, .num_ranges = ARRAY_SIZE(adc3xxx_ranges), - .max_register = 2 * ADC3XXX_PAGE_SIZE, + .max_register = 5 * ADC3XXX_PAGE_SIZE, }; struct adc3xxx_rate_divs { @@ -497,6 +548,83 @@ static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w, return 0; } +static int adc3xxx_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoeff = kcontrol->private_value >> 16; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = numcoeff; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; /* all coefficients are 16 bit */ + return 0; +} + +static int adc3xxx_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int numcoeff = kcontrol->private_value >> 16; + int reg = kcontrol->private_value & 0xffff; + int index = 0; + + for (index = 0; index < numcoeff; index++) { + unsigned int value_msb, value_lsb, value; + + value_msb = snd_soc_component_read(component, reg++); + if ((int)value_msb < 0) + return (int)value_msb; + + value_lsb = snd_soc_component_read(component, reg++); + if ((int)value_lsb < 0) + return (int)value_lsb; + + value = (value_msb << 8) | value_lsb; + ucontrol->value.integer.value[index] = value; + } + + return 0; +} + +static int adc3xxx_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int numcoeff = kcontrol->private_value >> 16; + int reg = kcontrol->private_value & 0xffff; + int index = 0; + int ret; + + for (index = 0; index < numcoeff; index++) { + unsigned int value = ucontrol->value.integer.value[index]; + unsigned int value_msb = (value >> 8) & 0xff; + unsigned int value_lsb = value & 0xff; + + ret = snd_soc_component_write(component, reg++, value_msb); + if (ret) + return ret; + + ret = snd_soc_component_write(component, reg++, value_lsb); + if (ret) + return ret; + } + + return 0; +} + +/* All on-chip filters have coefficients which are expressed in terms of + * 16 bit values, so represent them as strings of 16-bit integers. + */ +#define TI_COEFFICIENTS(xname, reg, numcoeffs) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = adc3xxx_coefficient_info, \ + .get = adc3xxx_coefficient_get,\ + .put = adc3xxx_coefficient_put, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .private_value = reg | (numcoeffs << 16) \ +} + static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" }; static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0, adc_softstepping_text); @@ -640,6 +768,17 @@ static const struct snd_kcontrol_new adc3xxx_snd_controls[] = { SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch", ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0), SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum), + + /* Coefficient memory for miniDSP. */ + /* For the default PRB_R1 processing block, the only available + * filter is the first order IIR. + */ + + TI_COEFFICIENTS("Left ADC IIR Coefficients N0 N1 D1", + ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB, 3), + + TI_COEFFICIENTS("Right ADC IIR Coefficients N0 N1 D1", + ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB, 3), }; /* Left input selection, Single Ended inputs and Differential inputs */ -- cgit v1.2.3 From 0f1d41a85bda6f3502634fe15fa21bfee4c668a4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 10 Feb 2022 17:20:52 +0000 Subject: ASoC: wm_adsp: Make compressed buffers optional Newer firmwares will support compressed buffers that may or may not exist, for example debugging streams. Update the driver to make a compressed stream optional. A warning will still be generated at DSP boot time and opening the stream will fail if the compressed buffer in question does not exist, however the DSP can still be booted and other features used. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220210172053.22782-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 54 ++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f3672e3d1703..8b9726f400a9 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1373,8 +1373,6 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) wm_adsp_buffer_clear(buf); - list_add_tail(&buf->list, &dsp->buffer_list); - return buf; } @@ -1391,10 +1389,6 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) return -EINVAL; } - buf = wm_adsp_buffer_alloc(dsp); - if (!buf) - return -ENOMEM; - xmalg = dsp->sys_config_size / sizeof(__be32); addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); @@ -1405,12 +1399,16 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) return -ENODEV; + buf = wm_adsp_buffer_alloc(dsp); + if (!buf) + return -ENOMEM; + addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); for (i = 0; i < 5; ++i) { ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &buf->host_buf_ptr); if (ret < 0) - return ret; + goto err; if (buf->host_buf_ptr) break; @@ -1418,18 +1416,27 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) usleep_range(1000, 2000); } - if (!buf->host_buf_ptr) - return -EIO; + if (!buf->host_buf_ptr) { + ret = -EIO; + goto err; + } buf->host_buf_mem_type = WMFW_ADSP2_XM; ret = wm_adsp_buffer_populate(buf); if (ret < 0) - return ret; + goto err; + + list_add_tail(&buf->list, &dsp->buffer_list); compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); return 0; + +err: + kfree(buf); + + return ret; } static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) @@ -1437,7 +1444,7 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) struct wm_adsp_host_buf_coeff_v1 coeff_v1; struct wm_adsp_compr_buf *buf; struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); - unsigned int version; + unsigned int version = 0; int ret, i; for (i = 0; i < 5; ++i) { @@ -1465,16 +1472,14 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) ret = wm_adsp_buffer_populate(buf); if (ret < 0) - return ret; + goto err; /* * v0 host_buffer coefficients didn't have versioning, so if the * control is one word, assume version 0. */ - if (cs_ctl->len == 4) { - compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); - return 0; - } + if (cs_ctl->len == 4) + goto done; version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; @@ -1483,7 +1488,8 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) adsp_err(dsp, "Host buffer coeff ver %u > supported version %u\n", version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); - return -EINVAL; + ret = -EINVAL; + goto err; } cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); @@ -1491,10 +1497,18 @@ static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part, (char *)&coeff_v1.name); +done: + list_add_tail(&buf->list, &dsp->buffer_list); + compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", buf->host_buf_ptr, version); return version; + +err: + kfree(buf); + + return ret; } static int wm_adsp_buffer_init(struct wm_adsp *dsp) @@ -1522,10 +1536,8 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp) if (list_empty(&dsp->buffer_list)) { /* Fall back to legacy support */ ret = wm_adsp_buffer_parse_legacy(dsp); - if (ret) { - adsp_err(dsp, "Failed to parse legacy: %d\n", ret); - goto error; - } + if (ret) + adsp_warn(dsp, "Failed to parse legacy: %d\n", ret); } return 0; -- cgit v1.2.3 From c55b3e46cb99a8342cad9c1a35485bfe15187832 Mon Sep 17 00:00:00 2001 From: Vlad Karpovich Date: Thu, 10 Feb 2022 17:20:53 +0000 Subject: ASoC: wm_adsp: Add trace caps to speaker protection FW Enable access to the speaker protection firmware debug stream using compress stream API and lower minimum fragment size to 16 words. Signed-off-by: Vlad Karpovich Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20220210172053.22782-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 8b9726f400a9..c8c8338cb401 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -180,7 +180,7 @@ struct wm_adsp_compr { #define WM_ADSP_MIN_FRAGMENTS 1 #define WM_ADSP_MAX_FRAGMENTS 256 -#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * CS_DSP_DATA_WORD_SIZE) +#define WM_ADSP_MIN_FRAGMENT_SIZE (16 * CS_DSP_DATA_WORD_SIZE) #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 @@ -296,7 +296,12 @@ static const struct { .num_caps = ARRAY_SIZE(trace_caps), .caps = trace_caps, }, - [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, + [WM_ADSP_FW_SPK_PROT] = { + .file = "spk-prot", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(trace_caps), + .caps = trace_caps, + }, [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, [WM_ADSP_FW_MISC] = { .file = "misc" }, -- cgit v1.2.3 From 695c105933cfa04ccf84088342193ae43e37e0f5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 10 Feb 2022 21:42:17 +0100 Subject: ASoC: Intel: bytcr_wm5102: use GFP_KERNEL Platform_driver probe functions aren't called with locks held and thus don't need GFP_ATOMIC. Use GFP_KERNEL instead. Problem found with Coccinelle. Signed-off-by: Julia Lawall Reviewed-by: Hans de Goede Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220210204223.104181-4-Julia.Lawall@inria.fr Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_wm5102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index 504ef4cab111..8d8e96e3cd2d 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -389,7 +389,7 @@ static int snd_byt_wm5102_mc_probe(struct platform_device *pdev) bool sof_parent; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_ATOMIC); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; -- cgit v1.2.3 From e7c799e76f070b4ac13498e532574466064ad6a5 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 11 Feb 2022 10:26:31 +0200 Subject: ASoC: SOF: compr: Mark snd_compress_ops static Functions won't be directly used outside of compress.c file so mark them as static. This will also fix warnings reported by kernel test robot: >> sound/soc/sof/compress.c:91:5: warning: no previous prototype for function 'sof_compr_open' [-Wmissing-prototypes] int sof_compr_open(struct snd_soc_component *component, ^ sound/soc/sof/compress.c:91:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int sof_compr_open(struct snd_soc_component *component, Fixes: 6324cf901e14 ("SoC: SOF: compr: Add compress ops implementation") Reported-by: kernel test robot Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20220211082631.179735-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/compress.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 2137d1d65dd9..2af8d75204e9 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -89,8 +89,8 @@ static int create_page_table(struct snd_soc_component *component, spcm->stream[dir].page_table.area, size); } -int sof_compr_open(struct snd_soc_component *component, - struct snd_compr_stream *cstream) +static int sof_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *crtd = cstream->runtime; @@ -125,8 +125,8 @@ int sof_compr_open(struct snd_soc_component *component, return 0; } -int sof_compr_free(struct snd_soc_component *component, - struct snd_compr_stream *cstream) +static int sof_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_compr_tstamp *tstamp = cstream->runtime->private_data; @@ -159,8 +159,8 @@ int sof_compr_free(struct snd_soc_component *component, return ret; } -int sof_compr_set_params(struct snd_soc_component *component, - struct snd_compr_stream *cstream, struct snd_compr_params *params) +static int sof_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_compr_params *params) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -222,8 +222,8 @@ int sof_compr_set_params(struct snd_soc_component *component, return 0; } -int sof_compr_get_params(struct snd_soc_component *component, - struct snd_compr_stream *cstream, struct snd_codec *params) +static int sof_compr_get_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_codec *params) { /* TODO: we don't query the supported codecs for now, if the * application asks for an unsupported codec the set_params() will fail. @@ -231,8 +231,8 @@ int sof_compr_get_params(struct snd_soc_component *component, return 0; } -int sof_compr_trigger(struct snd_soc_component *component, - struct snd_compr_stream *cstream, int cmd) +static int sof_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -271,9 +271,9 @@ int sof_compr_trigger(struct snd_soc_component *component, &reply, sizeof(reply)); } -int sof_compr_copy(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - char __user *buf, size_t count) +static int sof_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) { struct snd_compr_runtime *rtd = cstream->runtime; unsigned int offset, n; -- cgit v1.2.3 From a531caa5989e0913ffe8c1a224a130ccd8d2c02c Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 10 Feb 2022 20:54:23 +0200 Subject: ALSA: hda: Add PCI and HDMI IDs for Intel Raptor Lake Add a set of HD Audio PCI IDs, and the HDMI codec VID, for Intel Raptor Lake. Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220210185423.3671603-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 11 +++++++++++ sound/pci/hda/patch_hdmi.c | 1 + 2 files changed, 12 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a2922233e85f..a3c4d6e293e1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2510,6 +2510,17 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, { PCI_DEVICE(0x8086, 0x4b58), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Raptor Lake */ + { PCI_DEVICE(0x8086, 0x7a50), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51ca), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cb), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51ce), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cf), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 92df4f243ec6..64fe025fda86 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4390,6 +4390,7 @@ HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -- cgit v1.2.3 From bad03efd11dfa6f039fe494207996c482e9364d0 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Wed, 9 Feb 2022 07:01:33 -0800 Subject: ALSA: cleanup double word in comment Remove the second 'device'. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20220209150133.2291856-1-trix@redhat.com Signed-off-by: Takashi Iwai --- include/sound/hda_verbs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index e36b77531c5c..006d358acce2 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -461,7 +461,7 @@ enum { #define AC_DE_ELDV (1<<1) #define AC_DE_IA (1<<2) -/* device device types (0x0-0xf) */ +/* device types (0x0-0xf) */ enum { AC_JACK_LINE_OUT, AC_JACK_SPEAKER, -- cgit v1.2.3 From 0c483a07e92638aca1f7d42a4986e32c58d29ad2 Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Tue, 8 Feb 2022 11:42:17 +0300 Subject: ASoC: pcm3168a: cleanup unintuitive mask usage When checking if the requested parameters are supported, the driver uses PCM3168A_FMT_DSP_MASK to check for PCM3168A_FMT_DSP_* values. However, formally not only PCM3168A_FMT_DSP_* values match that condition, PCM3168A_FMT_I2S_TDM and PCM3168A_FMT_LEFT_J_TDM also do. The check still gives correct result because those extra values can't be in 'fmt' at the check location. Still, to make the code less cryptic, better to compare 'fmt' with PCM3168A_FMT_DSP_* values explicitly. Signed-off-by: Nikita Yushchenko Link: https://lore.kernel.org/r/20220208084220.1289836-2-nikita.yoush@cogentembedded.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index fdf92c8b28e1..987c5845f769 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -33,7 +33,6 @@ #define PCM3168A_FMT_DSP_B 0x5 #define PCM3168A_FMT_I2S_TDM 0x6 #define PCM3168A_FMT_LEFT_J_TDM 0x7 -#define PCM3168A_FMT_DSP_MASK 0x4 #define PCM3168A_NUM_SUPPLIES 6 static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { @@ -511,7 +510,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, fmt = PCM3168A_FMT_RIGHT_J_16; break; case 24: - if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) { + if (master_mode || (fmt == PCM3168A_FMT_DSP_A) || + (fmt == PCM3168A_FMT_DSP_B)) { dev_err(component->dev, "24-bit slots not supported in master mode, or slave mode using DSP\n"); return -EINVAL; } -- cgit v1.2.3 From c7270209fc6fc377ba5813e8d5b2ce2b26352ee7 Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Tue, 8 Feb 2022 11:42:18 +0300 Subject: ASoC: pcm3168a: refactor hw_params routine - group together code lines that calculate value for msad/msda field - rename variables to better match their meaning: val -> ms, max_ratio -> num_scki_ratios - update variable types to match exactly parameters or return types of the calls where those variables are used - write two fields of the same register in a single regmap call Signed-off-by: Nikita Yushchenko Link: https://lore.kernel.org/r/20220208084220.1289836-3-nikita.yoush@cogentembedded.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 67 ++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 987c5845f769..526e4562ccb5 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -462,40 +462,45 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; bool master_mode; - u32 val, mask, shift, reg; - unsigned int rate, fmt, ratio, max_ratio; - unsigned int tdm_slots; - int i, slot_width; - - rate = params_rate(params); - - ratio = pcm3168a->sysclk / rate; + unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots; + int i, num_scki_ratios, slot_width; if (dai->id == PCM3168A_DAI_DAC) { - max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC; + num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_DAC; reg = PCM3168A_DAC_PWR_MST_FMT; - mask = PCM3168A_DAC_MSDA_MASK; - shift = PCM3168A_DAC_MSDA_SHIFT; + mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK; + ms_shift = PCM3168A_DAC_MSDA_SHIFT; + fmt_shift = PCM3168A_DAC_FMT_SHIFT; } else { - max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC; + num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_ADC; reg = PCM3168A_ADC_MST_FMT; - mask = PCM3168A_ADC_MSAD_MASK; - shift = PCM3168A_ADC_MSAD_SHIFT; + mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK; + ms_shift = PCM3168A_ADC_MSAD_SHIFT; + fmt_shift = PCM3168A_ADC_FMTAD_SHIFT; } master_mode = io_params->master_mode; - fmt = io_params->fmt; - for (i = 0; i < max_ratio; i++) { - if (pcm3168a_scki_ratios[i] == ratio) - break; - } + if (master_mode) { + ratio = pcm3168a->sysclk / params_rate(params); - if (i == max_ratio) { - dev_err(component->dev, "unsupported sysclk ratio\n"); - return -EINVAL; + for (i = 0; i < num_scki_ratios; i++) { + if (pcm3168a_scki_ratios[i] == ratio) + break; + } + + if (i == num_scki_ratios) { + dev_err(component->dev, "unsupported sysclk ratio\n"); + return -EINVAL; + } + + ms = (i + 1); + } else { + ms = 0; } + fmt = io_params->fmt; + if (io_params->slot_width) slot_width = io_params->slot_width; else @@ -553,22 +558,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, } } - if (master_mode) - val = ((i + 1) << shift); - else - val = 0; - - regmap_update_bits(pcm3168a->regmap, reg, mask, val); - - if (dai->id == PCM3168A_DAI_DAC) { - mask = PCM3168A_DAC_FMT_MASK; - shift = PCM3168A_DAC_FMT_SHIFT; - } else { - mask = PCM3168A_ADC_FMTAD_MASK; - shift = PCM3168A_ADC_FMTAD_SHIFT; - } - - regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); + regmap_update_bits(pcm3168a->regmap, reg, mask, + (ms << ms_shift) | (fmt << fmt_shift)); return 0; } -- cgit v1.2.3 From 6bfc1242ee995f23f8c167bf1308a43b86560fce Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Tue, 8 Feb 2022 11:42:19 +0300 Subject: ASoC: pcm3168a: refactor format handling - drop incomplete (not tdm-aware) calculation/setting of hardware fmt value from pcm3168a_set_dai_fmt(); instead, store original SND_SOC_DAIFMT* setting in io_params - in pcm3168a_hw_params(), do all checks in terms of SND_SOC_DAIFMT*, and convert that to register bitfield values only to write to hardware Signed-off-by: Nikita Yushchenko Link: https://lore.kernel.org/r/20220208084220.1289836-4-nikita.yoush@cogentembedded.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 84 ++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 526e4562ccb5..1d3821f2c5f1 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -50,7 +50,7 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { /* ADC/DAC side parameters */ struct pcm3168a_io_params { bool master_mode; - unsigned int fmt; + unsigned int format; int tdm_slots; u32 tdm_mask; int slot_width; @@ -328,10 +328,11 @@ static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE; unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6; - if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) { + if (io_params->format == SND_SOC_DAIFMT_RIGHT_J) { /* S16_LE is only supported in RIGHT_J mode */ formats |= SNDRV_PCM_FMTBIT_S16_LE; @@ -339,7 +340,7 @@ static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai) * If multi DIN/DOUT is not selected, RIGHT_J can only support * two channels (no TDM support) */ - if (pcm3168a->io_params[dai->id].tdm_slots != 2) + if (io_params->tdm_slots != 2) channel_max = 2; } @@ -356,24 +357,15 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) { struct snd_soc_component *component = dai->component; struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); - u32 fmt, reg, mask, shift; + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; bool master_mode; switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_LEFT_J: - fmt = PCM3168A_FMT_LEFT_J; - break; case SND_SOC_DAIFMT_I2S: - fmt = PCM3168A_FMT_I2S; - break; case SND_SOC_DAIFMT_RIGHT_J: - fmt = PCM3168A_FMT_RIGHT_J; - break; case SND_SOC_DAIFMT_DSP_A: - fmt = PCM3168A_FMT_DSP_A; - break; case SND_SOC_DAIFMT_DSP_B: - fmt = PCM3168A_FMT_DSP_B; break; default: dev_err(component->dev, "unsupported dai format\n"); @@ -399,20 +391,8 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) return -EINVAL; } - if (dai->id == PCM3168A_DAI_DAC) { - reg = PCM3168A_DAC_PWR_MST_FMT; - mask = PCM3168A_DAC_FMT_MASK; - shift = PCM3168A_DAC_FMT_SHIFT; - } else { - reg = PCM3168A_ADC_MST_FMT; - mask = PCM3168A_ADC_FMTAD_MASK; - shift = PCM3168A_ADC_FMTAD_SHIFT; - } - - pcm3168a->io_params[dai->id].master_mode = master_mode; - pcm3168a->io_params[dai->id].fmt = fmt; - - regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); + io_params->master_mode = master_mode; + io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK; pcm3168a_update_fixup_pcm_stream(dai); @@ -461,7 +441,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; - bool master_mode; + bool master_mode, tdm_mode; + unsigned int format; unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots; int i, num_scki_ratios, slot_width; @@ -499,7 +480,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, ms = 0; } - fmt = io_params->fmt; + format = io_params->format; if (io_params->slot_width) slot_width = io_params->slot_width; @@ -508,15 +489,14 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, switch (slot_width) { case 16: - if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) { + if (master_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) { dev_err(component->dev, "16-bit slots are supported only for slave mode using right justified\n"); return -EINVAL; } - fmt = PCM3168A_FMT_RIGHT_J_16; break; case 24: - if (master_mode || (fmt == PCM3168A_FMT_DSP_A) || - (fmt == PCM3168A_FMT_DSP_B)) { + if (master_mode || (format == SND_SOC_DAIFMT_DSP_A) || + (format == SND_SOC_DAIFMT_DSP_B)) { dev_err(component->dev, "24-bit slots not supported in master mode, or slave mode using DSP\n"); return -EINVAL; } @@ -541,15 +521,14 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, * If pcm3168a->tdm_slots is set to 2 then DIN1/2/3/4 and DOUT1/2/3 is * used in normal mode, no need to switch to TDM modes. */ - if (tdm_slots > 2) { - switch (fmt) { - case PCM3168A_FMT_I2S: - case PCM3168A_FMT_DSP_A: - fmt = PCM3168A_FMT_I2S_TDM; - break; - case PCM3168A_FMT_LEFT_J: - case PCM3168A_FMT_DSP_B: - fmt = PCM3168A_FMT_LEFT_J_TDM; + tdm_mode = (tdm_slots > 2); + + if (tdm_mode) { + switch (format) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: break; default: dev_err(component->dev, @@ -558,6 +537,27 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, } } + switch (format) { + case SND_SOC_DAIFMT_I2S: + fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + fmt = (slot_width == 16) ? PCM3168A_FMT_RIGHT_J_16 : + PCM3168A_FMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_DSP_B; + break; + default: + return -EINVAL; + } + regmap_update_bits(pcm3168a->regmap, reg, mask, (ms << ms_shift) | (fmt << fmt_shift)); -- cgit v1.2.3 From 3e63d3c1a2e52fb60d66bb23cb62c92c92ad0a3f Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Tue, 8 Feb 2022 11:42:20 +0300 Subject: ASoC: pcm3168a: remove numeric PCM3168A_NUM_SUPPLIES Just use ARRAY_SIZE() instead. Signed-off-by: Nikita Yushchenko Link: https://lore.kernel.org/r/20220208084220.1289836-5-nikita.yoush@cogentembedded.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 1d3821f2c5f1..7417cf45d916 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -34,8 +34,7 @@ #define PCM3168A_FMT_I2S_TDM 0x6 #define PCM3168A_FMT_LEFT_J_TDM 0x7 -#define PCM3168A_NUM_SUPPLIES 6 -static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { +static const char *const pcm3168a_supply_names[] = { "VDD1", "VDD2", "VCCAD1", @@ -57,7 +56,7 @@ struct pcm3168a_io_params { }; struct pcm3168a_priv { - struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES]; + struct regulator_bulk_data supplies[ARRAY_SIZE(pcm3168a_supply_names)]; struct regmap *regmap; struct clk *scki; struct gpio_desc *gpio_rst; -- cgit v1.2.3 From 0b88a659002151e354272a13ae29d8b795ef1b46 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Feb 2022 16:58:11 +0000 Subject: ASoC: wm8731: Delete empty remove() function The I2C remove function is empty for the wm8731 driver, it can just be deleted. Signed-off-by: Mark Brown Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220211165811.1176005-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 86b1f6eaa599..5d4949c2ec9b 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -777,11 +777,6 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, return 0; } -static int wm8731_i2c_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id wm8731_i2c_id[] = { { "wm8731", 0 }, { } @@ -794,7 +789,6 @@ static struct i2c_driver wm8731_i2c_driver = { .of_match_table = wm8731_of_match, }, .probe = wm8731_i2c_probe, - .remove = wm8731_i2c_remove, .id_table = wm8731_i2c_id, }; #endif -- cgit v1.2.3 From 3c7a4c24bd0ac2dfeb4f3f9053a2207cad90c7dd Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Tue, 8 Feb 2022 14:17:27 +0200 Subject: ASoC: codec: wm8960: complete discharge on BIAS OFF->STANDBY On BIAS STANDBY->OFF transition the current implementation sleeps 600ms on suspend in order to discharge the chip. The suspend is propagated from "snd_soc_suspend" call for all audio cards in a serial fashion, thus in case of boards like i.MX8DXL EVK which has 3 distinct WM8960 codecs the total cumulated sleep on suspend is 1.8 seconds. On the other hand the BIAS OFF->STANDBY transition happens asynchronously with regard to "snd_soc_resume" - the call is propagated from "soc_resume_deferred" which is just scheduled from "snd_soc_resume", each card having its own work scheduled to execute "soc_resume_deferred" call. The patch performs discharge completion on BIAS OFF->STANDBY transition so that the cumulated effect on suspend described above is avoided and discharge is completed in paralel in case of multiple WM8960 codecs on the board. Signed-off-by: Viorel Suman Link: https://lore.kernel.org/r/20220208121727.4461-1-viorel.suman@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 499604f1e178..ca7660f4bb05 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -45,6 +45,8 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 +#define WM8960_DSCH_TOUT 600 /* discharge timeout, ms */ + static bool is_pll_freq_available(unsigned int source, unsigned int target); static int wm8960_set_pll(struct snd_soc_component *component, unsigned int freq_in, unsigned int freq_out); @@ -133,6 +135,7 @@ struct wm8960_priv { int freq_in; bool is_stream_in_use[2]; struct wm8960_data pdata; + ktime_t dsch_start; }; #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) @@ -898,6 +901,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_component *component, struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); u16 pm2 = snd_soc_component_read(component, WM8960_POWER2); int ret; + ktime_t tout; switch (level) { case SND_SOC_BIAS_ON: @@ -944,6 +948,11 @@ static int wm8960_set_bias_level_out3(struct snd_soc_component *component, case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* ensure discharge is complete */ + tout = WM8960_DSCH_TOUT - ktime_ms_delta(ktime_get(), wm8960->dsch_start); + if (tout > 0) + msleep(tout); + regcache_sync(wm8960->regmap); /* Enable anti-pop features */ @@ -973,9 +982,9 @@ static int wm8960_set_bias_level_out3(struct snd_soc_component *component, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); - /* Disable VMID and VREF, let them discharge */ + /* Disable VMID and VREF, mark discharge */ snd_soc_component_write(component, WM8960_POWER1, 0); - msleep(600); + wm8960->dsch_start = ktime_get(); break; } -- cgit v1.2.3 From 013cc2aea0f62f864c8497b8497299bed4a248fb Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Sat, 12 Feb 2022 17:54:31 +0530 Subject: ASoC: codec: wcd938x: Add switch control for selecting CTIA/OMTP Headset Add switch control for selecting CTIA or OMTP Headset by swapping gnd and mic with the help of GPIO. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1644668672-29790-2-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index eff200a07d9f..08d16a9398f6 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -194,6 +194,7 @@ struct wcd938x_priv { int ear_rx_path; int variant; int reset_gpio; + int us_euro_gpio; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; @@ -4196,6 +4197,33 @@ static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_pri dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); } +static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + int value; + + struct wcd938x_priv *wcd938x; + + if (!component) { + dev_err(component->dev, "%s component is NULL\n", __func__); + return false; + } + + wcd938x = snd_soc_component_get_drvdata(component); + if (!wcd938x) { + dev_err(component->dev, "%s private data is NULL\n", __func__); + return false; + } + + value = gpio_get_value(wcd938x->us_euro_gpio); + + gpio_set_value(wcd938x->us_euro_gpio, !value); + /* 20us sleep required after changing the gpio state*/ + usleep_range(20, 30); + + return true; +} + + static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device *dev) { struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg; @@ -4208,6 +4236,16 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device return wcd938x->reset_gpio; } + wcd938x->us_euro_gpio = of_get_named_gpio(dev->of_node, "us-euro-gpios", 0); + if (wcd938x->us_euro_gpio < 0) { + dev_err(dev, "Failed to get us-euro-gpios gpio: err = %d\n", wcd938x->us_euro_gpio); + } else { + cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; + gpio_direction_output(wcd938x->us_euro_gpio, 0); + /* 20us sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + } + wcd938x->supplies[0].supply = "vdd-rxtx"; wcd938x->supplies[1].supply = "vdd-io"; wcd938x->supplies[2].supply = "vdd-buck"; -- cgit v1.2.3 From 20ea94bc5317475af70f003075e7988715457d66 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Sat, 12 Feb 2022 17:54:32 +0530 Subject: ASoC: dt-bindings: wcd938x: Add gpio property for selecting CTIA/OMTP headset Add gpio property used for selecting CTIA/OMTP headset connected to wcd codec. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1644668672-29790-3-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml index cb74ce40c2e6..7bf1a5fffcd2 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml @@ -23,6 +23,10 @@ properties: description: GPIO spec for reset line to use maxItems: 1 + us-euro-gpios: + description: GPIO spec for swapping gnd and mic segments + maxItems: 1 + vdd-buck-supply: description: A reference to the 1.8V buck supply -- cgit v1.2.3 From 4965e38fa064056021254af4656b1089a42dc764 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Feb 2022 09:13:30 +0200 Subject: ASoC: SOF: Makefile: Fix randconfig sof-client build when SND_SOC_SOF=y Intel's kernel test robot found the following randconfig combination: SND_SOC_SOF=y SND_SOC_SOF_CLIENT=m In this the sof-client object is not going to be built into the snd-sof.o and we will have undefined references to the sof-client functions. Fixes: 6955d9512d0e ("ASoC: SOF: Introduce IPC SOF client support") Reported-by: kernel test robot Signed-off-by: Peter Ujfalusi > Link: https://lore.kernel.org/r/20220214071330.22151-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 4d31282c847d..a0459f06c68a 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,9 @@ 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 -snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o +ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) +snd-sof-objs += sof-client.o +endif snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o -- cgit v1.2.3 From 83a1bed1f49869ea0fc7de321d5dcc598d0dfb15 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 14 Feb 2022 21:32:23 +0000 Subject: ASoC: tegra20: spdif: make const array rates static Don't populate the read-only const array rates on the stack but instead it static. Also makes the object code a little smaller. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20220214213223.65780-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index d09cd7ee6879..64c2f304f254 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -186,7 +186,7 @@ static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, struct snd_soc_dai *dai = rule->private; struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); struct clk *parent = clk_get_parent(spdif->clk_spdif_out); - const unsigned int rates[] = { 32000, 44100, 48000 }; + static const unsigned int rates[] = { 32000, 44100, 48000 }; long i, parent_rate, valid_rates = 0; parent_rate = clk_get_rate(parent); -- cgit v1.2.3 From f7d344a2bd5ec81fbd1ce76928fd059e57ec9bea Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 10 Feb 2022 19:19:12 +0800 Subject: ASoC: soc-core: skip zero num_dai component in searching dai name In the case like dmaengine which's not a dai but as a component, the num_dai is zero, dmaengine component has the same component_of_node as cpu dai, when cpu dai component is not ready, but dmaengine component is ready, try to get cpu dai name, the snd_soc_get_dai_name() return -EINVAL, not -EPROBE_DEFER, that cause below error: asoc-simple-card : parse error -22 asoc-simple-card: probe of failed with error -22 The sound card failed to probe. So this patch fixes the issue above by skipping the zero num_dai component in searching dai name. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1644491952-7457-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 434e61b46983..a088bc9f7dd7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3233,7 +3233,7 @@ int snd_soc_get_dai_name(const struct of_phandle_args *args, for_each_component(pos) { struct device_node *component_of_node = soc_component_to_node(pos); - if (component_of_node != args->np) + if (component_of_node != args->np || !pos->num_dai) continue; ret = snd_soc_component_of_xlate_dai_name(pos, args, dai_name); -- cgit v1.2.3 From db0b4aedfab396a6fe631f5c3bb34319770f0581 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Wed, 16 Feb 2022 18:54:52 +0530 Subject: ASoC: codec: wcd938x: Update CTIA/OMTP switch control Convert gpio api's to gpio descriptor api's in CTIA/OMTP switch control. Remove redundant NULL checks in swap_gnd_mic function. Fixes: 013cc2aea0f6 ("ASoC: codec: wcd938x: Add switch control for selecting CTIA/OMTP Headset") Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1645017892-12522-1-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 08d16a9398f6..88a39e10ff47 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -194,7 +195,7 @@ struct wcd938x_priv { int ear_rx_path; int variant; int reset_gpio; - int us_euro_gpio; + struct gpio_desc *us_euro_gpio; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; @@ -4203,22 +4204,11 @@ static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component, bool activ struct wcd938x_priv *wcd938x; - if (!component) { - dev_err(component->dev, "%s component is NULL\n", __func__); - return false; - } - wcd938x = snd_soc_component_get_drvdata(component); - if (!wcd938x) { - dev_err(component->dev, "%s private data is NULL\n", __func__); - return false; - } - value = gpio_get_value(wcd938x->us_euro_gpio); + value = gpiod_get_value(wcd938x->us_euro_gpio); - gpio_set_value(wcd938x->us_euro_gpio, !value); - /* 20us sleep required after changing the gpio state*/ - usleep_range(20, 30); + gpiod_set_value(wcd938x->us_euro_gpio, !value); return true; } @@ -4236,16 +4226,15 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device return wcd938x->reset_gpio; } - wcd938x->us_euro_gpio = of_get_named_gpio(dev->of_node, "us-euro-gpios", 0); - if (wcd938x->us_euro_gpio < 0) { - dev_err(dev, "Failed to get us-euro-gpios gpio: err = %d\n", wcd938x->us_euro_gpio); - } else { - cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; - gpio_direction_output(wcd938x->us_euro_gpio, 0); - /* 20us sleep required after pulling the reset gpio to LOW */ - usleep_range(20, 30); + wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", + GPIOD_OUT_LOW); + if (IS_ERR(wcd938x->us_euro_gpio)) { + dev_err(dev, "us-euro swap Control GPIO not found\n"); + return PTR_ERR(wcd938x->us_euro_gpio); } + cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; + wcd938x->supplies[0].supply = "vdd-rxtx"; wcd938x->supplies[1].supply = "vdd-io"; wcd938x->supplies[2].supply = "vdd-buck"; -- cgit v1.2.3 From 83bfc7e793b555291785136c3ae86abcdc046887 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 16 Feb 2022 15:32:41 +0200 Subject: ASoC: SOF: core: unregister clients and machine drivers in .shutdown On a platform shutdown, the expectation for most drivers is that userspace tasks will release all resources. When those sequences do not complete, it can be the case that PCM devices exposed by ALSA cards are used *after* the DSP shutdown completes, leading to a platform hang. When the clients and machine drivers provide an _unregister callback, let's invoke it in the shutdown sequence. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20220216133241.3990281-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d99ecbb4282d..2a35d8ddf43e 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -463,10 +463,19 @@ EXPORT_SYMBOL(snd_sof_device_remove); int snd_sof_device_shutdown(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_sof_pdata *pdata = sdev->pdata; if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * make sure clients and machine driver(s) are unregistered to force + * all userspace devices to be closed prior to the DSP shutdown sequence + */ + sof_unregister_clients(sdev); + + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) return snd_sof_shutdown(sdev); -- cgit v1.2.3 From 15175a4f2bbbcbadb1a40098b6dcff0467300e99 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 16 Feb 2022 19:24:05 +0200 Subject: ALSA: hda/hdmi: add keep-alive support for ADL-P and DG2 Implement HDA keep alive (KAE) support for Intel display codecs. When no audio stream is active, the display codec will provide a continuous clock and a valid but silent audio stream to any connected HDMI/DP receiver. Without this, upon starting a new playback stream, initial samples may be lost as many receivers require time to initialize for new clock. This is a new feature in Intel AlderLake-P display codec implementation and replaces the Intel i915 silent-stream extension that has been used on older hardware. Main benefit of the new method is that codec no longer needs to be kept in D0 power state. This patch depends on commit 112a87c48e83 ("drm/i915/display: program audio CDCLK-TS for keepalives"). [ a minor coding-style fix by tiwai ] Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Jyri Sarha Link: https://lore.kernel.org/r/20220216172405.3994959-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 19 +++--- sound/pci/hda/patch_hdmi.c | 156 +++++++++++++++++++++++++++++++++------------ 2 files changed, 126 insertions(+), 49 deletions(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index febe1c2b7d9a..9f6c99c1d87b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -285,15 +285,16 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM bool "Enable Silent Stream always for HDMI" depends on SND_HDA_INTEL help - Intel hardware has a feature called 'silent stream', that - keeps external HDMI receiver's analog circuitry powered on - avoiding 2-3 sec silence during playback start. This mechanism - relies on setting channel_id as 0xf, sending info packet and - preventing codec D3 entry (increasing platform static power - consumption when HDMI receiver is plugged-in). 2-3 sec silence - at the playback start is expected whenever there is format change. - (default is 2 channel format). - Say Y to enable Silent Stream feature. + Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream + for HDMI on hardware that supports the feature. + + When enabled, the HDMI/DisplayPort codec will continue to provide + a continuous clock and a valid but silent data stream to + any connected external receiver. This allows to avoid gaps + at start of playback. Many receivers require multiple seconds + to start playing audio after the clock has been stopped. + This feature can impact power consumption as resources + are kept reserved both at transmitter and receiver. endif diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 64fe025fda86..69a912c52ca7 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -120,6 +120,12 @@ struct hdmi_pcm { struct snd_kcontrol *eld_ctl; }; +enum { + SILENT_STREAM_OFF = 0, + SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ + SILENT_STREAM_I915, /* Intel i915 extension */ +}; + struct hdmi_spec { struct hda_codec *codec; int num_cvts; @@ -179,7 +185,7 @@ struct hdmi_spec { hda_nid_t vendor_nid; const int *port_map; int port_num; - bool send_silent_stream; /* Flag to enable silent stream feature */ + int silent_stream_type; }; #ifdef CONFIG_SND_HDA_COMPONENT @@ -1665,18 +1671,71 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, #define I915_SILENT_FORMAT_BITS 16 #define I915_SILENT_FMT_MASK 0xf +static void silent_stream_enable_i915(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + unsigned int format; + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, + I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); +} + +static void silent_stream_set_kae(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + unsigned int param; + + codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + param = (param >> 16) & 0xff; + + if (enable) + param |= AC_DIG3_KAE; + else + param &= ~AC_DIG3_KAE; + + snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); +} + static void silent_stream_enable(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_cvt *per_cvt; int cvt_idx, pin_idx, err; - unsigned int format; + int keep_power = 0; + + /* + * Power-up will call hdmi_present_sense, so the PM calls + * have to be done without mutex held. + */ + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream enable ret=[%d]\n", err); + snd_hda_power_down_pm(codec); + return; + } mutex_lock(&per_pin->lock); if (per_pin->setup) { codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); + err = -EBUSY; goto unlock_out; } @@ -1703,22 +1762,23 @@ static void silent_stream_enable(struct hda_codec *codec, /* configure unused pins to choose other converters */ pin_cvt_fixup(codec, per_pin, 0); - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, I915_SILENT_RATE); - - /* trigger silent stream generation in hw */ - format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, - I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, - I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); - usleep_range(100, 200); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); - - per_pin->channels = I915_SILENT_CHANNELS; - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + switch (spec->silent_stream_type) { + case SILENT_STREAM_KAE: + silent_stream_set_kae(codec, per_pin, true); + break; + case SILENT_STREAM_I915: + silent_stream_enable_i915(codec, per_pin); + keep_power = 1; + break; + default: + break; + } unlock_out: mutex_unlock(&per_pin->lock); + + if (err || !keep_power) + snd_hda_power_down_pm(codec); } static void silent_stream_disable(struct hda_codec *codec, @@ -1726,7 +1786,16 @@ static void silent_stream_disable(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx; + int cvt_idx, err; + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream disable ret=[%d]\n", + err); + snd_hda_power_down_pm(codec); + return; + } mutex_lock(&per_pin->lock); if (!per_pin->silent_stream) @@ -1741,11 +1810,20 @@ static void silent_stream_disable(struct hda_codec *codec, per_cvt->assigned = 0; } + if (spec->silent_stream_type == SILENT_STREAM_I915) { + /* release ref taken in silent_stream_enable() */ + snd_hda_power_down_pm(codec); + } else if (spec->silent_stream_type == SILENT_STREAM_KAE) { + silent_stream_set_kae(codec, per_pin, false); + } + per_pin->cvt_nid = 0; per_pin->silent_stream = false; unlock_out: mutex_unlock(&per_pin->lock); + + snd_hda_power_down_pm(codec); } /* update ELD and jack state via audio component */ @@ -1767,29 +1845,11 @@ static void sync_eld_via_acomp(struct hda_codec *codec, monitor_next = per_pin->sink_eld.monitor_present; mutex_unlock(&per_pin->lock); - /* - * Power-up will call hdmi_present_sense, so the PM calls - * have to be done without mutex held. - */ - - if (spec->send_silent_stream) { - int pm_ret; - - if (!monitor_prev && monitor_next) { - pm_ret = snd_hda_power_up_pm(codec); - if (pm_ret < 0) - codec_err(codec, - "Monitor plugged-in, Failed to power up codec ret=[%d]\n", - pm_ret); + if (spec->silent_stream_type) { + if (!monitor_prev && monitor_next) silent_stream_enable(codec, per_pin); - } else if (monitor_prev && !monitor_next) { + else if (monitor_prev && !monitor_next) silent_stream_disable(codec, per_pin); - pm_ret = snd_hda_power_down_pm(codec); - if (pm_ret < 0) - codec_err(codec, - "Monitor plugged-out, Failed to power down codec ret=[%d]\n", - pm_ret); - } } } @@ -2982,7 +3042,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, * module param or Kconfig option */ if (send_silent_stream) - spec->send_silent_stream = true; + spec->silent_stream_type = SILENT_STREAM_I915; return parse_intel_hdmi(codec); } @@ -3035,6 +3095,22 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec) return ret; } +static int patch_i915_adlp_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int res; + + res = patch_i915_tgl_hdmi(codec); + if (!res) { + spec = codec->spec; + + if (spec->silent_stream_type) + spec->silent_stream_type = SILENT_STREAM_KAE; + } + + return res; +} + /* Intel Baytrail and Braswell; with eld notifier */ static int patch_i915_byt_hdmi(struct hda_codec *codec) { @@ -4391,10 +4467,10 @@ HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), -- cgit v1.2.3 From 4fe6a63077a6d3c143d68f6b96e4051f1d0740ac Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Thu, 17 Feb 2022 14:27:55 +0100 Subject: ASoC: SOF: Replace zero-length array with flexible-array member There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use "flexible array members"[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. This helps with the ongoing efforts to globally enable -Warray-bounds and get us closer to being able to tighten the FORTIFY_SOURCE routines on memcpy(). [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays Link: https://github.com/KSPP/linux/issues/78 Link: https://github.com/KSPP/linux/issues/180 Suggested-by: Gustavo A. R. Silva Signed-off-by: Stephen Kitt Acked-by: Pierre-Louis Bossart Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20220217132755.1786130-1-steve@sk2.org Signed-off-by: Mark Brown --- include/sound/sof/topology.h | 2 +- sound/soc/sof/topology.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index d12736e14b69..adee6afd1490 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -237,7 +237,7 @@ struct sof_ipc_comp_process { /* reserved for future use */ uint32_t reserved[7]; - uint8_t data[0]; + uint8_t data[]; } __packed; /* frees components, buffers and pipelines diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e72dcae5e7ee..1d119d1dd69d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2164,7 +2164,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, */ if (ipc_data_size) { for (i = 0; i < widget->num_kcontrols; i++) { - memcpy(&process->data + offset, + memcpy(&process->data[offset], wdata[i].pdata->data, wdata[i].pdata->size); offset += wdata[i].pdata->size; -- cgit v1.2.3 From ce6a70bfce21bb4edb7c0f29ecfb0522fa34ab71 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 24 Dec 2021 14:09:50 +0100 Subject: ASoC: Intel: sof_es8336: add quirk for Huawei D15 2021 Huawei D15 uses SSP_CODEC(0). Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/d560a1c76edb633c37acf04a9a82518b6233a719.1640351150.git.mchehab@kernel.org Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 20d577eaab6d..e6d599f0cd26 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -247,6 +247,14 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { SOF_ES8336_TGL_GPIO_QUIRK | SOF_ES8336_ENABLE_DMIC) }, + { + .callback = sof_es8336_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), + DMI_MATCH(DMI_BOARD_NAME, "BOHB-WAX9-PCB-B2"), + }, + .driver_data = (void *)SOF_ES8336_SSP_CODEC(0) + }, {} }; -- cgit v1.2.3 From fefee95488412796b293d28c948be6fce63d149b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:01 +0100 Subject: ALSA: hda: Add snd_hdac_ext_bus_link_at() helper This patch exposes a new helper to directly retrieve the link from the codec address, and makes use of this helper when retrieving the link from the codec name. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-2-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hdaudio_ext.h | 1 + sound/hda/ext/hdac_ext_controller.c | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 77123c3e4095..b0c8e4936168 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -28,6 +28,7 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *chip, bool enable, int index); int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus); +struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr); struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name); diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index b2df7b4f9227..b072392725c7 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -132,6 +132,26 @@ void snd_hdac_link_free_all(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); +/** + * snd_hdac_ext_bus_link_at - get link at specified address + * @bus: link's parent bus device + * @addr: codec device address + * + * Returns link object or NULL if matching link is not found. + */ +struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr) +{ + struct hdac_ext_link *hlink; + int i; + + list_for_each_entry(hlink, &bus->hlink_list, list) + for (i = 0; i < HDA_MAX_CODECS; i++) + if (hlink->lsdiid & (0x1 << addr)) + return hlink; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_at); + /** * snd_hdac_ext_bus_get_link - get link based on codec name * @bus: the pointer to HDAC bus object @@ -140,8 +160,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name) { - int i; - struct hdac_ext_link *hlink = NULL; int bus_idx, addr; if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) @@ -151,14 +169,7 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, if (addr < 0 || addr > 31) return NULL; - list_for_each_entry(hlink, &bus->hlink_list, list) { - for (i = 0; i < HDA_MAX_CODECS; i++) { - if (hlink->lsdiid & (0x1 << addr)) - return hlink; - } - } - - return NULL; + return snd_hdac_ext_bus_link_at(bus, addr); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link); -- cgit v1.2.3 From 595511a3ab80d9ec6649bbf22a99bee169026fd1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:02 +0100 Subject: ALSA: hda: Update and expose snd_hda_codec_device_init() With few changes, snd_hda_codec_device_init() can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed function. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-3-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 3 +++ sound/pci/hda/hda_codec.c | 45 +++++++++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 82d9daa17851..5e3cbcca42f0 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -306,6 +306,9 @@ struct hda_codec { /* * constructors */ +__printf(3, 4) struct hda_codec * +snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, + const char *fmt, ...); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f552785d301e..b7ac3a10d042 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -877,36 +877,48 @@ static void snd_hda_codec_dev_release(struct device *dev) #define DEV_NAME_LEN 31 -static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec **codecp) +/** + * snd_hda_codec_device_init - allocate HDA codec device + * @bus: codec's parent bus + * @codec_addr: the codec address on the parent bus + * @fmt: format string for the device's name + * + * Returns newly allocated codec device or ERR_PTR() on failure. + */ +struct hda_codec * +snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, + const char *fmt, ...) { + va_list vargs; char name[DEV_NAME_LEN]; struct hda_codec *codec; int err; - dev_dbg(card->dev, "%s: entry\n", __func__); - if (snd_BUG_ON(!bus)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return -EINVAL; + return ERR_PTR(-EINVAL); codec = kzalloc(sizeof(*codec), GFP_KERNEL); if (!codec) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + va_start(vargs, fmt); + vsprintf(name, fmt, vargs); + va_end(vargs); - sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); if (err < 0) { kfree(codec); - return err; + return ERR_PTR(err); } + codec->bus = bus; codec->core.type = HDA_DEV_LEGACY; - *codecp = codec; - return err; + return codec; } +EXPORT_SYMBOL_GPL(snd_hda_codec_device_init); /** * snd_hda_codec_new - create a HDA codec @@ -920,11 +932,13 @@ static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { - int ret; + struct hda_codec *codec; - ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); - if (ret < 0) - return ret; + codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d", + card->number, codec_addr); + if (IS_ERR(codec)) + return PTR_ERR(codec); + *codecp = codec; return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); } @@ -951,7 +965,6 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, codec->core.dev.release = snd_hda_codec_dev_release; codec->core.exec_verb = codec_exec_verb; - codec->bus = bus; codec->card = card; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); -- cgit v1.2.3 From 17e0c4cbb748fca764ee184c50b60b3a7b0e0dc7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:03 +0100 Subject: ALSA: hda: Update and expose codec register procedures With few changes, snd_hda_codec_register() and its unregister-counterpart can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed functions. Due to ALSA-device vs ASoC-component organization differences, new 'snddev_managed' argument is specified allowing for better control over codec registration process. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-4-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 5 ++++- sound/pci/hda/hda_codec.c | 35 ++++++++++++++++++++++++++--------- sound/pci/hda/hda_local.h | 1 - sound/soc/codecs/hdac_hda.c | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5e3cbcca42f0..f74abc13414f 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -312,9 +312,12 @@ snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec *codec); + unsigned int codec_addr, struct hda_codec *codec, + bool snddev_managed); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); +void snd_hda_codec_register(struct hda_codec *codec); +void snd_hda_codec_unregister(struct hda_codec *codec); /* * low level functions diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b7ac3a10d042..f0c74d3b4c65 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -813,7 +813,12 @@ void snd_hda_codec_display_power(struct hda_codec *codec, bool enable) snd_hdac_display_power(&codec->bus->core, codec->addr, enable); } -/* also called from hda_bind.c */ +/** + * snd_hda_codec_register - Finalize codec initialization + * @codec: codec device to register + * + * Also called from hda_bind.c + */ void snd_hda_codec_register(struct hda_codec *codec) { if (codec->registered) @@ -826,6 +831,7 @@ void snd_hda_codec_register(struct hda_codec *codec) codec->registered = 1; } } +EXPORT_SYMBOL_GPL(snd_hda_codec_register); static int snd_hda_codec_dev_register(struct snd_device *device) { @@ -833,10 +839,12 @@ static int snd_hda_codec_dev_register(struct snd_device *device) return 0; } -static int snd_hda_codec_dev_free(struct snd_device *device) +/** + * snd_hda_codec_unregister - Unregister specified codec device + * @codec: codec device to unregister + */ +void snd_hda_codec_unregister(struct hda_codec *codec) { - struct hda_codec *codec = device->device_data; - codec->in_freeing = 1; /* * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver. @@ -853,7 +861,12 @@ static int snd_hda_codec_dev_free(struct snd_device *device) */ if (codec->core.type == HDA_DEV_LEGACY) put_device(hda_codec_dev(codec)); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_unregister); +static int snd_hda_codec_dev_free(struct snd_device *device) +{ + snd_hda_codec_unregister(device->device_data); return 0; } @@ -940,12 +953,13 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, return PTR_ERR(codec); *codecp = codec; - return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); + return snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true); } EXPORT_SYMBOL_GPL(snd_hda_codec_new); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec *codec) + unsigned int codec_addr, struct hda_codec *codec, + bool snddev_managed) { char component[31]; hda_nid_t fg; @@ -1020,9 +1034,12 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, codec->core.subsystem_id, codec->core.revision_id); snd_component_add(card, component); - err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); - if (err < 0) - goto error; + if (snddev_managed) { + /* ASoC features component management instead */ + err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); + if (err < 0) + goto error; + } /* PM runtime needs to be enabled later after binding codec */ pm_runtime_forbid(&codec->core.dev); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8621f576446b..4c52dfb615bc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -135,7 +135,6 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) int snd_hda_codec_reset(struct hda_codec *codec); -void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index de5955db0a5f..667f3df239c7 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -413,7 +413,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) HDA_CODEC_IDX_CONTROLLER, true); ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, - hdev->addr, hcodec); + hdev->addr, hcodec, true); if (ret < 0) { dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); goto error_no_pm; -- cgit v1.2.3 From bb682f7a91af08f1fdb669d3911978d7166a65d1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:04 +0100 Subject: ALSA: hda: Expose codec cleanup and power-save functions With few changes, snd_hda_codec_set_power_save() and snd_hda_codec_cleanup_for_unbind() can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed functions. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-5-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 3 +++ sound/pci/hda/hda_codec.c | 14 ++++++++++++-- sound/pci/hda/hda_local.h | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index f74abc13414f..77426ff58338 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -318,6 +318,7 @@ int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_unregister(struct hda_codec *codec); +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); /* * low level functions @@ -496,9 +497,11 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) #define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) #define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core) #ifdef CONFIG_PM +void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay); void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); #else +static inline void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) {} static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {} #endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f0c74d3b4c65..5cbac315dbe1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -766,6 +766,10 @@ static void codec_release_pcms(struct hda_codec *codec) } } +/** + * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal + * @codec: codec device to cleanup + */ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) { if (codec->registered) { @@ -3401,7 +3405,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); #ifdef CONFIG_PM -static void codec_set_power_save(struct hda_codec *codec, int delay) +/** + * snd_hda_codec_set_power_save - Configure codec's runtime PM + * @codec: codec device to configure + * @delay: autosuspend delay + */ +void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) { struct device *dev = hda_codec_dev(codec); @@ -3419,6 +3428,7 @@ static void codec_set_power_save(struct hda_codec *codec, int delay) pm_runtime_forbid(dev); } } +EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save); /** * snd_hda_set_power_save - reprogram autosuspend for the given delay @@ -3432,7 +3442,7 @@ void snd_hda_set_power_save(struct hda_bus *bus, int delay) struct hda_codec *c; list_for_each_codec(c, bus) - codec_set_power_save(c, delay); + snd_hda_codec_set_power_save(c, delay); } EXPORT_SYMBOL_GPL(snd_hda_set_power_save); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4c52dfb615bc..aca592651870 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -135,7 +135,6 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) int snd_hda_codec_reset(struct hda_codec *codec); -void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); #define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) -- cgit v1.2.3 From f43156a9563f9262d7852ab79770f38f1fae7d76 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:35 +0530 Subject: ALSA: hda/tegra: Add Tegra234 hda driver support Add hda driver support for the Tegra234 chip. The hdacodec on this chip now supports DP MST feature, HDA block contains azalia controller and one hda-codec instance by supporting 4 independent output streams over DP MST mode. There is no input stream support. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-2-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 21 ++++++++++++++++-- sound/pci/hda/patch_hdmi.c | 54 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 773f4903550a..95df52b0505b 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -70,6 +70,7 @@ struct hda_tegra_soc { bool has_hda2codec_2x_reset; + bool has_hda2hdmi; }; struct hda_tegra { @@ -435,15 +436,23 @@ static int hda_tegra_create(struct snd_card *card, static const struct hda_tegra_soc tegra30_data = { .has_hda2codec_2x_reset = true, + .has_hda2hdmi = true, }; static const struct hda_tegra_soc tegra194_data = { .has_hda2codec_2x_reset = false, + .has_hda2hdmi = true, +}; + +static const struct hda_tegra_soc tegra234_data = { + .has_hda2codec_2x_reset = true, + .has_hda2hdmi = false, }; static const struct of_device_id hda_tegra_match[] = { { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, + { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); @@ -473,7 +482,14 @@ static int hda_tegra_probe(struct platform_device *pdev) } hda->resets[hda->nresets++].id = "hda"; - hda->resets[hda->nresets++].id = "hda2hdmi"; + + /* + * "hda2hdmi" is not applicable for Tegra234. This is because the + * codec is separate IP and not under display SOR partition now. + */ + if (hda->soc->has_hda2hdmi) + hda->resets[hda->nresets++].id = "hda2hdmi"; + /* * "hda2codec_2x" reset is not present on Tegra194. Though DT would * be updated to reflect this, but to have backward compatibility @@ -488,7 +504,8 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; hda->clocks[hda->nclocks++].id = "hda"; - hda->clocks[hda->nclocks++].id = "hda2hdmi"; + if (hda->soc->has_hda2hdmi) + hda->clocks[hda->nclocks++].id = "hda2hdmi"; hda->clocks[hda->nclocks++].id = "hda2codec_2x"; err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 69a912c52ca7..a134d91db5ff 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3927,17 +3927,29 @@ static int tegra_hdmi_build_pcms(struct hda_codec *codec) return 0; } -static int patch_tegra_hdmi(struct hda_codec *codec) +static int tegra_hdmi_init(struct hda_codec *codec) { - struct hdmi_spec *spec; - int err; + struct hdmi_spec *spec = codec->spec; + int i, err; - err = patch_generic_hdmi(codec); - if (err) + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); return err; + } + + for (i = 0; i < spec->num_cvts; i++) + snd_hda_codec_write(codec, spec->cvt_nids[i], 0, + AC_VERB_SET_DIGI_CONVERT_1, + AC_DIG1_ENABLE); + + generic_hdmi_init_per_pins(codec); codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec = codec->spec; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; @@ -3945,6 +3957,35 @@ static int patch_tegra_hdmi(struct hda_codec *codec) return 0; } +static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + return tegra_hdmi_init(codec); +} + +static int patch_tegra234_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + codec->dp_mst = true; + codec->mst_no_extra_pcms = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->dyn_pcm_assign = true; + + return tegra_hdmi_init(codec); +} + /* * ATI/AMD-specific implementations */ @@ -4398,6 +4439,7 @@ HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), -- cgit v1.2.3 From b58d511ded88ebfc14381eb9fe6e66080e8ed10f Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:36 +0530 Subject: ALSA: hda/tegra: Hardcode GCAP ISS value on T234 The GCAP register on Tegra234 implies no Input Streams(ISS) supported, but the HW output stream descriptor programming should start with offset 0x20*4 from base stream descriptor address. This will be a problem while calculating the offset for output stream descriptor which will be considering input stream also. So here output stream starts with offset 0 which is wrong as HW register for output stream offset starts with 4. So hardcode the input stream numbers to 4 to avoid the issue in offset calculation. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-3-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 95df52b0505b..2347d0304f93 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -315,6 +315,18 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) * hardcoded value */ chip->capture_streams = (gcap >> 8) & 0x0f; + + /* The GCAP register on Tegra234 implies no Input Streams(ISS) support, + * but the HW output stream descriptor programming should start with + * offset 0x20*4 from base stream descriptor address. This will be a + * problem while calculating the offset for output stream descriptor + * which will be considering input stream also. So here output stream + * starts with offset 0 which is wrong as HW register for output stream + * offset starts with 4. + */ + if (of_device_is_compatible(np, "nvidia,tegra234-hda")) + chip->capture_streams = 4; + chip->playback_streams = (gcap >> 12) & 0x0f; if (!chip->playback_streams && !chip->capture_streams) { /* gcap didn't give any info, switching to old method */ -- cgit v1.2.3 From 85f29492929bd52dc3b05876c7ae0362608475ce Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:37 +0530 Subject: ALSA: hda/tegra: Update scratch reg. communication Tegra234 chip scratch register communication between audio and hdmi driver differs slightly in the way it triggers the interrupt compared to legacy chips. Interrupt is triggered by writing non-zero values to verb 0xF80 instead of 31st bit of scratch register. DP MST support changed the NID to be used for scratch register read/write from audio function group NID to Converter widget NID. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-4-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 64 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a134d91db5ff..c85ed7bc121e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -168,6 +168,8 @@ struct hdmi_spec { bool dyn_pin_out; bool dyn_pcm_assign; bool dyn_pcm_no_legacy; + /* hdmi interrupt trigger control flag for Nvidia codec */ + bool hdmi_intr_trig_ctrl; bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ /* * Non-generic VIA/NVIDIA specific @@ -3797,8 +3799,11 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * +-----------------------------------| * * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). + * (i.e. it needs to be toggled). The trigger bit is not applicable from + * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt + * trigger to hdmi. */ +#define NVIDIA_SET_HOST_INTR 0xf80 #define NVIDIA_GET_SCRATCH0 0xfa6 #define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 #define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 @@ -3817,25 +3822,38 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, * the format is invalidated so that the HDMI codec can be disabled. */ -static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) +static void tegra_hdmi_set_format(struct hda_codec *codec, + hda_nid_t cvt_nid, + unsigned int format) { unsigned int value; + unsigned int nid = NVIDIA_AFG_NID; + struct hdmi_spec *spec = codec->spec; + + /* + * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. + * This resulted in moving scratch registers from audio function + * group to converter widget context. So CVT NID should be used for + * scratch register read/write for DP MST supported Tegra HDA codec. + */ + if (codec->dp_mst) + nid = cvt_nid; /* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0, + value = snd_hda_codec_read(codec, nid, 0, NVIDIA_GET_SCRATCH0, 0); value = (value >> 24) & 0xff; /* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE0, (format >> 0) & 0xff); - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE1, (format >> 8) & 0xff); /* bits [16:24] are unused */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE2, 0); /* @@ -3847,15 +3865,28 @@ static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) else value |= NVIDIA_SCRATCH_VALID; - /* - * Whenever the trigger bit is toggled, an interrupt is raised in the - * HDMI codec. The HDMI driver will use that as trigger to update its - * configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; + if (spec->hdmi_intr_trig_ctrl) { + /* + * For Tegra HDA Codec design from TEGRA234 onwards, the + * Interrupt to hdmi driver is triggered by writing + * non-zero values to verb 0xF80 instead of 31st bit of + * scratch register. + */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_HOST_INTR, 0x1); + } else { + /* + * Whenever the 31st trigger bit is toggled, an interrupt is raised + * in the HDMI codec. The HDMI driver will use that as trigger + * to update its configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER; - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + } } static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -3872,7 +3903,7 @@ static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, return err; /* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, format); + tegra_hdmi_set_format(codec, hinfo->nid, format); return 0; } @@ -3882,7 +3913,7 @@ static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, 0); + tegra_hdmi_set_format(codec, hinfo->nid, 0); return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); } @@ -3982,6 +4013,7 @@ static int patch_tegra234_hdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true; spec->dyn_pcm_assign = true; + spec->hdmi_intr_trig_ctrl = true; return tegra_hdmi_init(codec); } -- cgit v1.2.3 From d23c49562a887a8875b144d092034e14b0e279ff Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:39 +0530 Subject: dt-bindings: Document Tegra234 HDA support Update binding document for HDA support on Tegra234 chip. Tegra234 has max of 2 clocks and 2 resets which requires to add minItems and maxItems for clocks and resets as Tegra chips can now have minimum of 2 and maximum of 3 clocks and reset support. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-6-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml index 2c913aa44fee..12c31b4b99e1 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml @@ -23,6 +23,7 @@ properties: - const: nvidia,tegra30-hda - items: - enum: + - nvidia,tegra234-hda - nvidia,tegra194-hda - nvidia,tegra186-hda - nvidia,tegra210-hda @@ -41,9 +42,11 @@ properties: maxItems: 1 clocks: + minItems: 2 maxItems: 3 clock-names: + minItems: 2 items: - const: hda - const: hda2hdmi -- cgit v1.2.3 From b9afe038b1fba24e815000606d5877de97f9f154 Mon Sep 17 00:00:00 2001 From: Ajye Huang Date: Fri, 18 Feb 2022 16:27:41 +0800 Subject: ASoC: SOF: Intel: Add topology overwrite for Felwinter The Felwinter uses four max98360a amplifiers on corresponding CH0~CH3. There are four amps on the board connecting to headphone to SSP0 port, amp to SSP1,and the DAI format would be DSP_A,8-slots, 32 bit slot-width. CH0: L(Woofer), CH1:R(Woofer), CH2:L(Tweeter), CH3:R(Tweeter) Signed-off-by: Ajye Huang Signed-off-by: Brent Lu Reviewed-by: Pierre-Louis Bossart Reviewed-by: Curtis Malainey Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220218082741.1707209-1-brent.lu@intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-pci-dev.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 20c6ca37dbc4..61f2afd54c3e 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -67,6 +67,14 @@ static const struct dmi_system_id sof_tplg_table[] = { }, .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg", }, + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO_AMP-MAX98360_ALC5682VS_I2S_2WAY"), + }, + .driver_data = "sof-adl-max98360a-rt5682-2way.tplg", + }, {} }; -- cgit v1.2.3 From ff5a90173d981934e1134d28af3625acaab01d80 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Mon, 21 Feb 2022 13:57:16 +0800 Subject: ASoC: mediatek: mt8195: enable apll tuner Normally, the clock source of audio module is either 26M or APLL1/APLL2, but APLL1/APLL2 are not the multiple of 26M. In the patch, APLL1 and APLL2 tuners are enabled to handle sample rate mismatch when the data path crosses two different clock domains. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20220221055716.18580-1-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-clk.c | 282 ++++++++++++++++++++++++++++- sound/soc/mediatek/mt8195/mt8195-afe-clk.h | 11 ++ sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 3 - 3 files changed, 292 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c index c2543f4cffb7..efd5cc364a35 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c @@ -40,6 +40,8 @@ static const char *aud_clks[MT8195_CLK_NUM] = { [MT8195_CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp", /* afe clock gate */ [MT8195_CLK_AUD_AFE] = "aud_afe", + [MT8195_CLK_AUD_APLL1_TUNER] = "aud_apll1_tuner", + [MT8195_CLK_AUD_APLL2_TUNER] = "aud_apll2_tuner", [MT8195_CLK_AUD_APLL] = "aud_apll", [MT8195_CLK_AUD_APLL2] = "aud_apll2", [MT8195_CLK_AUD_DAC] = "aud_dac", @@ -77,6 +79,268 @@ static const char *aud_clks[MT8195_CLK_NUM] = { [MT8195_CLK_AUD_MEMIF_DL11] = "aud_memif_dl11", }; +struct mt8195_afe_tuner_cfg { + unsigned int id; + int apll_div_reg; + unsigned int apll_div_shift; + unsigned int apll_div_maskbit; + unsigned int apll_div_default; + int ref_ck_sel_reg; + unsigned int ref_ck_sel_shift; + unsigned int ref_ck_sel_maskbit; + unsigned int ref_ck_sel_default; + int tuner_en_reg; + unsigned int tuner_en_shift; + unsigned int tuner_en_maskbit; + int upper_bound_reg; + unsigned int upper_bound_shift; + unsigned int upper_bound_maskbit; + unsigned int upper_bound_default; + spinlock_t ctrl_lock; /* lock for apll tuner ctrl*/ + int ref_cnt; +}; + +static struct mt8195_afe_tuner_cfg mt8195_afe_tuner_cfgs[MT8195_AUD_PLL_NUM] = { + [MT8195_AUD_PLL1] = { + .id = MT8195_AUD_PLL1, + .apll_div_reg = AFE_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0xf, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_APLL_TUNER_CFG, + .ref_ck_sel_shift = 1, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x2, + .tuner_en_reg = AFE_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_APLL_TUNER_CFG, + .upper_bound_shift = 8, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x2, + }, + [MT8195_AUD_PLL2] = { + .id = MT8195_AUD_PLL2, + .apll_div_reg = AFE_APLL_TUNER_CFG1, + .apll_div_shift = 4, + .apll_div_maskbit = 0xf, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_APLL_TUNER_CFG1, + .ref_ck_sel_shift = 1, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x1, + .tuner_en_reg = AFE_APLL_TUNER_CFG1, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_APLL_TUNER_CFG1, + .upper_bound_shift = 8, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x2, + }, + [MT8195_AUD_PLL3] = { + .id = MT8195_AUD_PLL3, + .apll_div_reg = AFE_EARC_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x3, + .ref_ck_sel_reg = AFE_EARC_APLL_TUNER_CFG, + .ref_ck_sel_shift = 24, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x0, + .tuner_en_reg = AFE_EARC_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_EARC_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, + [MT8195_AUD_PLL4] = { + .id = MT8195_AUD_PLL4, + .apll_div_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_SPDIFIN_APLL_TUNER_CFG1, + .ref_ck_sel_shift = 8, + .ref_ck_sel_maskbit = 0x1, + .ref_ck_sel_default = 0, + .tuner_en_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, + [MT8195_AUD_PLL5] = { + .id = MT8195_AUD_PLL5, + .apll_div_reg = AFE_LINEIN_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x3, + .ref_ck_sel_reg = AFE_LINEIN_APLL_TUNER_CFG, + .ref_ck_sel_shift = 24, + .ref_ck_sel_maskbit = 0x1, + .ref_ck_sel_default = 0, + .tuner_en_reg = AFE_LINEIN_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_LINEIN_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, +}; + +static struct mt8195_afe_tuner_cfg *mt8195_afe_found_apll_tuner(unsigned int id) +{ + if (id >= MT8195_AUD_PLL_NUM) + return NULL; + + return &mt8195_afe_tuner_cfgs[id]; +} + +static int mt8195_afe_init_apll_tuner(unsigned int id) +{ + struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id); + + if (!cfg) + return -EINVAL; + + cfg->ref_cnt = 0; + spin_lock_init(&cfg->ctrl_lock); + + return 0; +} + +static int mt8195_afe_setup_apll_tuner(struct mtk_base_afe *afe, + unsigned int id) +{ + const struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id); + + if (!cfg) + return -EINVAL; + + regmap_update_bits(afe->regmap, cfg->apll_div_reg, + cfg->apll_div_maskbit << cfg->apll_div_shift, + cfg->apll_div_default << cfg->apll_div_shift); + + regmap_update_bits(afe->regmap, cfg->ref_ck_sel_reg, + cfg->ref_ck_sel_maskbit << cfg->ref_ck_sel_shift, + cfg->ref_ck_sel_default << cfg->ref_ck_sel_shift); + + regmap_update_bits(afe->regmap, cfg->upper_bound_reg, + cfg->upper_bound_maskbit << cfg->upper_bound_shift, + cfg->upper_bound_default << cfg->upper_bound_shift); + + return 0; +} + +static int mt8195_afe_enable_tuner_clk(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8195_afe_private *afe_priv = afe->platform_priv; + + switch (id) { + case MT8195_AUD_PLL1: + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL]); + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL1_TUNER]); + break; + case MT8195_AUD_PLL2: + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2]); + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2_TUNER]); + break; + default: + break; + } + + return 0; +} + +static int mt8195_afe_disable_tuner_clk(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8195_afe_private *afe_priv = afe->platform_priv; + + switch (id) { + case MT8195_AUD_PLL1: + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL1_TUNER]); + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL]); + break; + case MT8195_AUD_PLL2: + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2_TUNER]); + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_APLL2]); + break; + default: + break; + } + + return 0; +} + +static int mt8195_afe_enable_apll_tuner(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id); + unsigned long flags; + int ret = 0; + + if (!cfg) + return -EINVAL; + + ret = mt8195_afe_setup_apll_tuner(afe, id); + if (ret) + return ret; + + ret = mt8195_afe_enable_tuner_clk(afe, id); + if (ret) + return ret; + + spin_lock_irqsave(&cfg->ctrl_lock, flags); + + cfg->ref_cnt++; + if (cfg->ref_cnt == 1) + regmap_update_bits(afe->regmap, + cfg->tuner_en_reg, + cfg->tuner_en_maskbit << cfg->tuner_en_shift, + 1 << cfg->tuner_en_shift); + + spin_unlock_irqrestore(&cfg->ctrl_lock, flags); + + return ret; +} + +static int mt8195_afe_disable_apll_tuner(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8195_afe_tuner_cfg *cfg = mt8195_afe_found_apll_tuner(id); + unsigned long flags; + int ret = 0; + + if (!cfg) + return -EINVAL; + + spin_lock_irqsave(&cfg->ctrl_lock, flags); + + cfg->ref_cnt--; + if (cfg->ref_cnt == 0) + regmap_update_bits(afe->regmap, + cfg->tuner_en_reg, + cfg->tuner_en_maskbit << cfg->tuner_en_shift, + 0 << cfg->tuner_en_shift); + else if (cfg->ref_cnt < 0) + cfg->ref_cnt = 0; + + spin_unlock_irqrestore(&cfg->ctrl_lock, flags); + + ret = mt8195_afe_disable_tuner_clk(afe, id); + if (ret) + return ret; + + return ret; +} + int mt8195_afe_get_mclk_source_clk_id(int sel) { switch (sel) { @@ -113,7 +377,7 @@ int mt8195_afe_get_default_mclk_source_by_rate(int rate) int mt8195_afe_init_clock(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; - int i; + int i, ret; mt8195_audsys_clk_register(afe); @@ -133,6 +397,16 @@ int mt8195_afe_init_clock(struct mtk_base_afe *afe) } } + /* initial tuner */ + for (i = 0; i < MT8195_AUD_PLL_NUM; i++) { + ret = mt8195_afe_init_apll_tuner(i); + if (ret) { + dev_dbg(afe->dev, "%s(), init apll_tuner%d failed", + __func__, (i + 1)); + return -EINVAL; + } + } + return 0; } @@ -428,11 +702,17 @@ int mt8195_afe_enable_main_clock(struct mtk_base_afe *afe) mt8195_afe_enable_afe_on(afe); + mt8195_afe_enable_apll_tuner(afe, MT8195_AUD_PLL1); + mt8195_afe_enable_apll_tuner(afe, MT8195_AUD_PLL2); + return 0; } int mt8195_afe_disable_main_clock(struct mtk_base_afe *afe) { + mt8195_afe_disable_apll_tuner(afe, MT8195_AUD_PLL2); + mt8195_afe_disable_apll_tuner(afe, MT8195_AUD_PLL1); + mt8195_afe_disable_afe_on(afe); mt8195_afe_disable_timing_sys(afe); diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.h b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h index f8e6eeb29a89..40663e31becd 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.h +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h @@ -35,6 +35,8 @@ enum { MT8195_CLK_INFRA_AO_AUDIO_26M_B, MT8195_CLK_SCP_ADSP_AUDIODSP, MT8195_CLK_AUD_AFE, + MT8195_CLK_AUD_APLL1_TUNER, + MT8195_CLK_AUD_APLL2_TUNER, MT8195_CLK_AUD_APLL, MT8195_CLK_AUD_APLL2, MT8195_CLK_AUD_DAC, @@ -84,6 +86,15 @@ enum { MT8195_MCK_SEL_NUM, }; +enum { + MT8195_AUD_PLL1, + MT8195_AUD_PLL2, + MT8195_AUD_PLL3, + MT8195_AUD_PLL4, + MT8195_AUD_PLL5, + MT8195_AUD_PLL_NUM, +}; + struct mtk_base_afe; int mt8195_afe_get_mclk_source_clk_id(int sel); diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index e425f868476a..b77c2ba5a629 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -2583,8 +2583,6 @@ static bool mt8195_is_volatile_reg(struct device *dev, unsigned int reg) case AFE_IRQ3_CON_MON: case AFE_IRQ_MCU_MON2: case ADSP_IRQ_STATUS: - case AFE_APLL_TUNER_CFG: - case AFE_APLL_TUNER_CFG1: case AUDIO_TOP_STA0: case AUDIO_TOP_STA1: case AFE_GAIN1_CUR: @@ -2623,7 +2621,6 @@ static bool mt8195_is_volatile_reg(struct device *dev, unsigned int reg) case SPDIFIN_USERCODE10: case SPDIFIN_USERCODE11: case SPDIFIN_USERCODE12: - case AFE_SPDIFIN_APLL_TUNER_CFG: case AFE_LINEIN_APLL_TUNER_MON: case AFE_EARC_APLL_TUNER_MON: case AFE_CM0_MON: -- cgit v1.2.3 From 42c709c4e1ce4c136891530646c9abd5dff3524f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:04 +0000 Subject: ASoC: codecs: rx-macro: fix accessing compander for aux AUX interpolator does not have compander, so check before accessing compander data for this. Without this checkan array of out bounds access will be made in comp_enabled[] array. Fixes: 4f692926f562 ("ASoC: codecs: lpass-rx-macro: add dapm widgets and route") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 6ffe88345de5..1ac0bc1c86b3 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -2039,6 +2039,10 @@ static int rx_macro_load_compander_coeff(struct snd_soc_component *component, int i; int hph_pwr_mode; + /* AUX does not have compander */ + if (comp == INTERP_AUX) + return 0; + if (!rx->comp_enabled[comp]) return 0; -- cgit v1.2.3 From bcfe5f76cc4051ea3f9eb5d2c8ea621641f290a5 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:05 +0000 Subject: ASoC: codecs: rx-macro: fix accessing array out of bounds for enum type Accessing enums using integer would result in array out of bounds access on platforms like aarch64 where sizeof(long) is 8 compared to enum size which is 4 bytes. Fixes: 4f692926f562 ("ASoC: codecs: lpass-rx-macro: add dapm widgets and route") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 1ac0bc1c86b3..6da39ecd93f8 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -2272,7 +2272,7 @@ static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); struct rx_macro *rx = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = + ucontrol->value.enumerated.item[0] = rx->rx_port_value[widget->shift]; return 0; } @@ -2284,7 +2284,7 @@ static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; - u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 rx_port_value = ucontrol->value.enumerated.item[0]; u32 aif_rst; struct rx_macro *rx = snd_soc_component_get_drvdata(component); @@ -2396,7 +2396,7 @@ static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rx_macro *rx = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = rx->hph_pwr_mode; + ucontrol->value.enumerated.item[0] = rx->hph_pwr_mode; return 0; } @@ -2406,7 +2406,7 @@ static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rx_macro *rx = snd_soc_component_get_drvdata(component); - rx->hph_pwr_mode = ucontrol->value.integer.value[0]; + rx->hph_pwr_mode = ucontrol->value.enumerated.item[0]; return 0; } -- cgit v1.2.3 From 0ea5eff7c6063a8f124188424f8e4c6727f35051 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:07 +0000 Subject: ASoC: codecs: va-macro: fix accessing array out of bounds for enum type Accessing enums using integer would result in array out of bounds access on platforms like aarch64 where sizeof(long) is 8 compared to enum size which is 4 bytes. Fixes: 908e6b1df26e ("ASoC: codecs: lpass-va-macro: Add support to VA Macro") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-5-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 11147e35689b..e14c277e6a8b 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -780,7 +780,7 @@ static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int path = e->shift_l; - ucontrol->value.integer.value[0] = va->dec_mode[path]; + ucontrol->value.enumerated.item[0] = va->dec_mode[path]; return 0; } @@ -789,7 +789,7 @@ static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int path = e->shift_l; struct va_macro *va = snd_soc_component_get_drvdata(comp); -- cgit v1.2.3 From cc587b7c8fbbe128f6bd0dad025a0caea5e6d164 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:09 +0000 Subject: ASoC: codecs: wc938x: fix accessing array out of bounds for enum type Accessing enums using integer would result in array out of bounds access on platforms like aarch64 where sizeof(long) is 8 compared to enum size which is 4 bytes. Fix this by using enumerated items instead of integers. Fixes: e8ba1e05bdc0 ("ASoC: codecs: wcd938x: add basic controls") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-7-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 36cbc66914f9..33d5403d4e62 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -2504,7 +2504,7 @@ static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int path = e->shift_l; - ucontrol->value.integer.value[0] = wcd938x->tx_mode[path]; + ucontrol->value.enumerated.item[0] = wcd938x->tx_mode[path]; return 0; } @@ -2528,7 +2528,7 @@ static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = wcd938x->hph_mode; + ucontrol->value.enumerated.item[0] = wcd938x->hph_mode; return 0; } -- cgit v1.2.3 From b0217519236924f77a8382b4004e43ef8fd0dcbb Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:10 +0000 Subject: ASoC: codecs: wcd938x: fix kcontrol max values set "HPH Type" Kcontrol max value of WCD_MBHC_HPH_STEREO instead of UINT_MAX. set "HPHL/R Impedance" Kcontrols max value to INT_MAX instead of UINT_MAX as max field is integer type. Without this patch amixer for these controls will show -1 as max value to userspace. Fixes: bcee7ed09b8e ("ASoC: codecs: wcd938x: add Multi Button Headset Control support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-8-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 33d5403d4e62..9ae65cbabb1a 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3575,14 +3575,14 @@ static int wcd938x_hph_impedance_get(struct snd_kcontrol *kcontrol, } static const struct snd_kcontrol_new hph_type_detect_controls[] = { - SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, wcd938x_get_hph_type, NULL), }; static const struct snd_kcontrol_new impedance_detect_controls[] = { - SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, wcd938x_hph_impedance_get, NULL), - SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, wcd938x_hph_impedance_get, NULL), }; -- cgit v1.2.3 From 61163c3e7480106804269182e24db05244866493 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:11 +0000 Subject: ASoC: codecs: wcd934x: fix kcontrol max values set "HPH Type" Kcontrol max value of WCD_MBHC_HPH_STEREO instead of UINT_MAX. set "HPHL/R Impedance" Kcontrols max value to INT_MAX instead of UINT_MAX as max field is integer type. Without this patch amixer for these controls will show -1 as max value to userspace. Fixes: 9fb9b1690f0b ("ASoC: codecs: wcd934x: add mbhc support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-9-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 6c468527fec6..f2674905a4a7 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3023,14 +3023,14 @@ static int wcd934x_hph_impedance_get(struct snd_kcontrol *kcontrol, return 0; } static const struct snd_kcontrol_new hph_type_detect_controls[] = { - SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, wcd934x_get_hph_type, NULL), }; static const struct snd_kcontrol_new impedance_detect_controls[] = { - SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, wcd934x_hph_impedance_get, NULL), - SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, wcd934x_hph_impedance_get, NULL), }; -- cgit v1.2.3 From 4b0bec6088588a120d33db85b1f0d9f096d1df71 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 22 Feb 2022 18:32:12 +0000 Subject: ASoC: codecs: wcd934x: fix return value of wcd934x_rx_hph_mode_put wcd934x_rx_hph_mode_put currently returns zero eventhough it changes the value. Fix this, so that change notifications are sent correctly. Fixes: 1cde8b822332 ("ASoC: wcd934x: add basic controls") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220222183212.11580-10-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index f2674905a4a7..40b414867872 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3308,13 +3308,16 @@ static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc, mode_val = ucontrol->value.enumerated.item[0]; + if (mode_val == wcd->hph_mode) + return 0; + if (mode_val == 0) { dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); mode_val = CLS_H_LOHIFI; } wcd->hph_mode = mode_val; - return 0; + return 1; } static int slim_rx_mux_get(struct snd_kcontrol *kc, -- cgit v1.2.3 From b83eb8be4f2ca9d6beb1a8b66f666ef7039b7a64 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:19:12 +0000 Subject: ASoC: sc7280: Really depends on SOUNDWIRE The sc7280 driver really does depend on SOUNDWIRE since it calls various sdw_ functions (eg, sdw_enable_stream(), sdw_prepare_stream()) which do not have stubs when that is disabled so we can't build with COMPILE_TEST. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223011913.2753938-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index dd5949eb6b15..f09dc0fcff0b 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -178,7 +178,7 @@ config SND_SOC_SC7180 config SND_SOC_SC7280 tristate "SoC Machine driver for SC7280 boards" - depends on I2C && SOUNDWIRE || COMPILE_TEST + depends on I2C && SOUNDWIRE select SND_SOC_QCOM_COMMON select SND_SOC_LPASS_SC7280 select SND_SOC_MAX98357A -- cgit v1.2.3 From 5ca4cf2c83dac27768f1d7d3e2404f5a17830ca5 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 23 Feb 2022 12:49:31 +0530 Subject: ASoC: amd: vangogh: refactor i2s master mode clock sequence code Refactor I2S Master mode clock programming sequence code. This will also fix the i2s clocks restore issue during system level resume. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220223071959.13539-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-i2s.c | 26 +++++++------------------- sound/soc/amd/vangogh/acp5x.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c index 002db3971ca9..59a98f89a669 100644 --- a/sound/soc/amd/vangogh/acp5x-i2s.c +++ b/sound/soc/amd/vangogh/acp5x-i2s.c @@ -88,10 +88,9 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_soc_card *card; struct acp5x_platform_info *pinfo; struct i2s_dev_data *adata; - union acp_i2stdm_mstrclkgen mclkgen; u32 val; - u32 reg_val, frmt_reg, master_reg; + u32 reg_val, frmt_reg; u32 lrclk_div_val, bclk_div_val; lrclk_div_val = 0; @@ -160,20 +159,6 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream, acp_writel(val, rtd->acp5x_base + reg_val); if (adata->master_mode) { - switch (rtd->i2s_instance) { - case I2S_HS_INSTANCE: - master_reg = ACP_I2STDM2_MSTRCLKGEN; - break; - case I2S_SP_INSTANCE: - default: - master_reg = ACP_I2STDM0_MSTRCLKGEN; - break; - } - mclkgen.bits.i2stdm_master_mode = 0x1; - if (adata->tdm_mode) - mclkgen.bits.i2stdm_format_mode = 0x01; - else - mclkgen.bits.i2stdm_format_mode = 0x0; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: switch (params_rate(params)) { @@ -238,9 +223,8 @@ static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream, default: return -EINVAL; } - mclkgen.bits.i2stdm_bclk_div_val = bclk_div_val; - mclkgen.bits.i2stdm_lrclk_div_val = lrclk_div_val; - acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg); + rtd->lrclk_div = lrclk_div_val; + rtd->bclk_div = bclk_div_val; } return 0; } @@ -249,9 +233,11 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct i2s_stream_instance *rtd; + struct i2s_dev_data *adata; u32 ret, val, period_bytes, reg_val, ier_val, water_val; u32 buf_size, buf_reg; + adata = snd_soc_dai_get_drvdata(dai); rtd = substream->runtime->private_data; period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size); @@ -300,6 +286,8 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, } acp_writel(period_bytes, rtd->acp5x_base + water_val); acp_writel(buf_size, rtd->acp5x_base + buf_reg); + if (adata->master_mode) + acp5x_set_i2s_clk(adata, rtd); val = acp_readl(rtd->acp5x_base + reg_val); val = val | BIT(0); acp_writel(val, rtd->acp5x_base + reg_val); diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h index fe5e1fa98974..b85d3ee369a3 100644 --- a/sound/soc/amd/vangogh/acp5x.h +++ b/sound/soc/amd/vangogh/acp5x.h @@ -105,6 +105,8 @@ struct i2s_stream_instance { dma_addr_t dma_addr; u64 bytescount; void __iomem *acp5x_base; + u32 lrclk_div; + u32 bclk_div; }; union acp_dma_count { @@ -191,3 +193,30 @@ static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd, } return byte_count.bytescount; } + +static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata, + struct i2s_stream_instance *rtd) +{ + union acp_i2stdm_mstrclkgen mclkgen; + u32 master_reg; + + switch (rtd->i2s_instance) { + case I2S_HS_INSTANCE: + master_reg = ACP_I2STDM2_MSTRCLKGEN; + break; + case I2S_SP_INSTANCE: + default: + master_reg = ACP_I2STDM0_MSTRCLKGEN; + break; + } + + mclkgen.bits.i2stdm_master_mode = 0x1; + if (adata->tdm_mode) + mclkgen.bits.i2stdm_format_mode = 0x01; + else + mclkgen.bits.i2stdm_format_mode = 0x00; + + mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div; + mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div; + acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg); +} -- cgit v1.2.3 From aa9753a4677d0a2c53e7e46ca173c985a3f7b83e Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 23 Feb 2022 12:49:32 +0530 Subject: ASoC: nau8821: enable no_capture_mute flag Enable no_capture_mute_flag in nau8821 codec driver. This will fix active playback stream mute issue when capture stream got closed. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220223071959.13539-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8821.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index 2de818377484..d67dc27890a9 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -814,6 +814,7 @@ static const struct snd_soc_dai_ops nau8821_dai_ops = { .hw_params = nau8821_hw_params, .set_fmt = nau8821_set_dai_fmt, .mute_stream = nau8821_digital_mute, + .no_capture_mute = 1, }; #define NAU8821_RATES SNDRV_PCM_RATE_8000_192000 -- cgit v1.2.3 From 9a617f0e109cfba2017d76f807ebb3a00c47bdca Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 23 Feb 2022 12:49:33 +0530 Subject: ASoC: amd: vg: update platform clock control sequence Add pre power on widget event. Based on this event update platform clock control sequence. This will fix Codec clock and pll restoration issue during system level resume. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220223071959.13539-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 14cf325e4b23..e610616d796c 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -33,6 +33,8 @@ #define DUAL_CHANNEL 2 #define ACP5X_NUVOTON_CODEC_DAI "nau8821-hifi" #define VG_JUPITER 1 +#define ACP5X_NUVOTON_BCLK 3072000 +#define ACP5X_NAU8821_FREQ_OUT 12288000 static unsigned long acp5x_machine_id; static struct snd_soc_jack vg_headset; @@ -274,6 +276,15 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, dev_err(card->dev, "set sysclk err = %d\n", ret); return -EIO; } + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(codec_dai->dev, "can't set BLK clock %d\n", ret); + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, ACP5X_NUVOTON_BCLK, + ACP5X_NAU8821_FREQ_OUT); + if (ret < 0) + dev_err(codec_dai->dev, "can't set FLL: %d\n", ret); } return ret; } @@ -289,7 +300,7 @@ static const struct snd_soc_dapm_widget acp5x_8821_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - platform_clock_control, SND_SOC_DAPM_POST_PMD), + platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route acp5x_8821_audio_route[] = { -- cgit v1.2.3 From 0c38cc1dd17e766eada0aa44be4c1a47bcbb7bc3 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 23 Feb 2022 12:49:34 +0530 Subject: ASoC: amd: vg: apply sample bits pcm constraint ACP I2S controller has limitation to program different BCLK for TX and RX paths. Headset path uses I2S SP controller instance. As per requirement, Restricted 32 bits as sample bits by applying pcm constraint in startup calabck for Headset path. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220223071959.13539-5-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index e610616d796c..18b2fdc8dc9e 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -100,6 +100,13 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = { .mask = 0, }; +static const unsigned int acp5x_nau8821_format[] = {32}; + +static struct snd_pcm_hw_constraint_list constraints_sample_bits = { + .list = acp5x_nau8821_format, + .count = ARRAY_SIZE(acp5x_nau8821_format), +}; + static int acp5x_8821_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -115,6 +122,9 @@ static int acp5x_8821_startup(struct snd_pcm_substream *substream) &constraints_channels); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constraints_sample_bits); return 0; } -- cgit v1.2.3 From a9230ccc0c6f5fca0b94f57729dc61e0a6098a0a Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 23 Feb 2022 12:49:35 +0530 Subject: ASoC: amd: vg: update acp init and deinit sequence As part of ACP programming sequence, ACP_CONTROL and ACP_CLKMUX_SEL registers should be updated during acp init and de-init sequence. This patch updates register sequence during ACP init and deinit. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220223071959.13539-6-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/pci-acp5x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c index 2b6b9edc36e2..e0df17c88e8e 100644 --- a/sound/soc/amd/vangogh/pci-acp5x.c +++ b/sound/soc/amd/vangogh/pci-acp5x.c @@ -92,12 +92,14 @@ static int acp5x_init(void __iomem *acp5x_base) pr_err("ACP5x power on failed\n"); return ret; } + acp_writel(0x01, acp5x_base + ACP_CONTROL); /* Reset */ ret = acp5x_reset(acp5x_base); if (ret) { pr_err("ACP5x reset failed\n"); return ret; } + acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL); acp5x_enable_interrupts(acp5x_base); return 0; } @@ -113,6 +115,8 @@ static int acp5x_deinit(void __iomem *acp5x_base) pr_err("ACP5x reset failed\n"); return ret; } + acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL); + acp_writel(0x00, acp5x_base + ACP_CONTROL); return 0; } -- cgit v1.2.3 From 7e1d728a94ca78f8759ba14068932075ecdc562d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Feb 2022 14:52:37 +0100 Subject: ASoC: Intel: soc-acpi-byt: Add new WM5102 ACPI HID The Lenovo Yoga Tablet 2 1050F/L tablets use an ACPI HID of "10WM5102" for their wm5102 codec, add this to the list of HIDs for the wm5102 codec. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220223135237.731638-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-byt-match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 142000991813..c532529a3856 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -127,7 +127,7 @@ static const struct snd_soc_acpi_codecs rt5640_comp_ids = { static const struct snd_soc_acpi_codecs wm5102_comp_ids = { .num_codecs = 2, - .codecs = { "WM510204", "WM510205"}, + .codecs = { "10WM5102", "WM510204", "WM510205"}, }; static const struct snd_soc_acpi_codecs da7213_comp_ids = { -- cgit v1.2.3 From c07ac3ee76e5e5506bca9c03fbbb15e40ab28430 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Wed, 23 Feb 2022 18:14:50 +0800 Subject: ASoC: rt5682s: Fix the wrong jack type detected Some powers were changed during the jack insert detection and clk's enable/disable in CCF. If in parallel, the influence has a chance to detect the wrong jack type. We refer to the below commit of the variant codec (rt5682) to fix this issue. ASoC: rt5682: Fix deadlock on resume 1. Remove rt5682s_headset_detect in rt5682s_jd_check_handler and use jack_detect_work instead of. 2. Use dapm mutex used in CCF to protect most of jack_detect_work. Signed-off-by: Derek Fang Link: https://lore.kernel.org/r/20220223101450.4577-1-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682s.c | 26 +++++++++----------------- sound/soc/codecs/rt5682s.h | 1 - 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 1e662d1be2b3..92b8753f1267 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -822,6 +822,7 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) { struct rt5682s_priv *rt5682s = container_of(work, struct rt5682s_priv, jack_detect_work.work); + struct snd_soc_dapm_context *dapm; int val, btn_type; if (!rt5682s->component || !rt5682s->component->card || @@ -832,7 +833,9 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) return; } - mutex_lock(&rt5682s->jdet_mutex); + dapm = snd_soc_component_get_dapm(rt5682s->component); + + snd_soc_dapm_mutex_lock(dapm); mutex_lock(&rt5682s->calibrate_mutex); val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) @@ -889,6 +892,9 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) rt5682s->irq_work_delay_time = 50; } + mutex_unlock(&rt5682s->calibrate_mutex); + snd_soc_dapm_mutex_unlock(dapm); + snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type, SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); @@ -898,9 +904,6 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) schedule_delayed_work(&rt5682s->jd_check_work, 0); else cancel_delayed_work_sync(&rt5682s->jd_check_work); - - mutex_unlock(&rt5682s->calibrate_mutex); - mutex_unlock(&rt5682s->jdet_mutex); } static void rt5682s_jd_check_handler(struct work_struct *work) @@ -908,14 +911,9 @@ static void rt5682s_jd_check_handler(struct work_struct *work) struct rt5682s_priv *rt5682s = container_of(work, struct rt5682s_priv, jd_check_work.work); - if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) - & RT5682S_JDH_RS_MASK) { + if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) & RT5682S_JDH_RS_MASK) { /* jack out */ - rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0); - - snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type, - SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3); + schedule_delayed_work(&rt5682s->jack_detect_work, 0); } else { schedule_delayed_work(&rt5682s->jd_check_work, 500); } @@ -1323,7 +1321,6 @@ static int rt5682s_hp_amp_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 rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -1339,8 +1336,6 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w, snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666); snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a); - mutex_lock(&rt5682s->jdet_mutex); - snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2, RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK | RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN | @@ -1348,8 +1343,6 @@ static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w, usleep_range(5000, 10000); snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1, RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S); - - mutex_unlock(&rt5682s->jdet_mutex); break; case SND_SOC_DAPM_POST_PMD: @@ -3103,7 +3096,6 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c, mutex_init(&rt5682s->calibrate_mutex); mutex_init(&rt5682s->sar_mutex); - mutex_init(&rt5682s->jdet_mutex); rt5682s_calibrate(rt5682s); regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2, diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index 1bf2ef7ce578..397a2531b6f6 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -1446,7 +1446,6 @@ struct rt5682s_priv { struct delayed_work jd_check_work; struct mutex calibrate_mutex; struct mutex sar_mutex; - struct mutex jdet_mutex; #ifdef CONFIG_COMMON_CLK struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS]; -- cgit v1.2.3 From 76cdd90b27b4e7379ce4d9032dda1927ac69ad01 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Wed, 23 Feb 2022 17:38:49 +0200 Subject: ASoC: SOF: pcm: Add compress_ops for SOF platform component driver Now that sof_compressed_ops initial implementation was merged we can enable it in SOF platform component driver. This partially reverts commit 8a720724589e ("ASoC: SOF: pcm: Remove non existent CONFIG_SND_SOC_SOF_COMPRESS reference") Reported-by: Peter Ujfalusi Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20220223153849.84471-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 4 ++++ sound/soc/sof/sof-priv.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 137f8ed71677..a312ed855f1a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -922,6 +922,10 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->pointer = sof_pcm_pointer; pd->ack = sof_pcm_ack; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) + pd->compress_ops = &sof_compressed_ops; +#endif + pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2c8e556cd5cc..886787a9997f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -556,6 +556,11 @@ int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, const char *name, enum sof_debugfs_access_type access_type); +/* + * Platform specific ops. + */ +extern struct snd_compress_ops sof_compressed_ops; + /* * DSP Architectures. */ -- cgit v1.2.3 From eb8b5af702ca0b9adbccec1ddd944a282c57aa66 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Thu, 24 Feb 2022 10:29:05 +0100 Subject: ASoC: tlv320adc3xxx: Fix buggy return value snd_soc_component_update_bits returns 1 if the operation was successful and some bits were changed, so we cannot return this value directly as it can be interpreted as an error. Instead, do some minor mangling to avoid inadvertently returning an error. Signed-off-by: Ricard Wanderlof Link: https://lore.kernel.org/r/alpine.DEB.2.21.2202241021420.20760@lnxricardw1.se.axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 4baf3d881633..f15e3ea8685c 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1111,6 +1111,7 @@ static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); u8 clkdir = 0, format = 0; int master = 0; + int ret; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1161,10 +1162,13 @@ static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) adc3xxx->master = master; /* set clock direction and format */ - return snd_soc_component_update_bits(component, - ADC3XXX_INTERFACE_CTRL_1, - ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK, - clkdir | format); + ret = snd_soc_component_update_bits(component, + ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK, + clkdir | format); + if (ret < 0) + return ret; + return 0; } static const struct snd_soc_dai_ops adc3xxx_dai_ops = { -- cgit v1.2.3 From abdcf7282f390ae7b95de4bdf93b07ebabeb0f01 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 22 Feb 2022 18:12:11 +0530 Subject: ASoC: amd: acp: Change card name for Guybrush Machine Change sound card name for guybrush machine with rt5682 as primary codec and rt1019 amp to align with names given in UCM config. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220222124213.721224-2-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-legacy-mach.c | 4 ++-- sound/soc/amd/acp/acp-renoir.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 91140d15691b..50a5aa4d6da9 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -96,7 +96,7 @@ static int acp_asoc_probe(struct platform_device *pdev) static const struct platform_device_id board_ids[] = { { - .name = "rn_rt5682_rt1019", + .name = "acp3xalc56821019", .driver_data = (kernel_ulong_t)&rt5682_rt1019_data, }, { } @@ -113,5 +113,5 @@ module_platform_driver(acp_asoc_audio); MODULE_IMPORT_NS(SND_SOC_AMD_MACH); MODULE_DESCRIPTION("ACP chrome audio support"); -MODULE_ALIAS("platform:rn_rt5682_rt1019"); +MODULE_ALIAS("platform:acp3xalc56821019"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index d06ad5ce7fec..b8dc25a1d31d 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -47,7 +47,7 @@ static struct snd_soc_acpi_codecs amp_rt1019 = { static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { { .id = "10EC5682", - .drv_name = "rn_rt5682_rt1019", + .drv_name = "acp3xalc56821019", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &_rt1019, }, -- cgit v1.2.3 From eee33bac9e7d71e6eb9ab5f863efdb9d44174e0a Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 22 Feb 2022 18:12:12 +0530 Subject: ASoC: amd: acp-legacy: Add legacy card support for new machines We have newer renoir platforms with different codecs combinations. Add struct in legacy machine driver and add to list of supported renoir machine to support sound card registration on platform with rt5682s as primary headset codec and max98360 and rt1019 as speaker amp codec. This also fixes error reported by kernel robot: "error: 'EN_SPKR_GPIO_DW' undeclared here" Reported-by: kernel test robot Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220222124213.721224-3-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-legacy-mach.c | 30 ++++++++++++++++++++++++++++++ sound/soc/amd/acp/acp-renoir.c | 17 +++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 50a5aa4d6da9..5d276365d644 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -30,6 +30,26 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .gpio_spkr_en = EN_SPKR_GPIO_GB, }; +static struct acp_card_drvdata rt5682s_max_data = { + .hs_cpu_id = I2S_SP, + .amp_cpu_id = I2S_SP, + .dmic_cpu_id = DMIC, + .hs_codec_id = RT5682S, + .amp_codec_id = MAX98360A, + .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NONE, +}; + +static struct acp_card_drvdata rt5682s_rt1019_data = { + .hs_cpu_id = I2S_SP, + .amp_cpu_id = I2S_SP, + .dmic_cpu_id = DMIC, + .hs_codec_id = RT5682S, + .amp_codec_id = RT1019, + .dmic_codec_id = DMIC, + .gpio_spkr_en = EN_SPKR_GPIO_NONE, +}; + static const struct snd_kcontrol_new acp_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -99,6 +119,14 @@ static const struct platform_device_id board_ids[] = { .name = "acp3xalc56821019", .driver_data = (kernel_ulong_t)&rt5682_rt1019_data, }, + { + .name = "acp3xalc5682sm98360", + .driver_data = (kernel_ulong_t)&rt5682s_max_data, + }, + { + .name = "acp3xalc5682s1019", + .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data, + }, { } }; static struct platform_driver acp_asoc_audio = { @@ -114,4 +142,6 @@ module_platform_driver(acp_asoc_audio); MODULE_IMPORT_NS(SND_SOC_AMD_MACH); MODULE_DESCRIPTION("ACP chrome audio support"); MODULE_ALIAS("platform:acp3xalc56821019"); +MODULE_ALIAS("platform:acp3xalc5682sm98360"); +MODULE_ALIAS("platform:acp3xalc5682s1019"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index b8dc25a1d31d..35d66454e5a3 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -44,6 +44,11 @@ static struct snd_soc_acpi_codecs amp_rt1019 = { .codecs = {"10EC1019"} }; +static struct snd_soc_acpi_codecs amp_max = { + .num_codecs = 1, + .codecs = {"MX98360A"} +}; + static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { { .id = "10EC5682", @@ -51,6 +56,18 @@ static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &_rt1019, }, + { + .id = "RTL5682", + .drv_name = "acp3xalc5682sm98360", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + }, + { + .id = "RTL5682", + .drv_name = "acp3xalc5682s1019", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + }, { .id = "AMDI1019", .drv_name = "renoir-acp", -- cgit v1.2.3 From 1f197351b3af345b626735ed9cda43fa896c23d3 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Tue, 22 Feb 2022 18:12:13 +0530 Subject: ASoC: amd: acp: Add DMIC machine driver ops Add dmic ops and startup callback to add snd_pcm_hw_constraint for pdm related device node. Signed-off-by: Ajit Kumar Pandey Link: https://lore.kernel.org/r/20220222124213.721224-4-AjitKumar.Pandey@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index b45442a56c40..d3034ee2ff59 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -291,6 +291,32 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = { .shutdown = acp_card_shutdown, }; +static const unsigned int dmic_channels[] = { + DUAL_CHANNEL, FOUR_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list dmic_constraints_channels = { + .count = ARRAY_SIZE(dmic_channels), + .list = dmic_channels, + .mask = 0, +}; + +static int acp_card_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &dmic_constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops acp_card_dmic_ops = { + .startup = acp_card_dmic_startup, +}; + /* Declare RT1019 codec components */ SND_SOC_DAILINK_DEF(rt1019, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), @@ -633,6 +659,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].num_cpus = ARRAY_SIZE(pdm_dmic); links[i].platforms = platform_component; links[i].num_platforms = ARRAY_SIZE(platform_component); + links[i].ops = &acp_card_dmic_ops; links[i].dpcm_capture = 1; } -- cgit v1.2.3 From b7fb0ae09009d076964afe4c1a2bde1ee2bd88a9 Mon Sep 17 00:00:00 2001 From: Ammar Faizi Date: Fri, 25 Feb 2022 01:58:36 +0700 Subject: ASoC: SOF: Intel: Fix NULL ptr dereference when ENOMEM Do not call snd_dma_free_pages() when snd_dma_alloc_pages() returns -ENOMEM because it leads to a NULL pointer dereference bug. The dmesg says: [ T1387] sof-audio-pci-intel-tgl 0000:00:1f.3: error: memory alloc failed: -12 [ T1387] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ T1387] #PF: supervisor read access in kernel mode [ T1387] #PF: error_code(0x0000) - not-present page [ T1387] PGD 0 P4D 0 [ T1387] Oops: 0000 [#1] PREEMPT SMP NOPTI [ T1387] CPU: 6 PID: 1387 Comm: alsa-sink-HDA A Tainted: G W 5.17.0-rc4-superb-owl-00055-g80d47f5de5e3 [ T1387] Hardware name: HP HP Laptop 14s-dq2xxx/87FD, BIOS F.15 09/15/2021 [ T1387] RIP: 0010:dma_free_noncontiguous+0x37/0x80 [ T1387] Code: [... snip ...] [ T1387] RSP: 0000:ffffc90002b87770 EFLAGS: 00010246 [ T1387] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ T1387] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888101db30d0 [ T1387] RBP: 00000000fffffff4 R08: 0000000000000000 R09: 0000000000000000 [ T1387] R10: 0000000000000000 R11: ffffc90002b874d0 R12: 0000000000000001 [ T1387] R13: 0000000000058000 R14: ffff888105260c68 R15: ffff888105260828 [ T1387] FS: 00007f42e2ffd640(0000) GS:ffff888466b80000(0000) knlGS:0000000000000000 [ T1387] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ T1387] CR2: 0000000000000000 CR3: 000000014acf0003 CR4: 0000000000770ee0 [ T1387] PKRU: 55555554 [ T1387] Call Trace: [ T1387] [ T1387] cl_stream_prepare+0x10a/0x120 [snd_sof_intel_hda_common 146addf995b9279ae7f509621078cccbe4f875e1] [... snip ...] [ T1387] Cc: Daniel Baluta Cc: Jaroslav Kysela Cc: Kai Vehmanen Cc: Keyon Jie Cc: Liam Girdwood Cc: Mark Brown Cc: Rander Wang Cc: Ranjani Sridharan Cc: Takashi Iwai Cc: sound-open-firmware@alsa-project.org Cc: alsa-devel@alsa-project.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org # v5.2+ Fixes: d16046ffa6de040bf580a64d5f4d0aa18258a854 ("ASoC: SOF: Intel: Add Intel specific HDA firmware loader") Link: https://lore.kernel.org/lkml/20220224145124.15985-1-ammarfaizi2@gnuweeb.org/ # v1 Link: https://lore.kernel.org/lkml/20220224180850.34592-1-ammarfaizi2@gnuweeb.org/ # v2 Link: https://lore.kernel.org/lkml/20220224182818.40301-1-ammarfaizi2@gnuweeb.org/ # v3 Reviewed-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ammar Faizi Link: https://lore.kernel.org/r/20220224185836.44907-1-ammarfaizi2@gnuweeb.org Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 33306d2023a7..9bbfdab8009d 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -47,7 +47,7 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); if (ret < 0) { dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret); - goto error; + goto out_put; } hstream->period_bytes = 0;/* initialize period_bytes */ @@ -58,22 +58,23 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL); if (ret < 0) { dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); - goto error; + goto out_free; } } else { ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); - goto error; + goto out_free; } hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); } return dsp_stream; -error: - hda_dsp_stream_put(sdev, direction, hstream->stream_tag); +out_free: snd_dma_free_pages(dmab); +out_put: + hda_dsp_stream_put(sdev, direction, hstream->stream_tag); return ERR_PTR(ret); } -- cgit v1.2.3 From 74190d7cd3e8ab5123206d383dbfe125a4b7bb19 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:40 +0530 Subject: ASoC: qcom: Move lpass_pcm_data structure to lpass header Declare lpass_pcm_data structure in lpass header file instead of platform source file to make common use of it by other drivers Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-2-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 5 ----- sound/soc/qcom/lpass.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index a59e9d20cb46..a44162ce288a 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -18,11 +18,6 @@ #define DRV_NAME "lpass-platform" -struct lpass_pcm_data { - int dma_ch; - int i2s_port; -}; - #define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024) #define LPASS_PLATFORM_PERIODS 2 diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index c0f0247e6819..f0d21cd57aae 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -257,6 +257,11 @@ struct lpass_variant { int num_clks; }; +struct lpass_pcm_data { + int dma_ch; + int i2s_port; +}; + /* register the platform driver from the CPU DAI driver */ int asoc_qcom_lpass_platform_register(struct platform_device *); int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); -- cgit v1.2.3 From ddd60045caa59d4b3d4b2a4b48fefd4974198587 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:41 +0530 Subject: ASoC: qcom: lpass: Add dma fields for codec dma lpass interface Add lpass interface memebers to support audio path over codec dma. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-3-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass.h | 114 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index f0d21cd57aae..d69a06b35157 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -20,6 +20,16 @@ #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) #define LPASS_MAX_HDMI_DMA_CHANNELS (4) +#define LPASS_MAX_CDC_DMA_CHANNELS (8) +#define LPASS_MAX_VA_CDC_DMA_CHANNELS (8) +#define LPASS_CDC_DMA_INTF_ONE_CHANNEL (0x01) +#define LPASS_CDC_DMA_INTF_TWO_CHANNEL (0x03) +#define LPASS_CDC_DMA_INTF_FOUR_CHANNEL (0x0F) +#define LPASS_CDC_DMA_INTF_SIX_CHANNEL (0x3F) +#define LPASS_CDC_DMA_INTF_EIGHT_CHANNEL (0xFF) + +#define LPASS_ACTIVE_PDS (4) +#define LPASS_PROXY_PDS (8) #define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \ do { \ @@ -51,6 +61,12 @@ struct lpaif_dmactl { struct regmap_field *burst8; struct regmap_field *burst16; struct regmap_field *dynburst; + struct regmap_field *codec_enable; + struct regmap_field *codec_pack; + struct regmap_field *codec_intf; + struct regmap_field *codec_fs_sel; + struct regmap_field *codec_channel; + struct regmap_field *codec_fs_delay; }; /* Both the CPU DAI and platform drivers will access this data */ @@ -65,6 +81,11 @@ struct lpass_data { /* MI2S bit clock (derived from system clock by a divider */ struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS]; + struct clk *codec_mem0; + struct clk *codec_mem1; + struct clk *codec_mem2; + struct clk *va_mem0; + /* MI2S SD lines to use for playback/capture */ unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS]; unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS]; @@ -73,28 +94,43 @@ struct lpass_data { bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS]; int hdmi_port_enable; + int codec_dma_enable; /* low-power audio interface (LPAIF) registers */ void __iomem *lpaif; void __iomem *hdmiif; + void __iomem *rxtx_lpaif; + void __iomem *va_lpaif; + + u32 rxtx_cdc_dma_lpm_buf; + u32 va_cdc_dma_lpm_buf; /* regmap backed by the low-power audio interface (LPAIF) registers */ struct regmap *lpaif_map; struct regmap *hdmiif_map; + struct regmap *rxtx_lpaif_map; + struct regmap *va_lpaif_map; /* interrupts from the low-power audio interface (LPAIF) */ int lpaif_irq; int hdmiif_irq; + int rxtxif_irq; + int vaif_irq; + /* SOC specific variations in the LPASS IP integration */ struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ unsigned long dma_ch_bit_map; unsigned long hdmi_dma_ch_bit_map; + unsigned long rxtx_dma_ch_bit_map; + unsigned long va_dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct snd_pcm_substream *rxtx_substream[LPASS_MAX_CDC_DMA_CHANNELS]; + struct snd_pcm_substream *va_substream[LPASS_MAX_CDC_DMA_CHANNELS]; /* SOC specific clock list */ struct clk_bulk_data *clks; @@ -105,6 +141,12 @@ struct lpass_data { struct lpaif_dmactl *rd_dmactl; struct lpaif_dmactl *wr_dmactl; struct lpaif_dmactl *hdmi_rd_dmactl; + + /* Regmap fields of CODEC DMA CTRL registers */ + struct lpaif_dmactl *rxtx_rd_dmactl; + struct lpaif_dmactl *rxtx_wr_dmactl; + struct lpaif_dmactl *va_wr_dmactl; + /* Regmap fields of HDMI_CTRL registers*/ struct regmap_field *hdmitx_legacy_en; struct regmap_field *hdmitx_parity_calc_en; @@ -131,6 +173,24 @@ struct lpass_variant { u32 wrdma_reg_base; u32 wrdma_reg_stride; u32 wrdma_channels; + u32 rxtx_irq_reg_base; + u32 rxtx_irq_reg_stride; + u32 rxtx_irq_ports; + u32 rxtx_rdma_reg_base; + u32 rxtx_rdma_reg_stride; + u32 rxtx_rdma_channels; + u32 rxtx_wrdma_reg_base; + u32 rxtx_wrdma_reg_stride; + u32 rxtx_wrdma_channels; + u32 va_irq_reg_base; + u32 va_irq_reg_stride; + u32 va_irq_ports; + u32 va_rdma_reg_base; + u32 va_rdma_reg_stride; + u32 va_rdma_channels; + u32 va_wrdma_reg_base; + u32 va_wrdma_reg_stride; + u32 va_wrdma_channels; u32 i2sctrl_reg_base; u32 i2sctrl_reg_stride; u32 i2s_ports; @@ -234,12 +294,66 @@ struct lpass_variant { struct reg_field wrdma_enable; struct reg_field wrdma_dyncclk; + /* CDC RXTX RD_DMA */ + struct reg_field rxtx_rdma_intf; + struct reg_field rxtx_rdma_bursten; + struct reg_field rxtx_rdma_wpscnt; + struct reg_field rxtx_rdma_fifowm; + struct reg_field rxtx_rdma_enable; + struct reg_field rxtx_rdma_dyncclk; + struct reg_field rxtx_rdma_burst8; + struct reg_field rxtx_rdma_burst16; + struct reg_field rxtx_rdma_dynburst; + struct reg_field rxtx_rdma_codec_enable; + struct reg_field rxtx_rdma_codec_pack; + struct reg_field rxtx_rdma_codec_intf; + struct reg_field rxtx_rdma_codec_fs_sel; + struct reg_field rxtx_rdma_codec_ch; + struct reg_field rxtx_rdma_codec_fs_delay; + + /* CDC RXTX WR_DMA */ + struct reg_field rxtx_wrdma_intf; + struct reg_field rxtx_wrdma_bursten; + struct reg_field rxtx_wrdma_wpscnt; + struct reg_field rxtx_wrdma_fifowm; + struct reg_field rxtx_wrdma_enable; + struct reg_field rxtx_wrdma_dyncclk; + struct reg_field rxtx_wrdma_burst8; + struct reg_field rxtx_wrdma_burst16; + struct reg_field rxtx_wrdma_dynburst; + struct reg_field rxtx_wrdma_codec_enable; + struct reg_field rxtx_wrdma_codec_pack; + struct reg_field rxtx_wrdma_codec_intf; + struct reg_field rxtx_wrdma_codec_fs_sel; + struct reg_field rxtx_wrdma_codec_ch; + struct reg_field rxtx_wrdma_codec_fs_delay; + + /* CDC VA WR_DMA */ + struct reg_field va_wrdma_intf; + struct reg_field va_wrdma_bursten; + struct reg_field va_wrdma_wpscnt; + struct reg_field va_wrdma_fifowm; + struct reg_field va_wrdma_enable; + struct reg_field va_wrdma_dyncclk; + struct reg_field va_wrdma_burst8; + struct reg_field va_wrdma_burst16; + struct reg_field va_wrdma_dynburst; + struct reg_field va_wrdma_codec_enable; + struct reg_field va_wrdma_codec_pack; + struct reg_field va_wrdma_codec_intf; + struct reg_field va_wrdma_codec_fs_sel; + struct reg_field va_wrdma_codec_ch; + struct reg_field va_wrdma_codec_fs_delay; + /** * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x **/ u32 dmactl_audif_start; u32 wrdma_channel_start; + u32 rxtx_wrdma_channel_start; + u32 va_wrdma_channel_start; + /* SOC specific initialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); -- cgit v1.2.3 From 16413d5c5a2ed81d8fece1c5fe0b85752ecdbdf2 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:42 +0530 Subject: ASoC: qcom: Add helper function to get dma control and lpaif handle Add support function to get dma control and lpaif handle to avoid repeated code in platform driver Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-4-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 131 ++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index a44162ce288a..198f27c3486e 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -177,6 +177,75 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component, return 0; } +static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); + struct lpaif_dmactl *dmactl = NULL; + + switch (cpu_dai->driver->id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dmactl = drvdata->rd_dmactl; + else + dmactl = drvdata->wr_dmactl; + break; + case LPASS_DP_RX: + dmactl = drvdata->hdmi_rd_dmactl; + break; + } + + return dmactl; +} + +static int __lpass_get_id(const struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; + struct lpass_variant *v = drvdata->variant; + int id; + + switch (cpu_dai->driver->id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + id = pcm_data->dma_ch; + else + id = pcm_data->dma_ch - v->wrdma_channel_start; + break; + case LPASS_DP_RX: + id = pcm_data->dma_ch; + break; + } + + return id; +} + +static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); + struct regmap *map = NULL; + + switch (cpu_dai->driver->id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + map = drvdata->lpaif_map; + break; + case LPASS_DP_RX: + map = drvdata->hdmiif_map; + break; + } + + return map; +} + static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -191,22 +260,13 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, unsigned int channels = params_channels(params); unsigned int regval; struct lpaif_dmactl *dmactl; - int id, dir = substream->stream; + int id; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; unsigned int dai_id = cpu_dai->driver->id; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - id = pcm_data->dma_ch; - if (dai_id == LPASS_DP_RX) - dmactl = drvdata->hdmi_rd_dmactl; - else - dmactl = drvdata->rd_dmactl; - - } else { - dmactl = drvdata->wr_dmactl; - id = pcm_data->dma_ch - v->wrdma_channel_start; - } + dmactl = __lpass_get_dmactl_handle(substream, component); + id = __lpass_get_id(substream, component); bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -350,10 +410,7 @@ static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, struct regmap *map; unsigned int dai_id = cpu_dai->driver->id; - if (dai_id == LPASS_DP_RX) - map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; + map = __lpass_get_regmap_handle(substream, component); reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id); ret = regmap_write(map, reg, 0); @@ -379,23 +436,11 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, int ret, id, ch, dir = substream->stream; unsigned int dai_id = cpu_dai->driver->id; - ch = pcm_data->dma_ch; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - if (dai_id == LPASS_DP_RX) { - dmactl = drvdata->hdmi_rd_dmactl; - map = drvdata->hdmiif_map; - } else { - dmactl = drvdata->rd_dmactl; - map = drvdata->lpaif_map; - } - id = pcm_data->dma_ch; - } else { - dmactl = drvdata->wr_dmactl; - id = pcm_data->dma_ch - v->wrdma_channel_start; - map = drvdata->lpaif_map; - } + dmactl = __lpass_get_dmactl_handle(substream, component); + id = __lpass_get_id(substream, component); + map = __lpass_get_regmap_handle(substream, component); ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id), runtime->dma_addr); @@ -444,26 +489,14 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, struct lpaif_dmactl *dmactl; struct regmap *map; int ret, ch, id; - int dir = substream->stream; unsigned int reg_irqclr = 0, val_irqclr = 0; unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0; unsigned int dai_id = cpu_dai->driver->id; ch = pcm_data->dma_ch; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - id = pcm_data->dma_ch; - if (dai_id == LPASS_DP_RX) { - dmactl = drvdata->hdmi_rd_dmactl; - map = drvdata->hdmiif_map; - } else { - dmactl = drvdata->rd_dmactl; - map = drvdata->lpaif_map; - } - } else { - dmactl = drvdata->wr_dmactl; - id = pcm_data->dma_ch - v->wrdma_channel_start; - map = drvdata->lpaif_map; - } + dmactl = __lpass_get_dmactl_handle(substream, component); + id = __lpass_get_id(substream, component); + map = __lpass_get_regmap_handle(substream, component); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -597,11 +630,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct regmap *map; unsigned int dai_id = cpu_dai->driver->id; - if (dai_id == LPASS_DP_RX) - map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; - + map = __lpass_get_regmap_handle(substream, component); ch = pcm_data->dma_ch; ret = regmap_read(map, -- cgit v1.2.3 From dc8d9766bc03efee4d1b6dd912659858fdf981de Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:43 +0530 Subject: ASoC: qcom: Add register definition for codec rddma and wrdma Add register definitions for codec read dma and write dma lpass interface. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-5-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-lpaif-reg.h | 127 +++++++++++++++++++++++++++++++++++++-- sound/soc/qcom/lpass.h | 21 +++++++ 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 2eb03ad9b7c7..6d9d9d1f6a4d 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -74,6 +74,21 @@ #define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port)) #define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port)) +/* LPAIF RXTX IRQ */ +#define LPAIF_RXTX_IRQ_REG_ADDR(v, addr, port) \ + (v->rxtx_irq_reg_base + (addr) + v->rxtx_irq_reg_stride * (port)) + +#define LPAIF_RXTX_IRQEN_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x0, port) +#define LPAIF_RXTX_IRQSTAT_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port) +#define LPAIF_RXTX_IRQCLEAR_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0xC, port) + +/* LPAIF VA IRQ */ +#define LPAIF_VA_IRQ_REG_ADDR(v, addr, port) \ + (v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port)) + +#define LPAIF_VA_IRQEN_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0x0, port) +#define LPAIF_VA_IRQSTAT_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0x4, port) +#define LPAIF_VA_IRQCLEAR_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0xC, port) #define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \ ((v->hdmi_irq_reg_base) + (addr)) @@ -139,12 +154,112 @@ (LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \ LPAIF_WRDMA##reg##_REG(v, chan)) -#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id) -#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id) -#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id) -#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id) -#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PER, dai_id) -#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id) +#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, CTL, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id)) +#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, BASE, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id)) +#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, BUFF, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id)) +#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, CURR, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id)) +#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, PER, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, PER, dai_id)) +#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + __LPAIF_CDC_DMA_REG(v, chan, dir, PERCNT, dai_id) : \ + __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id)) + +#define LPAIF_CDC_RDMA_REG_ADDR(v, addr, chan, dai_id) \ + (is_rxtx_cdc_dma_port(dai_id) ? \ + (v->rxtx_rdma_reg_base + (addr) + v->rxtx_rdma_reg_stride * (chan)) : \ + (v->va_rdma_reg_base + (addr) + v->va_rdma_reg_stride * (chan))) + +#define LPAIF_CDC_RXTX_RDMACTL_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id) +#define LPAIF_CDC_RXTX_RDMABASE_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id) +#define LPAIF_CDC_RXTX_RDMABUFF_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id) +#define LPAIF_CDC_RXTX_RDMACURR_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id) +#define LPAIF_CDC_RXTX_RDMAPER_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id) +#define LPAIF_CDC_RXTX_RDMA_INTF_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id) + +#define LPAIF_CDC_VA_RDMACTL_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id) +#define LPAIF_CDC_VA_RDMABASE_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id) +#define LPAIF_CDC_VA_RDMABUFF_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id) +#define LPAIF_CDC_VA_RDMACURR_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id) +#define LPAIF_CDC_VA_RDMAPER_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id) +#define LPAIF_CDC_VA_RDMA_INTF_REG(v, chan, dai_id) \ + LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id) + +#define LPAIF_CDC_WRDMA_REG_ADDR(v, addr, chan, dai_id) \ + (is_rxtx_cdc_dma_port(dai_id) ? \ + (v->rxtx_wrdma_reg_base + (addr) + \ + v->rxtx_wrdma_reg_stride * (chan - v->rxtx_wrdma_channel_start)) : \ + (v->va_wrdma_reg_base + (addr) + \ + v->va_wrdma_reg_stride * (chan - v->va_wrdma_channel_start))) + +#define LPAIF_CDC_RXTX_WRDMACTL_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id) +#define LPAIF_CDC_RXTX_WRDMABASE_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id) +#define LPAIF_CDC_RXTX_WRDMABUFF_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id) +#define LPAIF_CDC_RXTX_WRDMACURR_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id) +#define LPAIF_CDC_RXTX_WRDMAPER_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id) +#define LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id) + +#define LPAIF_CDC_VA_WRDMACTL_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id) +#define LPAIF_CDC_VA_WRDMABASE_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id) +#define LPAIF_CDC_VA_WRDMABUFF_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id) +#define LPAIF_CDC_VA_WRDMACURR_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id) +#define LPAIF_CDC_VA_WRDMAPER_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id) +#define LPAIF_CDC_VA_WRDMA_INTF_REG(v, chan, dai_id) \ + LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id) + +#define __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) \ + (is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_RDMA##reg##_REG(v, chan, dai_id) : \ + LPAIF_CDC_VA_RDMA##reg##_REG(v, chan, dai_id)) + +#define __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id) \ + (is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_WRDMA##reg##_REG(v, chan, dai_id) : \ + LPAIF_CDC_VA_WRDMA##reg##_REG(v, chan, dai_id)) + +#define __LPAIF_CDC_DMA_REG(v, chan, dir, reg, dai_id) \ + ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) : \ + __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id)) + +#define LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) \ + ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + LPAIF_CDC_RDMA_INTF_REG(v, chan, dai_id) : \ + LPAIF_CDC_WRDMA_INTF_REG(v, chan, dai_id)) + +#define LPAIF_INTF_REG(v, chan, dir, dai_id) \ + (is_cdc_dma_port(dai_id) ? \ + LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) : \ + LPAIF_DMACTL_REG(v, chan, dir, dai_id)) #define LPAIF_DMACTL_BURSTEN_SINGLE 0 #define LPAIF_DMACTL_BURSTEN_INCR4 1 diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index d69a06b35157..fdcf99256ea8 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -38,6 +38,27 @@ return -EINVAL; \ } while (0) +static inline bool is_cdc_dma_port(int dai_id) +{ + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + return true; + } + return false; +} + +static inline bool is_rxtx_cdc_dma_port(int dai_id) +{ + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + return true; + } + return false; +} + struct lpaif_i2sctl { struct regmap_field *loopback; struct regmap_field *spken; -- cgit v1.2.3 From b138706225c9ce9fac7a4955df31d8f68bb1d409 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:44 +0530 Subject: ASoC: qcom: Add regmap config support for codec dma driver Update regmap configuration for supporting headset playback and capture and DMIC capture using codec dma interface Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-6-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 253 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 3bd9eb3cc688..e6846ad2b5fa 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -28,6 +28,8 @@ #define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2) #define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) #define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) +#define LPASS_REG_READ 1 +#define LPASS_REG_WRITE 0 /* * Channel maps for Quad channel playbacks on MI2S Secondary @@ -798,6 +800,189 @@ static struct regmap_config lpass_hdmi_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static bool __lpass_rxtx_regmap_accessible(struct device *dev, unsigned int reg, bool rw) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + for (i = 0; i < v->rxtx_irq_ports; ++i) { + if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) + return true; + if (reg == LPAIF_RXTX_IRQEN_REG(v, i)) + return true; + if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) + return true; + } + + for (i = 0; i < v->rxtx_rdma_channels; ++i) { + if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + if (rw == LPASS_REG_READ) { + if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + } + if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + } + + for (i = 0; i < v->rxtx_wrdma_channels; ++i) { + if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + if (rw == LPASS_REG_READ) { + if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + } + if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + } + return false; +} + +static bool lpass_rxtx_regmap_writeable(struct device *dev, unsigned int reg) +{ + return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_WRITE); +} + +static bool lpass_rxtx_regmap_readable(struct device *dev, unsigned int reg) +{ + return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_READ); +} + +static bool lpass_rxtx_regmap_volatile(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + for (i = 0; i < v->rxtx_irq_ports; ++i) { + if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) + return true; + if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) + return true; + } + + for (i = 0; i < v->rxtx_rdma_channels; ++i) + if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) + return true; + + for (i = 0; i < v->rxtx_wrdma_channels; ++i) + if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start, + LPASS_CDC_DMA_TX3)) + return true; + + return false; +} + +static bool __lpass_va_regmap_accessible(struct device *dev, unsigned int reg, bool rw) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + for (i = 0; i < v->va_irq_ports; ++i) { + if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) + return true; + if (reg == LPAIF_VA_IRQEN_REG(v, i)) + return true; + if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) + return true; + } + + for (i = 0; i < v->va_wrdma_channels; ++i) { + if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + if (rw == LPASS_REG_READ) { + if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + } + if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + } + return false; +} + +static bool lpass_va_regmap_writeable(struct device *dev, unsigned int reg) +{ + return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_WRITE); +} + +static bool lpass_va_regmap_readable(struct device *dev, unsigned int reg) +{ + return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_READ); +} + +static bool lpass_va_regmap_volatile(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + for (i = 0; i < v->va_irq_ports; ++i) { + if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) + return true; + if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) + return true; + } + + for (i = 0; i < v->va_wrdma_channels; ++i) { + if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start, + LPASS_CDC_DMA_VA_TX0)) + return true; + } + + return false; +} + +static struct regmap_config lpass_rxtx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = lpass_rxtx_regmap_writeable, + .readable_reg = lpass_rxtx_regmap_readable, + .volatile_reg = lpass_rxtx_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + +static struct regmap_config lpass_va_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = lpass_va_regmap_writeable, + .readable_reg = lpass_va_regmap_readable, + .volatile_reg = lpass_va_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, struct device_node *node, const char *name) @@ -857,6 +1042,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev, } if (id == LPASS_DP_RX) { data->hdmi_port_enable = 1; + } else if (is_cdc_dma_port(id)) { + data->codec_dma_enable = 1; } else { data->mi2s_playback_sd_mode[id] = of_lpass_cpu_parse_sd_lines(dev, node, @@ -868,10 +1055,33 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev, } } +static int of_lpass_cdc_dma_clks_parse(struct device *dev, + struct lpass_data *data) +{ + data->codec_mem0 = devm_clk_get(dev, "audio_cc_codec_mem0"); + if (IS_ERR(data->codec_mem0)) + return PTR_ERR(data->codec_mem0); + + data->codec_mem1 = devm_clk_get(dev, "audio_cc_codec_mem1"); + if (IS_ERR(data->codec_mem1)) + return PTR_ERR(data->codec_mem1); + + data->codec_mem2 = devm_clk_get(dev, "audio_cc_codec_mem2"); + if (IS_ERR(data->codec_mem2)) + return PTR_ERR(data->codec_mem2); + + data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0"); + if (IS_ERR(data->va_mem0)) + return PTR_ERR(data->va_mem0); + + return 0; +} + int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) { struct lpass_data *drvdata; struct device_node *dsp_of_node; + struct resource *res; struct lpass_variant *variant; struct device *dev = &pdev->dev; const struct of_device_id *match; @@ -897,6 +1107,47 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) of_lpass_cpu_parse_dai_data(dev, drvdata); + if (drvdata->codec_dma_enable) { + drvdata->rxtx_lpaif = + devm_platform_ioremap_resource_byname(pdev, "lpass-rxtx-lpaif"); + if (IS_ERR(drvdata->rxtx_lpaif)) + return PTR_ERR(drvdata->rxtx_lpaif); + + drvdata->va_lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-va-lpaif"); + if (IS_ERR(drvdata->va_lpaif)) + return PTR_ERR(drvdata->va_lpaif); + + lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant, + variant->rxtx_wrdma_channels + + variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3); + + drvdata->rxtx_lpaif_map = devm_regmap_init_mmio(dev, drvdata->rxtx_lpaif, + &lpass_rxtx_regmap_config); + if (IS_ERR(drvdata->rxtx_lpaif_map)) + return PTR_ERR(drvdata->rxtx_lpaif_map); + + lpass_va_regmap_config.max_register = LPAIF_CDC_VA_WRDMAPER_REG(variant, + variant->va_wrdma_channels + + variant->va_wrdma_channel_start, LPASS_CDC_DMA_VA_TX0); + + drvdata->va_lpaif_map = devm_regmap_init_mmio(dev, drvdata->va_lpaif, + &lpass_va_regmap_config); + if (IS_ERR(drvdata->va_lpaif_map)) + return PTR_ERR(drvdata->va_lpaif_map); + + ret = of_lpass_cdc_dma_clks_parse(dev, drvdata); + if (ret) { + dev_err(dev, "failed to get cdc dma clocks %d\n", ret); + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm"); + drvdata->rxtx_cdc_dma_lpm_buf = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm"); + drvdata->va_cdc_dma_lpm_buf = res->start; + } + drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif"); if (IS_ERR(drvdata->lpaif)) return PTR_ERR(drvdata->lpaif); @@ -939,7 +1190,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) for (i = 0; i < variant->num_dai; i++) { dai_id = variant->dai_driver[i].id; - if (dai_id == LPASS_DP_RX) + if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id)) continue; drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev, -- cgit v1.2.3 From 7d7209557b6712e8aa72ac1ce67a3fe209f5f889 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:45 +0530 Subject: ASoC: qcom: Add support for codec dma driver Upadate lpass cpu and platform driver to support audio over codec dma in ADSP bypass use case. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reported-by: kernel test robot Link: https://lore.kernel.org/r/1645716828-15305-7-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 492 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 475 insertions(+), 17 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 198f27c3486e..b3af971286bb 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -20,6 +20,9 @@ #define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024) #define LPASS_PLATFORM_PERIODS 2 +#define LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE (8 * 1024) +#define LPASS_VA_CDC_DMA_LPM_BUFF_SIZE (12 * 1024) +#define LPASS_CDC_DMA_REGISTER_FIELDS_MAX 15 static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | @@ -45,6 +48,99 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { .fifo_size = 0, }; +static const struct snd_pcm_hardware lpass_platform_rxtx_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE, + .period_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE / + LPASS_PLATFORM_PERIODS, + .period_bytes_min = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE / + LPASS_PLATFORM_PERIODS, + .periods_min = LPASS_PLATFORM_PERIODS, + .periods_max = LPASS_PLATFORM_PERIODS, + .fifo_size = 0, +}; + +static const struct snd_pcm_hardware lpass_platform_va_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE, + .period_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE / + LPASS_PLATFORM_PERIODS, + .period_bytes_min = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE / + LPASS_PLATFORM_PERIODS, + .periods_min = LPASS_PLATFORM_PERIODS, + .periods_max = LPASS_PLATFORM_PERIODS, + .fifo_size = 0, +}; + +static int lpass_platform_alloc_rxtx_dmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *rd_dmactl, *wr_dmactl; + int rval; + + rd_dmactl = devm_kzalloc(dev, sizeof(*rd_dmactl), GFP_KERNEL); + if (!rd_dmactl) + return -ENOMEM; + + wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL); + if (!wr_dmactl) + return -ENOMEM; + + drvdata->rxtx_rd_dmactl = rd_dmactl; + drvdata->rxtx_wr_dmactl = wr_dmactl; + + rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf, + &v->rxtx_rdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX); + if (rval) + return rval; + + return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf, + &v->rxtx_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX); +} + +static int lpass_platform_alloc_va_dmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *wr_dmactl; + + wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL); + if (!wr_dmactl) + return -ENOMEM; + + drvdata->va_wr_dmactl = wr_dmactl; + return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf, + &v->va_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX); +} + + static int lpass_platform_alloc_dmactl_fields(struct device *dev, struct regmap *map) { @@ -123,25 +219,55 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, return dma_ch; } - if (cpu_dai->driver->id == LPASS_DP_RX) { - map = drvdata->hdmiif_map; - drvdata->hdmi_substream[dma_ch] = substream; - } else { + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: map = drvdata->lpaif_map; drvdata->substream[dma_ch] = substream; + break; + case LPASS_DP_RX: + map = drvdata->hdmiif_map; + drvdata->hdmi_substream[dma_ch] = substream; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + map = drvdata->rxtx_lpaif_map; + drvdata->rxtx_substream[dma_ch] = substream; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + map = drvdata->va_lpaif_map; + drvdata->va_substream[dma_ch] = substream; + break; + default: + break; } + data->dma_ch = dma_ch; - ret = regmap_write(map, - LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0); - if (ret) { - dev_err(soc_runtime->dev, - "error writing to rdmactl reg: %d\n", ret); - return ret; + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case LPASS_DP_RX: + ret = regmap_write(map, LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0); + if (ret) { + kfree(data); + dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); + return ret; + } + snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); + runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + snd_soc_set_runtime_hwparams(substream, &lpass_platform_rxtx_hardware); + runtime->dma_bytes = lpass_platform_rxtx_hardware.buffer_bytes_max; + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + snd_soc_set_runtime_hwparams(substream, &lpass_platform_va_hardware); + runtime->dma_bytes = lpass_platform_va_hardware.buffer_bytes_max; + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + break; + default: + break; } - snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); - - runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; - ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { @@ -166,10 +292,25 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component, unsigned int dai_id = cpu_dai->driver->id; data = runtime->private_data; - if (dai_id == LPASS_DP_RX) - drvdata->hdmi_substream[data->dma_ch] = NULL; - else + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: drvdata->substream[data->dma_ch] = NULL; + break; + case LPASS_DP_RX: + drvdata->hdmi_substream[data->dma_ch] = NULL; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + drvdata->rxtx_substream[data->dma_ch] = NULL; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + drvdata->va_substream[data->dma_ch] = NULL; + break; + default: + break; + } + if (v->free_dma_channel) v->free_dma_channel(drvdata, data->dma_ch, dai_id); @@ -195,6 +336,15 @@ static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_subst case LPASS_DP_RX: dmactl = drvdata->hdmi_rd_dmactl; break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + dmactl = drvdata->rxtx_rd_dmactl; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + dmactl = drvdata->rxtx_wr_dmactl; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + dmactl = drvdata->va_wr_dmactl; + break; } return dmactl; @@ -221,6 +371,15 @@ static int __lpass_get_id(const struct snd_pcm_substream *substream, case LPASS_DP_RX: id = pcm_data->dma_ch; break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + id = pcm_data->dma_ch; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + id = pcm_data->dma_ch - v->va_wrdma_channel_start; + break; } return id; @@ -241,6 +400,13 @@ static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream * case LPASS_DP_RX: map = drvdata->hdmiif_map; break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + map = drvdata->rxtx_lpaif_map; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + map = drvdata->va_lpaif_map; + break; } return map; @@ -321,6 +487,10 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, return ret; } + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: break; default: dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id); @@ -410,6 +580,8 @@ static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, struct regmap *map; unsigned int dai_id = cpu_dai->driver->id; + if (is_cdc_dma_port(dai_id)) + return 0; map = __lpass_get_regmap_handle(substream, component); reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id); @@ -466,6 +638,14 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, return ret; } + if (is_cdc_dma_port(dai_id)) { + ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8); + if (ret) { + dev_err(soc_runtime->dev, "error writing fifowm field to dmactl reg: %d, id: %d\n", + ret, id); + return ret; + } + } ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", @@ -547,6 +727,35 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, val_mask = LPAIF_IRQ_ALL(ch); val_irqen = LPAIF_IRQ_ALL(ch); break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg field: %d\n", ret); + return ret; + } + reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + + reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg field: %d\n", ret); + return ret; + } + reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + + reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; default: dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); return -EINVAL; @@ -598,6 +807,37 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, val_mask = LPAIF_IRQ_ALL(ch); val_irqen = 0; break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg field: %d\n", ret); + return ret; + } + + reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + + reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg field: %d\n", ret); + return ret; + } + + reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + + reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; default: dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); return -EINVAL; @@ -652,6 +892,35 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( return bytes_to_frames(substream->runtime, curr_addr - base_addr); } +static int lpass_platform_cdc_dma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long size, offset; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + return io_remap_pfn_range(vma, vma->vm_start, + (runtime->dma_addr + offset) >> PAGE_SHIFT, + size, vma->vm_page_prot); + +} + +static int lpass_platform_pcmops_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + unsigned int dai_id = cpu_dai->driver->id; + + if (is_cdc_dma_port(dai_id)) + return lpass_platform_cdc_dma_mmap(substream, vma); + + return snd_pcm_lib_default_mmap(substream, vma); +} + static irqreturn_t lpass_dma_interrupt_handler( struct snd_pcm_substream *substream, struct lpass_data *drvdata, @@ -684,6 +953,17 @@ static irqreturn_t lpass_dma_interrupt_handler( reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); val = 0; break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + map = drvdata->rxtx_lpaif_map; + reg = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val = 0; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + map = drvdata->va_lpaif_map; + reg = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val = 0; + break; default: dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); return -EINVAL; @@ -791,16 +1071,115 @@ static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data) return rv; } } + return IRQ_HANDLED; +} + +static irqreturn_t lpass_platform_rxtxif_irq(int irq, void *data) +{ + struct lpass_data *drvdata = data; + struct lpass_variant *v = drvdata->variant; + unsigned int irqs; + irqreturn_t rv; + int chan; + + rv = regmap_read(drvdata->rxtx_lpaif_map, + LPAIF_RXTX_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs); + + /* Handle per channel interrupts */ + for (chan = 0; chan < LPASS_MAX_CDC_DMA_CHANNELS; chan++) { + if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->rxtx_substream[chan]) { + rv = lpass_dma_interrupt_handler( + drvdata->rxtx_substream[chan], + drvdata, chan, irqs); + if (rv != IRQ_HANDLED) + return rv; + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t lpass_platform_vaif_irq(int irq, void *data) +{ + struct lpass_data *drvdata = data; + struct lpass_variant *v = drvdata->variant; + unsigned int irqs; + irqreturn_t rv; + int chan; + + rv = regmap_read(drvdata->va_lpaif_map, + LPAIF_VA_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs); + /* Handle per channel interrupts */ + for (chan = 0; chan < LPASS_MAX_VA_CDC_DMA_CHANNELS; chan++) { + if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->va_substream[chan]) { + rv = lpass_dma_interrupt_handler( + drvdata->va_substream[chan], + drvdata, chan, irqs); + if (rv != IRQ_HANDLED) + return rv; + } + } return IRQ_HANDLED; } +static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *component, + struct snd_pcm *pcm, int dai_id) +{ + struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + else + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + + buf = &substream->dma_buffer; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + + /* Assign Codec DMA buffer pointers */ + buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS; + + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max; + buf->addr = drvdata->rxtx_cdc_dma_lpm_buf; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max; + buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + buf->bytes = lpass_platform_va_hardware.buffer_bytes_max; + buf->addr = drvdata->va_cdc_dma_lpm_buf; + break; + default: + break; + } + + buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WT); + + return 0; +} + static int lpass_platform_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + unsigned int dai_id = cpu_dai->driver->id; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; + /* + * Lpass codec dma can access only lpass lpm hardware memory. + * ioremap is for HLOS to access hardware memory. + */ + if (is_cdc_dma_port(dai_id)) + return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id); + return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); } @@ -837,6 +1216,31 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component) return regcache_sync(map); } +static int lpass_platform_copy(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, unsigned long bytes) +{ + struct snd_pcm_runtime *rt = substream->runtime; + unsigned int dai_id = component->id; + int ret = 0; + + void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos + + channel * (rt->dma_bytes / rt->channels)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (is_cdc_dma_port(dai_id)) + ret = copy_from_user_toio(dma_buf, buf, bytes); + else + ret = copy_from_user((void __force *)dma_buf, buf, bytes); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (is_cdc_dma_port(dai_id)) + ret = copy_to_user_fromio(buf, dma_buf, bytes); + else + ret = copy_to_user(buf, (void __force *)dma_buf, bytes); + } + + return ret; +} static const struct snd_soc_component_driver lpass_component_driver = { .name = DRV_NAME, @@ -847,9 +1251,11 @@ static const struct snd_soc_component_driver lpass_component_driver = { .prepare = lpass_platform_pcmops_prepare, .trigger = lpass_platform_pcmops_trigger, .pointer = lpass_platform_pcmops_pointer, + .mmap = lpass_platform_pcmops_mmap, .pcm_construct = lpass_platform_pcm_new, .suspend = lpass_platform_pcmops_suspend, .resume = lpass_platform_pcmops_resume, + .copy_user = lpass_platform_copy, }; @@ -887,6 +1293,58 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) return ret; } + if (drvdata->codec_dma_enable) { + ret = regmap_write(drvdata->rxtx_lpaif_map, + LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0); + if (ret) { + dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret); + return ret; + } + ret = regmap_write(drvdata->va_lpaif_map, + LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0); + if (ret) { + dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret); + return ret; + } + drvdata->rxtxif_irq = platform_get_irq_byname(pdev, "lpass-irq-rxtxif"); + if (drvdata->rxtxif_irq < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, drvdata->rxtxif_irq, + lpass_platform_rxtxif_irq, 0, "lpass-irq-rxtxif", drvdata); + if (ret) { + dev_err(&pdev->dev, "rxtx irq request failed: %d\n", ret); + return ret; + } + + ret = lpass_platform_alloc_rxtx_dmactl_fields(&pdev->dev, + drvdata->rxtx_lpaif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing rxtx dmactl fields: %d\n", ret); + return ret; + } + + drvdata->vaif_irq = platform_get_irq_byname(pdev, "lpass-irq-vaif"); + if (drvdata->vaif_irq < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, drvdata->vaif_irq, + lpass_platform_vaif_irq, 0, "lpass-irq-vaif", drvdata); + if (ret) { + dev_err(&pdev->dev, "va irq request failed: %d\n", ret); + return ret; + } + + ret = lpass_platform_alloc_va_dmactl_fields(&pdev->dev, + drvdata->va_lpaif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing va dmactl fields: %d\n", ret); + return ret; + } + } + if (drvdata->hdmi_port_enable) { drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi"); if (drvdata->hdmiif_irq < 0) -- cgit v1.2.3 From b81af585ea54ee9f749391e594ee9cbd44061eae Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:46 +0530 Subject: ASoC: qcom: Add lpass CPU driver for codec dma control Add lpass cpu driver to support audio over codec dma for ADSP bypass usecase. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-8-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 4 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/lpass-cdc-dma.c | 301 +++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/lpass.h | 1 + 4 files changed, 308 insertions(+) create mode 100644 sound/soc/qcom/lpass-cdc-dma.c diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index f09dc0fcff0b..4ab3ead3472d 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -20,6 +20,10 @@ config SND_SOC_LPASS_PLATFORM tristate select REGMAP_MMIO +config SND_SOC_LPASS_CDC_DMA + tristate + select REGMAP_MMIO + config SND_SOC_LPASS_IPQ806X tristate select SND_SOC_LPASS_CPU diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 625aec63b4ee..010b44cc65a0 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Platform snd-soc-lpass-cpu-objs := lpass-cpu.o +snd-soc-lpass-cdc-dma-objs := lpass-cdc-dma.o snd-soc-lpass-hdmi-objs := lpass-hdmi.o snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o @@ -8,6 +9,7 @@ snd-soc-lpass-apq8016-objs := lpass-apq8016.o snd-soc-lpass-sc7180-objs := lpass-sc7180.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o +obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c new file mode 100644 index 000000000000..31b9f1c22bee --- /dev/null +++ b/sound/soc/qcom/lpass-cdc-dma.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 The Linux Foundation. All rights reserved. + * + * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS + */ + +#include +#include +#include +#include +#include + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +#define CODEC_MEM_HZ_NORMAL 153600000 + +enum codec_dma_interfaces { + LPASS_CDC_DMA_INTERFACE1 = 1, + LPASS_CDC_DMA_INTERFACE2, + LPASS_CDC_DMA_INTERFACE3, + LPASS_CDC_DMA_INTERFACE4, + LPASS_CDC_DMA_INTERFACE5, + LPASS_CDC_DMA_INTERFACE6, + LPASS_CDC_DMA_INTERFACE7, + LPASS_CDC_DMA_INTERFACE8, + LPASS_CDC_DMA_INTERFACE9, + LPASS_CDC_DMA_INTERFACE10, +}; + +static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, + struct lpaif_dmactl **dmactl, int *id) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; + struct lpass_variant *v = drvdata->variant; + unsigned int dai_id = cpu_dai->driver->id; + + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + *dmactl = drvdata->rxtx_rd_dmactl; + *id = pcm_data->dma_ch; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + *dmactl = drvdata->rxtx_wr_dmactl; + *id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start; + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + *dmactl = drvdata->va_wr_dmactl; + *id = pcm_data->dma_ch - v->va_wrdma_channel_start; + break; + default: + dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id); + break; + } +} + +static int __lpass_get_codec_dma_intf_type(int dai_id) +{ + int ret; + + switch (dai_id) { + case LPASS_CDC_DMA_RX0: + case LPASS_CDC_DMA_TX0: + case LPASS_CDC_DMA_VA_TX0: + ret = LPASS_CDC_DMA_INTERFACE1; + break; + case LPASS_CDC_DMA_RX1: + case LPASS_CDC_DMA_TX1: + case LPASS_CDC_DMA_VA_TX1: + ret = LPASS_CDC_DMA_INTERFACE2; + break; + case LPASS_CDC_DMA_RX2: + case LPASS_CDC_DMA_TX2: + case LPASS_CDC_DMA_VA_TX2: + ret = LPASS_CDC_DMA_INTERFACE3; + break; + case LPASS_CDC_DMA_RX3: + case LPASS_CDC_DMA_TX3: + case LPASS_CDC_DMA_VA_TX3: + ret = LPASS_CDC_DMA_INTERFACE4; + break; + case LPASS_CDC_DMA_RX4: + case LPASS_CDC_DMA_TX4: + case LPASS_CDC_DMA_VA_TX4: + ret = LPASS_CDC_DMA_INTERFACE5; + break; + case LPASS_CDC_DMA_RX5: + case LPASS_CDC_DMA_TX5: + case LPASS_CDC_DMA_VA_TX5: + ret = LPASS_CDC_DMA_INTERFACE6; + break; + case LPASS_CDC_DMA_RX6: + case LPASS_CDC_DMA_TX6: + case LPASS_CDC_DMA_VA_TX6: + ret = LPASS_CDC_DMA_INTERFACE7; + break; + case LPASS_CDC_DMA_RX7: + case LPASS_CDC_DMA_TX7: + case LPASS_CDC_DMA_VA_TX7: + ret = LPASS_CDC_DMA_INTERFACE8; + break; + case LPASS_CDC_DMA_RX8: + case LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX8: + ret = LPASS_CDC_DMA_INTERFACE9; + break; + case LPASS_CDC_DMA_RX9: + ret = LPASS_CDC_DMA_INTERFACE10; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); + struct lpaif_dmactl *dmactl = NULL; + struct device *dev = soc_runtime->dev; + int ret, id, codec_intf; + unsigned int dai_id = cpu_dai->driver->id; + + codec_intf = __lpass_get_codec_dma_intf_type(dai_id); + if (codec_intf < 0) { + dev_err(dev, "failed to get codec_intf: %d\n", codec_intf); + return codec_intf; + } + + __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); + if (!dmactl) + return -EINVAL; + + ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf); + if (ret) { + dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0); + if (ret) { + dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0); + if (ret) { + dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->codec_pack, id, 0x1); + if (ret) { + dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON); + if (ret) { + dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret); + return ret; + } + return 0; +} + +static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + + switch (dai->id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL); + clk_prepare_enable(drvdata->codec_mem0); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: + clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL); + clk_prepare_enable(drvdata->va_mem0); + break; + default: + dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); + break; + } + return 0; +} + +static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + + switch (dai->id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + clk_disable_unprepare(drvdata->codec_mem0); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: + clk_disable_unprepare(drvdata->va_mem0); + break; + default: + dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); + break; + } +} + +static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct lpaif_dmactl *dmactl = NULL; + unsigned int ret, regval; + unsigned int channels = params_channels(params); + int id; + + switch (channels) { + case 1: + regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL; + break; + case 2: + regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL; + break; + case 4: + regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL; + break; + case 6: + regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL; + break; + case 8: + regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL; + break; + default: + dev_err(soc_runtime->dev, "invalid PCM config\n"); + return -EINVAL; + } + + __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); + if (!dmactl) + return -EINVAL; + + ret = regmap_fields_write(dmactl->codec_channel, id, regval); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl codec_channel reg field: %d\n", ret); + return ret; + } + return 0; +} + +static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct lpaif_dmactl *dmactl; + int ret = 0, id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + __lpass_platform_codec_intf_init(dai, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); + if (!dmactl) + return -EINVAL; + + ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl codec_enable reg: %d\n", ret); + return ret; + } + break; + default: + ret = -EINVAL; + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd); + break; + } + return ret; +} + +const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = { + .startup = lpass_cdc_dma_daiops_startup, + .shutdown = lpass_cdc_dma_daiops_shutdown, + .hw_params = lpass_cdc_dma_daiops_hw_params, + .trigger = lpass_cdc_dma_daiops_trigger, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops); + +MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index fdcf99256ea8..dd78600fc7b0 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -406,5 +406,6 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai); extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); +extern const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops; #endif /* __LPASS_H__ */ -- cgit v1.2.3 From f3fc4fbfa2d2a09cb279af4e290d0a6dbbc93c7e Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:47 +0530 Subject: ASoC: dt-bindings: Add SC7280 lpass cpu bindings Add bindings for sc7280 lpass cpu driver which supports audio over i2s based speaker, soundwire based headset, msm dmics and HDMI Port. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1645716828-15305-9-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,lpass-cpu.yaml | 75 +++++++++++++++++++--- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml index 1e23c0e20bc1..2c81efb5fa37 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml @@ -22,35 +22,41 @@ properties: - qcom,lpass-cpu - qcom,apq8016-lpass-cpu - qcom,sc7180-lpass-cpu + - qcom,sc7280-lpass-cpu reg: - maxItems: 2 + minItems: 2 + maxItems: 6 description: LPAIF core registers reg-names: - maxItems: 2 + minItems: 2 + maxItems: 6 clocks: minItems: 3 - maxItems: 6 + maxItems: 7 clock-names: minItems: 3 - maxItems: 6 + maxItems: 7 interrupts: - maxItems: 2 + minItems: 2 + maxItems: 4 description: LPAIF DMA buffer interrupt interrupt-names: - maxItems: 2 + minItems: 2 + maxItems: 4 qcom,adsp: $ref: /schemas/types.yaml#/definitions/phandle description: Phandle for the audio DSP node iommus: - maxItems: 2 + minItems: 2 + maxItems: 3 description: Phandle to apps_smmu node with sid mask power-domains: @@ -69,7 +75,7 @@ patternProperties: "^dai-link@[0-9a-f]$": type: object description: | - LPASS CPU dai node for each I2S device. Bindings of each node + LPASS CPU dai node for each I2S device or Soundwire device. Bindings of each node depends on the specific driver providing the functionality and properties. properties: @@ -174,6 +180,59 @@ allOf: - iommus - power-domains + - if: + properties: + compatible: + contains: + const: qcom,sc7280-lpass-cpu + + then: + properties: + clock-names: + oneOf: + - items: #for I2S + - const: aon_cc_audio_hm_h + - const: core_cc_sysnoc_mport_core + - const: core_cc_ext_if1_ibit + - items: #for Soundwire + - const: aon_cc_audio_hm_h + - const: audio_cc_codec_mem0 + - const: audio_cc_codec_mem1 + - const: audio_cc_codec_mem2 + - items: #for HDMI + - const: aon_cc_audio_hm_h + + reg-names: + anyOf: + - items: #for I2S + - const: lpass-lpaif + - items: #for I2S and HDMI + - const: lpass-hdmiif + - const: lpass-lpaif + - items: #for I2S, soundwire and HDMI + - const: lpass-hdmiif + - const: lpass-lpaif + - const: lpass-rxtx-cdc-dma-lpm + - const: lpass-rxtx-lpaif + - const: lpass-va-lpaif + - const: lpass-va-cdc-dma-lpm + interrupt-names: + anyOf: + - items: #for I2S + - const: lpass-irq-lpaif + - items: #for I2S and HDMI + - const: lpass-irq-lpaif + - const: lpass-irq-hdmi + - items: #for I2S, soundwire and HDMI + - const: lpass-irq-lpaif + - const: lpass-irq-hdmi + - const: lpass-irq-vaif + - const: lpass-irq-rxtxif + + required: + - iommus + - power-domains + examples: - | #include -- cgit v1.2.3 From b62c4e5fba2f910bc9f23ae152d11627e4c2f00f Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Thu, 24 Feb 2022 21:03:48 +0530 Subject: ASoC: qcom: lpass-sc7280: Add platform driver for lpass audio Add platform driver for configuring sc7280 lpass core I2S and DMA configuration to support playback & capture to external codecs connected over secondary MI2S interface and soundwire interface. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1645716828-15305-10-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 7 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/lpass-sc7280.c | 438 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+) create mode 100644 sound/soc/qcom/lpass-sc7280.c diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 4ab3ead3472d..ae99833b3fdb 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -40,6 +40,13 @@ config SND_SOC_LPASS_SC7180 select SND_SOC_LPASS_PLATFORM select SND_SOC_LPASS_HDMI +config SND_SOC_LPASS_SC7280 + tristate + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + select SND_SOC_LPASS_HDMI + select SND_SOC_LPASS_CDC_DMA + config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" depends on GPIOLIB diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 010b44cc65a0..8b7b876899a8 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -7,6 +7,7 @@ snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o snd-soc-lpass-sc7180-objs := lpass-sc7180.o +snd-soc-lpass-sc7280-objs := lpass-sc7280.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o @@ -15,6 +16,7 @@ obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o +obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o # Machine snd-soc-storm-objs := storm.o diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c new file mode 100644 index 000000000000..70c4df87d957 --- /dev/null +++ b/sound/soc/qcom/lpass-sc7280.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS + */ + +#include +#include +#include +#include + +#include + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = { + { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Primary Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = LPASS_DP_RX, + .name = "Hdmi", + .playback = { + .stream_name = "DP Playback", + .formats = SNDRV_PCM_FMTBIT_S24, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_lpass_hdmi_dai_ops, + }, { + .id = LPASS_CDC_DMA_RX0, + .name = "CDC DMA RX", + .playback = { + .stream_name = "WCD Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_lpass_cdc_dma_dai_ops, + }, { + .id = LPASS_CDC_DMA_TX3, + .name = "CDC DMA TX", + .capture = { + .stream_name = "WCD Capture", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &asoc_qcom_lpass_cdc_dma_dai_ops, + }, { + .id = LPASS_CDC_DMA_VA_TX0, + .name = "CDC DMA VA", + .capture = { + .stream_name = "DMIC Capture", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 4, + }, + .ops = &asoc_qcom_lpass_cdc_dma_dai_ops, + }, +}; + +static int sc7280_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction, unsigned int dai_id) +{ + struct lpass_variant *v = drvdata->variant; + int chan = 0; + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + v->rdma_channels); + + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } + set_bit(chan, &drvdata->dma_ch_bit_map); + break; + case LPASS_DP_RX: + chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map, + v->hdmi_rdma_channels); + if (chan >= v->hdmi_rdma_channels) + return -EBUSY; + set_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + chan = find_first_zero_bit(&drvdata->rxtx_dma_ch_bit_map, + v->rxtx_rdma_channels); + if (chan >= v->rxtx_rdma_channels) + return -EBUSY; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + chan = find_next_zero_bit(&drvdata->rxtx_dma_ch_bit_map, + v->rxtx_wrdma_channel_start + + v->rxtx_wrdma_channels, + v->rxtx_wrdma_channel_start); + if (chan >= v->rxtx_wrdma_channel_start + v->rxtx_wrdma_channels) + return -EBUSY; + set_bit(chan, &drvdata->rxtx_dma_ch_bit_map); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + chan = find_next_zero_bit(&drvdata->va_dma_ch_bit_map, + v->va_wrdma_channel_start + + v->va_wrdma_channels, + v->va_wrdma_channel_start); + if (chan >= v->va_wrdma_channel_start + v->va_wrdma_channels) + return -EBUSY; + set_bit(chan, &drvdata->va_dma_ch_bit_map); + break; + default: + break; + } + + return chan; +} + +static int sc7280_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) +{ + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + clear_bit(chan, &drvdata->dma_ch_bit_map); + break; + case LPASS_DP_RX: + clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + clear_bit(chan, &drvdata->rxtx_dma_ch_bit_map); + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + clear_bit(chan, &drvdata->va_dma_ch_bit_map); + break; + default: + break; + } + + return 0; +} + +static int sc7280_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; + struct device *dev = &pdev->dev; + int ret, i; + + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + if (!drvdata->clks) + return -ENOMEM; + + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "sc7280 clk_enable failed\n"); + return ret; + } + + return 0; +} + +static int sc7280_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + + return 0; +} + +static struct lpass_variant sc7280_data = { + .i2sctrl_reg_base = 0x1000, + .i2sctrl_reg_stride = 0x1000, + .i2s_ports = 3, + .irq_reg_base = 0x9000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0xC000, + .rdma_reg_stride = 0x1000, + .rdma_channels = 5, + .rxtx_rdma_reg_base = 0xC000, + .rxtx_rdma_reg_stride = 0x1000, + .rxtx_rdma_channels = 8, + .hdmi_rdma_reg_base = 0x64000, + .hdmi_rdma_reg_stride = 0x1000, + .hdmi_rdma_channels = 4, + .dmactl_audif_start = 1, + .wrdma_reg_base = 0x18000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, + .rxtx_irq_reg_base = 0x9000, + .rxtx_irq_reg_stride = 0x1000, + .rxtx_irq_ports = 3, + .rxtx_wrdma_reg_base = 0x18000, + .rxtx_wrdma_reg_stride = 0x1000, + .rxtx_wrdma_channel_start = 5, + .rxtx_wrdma_channels = 6, + .va_wrdma_reg_base = 0x18000, + .va_wrdma_reg_stride = 0x1000, + .va_wrdma_channel_start = 5, + .va_wrdma_channels = 3, + .va_irq_reg_base = 0x9000, + .va_irq_reg_stride = 0x1000, + .va_irq_ports = 3, + + .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000), + .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000), + .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000), + .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000), + .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000), + .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000), + .wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000), + .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000), + + .rxtx_rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 7, 0x1000), + .rxtx_rdma_fifowm = REG_FIELD_ID(0xC000, 1, 11, 7, 0x1000), + .rxtx_rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 7, 0x1000), + .rxtx_rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 7, 0x1000), + .rxtx_rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 7, 0x1000), + .rxtx_rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 7, 0x1000), + + .rxtx_rdma_codec_ch = REG_FIELD_ID(0xC050, 0, 7, 7, 0x1000), + .rxtx_rdma_codec_intf = REG_FIELD_ID(0xC050, 16, 19, 7, 0x1000), + .rxtx_rdma_codec_fs_delay = REG_FIELD_ID(0xC050, 21, 24, 7, 0x1000), + .rxtx_rdma_codec_fs_sel = REG_FIELD_ID(0xC050, 25, 27, 7, 0x1000), + .rxtx_rdma_codec_pack = REG_FIELD_ID(0xC050, 29, 29, 5, 0x1000), + .rxtx_rdma_codec_enable = REG_FIELD_ID(0xC050, 30, 30, 7, 0x1000), + + .rxtx_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000), + .rxtx_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000), + .rxtx_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000), + .rxtx_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000), + .rxtx_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000), + .rxtx_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000), + + .rxtx_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000), + .rxtx_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000), + .rxtx_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000), + .rxtx_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000), + .rxtx_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000), + .rxtx_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000), + + .va_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000), + .va_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000), + .va_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000), + .va_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000), + .va_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000), + .va_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000), + + .va_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000), + .va_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000), + .va_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000), + .va_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000), + .va_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000), + .va_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000), + + .hdmi_tx_ctl_addr = 0x1000, + .hdmi_legacy_addr = 0x1008, + .hdmi_vbit_addr = 0x610c0, + .hdmi_ch_lsb_addr = 0x61048, + .hdmi_ch_msb_addr = 0x6104c, + .ch_stride = 0x8, + .hdmi_parity_addr = 0x61034, + .hdmi_dmactl_addr = 0x61038, + .hdmi_dma_stride = 0x4, + .hdmi_DP_addr = 0x610c8, + .hdmi_sstream_addr = 0x6101c, + .hdmi_irq_reg_base = 0x63000, + .hdmi_irq_ports = 1, + + .hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000), + .hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000), + .hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000), + .hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000), + .hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000), + .hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000), + .hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000), + .hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000), + + .sstream_en = REG_FIELD(0x6101c, 0, 0), + .dma_sel = REG_FIELD(0x6101c, 1, 2), + .auto_bbit_en = REG_FIELD(0x6101c, 3, 3), + .layout = REG_FIELD(0x6101c, 4, 4), + .layout_sp = REG_FIELD(0x6101c, 5, 8), + .set_sp_on_en = REG_FIELD(0x6101c, 10, 10), + .dp_audio = REG_FIELD(0x6101c, 11, 11), + .dp_staffing_en = REG_FIELD(0x6101c, 12, 12), + .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13), + + .mute = REG_FIELD(0x610c8, 0, 0), + .as_sdp_cc = REG_FIELD(0x610c8, 1, 3), + .as_sdp_ct = REG_FIELD(0x610c8, 4, 7), + .aif_db4 = REG_FIELD(0x610c8, 8, 15), + .frequency = REG_FIELD(0x610c8, 16, 21), + .mst_index = REG_FIELD(0x610c8, 28, 29), + .dptx_index = REG_FIELD(0x610c8, 30, 31), + + .soft_reset = REG_FIELD(0x1000, 31, 31), + .force_reset = REG_FIELD(0x1000, 30, 30), + + .use_hw_chs = REG_FIELD(0x61038, 0, 0), + .use_hw_usr = REG_FIELD(0x61038, 1, 1), + .hw_chs_sel = REG_FIELD(0x61038, 2, 4), + .hw_usr_sel = REG_FIELD(0x61038, 5, 6), + + .replace_vbit = REG_FIELD(0x610c0, 0, 0), + .vbit_stream = REG_FIELD(0x610c0, 1, 1), + + .legacy_en = REG_FIELD(0x1008, 0, 0), + .calc_en = REG_FIELD(0x61034, 0, 0), + .lsb_bits = REG_FIELD(0x61048, 0, 31), + .msb_bits = REG_FIELD(0x6104c, 0, 31), + + .clk_name = (const char*[]) { + "core_cc_sysnoc_mport_core" + }, + .num_clks = 1, + + .dai_driver = sc7280_lpass_cpu_dai_driver, + .num_dai = ARRAY_SIZE(sc7280_lpass_cpu_dai_driver), + .dai_osr_clk_names = (const char *[]) { + "audio_cc_ext_mclk0", + "null" + }, + .dai_bit_clk_names = (const char *[]) { + "core_cc_ext_if0_ibit", + "core_cc_ext_if1_ibit" + }, + .init = sc7280_lpass_init, + .exit = sc7280_lpass_exit, + .alloc_dma_channel = sc7280_lpass_alloc_dma_channel, + .free_dma_channel = sc7280_lpass_free_dma_channel, +}; + +static const struct of_device_id sc7280_lpass_cpu_device_id[] = { + {.compatible = "qcom,sc7280-lpass-cpu", .data = &sc7280_data}, + {} +}; +MODULE_DEVICE_TABLE(of, sc7280_lpass_cpu_device_id); + +static struct platform_driver sc7280_lpass_cpu_platform_driver = { + .driver = { + .name = "sc7280-lpass-cpu", + .of_match_table = of_match_ptr(sc7280_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, + .shutdown = asoc_qcom_lpass_cpu_platform_shutdown, +}; + +module_platform_driver(sc7280_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("SC7280 LPASS CPU DRIVER"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 299905881ebd3b7ee5e3525356529c3d0ceb0688 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 24 Feb 2022 19:23:24 +0200 Subject: ASoC: SOF: Declare sof_compress_ops in sof-priv.h Fix the following sparse error: sound/soc/sof/compress.c:310:25: error: symbol 'sof_compressed_ops' was not declared. Should it be static? Do not enable set the pd->compress_ops yet as it is not a valid assumption that real compress support really works when CONFIG_SND_SOC_SOF_COMPRESS is set as the HDA Probes support also selects it, but compressed audio is not supported (yet) on Intel platforms. Fixes: 6324cf901e14c ("ASoC: SOF: compr: Add compress ops implementation") Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220224172324.17976-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 886787a9997f..1cc6dce17582 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -497,6 +497,11 @@ void snd_sof_complete(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); +/* + * Compress support + */ +extern struct snd_compress_ops sof_compressed_ops; + /* * Firmware loading. */ -- cgit v1.2.3 From 3304a242f45a501a5de69492e421a45c468c89ea Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Fri, 25 Feb 2022 19:23:58 +0800 Subject: ASoC: amd: Use platform_get_irq_byname() to get the interrupt platform_get_resource_byname(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypasses the hierarchical setup and messes up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq_byname(). Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220225112358.19403-1-tangmeng@uniontech.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-renoir.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 35d66454e5a3..738cf2e2b973 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -282,13 +282,10 @@ static int renoir_audio_probe(struct platform_device *pdev) if (!adata->acp_base) return -ENOMEM; - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + adata->i2s_irq = platform_get_irq_byname(pdev, "acp_dai_irq"); + if (adata->i2s_irq < 0) return -ENODEV; - } - adata->i2s_irq = res->start; adata->dev = dev; adata->dai_driver = acp_renoir_dai; adata->num_dai = ARRAY_SIZE(acp_renoir_dai); -- cgit v1.2.3 From c8775fb16b9bb37cf811ea0b55d50d8f42054c5d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 25 Feb 2022 14:00:34 +0200 Subject: ASoC: SOF: sof-priv: Drop duplicate sof_compressed_ops declaration Other commit added the declaration of the sof_compressed_ops, drop the instance which added it as Platform specific ops, which the sof_compressed_ops is not. 76cdd90b27b4e ("ASoC: SOF: pcm: Add compress_ops for SOF platform component driver") Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220225120034.11028-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 1cc6dce17582..2e19ac619ad5 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -561,11 +561,6 @@ int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, const char *name, enum sof_debugfs_access_type access_type); -/* - * Platform specific ops. - */ -extern struct snd_compress_ops sof_compressed_ops; - /* * DSP Architectures. */ -- cgit v1.2.3 From 4d06f92f38b799295ae22c98be7a20cac3e2a1a7 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Fri, 25 Feb 2022 05:10:30 -0800 Subject: ASoC: rt5663: check the return value of devm_kzalloc() in rt5663_parse_dp() The function devm_kzalloc() in rt5663_parse_dp() can fail, so its return value should be checked. Fixes: 457c25efc592 ("ASoC: rt5663: Add the function of impedance sensing") Reported-by: TOTE Robot Signed-off-by: Jia-Ju Bai Link: https://lore.kernel.org/r/20220225131030.27248-1-baijiaju1990@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5663.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 2138f62e6af5..3a8fba101b20 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3478,6 +3478,8 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) table_size = sizeof(struct impedance_mapping_table) * rt5663->pdata.impedance_sensing_num; rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); + if (!rt5663->imp_table) + return -ENOMEM; ret = device_property_read_u32_array(dev, "realtek,impedance_sensing_table", (u32 *)rt5663->imp_table, table_size); -- cgit v1.2.3 From 431f9a77a4a62694ce90742d1f4c5abe1b8b6612 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Fri, 25 Feb 2022 05:16:45 -0800 Subject: ASoC: acp: check the return value of devm_kzalloc() in acp_legacy_dai_links_create() The function devm_kzalloc() in acp_legacy_dai_links_create() can fail, so its return value should be checked. Fixes: d4c750f2c7d4 ("ASoC: amd: acp: Add generic machine driver support for ACP cards") Reported-by: TOTE Robot Signed-off-by: Jia-Ju Bai Link: https://lore.kernel.org/r/20220225131645.27556-1-baijiaju1990@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index d3034ee2ff59..755215d947cf 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -584,6 +584,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) num_links++; links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * num_links, GFP_KERNEL); + if (!links) + return -ENOMEM; if (drv_data->hs_cpu_id == I2S_SP) { links[i].name = "acp-headset-codec"; -- cgit v1.2.3 From 5a5d2316a5292222383d4e3589b8f5144f7c9b49 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Fri, 25 Feb 2022 18:12:23 +0530 Subject: ASoC: qcom: lpass-platform: Update warning print to control excess logging Update dev_warn to dev_warn_ratelimit to control excess xrun logging in lpass platform driver. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1645792943-24845-1-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index b3af971286bb..1ce0878665ca 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -986,7 +986,8 @@ static irqreturn_t lpass_dma_interrupt_handler( "error writing to irqclear reg: %d\n", rv); return IRQ_NONE; } - dev_warn(soc_runtime->dev, "xrun warning\n"); + dev_warn_ratelimited(soc_runtime->dev, "xrun warning\n"); + snd_pcm_stop_xrun(substream); ret = IRQ_HANDLED; } -- cgit v1.2.3 From bed17757521b8beee2b565ce7860808a6a6e37ed Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:03 +0000 Subject: ASoC: codecs: va-macro: move to individual clks from bulk Using bulk clocks and referencing them individually using array index is not great for readers. So move them to individual clocks handling and also remove some unnecessary error handling in the code. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 65 ++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 11147e35689b..7627e8aea688 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -193,7 +193,9 @@ struct va_macro { int dec_mode[VA_MACRO_NUM_DECIMATORS]; struct regmap *regmap; - struct clk_bulk_data clks[VA_NUM_CLKS_MAX]; + struct clk *mclk; + struct clk *macro; + struct clk *dcodec; struct clk_hw hw; s32 dmic_0_1_clk_cnt; @@ -1321,7 +1323,7 @@ static const struct clk_ops fsgen_gate_ops = { static int va_macro_register_fsgen_output(struct va_macro *va) { - struct clk *parent = va->clks[2].clk; + struct clk *parent = va->mclk; struct device *dev = va->dev; struct device_node *np = dev->of_node; const char *parent_clk_name; @@ -1404,15 +1406,18 @@ static int va_macro_probe(struct platform_device *pdev) return -ENOMEM; va->dev = dev; - va->clks[0].id = "macro"; - va->clks[1].id = "dcodec"; - va->clks[2].id = "mclk"; - ret = devm_clk_bulk_get_optional(dev, VA_NUM_CLKS_MAX, va->clks); - if (ret) { - dev_err(dev, "Error getting VA Clocks (%d)\n", ret); - return ret; - } + va->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(va->macro)) + return PTR_ERR(va->macro); + + va->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(va->dcodec)) + return PTR_ERR(va->dcodec); + + va->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(va->mclk)) + return PTR_ERR(va->mclk); ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate", &sample_rate); @@ -1425,12 +1430,6 @@ static int va_macro_probe(struct platform_device *pdev) return -EINVAL; } - /* mclk rate */ - clk_set_rate(va->clks[1].clk, VA_MACRO_MCLK_FREQ); - ret = clk_bulk_prepare_enable(VA_NUM_CLKS_MAX, va->clks); - if (ret) - return ret; - base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { ret = PTR_ERR(base); @@ -1444,21 +1443,41 @@ static int va_macro_probe(struct platform_device *pdev) } dev_set_drvdata(dev, va); - ret = va_macro_register_fsgen_output(va); + + /* mclk rate */ + clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + + ret = clk_prepare_enable(va->macro); if (ret) goto err; + ret = clk_prepare_enable(va->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(va->mclk); + if (ret) + goto err_mclk; + + ret = va_macro_register_fsgen_output(va); + if (ret) + goto err_clkout; + ret = devm_snd_soc_register_component(dev, &va_macro_component_drv, va_macro_dais, ARRAY_SIZE(va_macro_dais)); if (ret) - goto err; + goto err_clkout; - return ret; + return 0; +err_clkout: + clk_disable_unprepare(va->mclk); +err_mclk: + clk_disable_unprepare(va->dcodec); +err_dcodec: + clk_disable_unprepare(va->macro); err: - clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks); - return ret; } @@ -1466,7 +1485,9 @@ static int va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); - clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks); + clk_disable_unprepare(va->mclk); + clk_disable_unprepare(va->dcodec); + clk_disable_unprepare(va->macro); return 0; } -- cgit v1.2.3 From 70a5e96bad592145ba25365689a2d7d8dedb3bd9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:04 +0000 Subject: ASoC: codecs: rx-macro: move clk provider to managed variants move clk provider registration to managed api variants, this should help with some code tidyup. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 29d214f784d1..0822fe618d50 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3475,10 +3475,9 @@ static const struct clk_ops swclk_gate_ops = { }; -static struct clk *rx_macro_register_mclk_output(struct rx_macro *rx) +static int rx_macro_register_mclk_output(struct rx_macro *rx) { struct device *dev = rx->dev; - struct device_node *np = dev->of_node; const char *parent_clk_name = NULL; const char *clk_name = "lpass-rx-mclk"; struct clk_hw *hw; @@ -3494,13 +3493,11 @@ static struct clk *rx_macro_register_mclk_output(struct rx_macro *rx) init.num_parents = 1; rx->hw.init = &init; hw = &rx->hw; - ret = clk_hw_register(rx->dev, hw); + ret = devm_clk_hw_register(rx->dev, hw); if (ret) - return ERR_PTR(ret); - - of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + return ret; - return NULL; + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); } static const struct snd_soc_component_driver rx_macro_component_drv = { @@ -3558,22 +3555,26 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) return ret; - rx_macro_register_mclk_output(rx); + ret = rx_macro_register_mclk_output(rx); + if (ret) + goto err; ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv, rx_macro_dai, ARRAY_SIZE(rx_macro_dai)); if (ret) - clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks); + goto err; return ret; +err: + clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks); + return ret; } static int rx_macro_remove(struct platform_device *pdev) { struct rx_macro *rx = dev_get_drvdata(&pdev->dev); - of_clk_del_provider(pdev->dev.of_node); clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks); return 0; } -- cgit v1.2.3 From db8665a3e904f579840417f9414415c4dd54ac84 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:05 +0000 Subject: ASoC: codecs: tx-macro: move clk provider to managed variants move clk provider registration to managed api variants, this should help with some code tidyup. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-4-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 9c96ab1bf84f..e87fe1cf7307 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1739,10 +1739,9 @@ static const struct clk_ops swclk_gate_ops = { }; -static struct clk *tx_macro_register_mclk_output(struct tx_macro *tx) +static int tx_macro_register_mclk_output(struct tx_macro *tx) { struct device *dev = tx->dev; - struct device_node *np = dev->of_node; const char *parent_clk_name = NULL; const char *clk_name = "lpass-tx-mclk"; struct clk_hw *hw; @@ -1758,13 +1757,11 @@ static struct clk *tx_macro_register_mclk_output(struct tx_macro *tx) init.num_parents = 1; tx->hw.init = &init; hw = &tx->hw; - ret = clk_hw_register(tx->dev, hw); + ret = devm_clk_hw_register(dev, hw); if (ret) - return ERR_PTR(ret); - - of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + return ret; - return NULL; + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); } static const struct snd_soc_component_driver tx_macro_component_drv = { @@ -1837,7 +1834,9 @@ static int tx_macro_probe(struct platform_device *pdev) if (ret) return ret; - tx_macro_register_mclk_output(tx); + ret = tx_macro_register_mclk_output(tx); + if (ret) + goto err; ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv, tx_macro_dai, @@ -1855,8 +1854,6 @@ static int tx_macro_remove(struct platform_device *pdev) { struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - of_clk_del_provider(pdev->dev.of_node); - clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks); return 0; -- cgit v1.2.3 From 43b647d9940454263421f9a1c756680bdf1d443c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:06 +0000 Subject: ASoC: codecs: rx-macro: move to individual clks from bulk Using bulk clocks and referencing them individually using array index is not great for readers. So move them to individual clocks handling and also remove some unnecessary error handling in the code. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-5-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 85 +++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 0822fe618d50..c627b0afdf7e 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -608,7 +608,11 @@ struct rx_macro { int softclip_clk_users; struct regmap *regmap; - struct clk_bulk_data clks[RX_NUM_CLKS_MAX]; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; struct clk_hw hw; }; #define to_rx_macro(_hw) container_of(_hw, struct rx_macro, hw) @@ -3484,7 +3488,7 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(rx->clks[2].clk); + parent_clk_name = __clk_get_name(rx->mclk); init.name = clk_name; init.ops = &swclk_gate_ops; @@ -3522,17 +3526,25 @@ static int rx_macro_probe(struct platform_device *pdev) if (!rx) return -ENOMEM; - rx->clks[0].id = "macro"; - rx->clks[1].id = "dcodec"; - rx->clks[2].id = "mclk"; - rx->clks[3].id = "npl"; - rx->clks[4].id = "fsgen"; + rx->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(rx->macro)) + return PTR_ERR(rx->macro); - ret = devm_clk_bulk_get_optional(dev, RX_NUM_CLKS_MAX, rx->clks); - if (ret) { - dev_err(dev, "Error getting RX Clocks (%d)\n", ret); - return ret; - } + rx->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(rx->dcodec)) + return PTR_ERR(rx->dcodec); + + rx->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(rx->mclk)) + return PTR_ERR(rx->mclk); + + rx->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(rx->npl)) + return PTR_ERR(rx->npl); + + rx->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(rx->fsgen)) + return PTR_ERR(rx->fsgen); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -3548,26 +3560,52 @@ static int rx_macro_probe(struct platform_device *pdev) rx->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(rx->clks[2].clk, MCLK_FREQ); - clk_set_rate(rx->clks[3].clk, 2 * MCLK_FREQ); + clk_set_rate(rx->mclk, MCLK_FREQ); + clk_set_rate(rx->npl, 2 * MCLK_FREQ); - ret = clk_bulk_prepare_enable(RX_NUM_CLKS_MAX, rx->clks); + ret = clk_prepare_enable(rx->macro); if (ret) - return ret; + goto err; + + ret = clk_prepare_enable(rx->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(rx->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(rx->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(rx->fsgen); + if (ret) + goto err_fsgen; ret = rx_macro_register_mclk_output(rx); if (ret) - goto err; + goto err_clkout; ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv, rx_macro_dai, ARRAY_SIZE(rx_macro_dai)); if (ret) - goto err; + goto err_clkout; - return ret; + return 0; + +err_clkout: + clk_disable_unprepare(rx->fsgen); +err_fsgen: + clk_disable_unprepare(rx->npl); +err_npl: + clk_disable_unprepare(rx->mclk); +err_mclk: + clk_disable_unprepare(rx->dcodec); +err_dcodec: + clk_disable_unprepare(rx->macro); err: - clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks); return ret; } @@ -3575,7 +3613,12 @@ static int rx_macro_remove(struct platform_device *pdev) { struct rx_macro *rx = dev_get_drvdata(&pdev->dev); - clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks); + clk_disable_unprepare(rx->mclk); + clk_disable_unprepare(rx->npl); + clk_disable_unprepare(rx->fsgen); + clk_disable_unprepare(rx->macro); + clk_disable_unprepare(rx->dcodec); + return 0; } -- cgit v1.2.3 From 512864c4ffa70522b9c44d5b40c15273330ae9c7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:07 +0000 Subject: ASoC: codecs: tx-macro: move to individual clks from bulk Using bulk clocks and referencing them individually using array index is not great for readers. So move them to individual clocks handling and also remove some unnecessary error handling in the code. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-6-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 87 +++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index e87fe1cf7307..cd2a8c300197 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -258,7 +259,11 @@ struct tx_macro { unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS]; unsigned long active_decimator[TX_MACRO_MAX_DAIS]; struct regmap *regmap; - struct clk_bulk_data clks[TX_NUM_CLKS_MAX]; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; struct clk_hw hw; bool dec_active[NUM_DECIMATORS]; bool reset_swr; @@ -1748,7 +1753,7 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(tx->clks[2].clk); + parent_clk_name = __clk_get_name(tx->mclk); init.name = clk_name; init.ops = &swclk_gate_ops; @@ -1787,17 +1792,25 @@ static int tx_macro_probe(struct platform_device *pdev) if (!tx) return -ENOMEM; - tx->clks[0].id = "macro"; - tx->clks[1].id = "dcodec"; - tx->clks[2].id = "mclk"; - tx->clks[3].id = "npl"; - tx->clks[4].id = "fsgen"; + tx->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(tx->macro)) + return PTR_ERR(tx->macro); - ret = devm_clk_bulk_get_optional(dev, TX_NUM_CLKS_MAX, tx->clks); - if (ret) { - dev_err(dev, "Error getting RX Clocks (%d)\n", ret); - return ret; - } + tx->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(tx->dcodec)) + return PTR_ERR(tx->dcodec); + + tx->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(tx->mclk)) + return PTR_ERR(tx->mclk); + + tx->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(tx->npl)) + return PTR_ERR(tx->npl); + + tx->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(tx->fsgen)) + return PTR_ERR(tx->fsgen); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -1827,26 +1840,52 @@ static int tx_macro_probe(struct platform_device *pdev) tx->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(tx->clks[2].clk, MCLK_FREQ); - clk_set_rate(tx->clks[3].clk, 2 * MCLK_FREQ); + clk_set_rate(tx->mclk, MCLK_FREQ); + clk_set_rate(tx->npl, 2 * MCLK_FREQ); - ret = clk_bulk_prepare_enable(TX_NUM_CLKS_MAX, tx->clks); + ret = clk_prepare_enable(tx->macro); if (ret) - return ret; + goto err; + + ret = clk_prepare_enable(tx->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(tx->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(tx->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(tx->fsgen); + if (ret) + goto err_fsgen; ret = tx_macro_register_mclk_output(tx); if (ret) - goto err; + goto err_clkout; ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv, tx_macro_dai, ARRAY_SIZE(tx_macro_dai)); if (ret) - goto err; - return ret; -err: - clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks); + goto err_clkout; + return 0; + +err_clkout: + clk_disable_unprepare(tx->fsgen); +err_fsgen: + clk_disable_unprepare(tx->npl); +err_npl: + clk_disable_unprepare(tx->mclk); +err_mclk: + clk_disable_unprepare(tx->dcodec); +err_dcodec: + clk_disable_unprepare(tx->macro); +err: return ret; } @@ -1854,7 +1893,11 @@ static int tx_macro_remove(struct platform_device *pdev) { struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks); + clk_disable_unprepare(tx->macro); + clk_disable_unprepare(tx->dcodec); + clk_disable_unprepare(tx->mclk); + clk_disable_unprepare(tx->npl); + clk_disable_unprepare(tx->fsgen); return 0; } -- cgit v1.2.3 From e252801deb253581892ca6beba625d553d63d538 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:08 +0000 Subject: ASoC: codecs: wsa-macro: move to individual clks from bulk Using bulk clocks and referencing them individually using array index is not great for readers. So move them to individual clocks handling and also remove some unnecessary error handling in the code. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-7-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 88 ++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 69d2915f40d8..c7b08a2bc234 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -347,7 +347,11 @@ struct wsa_macro { int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; struct regmap *regmap; - struct clk_bulk_data clks[WSA_NUM_CLKS_MAX]; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; struct clk_hw hw; }; #define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) @@ -2350,7 +2354,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(wsa->clks[2].clk); + parent_clk_name = __clk_get_name(wsa->mclk); init.name = clk_name; init.ops = &swclk_gate_ops; @@ -2388,17 +2392,25 @@ static int wsa_macro_probe(struct platform_device *pdev) if (!wsa) return -ENOMEM; - wsa->clks[0].id = "macro"; - wsa->clks[1].id = "dcodec"; - wsa->clks[2].id = "mclk"; - wsa->clks[3].id = "npl"; - wsa->clks[4].id = "fsgen"; + wsa->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(wsa->macro)) + return PTR_ERR(wsa->macro); - ret = devm_clk_bulk_get(dev, WSA_NUM_CLKS_MAX, wsa->clks); - if (ret) { - dev_err(dev, "Error getting WSA Clocks (%d)\n", ret); - return ret; - } + wsa->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(wsa->dcodec)) + return PTR_ERR(wsa->dcodec); + + wsa->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wsa->mclk)) + return PTR_ERR(wsa->mclk); + + wsa->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(wsa->npl)) + return PTR_ERR(wsa->npl); + + wsa->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(wsa->fsgen)) + return PTR_ERR(wsa->fsgen); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -2414,25 +2426,53 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(wsa->clks[2].clk, WSA_MACRO_MCLK_FREQ); - clk_set_rate(wsa->clks[3].clk, WSA_MACRO_MCLK_FREQ); + clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); + clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - ret = clk_bulk_prepare_enable(WSA_NUM_CLKS_MAX, wsa->clks); + ret = clk_prepare_enable(wsa->macro); if (ret) - return ret; + goto err; + + ret = clk_prepare_enable(wsa->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(wsa->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(wsa->fsgen); + if (ret) + goto err_fsgen; + + ret = wsa_macro_register_mclk_output(wsa); + if (ret) + goto err_clkout; - wsa_macro_register_mclk_output(wsa); ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv, wsa_macro_dai, ARRAY_SIZE(wsa_macro_dai)); if (ret) - goto err; + goto err_clkout; - return ret; -err: - clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks); + return 0; +err_clkout: + clk_disable_unprepare(wsa->fsgen); +err_fsgen: + clk_disable_unprepare(wsa->npl); +err_npl: + clk_disable_unprepare(wsa->mclk); +err_mclk: + clk_disable_unprepare(wsa->dcodec); +err_dcodec: + clk_disable_unprepare(wsa->macro); +err: return ret; } @@ -2441,7 +2481,11 @@ static int wsa_macro_remove(struct platform_device *pdev) { struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); - clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks); + clk_disable_unprepare(wsa->macro); + clk_disable_unprepare(wsa->dcodec); + clk_disable_unprepare(wsa->mclk); + clk_disable_unprepare(wsa->npl); + clk_disable_unprepare(wsa->fsgen); return 0; } -- cgit v1.2.3 From 05a41340e56f716ef9f83006990f6eea153c5fe0 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:09 +0000 Subject: ASoC: codecs: wsa-macro: setup soundwire clks correctly For SoundWire Frame sync to be generated correctly we need both MCLK and MCLKx2 (npl). Without pm runtime enabled these two clocks will remain on, however after adding pm runtime support its possible that NPl clock could be turned off even when SoundWire controller is active. Fix this by enabling mclk and npl clk when SoundWire clks are enabled. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-8-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index c7b08a2bc234..94b8e2c59c26 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2260,6 +2260,13 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) struct regmap *regmap = wsa->regmap; if (enable) { + int ret; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) { + dev_err(wsa->dev, "failed to enable mclk\n"); + return ret; + } wsa_macro_mclk_enable(wsa, true); /* reset swr ip */ @@ -2284,6 +2291,7 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, CDC_WSA_SWR_CLK_EN_MASK, 0); wsa_macro_mclk_enable(wsa, false); + clk_disable_unprepare(wsa->mclk); } return 0; @@ -2354,7 +2362,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(wsa->mclk); + parent_clk_name = __clk_get_name(wsa->npl); init.name = clk_name; init.ops = &swclk_gate_ops; -- cgit v1.2.3 From 31bd0db84c6010cd6cf38048570b51aaae26d91d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:10 +0000 Subject: ASoC: codecs: tx-macro: setup soundwire clks correctly For SoundWire Frame sync to be generated correctly we need both MCLK and MCLKx2 (npl). Without pm runtime enabled these two clocks will remain on, however after adding pm runtime support its possible that NPl clock could be turned off even when SoundWire controller is active. Fix this by enabling mclk and npl clk when SoundWire clks are enabled. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-9-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index cd2a8c300197..27a8774e6360 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1690,6 +1690,13 @@ static int swclk_gate_enable(struct clk_hw *hw) { struct tx_macro *tx = to_tx_macro(hw); struct regmap *regmap = tx->regmap; + int ret; + + ret = clk_prepare_enable(tx->mclk); + if (ret) { + dev_err(tx->dev, "failed to enable mclk\n"); + return ret; + } tx_macro_mclk_enable(tx, true); if (tx->reset_swr) @@ -1717,6 +1724,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_TX_SWR_CLK_EN_MASK, 0x0); tx_macro_mclk_enable(tx, false); + clk_disable_unprepare(tx->mclk); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -1753,7 +1761,7 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(tx->mclk); + parent_clk_name = __clk_get_name(tx->npl); init.name = clk_name; init.ops = &swclk_gate_ops; -- cgit v1.2.3 From eaba113430d6c5e2c74fc8061fbd86efc000e99c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:11 +0000 Subject: ASoC: codecs: rx-macro: setup soundwire clks correctly For SoundWire Frame sync to be generated correctly we need both MCLK and MCLKx2 (npl). Without pm runtime enabled these two clocks will remain on, however after adding pm runtime support its possible that NPl clock could be turned off even when SoundWire controller is active. Fix this by enabling mclk and npl clk when SoundWire clks are enabled. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-10-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index c627b0afdf7e..65fc3698492f 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3426,6 +3426,13 @@ static int rx_macro_component_probe(struct snd_soc_component *component) static int swclk_gate_enable(struct clk_hw *hw) { struct rx_macro *rx = to_rx_macro(hw); + int ret; + + ret = clk_prepare_enable(rx->mclk); + if (ret) { + dev_err(rx->dev, "unable to prepare mclk\n"); + return ret; + } rx_macro_mclk_enable(rx, true); if (rx->reset_swr) @@ -3452,6 +3459,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_RX_SWR_CLK_EN_MASK, 0); rx_macro_mclk_enable(rx, false); + clk_disable_unprepare(rx->mclk); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -3488,7 +3496,7 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx) struct clk_init_data init; int ret; - parent_clk_name = __clk_get_name(rx->mclk); + parent_clk_name = __clk_get_name(rx->npl); init.name = clk_name; init.ops = &swclk_gate_ops; -- cgit v1.2.3 From 72ad25eabda0ac2b13ab8f418dc5d360aded172c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:12 +0000 Subject: ASoC: codecs: va-macro: add runtime pm support Add pm runtime support to VA Macro. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-11-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 7627e8aea688..4687319303da 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1469,6 +1470,12 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; err_clkout: @@ -1492,6 +1499,39 @@ static int va_macro_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused va_macro_runtime_suspend(struct device *dev) +{ + struct va_macro *va = dev_get_drvdata(dev); + + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); + + clk_disable_unprepare(va->mclk); + + return 0; +} + +static int __maybe_unused va_macro_runtime_resume(struct device *dev) +{ + struct va_macro *va = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(va->mclk); + if (ret) { + dev_err(va->dev, "unable to prepare mclk\n"); + return ret; + } + + regcache_cache_only(va->regmap, false); + regcache_sync(va->regmap); + return 0; +} + + +static const struct dev_pm_ops va_macro_pm_ops = { + SET_RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL) +}; + static const struct of_device_id va_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-va-macro" }, { .compatible = "qcom,sm8250-lpass-va-macro" }, @@ -1504,6 +1544,7 @@ static struct platform_driver va_macro_driver = { .name = "va_macro", .of_match_table = va_macro_dt_match, .suppress_bind_attrs = true, + .pm = &va_macro_pm_ops, }, .probe = va_macro_probe, .remove = va_macro_remove, -- cgit v1.2.3 From c96baa2949b245da7e0d0e9e2a44cd4471c9f303 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:13 +0000 Subject: ASoC: codecs: wsa-macro: add runtime pm support Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-12-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 94b8e2c59c26..27da6c6c3c5a 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "lpass-wsa-macro.h" @@ -2468,6 +2469,12 @@ static int wsa_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; err_clkout: @@ -2498,6 +2505,59 @@ static int wsa_macro_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev) +{ + struct wsa_macro *wsa = dev_get_drvdata(dev); + + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + + clk_disable_unprepare(wsa->mclk); + clk_disable_unprepare(wsa->npl); + clk_disable_unprepare(wsa->fsgen); + + return 0; +} + +static int __maybe_unused wsa_macro_runtime_resume(struct device *dev) +{ + struct wsa_macro *wsa = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(wsa->npl); + if (ret) { + dev_err(dev, "unable to prepare mclkx2\n"); + goto err_npl; + } + + ret = clk_prepare_enable(wsa->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + + regcache_cache_only(wsa->regmap, false); + regcache_sync(wsa->regmap); + + return 0; +err_fsgen: + clk_disable_unprepare(wsa->npl); +err_npl: + clk_disable_unprepare(wsa->mclk); + + return ret; +} + +static const struct dev_pm_ops wsa_macro_pm_ops = { + SET_RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL) +}; + static const struct of_device_id wsa_macro_dt_match[] = { {.compatible = "qcom,sc7280-lpass-wsa-macro"}, {.compatible = "qcom,sm8250-lpass-wsa-macro"}, @@ -2509,6 +2569,7 @@ static struct platform_driver wsa_macro_driver = { .driver = { .name = "wsa_macro", .of_match_table = wsa_macro_dt_match, + .pm = &wsa_macro_pm_ops, }, .probe = wsa_macro_probe, .remove = wsa_macro_remove, -- cgit v1.2.3 From 366ff79ed5392ac518fd43cb44f82f63b87c313e Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:14 +0000 Subject: ASoC: codecs: rx-macro: add runtime pm support Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-13-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 65fc3698492f..58dc86756804 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -3601,6 +3602,13 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; err_clkout: @@ -3637,11 +3645,65 @@ static const struct of_device_id rx_macro_dt_match[] = { }; MODULE_DEVICE_TABLE(of, rx_macro_dt_match); +static int __maybe_unused rx_macro_runtime_suspend(struct device *dev) +{ + struct rx_macro *rx = dev_get_drvdata(dev); + + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + + clk_disable_unprepare(rx->mclk); + clk_disable_unprepare(rx->npl); + clk_disable_unprepare(rx->fsgen); + + return 0; +} + +static int __maybe_unused rx_macro_runtime_resume(struct device *dev) +{ + struct rx_macro *rx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(rx->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(rx->npl); + if (ret) { + dev_err(dev, "unable to prepare mclkx2\n"); + goto err_npl; + } + + ret = clk_prepare_enable(rx->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + regcache_cache_only(rx->regmap, false); + regcache_sync(rx->regmap); + rx->reset_swr = true; + + return 0; +err_fsgen: + clk_disable_unprepare(rx->npl); +err_npl: + clk_disable_unprepare(rx->mclk); + + return ret; +} + +static const struct dev_pm_ops rx_macro_pm_ops = { + SET_RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL) +}; + static struct platform_driver rx_macro_driver = { .driver = { .name = "rx_macro", .of_match_table = rx_macro_dt_match, .suppress_bind_attrs = true, + .pm = &rx_macro_pm_ops, }, .probe = rx_macro_probe, .remove = rx_macro_remove, -- cgit v1.2.3 From 1fb83bc5cf640c821910424cd237e2df1e81be6f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:15 +0000 Subject: ASoC: codecs: tx-macro: add runtime pm support Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-14-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 27a8774e6360..f2f0d1c4c438 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1881,6 +1881,12 @@ static int tx_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; err_clkout: @@ -1910,6 +1916,60 @@ static int tx_macro_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused tx_macro_runtime_suspend(struct device *dev) +{ + struct tx_macro *tx = dev_get_drvdata(dev); + + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + + clk_disable_unprepare(tx->mclk); + clk_disable_unprepare(tx->npl); + clk_disable_unprepare(tx->fsgen); + + return 0; +} + +static int __maybe_unused tx_macro_runtime_resume(struct device *dev) +{ + struct tx_macro *tx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(tx->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(tx->npl); + if (ret) { + dev_err(dev, "unable to prepare npl\n"); + goto err_npl; + } + + ret = clk_prepare_enable(tx->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + + regcache_cache_only(tx->regmap, false); + regcache_sync(tx->regmap); + tx->reset_swr = true; + + return 0; +err_fsgen: + clk_disable_unprepare(tx->npl); +err_npl: + clk_disable_unprepare(tx->mclk); + + return ret; +} + +static const struct dev_pm_ops tx_macro_pm_ops = { + SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) +}; + static const struct of_device_id tx_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-tx-macro" }, { .compatible = "qcom,sm8250-lpass-tx-macro" }, @@ -1921,6 +1981,7 @@ static struct platform_driver tx_macro_driver = { .name = "tx_macro", .of_match_table = tx_macro_dt_match, .suppress_bind_attrs = true, + .pm = &tx_macro_pm_ops, }, .probe = tx_macro_probe, .remove = tx_macro_remove, -- cgit v1.2.3 From 584a6301e1d548b2875a47b44f6aecb70a80ee53 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:17 +0000 Subject: ASoC: codecs: wcd938x: add simple clk stop support mark WCD938x as clock stop capable. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-16-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x-sdw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 1fa05ec7459a..1bf3c06a2b62 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -249,6 +249,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; if (wcd->is_tx) { pdev->prop.source_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0); pdev->prop.src_dpn_prop = wcd938x_dpn_prop; -- cgit v1.2.3 From cc4d891f1876242400ef21d95ab9e9553e9d10b0 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 24 Feb 2022 11:17:18 +0000 Subject: ASoC: codecs: wcd-mbhc: add runtime pm support under low power state a SoundWire Wake IRQ could trigger MBHC interrupts so make sure that codec is not in suspended state when this happens. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220224111718.6264-17-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 7488a150a138..c53c2ef33e1a 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -711,6 +712,16 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data) static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) { struct snd_soc_component *component = mbhc->component; + int ret; + + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(component->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(component->dev); + return ret; + } mutex_lock(&mbhc->lock); @@ -751,6 +762,9 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_unlock(&mbhc->lock); + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + return 0; } @@ -1078,10 +1092,19 @@ static void wcd_correct_swch_plug(struct work_struct *work) int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv; bool is_spl_hs = false; bool is_pa_on; + int ret; mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); component = mbhc->component; + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(component->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(component->dev); + return; + } micbias_mv = wcd_mbhc_get_micbias(mbhc); hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); @@ -1232,6 +1255,9 @@ exit: if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(component, true); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); } static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) -- cgit v1.2.3 From 9e3d83c52844f955aa2975f78cee48bf9f72f5e1 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Sat, 26 Feb 2022 23:39:18 +0530 Subject: ASoC: codecs: Add power domains support in digital macro codecs Add support for enabling required power domains in digital macro codecs. macro and dcodec power domains are being requested as clocks by HLOS in ADSP based architectures and ADSP internally handling as powerdomains. In ADSP bypass case need to handle them as power domains explicitly. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Reported-by: kernel test robot Link: https://lore.kernel.org/r/1645898959-11231-2-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 7 ++++ sound/soc/codecs/Makefile | 2 ++ sound/soc/codecs/lpass-macro-common.c | 67 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/lpass-macro-common.h | 17 +++++++++ sound/soc/codecs/lpass-rx-macro.c | 10 +++++- sound/soc/codecs/lpass-tx-macro.c | 9 +++++ sound/soc/codecs/lpass-va-macro.c | 10 ++++++ sound/soc/qcom/Kconfig | 1 + 8 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/lpass-macro-common.c create mode 100644 sound/soc/codecs/lpass-macro-common.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c2627f7489a4..4de029ae377c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -244,6 +244,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WCD9335 imply SND_SOC_WCD934X imply SND_SOC_WCD938X_SDW + imply SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO imply SND_SOC_WL1273 @@ -2008,6 +2009,9 @@ config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C +config SND_SOC_LPASS_MACRO_COMMON + tristate + config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK select REGMAP_MMIO @@ -2016,16 +2020,19 @@ config SND_SOC_LPASS_WSA_MACRO config SND_SOC_LPASS_VA_MACRO depends on COMMON_CLK select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_RX_MACRO depends on COMMON_CLK select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_TX_MACRO depends on COMMON_CLK select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b4e11c3e4a08..c3c6059a5f8a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -112,6 +112,7 @@ snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-lochnagar-sc-objs := lochnagar-sc.o +snd-soc-lpass-macro-common-objs := lpass-macro-common.o snd-soc-lpass-rx-macro-objs := lpass-rx-macro.o snd-soc-lpass-tx-macro-objs := lpass-tx-macro.o snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o @@ -676,6 +677,7 @@ obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SND_SOC_LPASS_MACRO_COMMON) += snd-soc-lpass-macro-common.o obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c new file mode 100644 index 000000000000..6cede75ed3b5 --- /dev/null +++ b/sound/soc/codecs/lpass-macro-common.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2022, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" + +struct lpass_macro *lpass_macro_pds_init(struct device *dev) +{ + struct lpass_macro *l_pds; + int ret; + + if (!of_find_property(dev->of_node, "power-domains", NULL)) + return NULL; + + l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL); + if (!l_pds) + return ERR_PTR(-ENOMEM); + + l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro"); + if (IS_ERR_OR_NULL(l_pds->macro_pd)) + return NULL; + + ret = pm_runtime_get_sync(l_pds->macro_pd); + if (ret < 0) { + pm_runtime_put_noidle(l_pds->macro_pd); + goto macro_err; + } + + l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec"); + if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) + goto dcodec_err; + + ret = pm_runtime_get_sync(l_pds->dcodec_pd); + if (ret < 0) { + pm_runtime_put_noidle(l_pds->dcodec_pd); + goto dcodec_sync_err; + } + return l_pds; + +dcodec_sync_err: + dev_pm_domain_detach(l_pds->dcodec_pd, false); +dcodec_err: + pm_runtime_put(l_pds->macro_pd); +macro_err: + dev_pm_domain_detach(l_pds->macro_pd, false); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(lpass_macro_pds_init); + +void lpass_macro_pds_exit(struct lpass_macro *pds) +{ + pm_runtime_put(pds->macro_pd); + dev_pm_domain_detach(pds->macro_pd, false); + pm_runtime_put(pds->dcodec_pd); + dev_pm_domain_detach(pds->dcodec_pd, false); +} +EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); + +MODULE_DESCRIPTION("Common macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h new file mode 100644 index 000000000000..f2cbf9fe2c6e --- /dev/null +++ b/sound/soc/codecs/lpass-macro-common.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, The Linux Foundation. All rights reserved. + */ + +#ifndef __LPASS_MACRO_COMMON_H__ +#define __LPASS_MACRO_COMMON_H__ + +struct lpass_macro { + struct device *macro_pd; + struct device *dcodec_pd; +}; + +struct lpass_macro *lpass_macro_pds_init(struct device *dev); +void lpass_macro_pds_exit(struct lpass_macro *pds); + +#endif /* __LPASS_MACRO_COMMON_H__ */ diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 58dc86756804..fc1d2255156b 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -15,6 +15,8 @@ #include #include +#include "lpass-macro-common.h" + #define CDC_RX_TOP_TOP_CFG0 (0x0000) #define CDC_RX_TOP_SWR_CTRL (0x0008) #define CDC_RX_TOP_DEBUG (0x000C) @@ -607,7 +609,7 @@ struct rx_macro { int is_softclip_on; int is_aux_hpf_on; int softclip_clk_users; - + struct lpass_macro *pds; struct regmap *regmap; struct clk *mclk; struct clk *npl; @@ -3555,6 +3557,10 @@ static int rx_macro_probe(struct platform_device *pdev) if (IS_ERR(rx->fsgen)) return PTR_ERR(rx->fsgen); + rx->pds = lpass_macro_pds_init(dev); + if (IS_ERR(rx->pds)) + return PTR_ERR(rx->pds); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -3635,6 +3641,8 @@ static int rx_macro_remove(struct platform_device *pdev) clk_disable_unprepare(rx->macro); clk_disable_unprepare(rx->dcodec); + lpass_macro_pds_exit(rx->pds); + return 0; } diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index f2f0d1c4c438..b492d5984819 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -14,6 +14,8 @@ #include #include +#include "lpass-macro-common.h" + #define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000) #define CDC_TX_MCLK_EN_MASK BIT(0) #define CDC_TX_MCLK_ENABLE BIT(0) @@ -271,6 +273,7 @@ struct tx_macro { u16 dmic_clk_div; bool bcs_enable; int dec_mode[NUM_DECIMATORS]; + struct lpass_macro *pds; bool bcs_clk_en; }; #define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw) @@ -1820,6 +1823,10 @@ static int tx_macro_probe(struct platform_device *pdev) if (IS_ERR(tx->fsgen)) return PTR_ERR(tx->fsgen); + tx->pds = lpass_macro_pds_init(dev); + if (IS_ERR(tx->pds)) + return PTR_ERR(tx->pds); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -1957,6 +1964,8 @@ static int __maybe_unused tx_macro_runtime_resume(struct device *dev) regcache_sync(tx->regmap); tx->reset_swr = true; + lpass_macro_pds_exit(tx->pds); + return 0; err_fsgen: clk_disable_unprepare(tx->npl); diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 4687319303da..275f2cd78c9c 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -16,6 +16,8 @@ #include #include +#include "lpass-macro-common.h" + /* VA macro registers */ #define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) #define CDC_VA_MCLK_CONTROL_EN BIT(0) @@ -198,6 +200,7 @@ struct va_macro { struct clk *macro; struct clk *dcodec; struct clk_hw hw; + struct lpass_macro *pds; s32 dmic_0_1_clk_cnt; s32 dmic_2_3_clk_cnt; @@ -1420,6 +1423,10 @@ static int va_macro_probe(struct platform_device *pdev) if (IS_ERR(va->mclk)) return PTR_ERR(va->mclk); + va->pds = lpass_macro_pds_init(dev); + if (IS_ERR(va->pds)) + return PTR_ERR(va->pds); + ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate", &sample_rate); if (ret) { @@ -1524,6 +1531,9 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) regcache_cache_only(va->regmap, false); regcache_sync(va->regmap); + + lpass_macro_pds_exit(va->pds); + return 0; } diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index ae99833b3fdb..0cd0dae5c545 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -194,6 +194,7 @@ config SND_SOC_SC7280 select SND_SOC_LPASS_SC7280 select SND_SOC_MAX98357A select SND_SOC_WCD938X + select SND_SOC_LPASS_MACRO_COMMON select SND_SOC_LPASS_RX_MACRO select SND_SOC_LPASS_TX_MACRO help -- cgit v1.2.3 From 6619c7d4379aca716a90f7581be2853071c086f6 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Sat, 26 Feb 2022 23:39:19 +0530 Subject: ASoC: qcom: dt-bindings: Add bindings for power domains in lpass digital codecs Include power domain dt-bindings for lpass digital codecs RX, TX and VA macros, which are required for ADSP bypass architecture. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1645898959-11231-3-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml | 8 ++++++++ Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml | 8 ++++++++ Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml index bc762b39c68a..6127df5836c2 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml @@ -39,6 +39,14 @@ properties: items: - const: mclk + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: macro + - const: dcodec + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml index 74f53864e7a7..3f0f99cb18ca 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml @@ -39,6 +39,14 @@ properties: items: - const: mclk + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: macro + - const: dcodec + qcom,dmic-sample-rate: description: dmic sample rate $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index 99f2c3687fbd..9868a5ebe27e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -37,6 +37,14 @@ properties: items: - const: fsgen + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: macro + - const: dcodec + qcom,dmic-sample-rate: description: dmic sample rate $ref: /schemas/types.yaml#/definitions/uint32 -- cgit v1.2.3 From 8e08a0c9225f15c7561938e700ae89612ed78afb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:51:35 +0000 Subject: ASoC: lm49453: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the lm49453 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222225135.3726158-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/lm49453.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index fb0fb23537e7..973d781b4b6a 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1146,17 +1146,17 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) int clk_phase = 0; int clk_shift = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: aif_val = 0; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS | LM49453_AUDIO_PORT1_BASIC_SYNC_MS; break; -- cgit v1.2.3 From 7cba0b38c3e8eff221afb9c8b55d9aecaa6bcf74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:27:50 +0000 Subject: ASoC: tscs42xx: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the tscs42xx driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223002751.1574345-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tscs42xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index fb861baf50e8..5b63e017a43b 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1197,9 +1197,9 @@ static int tscs42xx_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; int ret; - /* Slave mode not supported since it needs always-on frame clock */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* Consumer mode not supported since it needs always-on frame clock */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: ret = snd_soc_component_update_bits(component, R_AIC1, RM_AIC1_MS, RV_AIC1_MS_MASTER); if (ret < 0) { -- cgit v1.2.3 From df63fb10a5b7e00dc247f4d2e229f98898ed21c1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:27:51 +0000 Subject: ASoC: tscs454: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the tscs454 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223002751.1574345-2-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tscs454.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 43220bb36701..7e1826d6f06f 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -57,7 +57,7 @@ struct internal_rate { struct aif { unsigned int id; - bool master; + bool provider; struct pll *pll; }; @@ -756,8 +756,8 @@ static int pll_power_event(struct snd_soc_dapm_widget *w, return 0; } -static inline int aif_set_master(struct snd_soc_component *component, - unsigned int aif_id, bool master) +static inline int aif_set_provider(struct snd_soc_component *component, + unsigned int aif_id, bool provider) { unsigned int reg; unsigned int mask; @@ -780,12 +780,12 @@ static inline int aif_set_master(struct snd_soc_component *component, return ret; } mask = FM_I2SPCTL_PORTMS; - val = master ? FV_PORTMS_MASTER : FV_PORTMS_SLAVE; + val = provider ? FV_PORTMS_MASTER : FV_PORTMS_SLAVE; ret = snd_soc_component_update_bits(component, reg, mask, val); if (ret < 0) { dev_err(component->dev, "Failed to set DAI %d to %s (%d)\n", - aif_id, master ? "master" : "slave", ret); + aif_id, provider ? "provider" : "consumer", ret); return ret; } @@ -797,7 +797,7 @@ int aif_prepare(struct snd_soc_component *component, struct aif *aif) { int ret; - ret = aif_set_master(component, aif->id, aif->master); + ret = aif_set_provider(component, aif->id, aif->provider); if (ret < 0) return ret; @@ -820,7 +820,7 @@ static inline int aif_free(struct snd_soc_component *component, if (!aif_active(&tscs454->aifs_status, aif->id)) { /* Do config in slave mode */ - aif_set_master(component, aif->id, false); + aif_set_provider(component, aif->id, false); dev_dbg(component->dev, "Freeing pll %d from aif %d\n", aif->pll->id, aif->id); free_pll(aif->pll); @@ -2708,17 +2708,17 @@ static int tscs454_set_bclk_ratio(struct snd_soc_dai *dai, return 0; } -static inline int set_aif_master_from_fmt(struct snd_soc_component *component, +static inline int set_aif_provider_from_fmt(struct snd_soc_component *component, struct aif *aif, unsigned int fmt) { int ret; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - aif->master = true; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + aif->provider = true; break; - case SND_SOC_DAIFMT_CBS_CFS: - aif->master = false; + case SND_SOC_DAIFMT_CBC_CFC: + aif->provider = false; break; default: ret = -EINVAL; @@ -2888,7 +2888,7 @@ static int tscs454_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct aif *aif = &tscs454->aifs[dai->id]; int ret; - ret = set_aif_master_from_fmt(component, aif, fmt); + ret = set_aif_provider_from_fmt(component, aif, fmt); if (ret < 0) return ret; -- cgit v1.2.3 From 87d71a12877114b4ad60ce5b93482505bac88d6e Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Sun, 27 Feb 2022 13:09:28 +0800 Subject: ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypassed the hierarchical setup and messed up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220227050928.32270-1-tangmeng@uniontech.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 11 ++++------- sound/soc/amd/raven/acp3x-pcm-dma.c | 8 ++------ sound/soc/amd/renoir/acp3x-pdm-dma.c | 7 ++----- sound/soc/amd/vangogh/acp5x-pcm-dma.c | 7 ++----- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 8fa2e2fde4f1..1cd2e70a57df 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1217,9 +1217,8 @@ static const struct snd_soc_component_driver acp_asoc_platform = { static int acp_audio_probe(struct platform_device *pdev) { - int status; + int status, irq; struct audio_drv_data *audio_drv_data; - struct resource *res; const u32 *pdata = pdev->dev.platform_data; if (!pdata) { @@ -1249,13 +1248,11 @@ static int acp_audio_probe(struct platform_device *pdev) audio_drv_data->asic_type = *pdata; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) return -ENODEV; - } - status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler, + status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, 0, "ACP_IRQ", &pdev->dev); if (status) { dev_err(&pdev->dev, "ACP IRQ request failed\n"); diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 75c06697fa09..e4f8dbf0d11d 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -394,13 +394,9 @@ static int acp3x_audio_probe(struct platform_device *pdev) if (!adata->acp3x_base) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + adata->i2s_irq = platform_get_irq(pdev, 0); + if (adata->i2s_irq < 0) return -ENODEV; - } - - adata->i2s_irq = res->start; dev_set_drvdata(&pdev->dev, adata); status = devm_snd_soc_register_component(&pdev->dev, diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index 9dd22a2fa2e5..88a242538461 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -399,13 +399,10 @@ static int acp_pdm_audio_probe(struct platform_device *pdev) if (!adata->acp_base) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + adata->pdm_irq = platform_get_irq(pdev, 0); + if (adata->pdm_irq < 0) return -ENODEV; - } - adata->pdm_irq = res->start; adata->capture_stream = NULL; dev_set_drvdata(&pdev->dev, adata); diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index f10de38976cb..c8cd1777a63c 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -388,13 +388,10 @@ static int acp5x_audio_probe(struct platform_device *pdev) if (!adata->acp5x_base) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + adata->i2s_irq = platform_get_irq(pdev, 0); + if (adata->i2s_irq < 0) return -ENODEV; - } - adata->i2s_irq = res->start; dev_set_drvdata(&pdev->dev, adata); status = devm_snd_soc_register_component(&pdev->dev, &acp5x_i2s_component, -- cgit v1.2.3 From a325068e2be110254ab19e2c0fdcfc9f2b9d4694 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:25:02 +0000 Subject: ASoC: sti-sas: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the sti-sas driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223002502.1451015-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/sti-sas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 82a24e330065..3be4940e3c77 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -154,10 +154,10 @@ static int sti_sas_init_sas_registers(struct snd_soc_component *component, static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { /* Sanity check only */ - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { dev_err(dai->component->dev, - "%s: ERROR: Unsupporter master mask 0x%x\n", - __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + "%s: ERROR: Unsupported clocking 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); return -EINVAL; } -- cgit v1.2.3 From 10daafb04dce7d6c09b3dffccc4950f054fe2b46 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:17:37 +0000 Subject: ASoC: si476x: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the si476x driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223001737.1360028-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/si476x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 8d88db9c11a6..8bd2edf70f13 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -69,7 +69,7 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, int err; u16 format = 0; - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) return -EINVAL; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { -- cgit v1.2.3 From 0dc974725988e699ffec93f22dd829f1872befc7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:37:07 +0000 Subject: ASoC: uda1380: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the uda1380 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223003707.1903798-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/uda1380.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 89f2bfeeb70e..13060a9a2388 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -435,8 +435,8 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai, iface |= R01_SFORI_MSB | R01_SFORO_MSB; } - /* DATAI is slave only, so in single-link mode, this has to be slave */ - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + /* DATAI is consumer only */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) return -EINVAL; uda1380_write_reg_cache(component, UDA1380_IFACE, iface); @@ -465,8 +465,8 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai, iface |= R01_SFORI_MSB; } - /* DATAI is slave only, so this has to be slave */ - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + /* DATAI is consumer only */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) return -EINVAL; uda1380_write(component, UDA1380_IFACE, iface); @@ -495,7 +495,7 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai, iface |= R01_SFORO_MSB; } - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM) + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_CBP_CFP) iface |= R01_SIM; uda1380_write(component, UDA1380_IFACE, iface); -- cgit v1.2.3 From aac21939bb2c8989082048054e1887d3be459980 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:32:07 +0000 Subject: ASoC: uda1334: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the uda1334 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223003207.1748248-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/uda1334.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c index 21ab8c5487ba..8670a2a05a56 100644 --- a/sound/soc/codecs/uda1334.c +++ b/sound/soc/codecs/uda1334.c @@ -172,7 +172,7 @@ static int uda1334_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) SND_SOC_DAIFMT_MASTER_MASK); if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS)) { + SND_SOC_DAIFMT_CBC_CFC)) { dev_err(codec_dai->dev, "Invalid DAI format\n"); return -EINVAL; } -- cgit v1.2.3 From 7effe2cde907a5517489bf3927df7822c3acb13c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:30:46 +0000 Subject: ASoC: twl4030: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the twl4030 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220223003046.1697784-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index e059711ff293..0ba3546ef870 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1840,13 +1840,12 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF); format = old_format; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: format &= ~(TWL4030_AIF_SLAVE_EN); format &= ~(TWL4030_CLK256FS_EN); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: format |= TWL4030_AIF_SLAVE_EN; format |= TWL4030_CLK256FS_EN; break; @@ -2038,9 +2037,8 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, old_format = twl4030_read(component, TWL4030_REG_VOICE_IF); format = old_format; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: format &= ~(TWL4030_VIF_SLAVE_EN); break; case SND_SOC_DAIFMT_CBS_CFS: -- cgit v1.2.3 From cce15e481859720a5889453e43ba36edd5154ae1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:27:15 +0000 Subject: ASoC: es7134: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the es7134 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222222715.2994339-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/es7134.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c index e2b79879354b..f443351677df 100644 --- a/sound/soc/codecs/es7134.c +++ b/sound/soc/codecs/es7134.c @@ -94,7 +94,7 @@ static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) SND_SOC_DAIFMT_MASTER_MASK); if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS)) { + SND_SOC_DAIFMT_CBC_CFC)) { dev_err(codec_dai->dev, "Invalid DAI format\n"); return -EINVAL; } -- cgit v1.2.3 From c9fa2165c0b410f195ed2bd15c07a4baa3bca482 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:38:10 +0000 Subject: ASoC: wl1273: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the wl1273 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223003810.1947495-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/wl1273.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index d8ced4559bf2..02232f64110e 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -414,13 +414,13 @@ int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt) case WL1273_MODE_FM_TX: *fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; break; case WL1273_MODE_BT: *fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; break; default: -- cgit v1.2.3 From b6be2012b0393003d5cb9d23504c8e17c995437b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:34:53 +0000 Subject: ASoC: es8316: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the es8316 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222223453.3190333-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/es8316.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 8f30a3ea8bfe..ff33eab6f9de 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -401,8 +401,8 @@ static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai, u8 clksw; u8 mask; - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { - dev_err(component->dev, "Codec driver only supports slave mode\n"); + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_err(component->dev, "Codec driver only supports consumer mode\n"); return -EINVAL; } -- cgit v1.2.3 From 58d858ae1c44e23ff48077b60b575359e81bb0ea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:48:16 +0000 Subject: ASoC: inno_rk3036: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the inno_rk3036 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222224816.3636987-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/inno_rk3036.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c index e05c4f27486e..ca0f4c1911e4 100644 --- a/sound/soc/codecs/inno_rk3036.c +++ b/sound/soc/codecs/inno_rk3036.c @@ -200,12 +200,12 @@ static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: reg01_val |= INNO_R01_PINDIR_IN_SLAVE | INNO_R01_I2SMODE_SLAVE; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: reg01_val |= INNO_R01_PINDIR_OUT_MASTER | INNO_R01_I2SMODE_MASTER; break; -- cgit v1.2.3 From 6d2608804ebe3516f9dd30c5428b11d9bc121764 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:35:34 +0000 Subject: ASoC: es8328: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the es8328 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222223534.3212743-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 9632afc2d4d6..3f00ead97006 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -84,7 +84,7 @@ struct es8328_priv { int mclkdiv2; const struct snd_pcm_hw_constraint_list *sysclk_constraints; const int *mclk_ratios; - bool master; + bool provider; struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; }; @@ -462,7 +462,7 @@ static int es8328_startup(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); - if (es8328->master && es8328->sysclk_constraints) + if (es8328->provider && es8328->sysclk_constraints) snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, es8328->sysclk_constraints); @@ -486,7 +486,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, else reg = ES8328_ADCCONTROL5; - if (es8328->master) { + if (es8328->provider) { if (!es8328->sysclk_constraints) { dev_err(component->dev, "No MCLK configured\n"); return -EINVAL; @@ -590,19 +590,19 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, u8 dac_mode = 0; u8 adc_mode = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: /* Master serial port mode, with BCLK generated automatically */ snd_soc_component_update_bits(component, ES8328_MASTERMODE, ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC); - es8328->master = true; + es8328->provider = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: /* Slave serial port mode */ snd_soc_component_update_bits(component, ES8328_MASTERMODE, ES8328_MASTERMODE_MSC, 0); - es8328->master = false; + es8328->provider = false; break; default: return -EINVAL; -- cgit v1.2.3 From 83b713619ee1b15e09eae11a92a7f3305534223d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 26 Feb 2022 01:00:22 +0530 Subject: ASoC: amd: vg: fix for pm resume callback sequence The previous condition is used to cross check only the active stream status for I2S HS instance playback and capture use cases. Modified logic to invoke sequence for two i2s controller instances. This also fixes warnings reported by kernel robot: "warning: variable 'frmt_val' set but not used" "warning: variable 'reg_val' set but not used" Reported-by: kernel test robot Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220225193054.24916-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-pcm-dma.c | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index c8cd1777a63c..e4e668593b3d 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -423,51 +423,51 @@ static int acp5x_audio_remove(struct platform_device *pdev) static int __maybe_unused acp5x_pcm_resume(struct device *dev) { struct i2s_dev_data *adata; - u32 val, reg_val, frmt_val; + struct i2s_stream_instance *rtd; + u32 val; - reg_val = 0; - frmt_val = 0; adata = dev_get_drvdata(dev); if (adata->play_stream && adata->play_stream->runtime) { - struct i2s_stream_instance *rtd = - adata->play_stream->runtime->private_data; + rtd = adata->play_stream->runtime->private_data; config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); - switch (rtd->i2s_instance) { - case I2S_HS_INSTANCE: - reg_val = ACP_HSTDM_ITER; - frmt_val = ACP_HSTDM_TXFRMT; - break; - case I2S_SP_INSTANCE: - default: - reg_val = ACP_I2STDM_ITER; - frmt_val = ACP_I2STDM_TXFRMT; + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT); + val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER); + } + } + if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) { + rtd = adata->i2ssp_play_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT); + val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER); } - acp_writel((rtd->xfer_resolution << 3), - rtd->acp5x_base + reg_val); } if (adata->capture_stream && adata->capture_stream->runtime) { - struct i2s_stream_instance *rtd = - adata->capture_stream->runtime->private_data; + rtd = adata->capture_stream->runtime->private_data; config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); - switch (rtd->i2s_instance) { - case I2S_HS_INSTANCE: - reg_val = ACP_HSTDM_IRER; - frmt_val = ACP_HSTDM_RXFRMT; - break; - case I2S_SP_INSTANCE: - default: - reg_val = ACP_I2STDM_IRER; - frmt_val = ACP_I2STDM_RXFRMT; + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT); + val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER); } - acp_writel((rtd->xfer_resolution << 3), - rtd->acp5x_base + reg_val); } - if (adata->tdm_mode == TDM_ENABLE) { - acp_writel(adata->tdm_fmt, adata->acp5x_base + frmt_val); - val = acp_readl(adata->acp5x_base + reg_val); - acp_writel(val | 0x2, adata->acp5x_base + reg_val); + if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) { + rtd = adata->i2ssp_capture_stream->runtime->private_data; + config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); + acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER); + if (adata->tdm_mode == TDM_ENABLE) { + acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT); + val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER); + acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER); + } } acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); return 0; -- cgit v1.2.3 From 5363d7304e31692f8f6da86ed7a49d3c28a2e32a Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 26 Feb 2022 01:00:23 +0530 Subject: ASoC: amd: vg: update DAI link name Update DAI link name as "acp5x-8821-play". Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220225193054.24916-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 14cf325e4b23..f5d04c7e2a1f 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -230,7 +230,7 @@ SND_SOC_DAILINK_DEF(platform, static struct snd_soc_dai_link acp5x_dai[] = { { - .name = "acp5x-8825-play", + .name = "acp5x-8821-play", .stream_name = "Playback/Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, -- cgit v1.2.3 From 728a592619cfb9be8b66600d04ef9fee9237fe7e Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 26 Feb 2022 01:00:24 +0530 Subject: ASoC: amd: vg: remove warnings and errors pointed out by checkpatch pl Fix checkpatch pl errors and warnings in vangogh machine driver. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220225193054.24916-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index f5d04c7e2a1f..16b7c3f70baf 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -320,7 +320,6 @@ static struct snd_soc_card acp5x_card = { .num_controls = ARRAY_SIZE(acp5x_8821_controls), }; - static int acp5x_vg_quirk_cb(const struct dmi_system_id *id) { acp5x_machine_id = VG_JUPITER; @@ -350,7 +349,7 @@ static int acp5x_probe(struct platform_device *pdev) return -ENOMEM; dmi_check_system(acp5x_vg_quirk_table); - switch(acp5x_machine_id) { + switch (acp5x_machine_id) { case VG_JUPITER: card = &acp5x_card; acp5x_card.dev = &pdev->dev; -- cgit v1.2.3 From 6f989800639a7a29ab9a02e165b04dc144dd4f2b Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Sat, 26 Feb 2022 01:00:25 +0530 Subject: ASoC: amd: vangogh: fix uninitialized symbol warning in machine driver Fixed below smatch static checker warning. sound/soc/amd/vangogh/acp5x-mach.c:190 acp5x_cs35l41_hw_params() error: uninitialized symbol 'ret'. Reported-by: Dan Carpenter Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20220225193054.24916-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 16b7c3f70baf..25b5166b23f8 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -165,6 +165,7 @@ static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream, unsigned int num_codecs = rtd->num_codecs; unsigned int bclk_val; + ret = 0; for (i = 0; i < num_codecs; i++) { codec_dai = asoc_rtd_to_codec(rtd, i); if ((strcmp(codec_dai->name, "spi-VLV1776:00") == 0) || -- cgit v1.2.3 From 032959d880fd99a790607cf59cf6c00b77a32cef Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Sun, 27 Feb 2022 22:56:32 +0000 Subject: ASoC: dt-bindings: renesas,rz-ssi: Document RZ/V2L SoC Document RZ/V2L SSI bindings. RZ/V2L SSI is identical to one found on the RZ/G2L SoC. No driver changes are required as generic compatible string "renesas,rz-ssi" will be used as a fallback. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220227225633.28829-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml b/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml index 414ff8035a4e..7e8d252f7bca 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/sound/renesas,rz-ssi.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/G2L ASoC Sound Serial Interface (SSIF-2) +title: Renesas RZ/{G2L,V2L} ASoC Sound Serial Interface (SSIF-2) maintainers: - Biju Das @@ -14,6 +14,7 @@ properties: items: - enum: - renesas,r9a07g044-ssi # RZ/G2{L,LC} + - renesas,r9a07g054-ssi # RZ/V2L - const: renesas,rz-ssi reg: -- cgit v1.2.3 From addeb10c95b2b588943f005d4e7a24b7cef650f4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:50:14 +0000 Subject: ASoC: isabelle: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the isabelle driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222225014.3691208-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/isabelle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 79afced75d76..1d86b6a0eb9d 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -973,11 +973,11 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct snd_soc_component *component = codec_dai->component; unsigned int aif_val = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: aif_val &= ~ISABELLE_AIF_MS; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif_val |= ISABELLE_AIF_MS; break; default: -- cgit v1.2.3 From f948202284a1c86f09f880653962c9c71d5bc3a7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:14:16 +0000 Subject: ASoC: mc13783: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the mc13783 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223001416.1235951-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/mc13783.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index a21072503cb9..08517547e66c 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -181,15 +181,14 @@ static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt, } /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: val |= AUDIO_C_CLK_EN; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: val |= AUDIO_CSM; break; - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBS_CFM: + default: return -EINVAL; } @@ -217,11 +216,11 @@ static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt) return ret; /* - * In synchronous mode force the voice codec into slave mode + * In synchronous mode force the voice codec into consumer mode * so that the clock / framesync from the stereo DAC is used */ - fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - fmt |= SND_SOC_DAIFMT_CBS_CFS; + fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + fmt |= SND_SOC_DAIFMT_CBC_CFC; ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); return ret; -- cgit v1.2.3 From ed7c9fef11931fc5d32a83d68017ff390bf5c280 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Mon, 28 Feb 2022 11:15:40 +0800 Subject: ASoC: ti: davinci-i2s: Add check for clk_enable() As the potential failure of the clk_enable(), it should be better to check it and return error if fails. Fixes: 5f9a50c3e55e ("ASoC: Davinci: McBSP: add device tree support for McBSP") Signed-off-by: Jiasheng Jiang Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220228031540.3571959-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/ti/davinci-i2s.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index 6dca51862dd7..0363a088d2e0 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -708,7 +708,9 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return -ENODEV; - clk_enable(dev->clk); + ret = clk_enable(dev->clk); + if (ret) + goto err_put_clk; dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); @@ -730,6 +732,7 @@ err_unregister_component: snd_soc_unregister_component(&pdev->dev); err_release_clk: clk_disable(dev->clk); +err_put_clk: clk_put(dev->clk); return ret; } -- cgit v1.2.3 From 7d642c9c14b6fae0c05b91a92a86fae6674a2126 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 24 Feb 2022 09:10:46 +0800 Subject: ASoC: mediatek: mt8195: Remove unnecessary print function dev_err() The print function dev_err() is redundant because platform_get_irq() already prints an error. Eliminate the follow coccicheck warning: ./sound/soc/mediatek/mt8195/mt8195-afe-pcm.c:3126:2-9: line 3126 is redundant because platform_get_irq() already prints an error Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20220224011046.76904-1-yang.lee@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index b77c2ba5a629..550636500949 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3122,10 +3122,8 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) /* request irq */ irq_id = platform_get_irq(pdev, 0); - if (irq_id < 0) { - dev_err(dev, "%s no irq found\n", dev->of_node->name); + if (irq_id < 0) return -ENXIO; - } ret = devm_request_irq(dev, irq_id, mt8195_afe_irq_handler, IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); -- cgit v1.2.3 From 9b3ff6378df33dbea4b9459ee804b25f1ce294ca Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 23 Feb 2022 14:06:25 +0100 Subject: ASoC: fsl: Drop unused argument from imx_pcm_dma_init() Since 70d435ba1cd ("ASoC: imx-pcm-dma: simplify pcm_config") the size argument to imx_pcm_dma_init() is unused, so drop it. Also remove the now unused defines that the users of imx_pcm_dma_init() used to pass the size argument Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220223130625.3430589-1-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_aud2htx.c | 2 +- sound/soc/fsl/fsl_esai.c | 2 +- sound/soc/fsl/fsl_sai.c | 2 +- sound/soc/fsl/fsl_spdif.c | 2 +- sound/soc/fsl/fsl_ssi.c | 2 +- sound/soc/fsl/imx-pcm-dma.c | 2 +- sound/soc/fsl/imx-pcm.h | 7 ++----- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index 99ab7f0241cf..422922146f2a 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -241,7 +241,7 @@ static int fsl_aud2htx_probe(struct platform_device *pdev) return ret; } - ret = imx_pcm_dma_init(pdev, IMX_DEFAULT_DMABUF_SIZE); + ret = imx_pcm_dma_init(pdev); if (ret) dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 3a9e2df4e16f..ed444e8f1d6b 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -1077,7 +1077,7 @@ static int fsl_esai_probe(struct platform_device *pdev) * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime(). */ - ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE); + ret = imx_pcm_dma_init(pdev); if (ret) { dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); goto err_pm_get_sync; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index cab015d96889..c2d423d1d87e 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1161,7 +1161,7 @@ static int fsl_sai_probe(struct platform_device *pdev) * is not defer probe for platform component in snd_soc_add_pcm_runtime(). */ if (sai->soc_data->use_imx_pcm) { - ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE); + ret = imx_pcm_dma_init(pdev); if (ret) goto err_pm_get_sync; } else { diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index d178b479c8bd..57c41b2f7d17 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1522,7 +1522,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime(). */ - ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE); + ret = imx_pcm_dma_init(pdev); if (ret) { dev_err_probe(&pdev->dev, ret, "imx_pcm_dma_init failed\n"); goto err_pm_disable; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 1169d1104b9e..ca30a4ede076 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1372,7 +1372,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, if (ret) goto error_pcm; } else { - ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); + ret = imx_pcm_dma_init(pdev); if (ret) goto error_pcm; } diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 04a9bc749016..14e94270911c 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -34,7 +34,7 @@ static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { .compat_filter_fn = filter, }; -int imx_pcm_dma_init(struct platform_device *pdev, size_t size) +int imx_pcm_dma_init(struct platform_device *pdev) { struct snd_dmaengine_pcm_config *config; diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 5dd406774d3e..5c6cf1ca8c8a 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -17,9 +17,6 @@ #define IMX_SSI_DMABUF_SIZE (64 * 1024) #define IMX_DEFAULT_DMABUF_SIZE (64 * 1024) -#define IMX_SAI_DMABUF_SIZE (64 * 1024) -#define IMX_SPDIF_DMABUF_SIZE (64 * 1024) -#define IMX_ESAI_DMABUF_SIZE (256 * 1024) static inline void imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, @@ -40,9 +37,9 @@ struct imx_pcm_fiq_params { }; #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) -int imx_pcm_dma_init(struct platform_device *pdev, size_t size); +int imx_pcm_dma_init(struct platform_device *pdev); #else -static inline int imx_pcm_dma_init(struct platform_device *pdev, size_t size) +static inline int imx_pcm_dma_init(struct platform_device *pdev) { return -ENODEV; } -- cgit v1.2.3 From 3c7d8a35179eed0fe75e142028b599268657091d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:47:31 +0000 Subject: ASoC: pcm3060: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the pcm3060 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223014731.2765283-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3060.c | 14 +++++++------- sound/soc/codecs/pcm3060.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index b2358069cf9b..4e3bfb9fa444 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -68,15 +68,15 @@ static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - priv->dai[dai->id].is_master = true; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + priv->dai[dai->id].is_provider = true; break; - case SND_SOC_DAIFMT_CBS_CFS: - priv->dai[dai->id].is_master = false; + case SND_SOC_DAIFMT_CBC_CFC: + priv->dai[dai->id].is_provider = false; break; default: - dev_err(comp->dev, "unsupported DAI master mode: 0x%x\n", fmt); + dev_err(comp->dev, "unsupported DAI mode: 0x%x\n", fmt); return -EINVAL; } @@ -116,7 +116,7 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, unsigned int reg; unsigned int val; - if (!priv->dai[dai->id].is_master) { + if (!priv->dai[dai->id].is_provider) { val = PCM3060_REG_MS_S; goto val_ready; } diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index 18d51e5dac2c..5e1185e7b03d 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -23,7 +23,7 @@ extern const struct regmap_config pcm3060_regmap; #define PCM3060_CLK2 2 struct pcm3060_priv_dai { - bool is_master; + bool is_provider; unsigned int sclk_freq; }; -- cgit v1.2.3 From 1900cb5375c3e95382601deed6ef95fb7b121a2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:48:43 +0000 Subject: ASoC: pcm1681: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the pcm1681 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223014846.2765382-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm1681.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 5b78e9299c95..9eb65f94fc4d 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -136,8 +136,8 @@ static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); - /* The PCM1681 can only be slave to all clocks */ - if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + /* The PCM1681 can only be consumer to all clocks */ + if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { dev_err(component->dev, "Invalid clocking mode\n"); return -EINVAL; } -- cgit v1.2.3 From 765e30ac665777d7d7ad4fe75490138a85931d3a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:48:44 +0000 Subject: ASoC: pcm186x: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the pcm186x driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223014846.2765382-2-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm186x.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index b8845f45549e..2c78dccb3f62 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -39,7 +39,7 @@ struct pcm186x_priv { unsigned int sysclk; unsigned int tdm_offset; bool is_tdm_mode; - bool is_master_mode; + bool is_provider_mode; }; static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0); @@ -340,8 +340,8 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream, PCM186X_PCM_CFG_TDM_LRCK_MODE); } - /* Only configure clock dividers in master mode. */ - if (priv->is_master_mode) { + /* Only configure clock dividers in provider mode. */ + if (priv->is_provider_mode) { div_bck = priv->sysclk / (div_lrck * rate); dev_dbg(component->dev, @@ -364,18 +364,17 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format) dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format); - /* set master/slave audio interface */ - switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: if (!priv->sysclk) { - dev_err(component->dev, "operating in master mode requires sysclock to be configured\n"); + dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n"); return -EINVAL; } clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE; - priv->is_master_mode = true; + priv->is_provider_mode = true; break; - case SND_SOC_DAIFMT_CBS_CFS: - priv->is_master_mode = false; + case SND_SOC_DAIFMT_CBC_CFC: + priv->is_provider_mode = false; break; default: dev_err(component->dev, "Invalid DAI master/slave interface\n"); -- cgit v1.2.3 From 9231bb1b25348bff738181e4614c997bcdeba7cf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:48:45 +0000 Subject: ASoC: pcm3168a: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the pcm3168a driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223014846.2765382-3-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 7417cf45d916..cf27f05dc46a 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -48,7 +48,7 @@ static const char *const pcm3168a_supply_names[] = { /* ADC/DAC side parameters */ struct pcm3168a_io_params { - bool master_mode; + bool provider_mode; unsigned int format; int tdm_slots; u32 tdm_mask; @@ -357,7 +357,7 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) struct snd_soc_component *component = dai->component; struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; - bool master_mode; + bool provider_mode; switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_LEFT_J: @@ -371,15 +371,15 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) return -EINVAL; } - switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - master_mode = false; + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + provider_mode = false; break; - case SND_SOC_DAIFMT_CBM_CFM: - master_mode = true; + case SND_SOC_DAIFMT_CBP_CFP: + provider_mode = true; break; default: - dev_err(component->dev, "unsupported master/slave mode\n"); + dev_err(component->dev, "unsupported provider mode\n"); return -EINVAL; } @@ -390,7 +390,7 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) return -EINVAL; } - io_params->master_mode = master_mode; + io_params->provider_mode = provider_mode; io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK; pcm3168a_update_fixup_pcm_stream(dai); @@ -440,7 +440,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; - bool master_mode, tdm_mode; + bool provider_mode, tdm_mode; unsigned int format; unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots; int i, num_scki_ratios, slot_width; @@ -459,9 +459,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, fmt_shift = PCM3168A_ADC_FMTAD_SHIFT; } - master_mode = io_params->master_mode; + provider_mode = io_params->provider_mode; - if (master_mode) { + if (provider_mode) { ratio = pcm3168a->sysclk / params_rate(params); for (i = 0; i < num_scki_ratios; i++) { @@ -488,15 +488,15 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, switch (slot_width) { case 16: - if (master_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) { - dev_err(component->dev, "16-bit slots are supported only for slave mode using right justified\n"); + if (provider_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) { + dev_err(component->dev, "16-bit slots are supported only for consumer mode using right justified\n"); return -EINVAL; } break; case 24: - if (master_mode || (format == SND_SOC_DAIFMT_DSP_A) || - (format == SND_SOC_DAIFMT_DSP_B)) { - dev_err(component->dev, "24-bit slots not supported in master mode, or slave mode using DSP\n"); + if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) || + (format == SND_SOC_DAIFMT_DSP_B)) { + dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n"); return -EINVAL; } break; -- cgit v1.2.3 From e0dab08973c890d554e14089b756e81b50ed8af4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 01:48:46 +0000 Subject: ASoC: pcm512x: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the pcm512x driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223014846.2765382-4-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm512x.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 60dee41816dc..a3ff4a07aff7 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -652,12 +652,12 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + switch (pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBP_CFC: return pcm512x_dai_startup_master(substream, dai); - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: return pcm512x_dai_startup_slave(substream, dai); default: @@ -1202,8 +1202,8 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return ret; } - if ((pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBS_CFS) { + if ((pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == + SND_SOC_DAIFMT_CBC_CFC) { ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, PCM512x_DCAS, 0); if (ret != 0) { @@ -1340,21 +1340,21 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) int afmt; int offset = 0; int clock_output; - int master_mode; + int provider_mode; int ret; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: clock_output = 0; - master_mode = 0; + provider_mode = 0; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clock_output = PCM512x_BCKO | PCM512x_LRKO; - master_mode = PCM512x_RLRK | PCM512x_RBCK; + provider_mode = PCM512x_RLRK | PCM512x_RBCK; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: clock_output = PCM512x_BCKO; - master_mode = PCM512x_RBCK; + provider_mode = PCM512x_RBCK; break; default: return -EINVAL; @@ -1370,9 +1370,9 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, PCM512x_RLRK | PCM512x_RBCK, - master_mode); + provider_mode); if (ret != 0) { - dev_err(component->dev, "Failed to enable master mode: %d\n", ret); + dev_err(component->dev, "Failed to enable provider mode: %d\n", ret); return ret; } -- cgit v1.2.3 From ca7176f695cfa7ddb8498b5114f47e7a443f97bb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:16:36 +0000 Subject: ASoC: ml26124: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the ml26124 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223001636.1321505-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/ml26124.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 4d7c0be2a4aa..0823527e4a75 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -402,12 +402,11 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned char mode; struct snd_soc_component *component = codec_dai->component; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: mode = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: mode = 0; break; default: -- cgit v1.2.3 From 6370c4436b639069d5c5d97cb896157f780cb779 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Feb 2022 00:34:09 +0000 Subject: ASoC: uda134x: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the uda134x driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220223003409.1820405-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/uda134x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index bf9182cedb82..037833c509f7 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -272,9 +272,9 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, pr_debug("%s fmt: %08X\n", __func__, fmt); - /* codec supports only full slave mode */ - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { - printk(KERN_ERR "%s unsupported slave mode\n", __func__); + /* codec supports only full consumer mode */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + printk(KERN_ERR "%s unsupported clocking mode\n", __func__); return -EINVAL; } -- cgit v1.2.3 From 14688a14dac34aac979c1e5386278f914a73164f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:17 +0000 Subject: ASoC: max98088: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98088 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98088.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index f8e49e45ce33..429717d4ac5a 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1156,20 +1156,18 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai, if (fmt != cdata->fmt) { cdata->fmt = fmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* Slave mode PLL */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI, 0x80); snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO, 0x00); break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Set to master mode */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ reg14val |= M98088_DAI_MAS; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; @@ -1227,20 +1225,18 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai, if (fmt != cdata->fmt) { cdata->fmt = fmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* Slave mode PLL */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI, 0x80); snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO, 0x00); break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Set to master mode */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ reg1Cval |= M98088_DAI_MAS; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; -- cgit v1.2.3 From 2232314b7dbcf19a897e72e00d154b19669181e5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:18 +0000 Subject: ASoC: max98095: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98095 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-2-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98095.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 736cd70be725..4977b00ddf5f 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1168,20 +1168,18 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai, if (fmt != cdata->fmt) { cdata->fmt = fmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* Slave mode PLL */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI, 0x80); snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO, 0x00); break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Set to master mode */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ regval |= M98095_DAI_MAS; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; @@ -1236,20 +1234,18 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai, if (fmt != cdata->fmt) { cdata->fmt = fmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* Slave mode PLL */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI, 0x80); snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO, 0x00); break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Set to master mode */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ regval |= M98095_DAI_MAS; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; @@ -1305,20 +1301,18 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai, if (fmt != cdata->fmt) { cdata->fmt = fmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* Slave mode PLL */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI, 0x80); snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO, 0x00); break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Set to master mode */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ regval |= M98095_DAI_MAS; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; -- cgit v1.2.3 From 012df28f5e143747c82d2b03428f607445fda893 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:19 +0000 Subject: ASoC: max98371: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98371 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-3-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98371.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index e424779db02b..8d42f523e420 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -184,8 +184,8 @@ static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai, struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component); unsigned int val = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: break; default: dev_err(component->dev, "DAI clock mode unsupported"); -- cgit v1.2.3 From c536d745adbc83abb782077a212a8cbdd7300b54 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:20 +0000 Subject: ASoC: max98390: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98390 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-4-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98390.c | 10 +++++----- sound/soc/codecs/max98390.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index d1882cbc9381..40fd6f363f35 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -174,12 +174,12 @@ static int max98390_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: mode = MAX98390_PCM_MASTER_MODE_SLAVE; break; - case SND_SOC_DAIFMT_CBM_CFM: - max98390->master = true; + case SND_SOC_DAIFMT_CBP_CFP: + max98390->provider = true; mode = MAX98390_PCM_MASTER_MODE_MASTER; break; default: @@ -265,7 +265,7 @@ static int max98390_set_clock(struct snd_soc_component *component, * snd_pcm_format_width(params_format(params)); int value; - if (max98390->master) { + if (max98390->provider) { int i; /* match rate to closest value */ for (i = 0; i < ARRAY_SIZE(rate_table); i++) { diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h index c250740f73a2..f4d6758ab4c6 100644 --- a/sound/soc/codecs/max98390.h +++ b/sound/soc/codecs/max98390.h @@ -656,7 +656,7 @@ struct max98390_priv { struct regmap *regmap; unsigned int sysclk; - unsigned int master; + unsigned int provider; unsigned int tdm_mode; unsigned int v_l_slot; unsigned int i_l_slot; -- cgit v1.2.3 From 9dcef176c4d68abc9765b420c38cca6aa85600da Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:21 +0000 Subject: ASoC: max9850: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max9850 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-5-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max9850.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index dec51893af74..e073f0e029be 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -173,12 +173,12 @@ static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct snd_soc_component *component = codec_dai->component; u8 da = 0; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* set clock provider for audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: da |= MAX9850_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; -- cgit v1.2.3 From d14c87d8ef0da4500ee8a0a3273972f4bb549412 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:22 +0000 Subject: ASoC: max9860: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max9860 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-6-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max9860.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 7c9686be59d9..82f20a8e27ad 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -268,11 +268,11 @@ static int max9860_hw_params(struct snd_pcm_substream *substream, if (params_channels(params) == 2) ifc1b |= MAX9860_ST; - switch (max9860->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (max9860->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = MAX9860_MASTER; break; default: -- cgit v1.2.3 From 2594d0aaedade65b4f746cda0f304400eb1a9870 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:23 +0000 Subject: ASoC: max9867: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max9867 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-7-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 09b2d730e9fd..c2b1151c75cc 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -19,7 +19,7 @@ struct max9867_priv { struct regmap *regmap; const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk, pclk; - bool master, dsp_a; + bool provider, dsp_a; unsigned int adc_dac_active; }; @@ -335,7 +335,7 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream, MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, MAX9867_NI_LOW_MASK, 0x00FF & ni); - if (max9867->master) { + if (max9867->provider) { if (max9867->dsp_a) { value = MAX9867_IFC1B_48X; } else { @@ -442,14 +442,14 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); u8 iface1A, iface1B; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - max9867->master = true; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + max9867->provider = true; iface1A = MAX9867_MASTER; iface1B = MAX9867_IFC1B_48X; break; - case SND_SOC_DAIFMT_CBS_CFS: - max9867->master = false; + case SND_SOC_DAIFMT_CBC_CFC: + max9867->provider = false; iface1A = iface1B = 0; break; default: -- cgit v1.2.3 From 02dd4e6ba25ca343adf228c34955ca0036a6ade3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:24 +0000 Subject: ASoC: max98925: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98925 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-8-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98925.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index ddaccc24b0cb..f34fa274ae4f 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -300,25 +300,22 @@ static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int invert = 0; dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* set DAI to slave mode */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE2, M98925_DAI_MAS_MASK, 0); max98925_set_sense_data(max98925); break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: /* - * set left channel DAI to master mode, - * right channel always slave + * set left channel DAI to provider mode, + * right channel always consumer */ regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE2, M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK); break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: dev_err(component->dev, "DAI clock mode unsupported"); return -EINVAL; -- cgit v1.2.3 From 502e1c8d07222561b046a1da7b8207696f575288 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:25 +0000 Subject: ASoC: max98926: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98926 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-9-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98926.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index f286e572263e..1fbbc62bb0a2 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -331,8 +331,8 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: max98926_set_sense_data(max98926); break; default: -- cgit v1.2.3 From 677c90bac3f198434f0256338cefc77e4663bcca Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 23:40:26 +0000 Subject: ASoC: max98927: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the max98927 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222234026.712070-10-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98927.c | 11 ++++++----- sound/soc/codecs/max98927.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index fd84780bf689..bf78d3c98514 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -148,12 +148,13 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + max98927->provider = false; mode = MAX98927_PCM_MASTER_MODE_SLAVE; break; - case SND_SOC_DAIFMT_CBM_CFM: - max98927->master = true; + case SND_SOC_DAIFMT_CBP_CFP: + max98927->provider = true; mode = MAX98927_PCM_MASTER_MODE_MASTER; break; default: @@ -270,7 +271,7 @@ static int max98927_set_clock(struct max98927_priv *max98927, int blr_clk_ratio = params_channels(params) * max98927->ch_size; int value; - if (max98927->master) { + if (max98927->provider) { int i; /* match rate to closest value */ for (i = 0; i < ARRAY_SIZE(rate_table); i++) { diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h index 13f5066d7419..2353910f5f17 100644 --- a/sound/soc/codecs/max98927.h +++ b/sound/soc/codecs/max98927.h @@ -264,7 +264,7 @@ struct max98927_priv { unsigned int ch_size; unsigned int rate; unsigned int iface; - unsigned int master; + unsigned int provider; unsigned int digital_gain; bool tdm_mode; }; -- cgit v1.2.3 From 316cd9412679fb36a0d4bcc6f4a045da467334e9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Feb 2022 22:33:00 +0000 Subject: ASoC: es7241: Use modern ASoC DAI format terminology As part of moving to remove the old style defines for the bus clocks update the es7241 driver to use more modern terminology for clocking. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220222223300.3120298-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/es7241.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c index 9f20bfb855b3..0baa86241cf9 100644 --- a/sound/soc/codecs/es7241.c +++ b/sound/soc/codecs/es7241.c @@ -29,7 +29,7 @@ struct es7241_data { struct gpio_desc *m1; unsigned int fmt; unsigned int mclk; - bool is_slave; + bool is_consumer; const struct es7241_chip *chip; }; @@ -46,9 +46,9 @@ static void es7241_set_mode(struct es7241_data *priv, int m0, int m1) gpiod_set_value_cansleep(priv->reset, 1); } -static int es7241_set_slave_mode(struct es7241_data *priv, - const struct es7241_clock_mode *mode, - unsigned int mfs) +static int es7241_set_consumer_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) { int j; @@ -67,9 +67,9 @@ out_ok: return 0; } -static int es7241_set_master_mode(struct es7241_data *priv, - const struct es7241_clock_mode *mode, - unsigned int mfs) +static int es7241_set_provider_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) { /* * We can't really set clock ratio, if the mclk/lrclk is different @@ -98,10 +98,10 @@ static int es7241_hw_params(struct snd_pcm_substream *substream, if (rate < mode->rate_min || rate >= mode->rate_max) continue; - if (priv->is_slave) - return es7241_set_slave_mode(priv, mode, mfs); + if (priv->is_consumer) + return es7241_set_consumer_mode(priv, mode, mfs); else - return es7241_set_master_mode(priv, mode, mfs); + return es7241_set_provider_mode(priv, mode, mfs); } /* should not happen */ @@ -136,12 +136,12 @@ static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - priv->is_slave = true; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + priv->is_consumer = true; break; - case SND_SOC_DAIFMT_CBM_CFM: - priv->is_slave = false; + case SND_SOC_DAIFMT_CBP_CFP: + priv->is_consumer = false; break; default: -- cgit v1.2.3 From a544684b790f3e9f75173b3b42d7dad1c89dd237 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Fri, 25 Feb 2022 19:19:29 +0800 Subject: ALSA: mips: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypassed the hierarchical setup and messed up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220225111929.17194-1-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/mips/snd-n64.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/mips/snd-n64.c b/sound/mips/snd-n64.c index 463a6fe589eb..bff6d85b8fe2 100644 --- a/sound/mips/snd-n64.c +++ b/sound/mips/snd-n64.c @@ -289,8 +289,7 @@ static int __init n64audio_probe(struct platform_device *pdev) struct snd_card *card; struct snd_pcm *pcm; struct n64audio *priv; - struct resource *res; - int err; + int err, irq; err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, @@ -337,12 +336,12 @@ static int __init n64audio_probe(struct platform_device *pdev) strcpy(card->shortname, "N64 Audio"); strcpy(card->longname, "N64 Audio"); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { err = -EINVAL; goto fail_dma_alloc; } - if (devm_request_irq(&pdev->dev, res->start, n64audio_isr, + if (devm_request_irq(&pdev->dev, irq, n64audio_isr, IRQF_SHARED, "N64 Audio", priv)) { err = -EBUSY; goto fail_dma_alloc; -- cgit v1.2.3 From ca1697eb09208f0168d94b88b72f57505339cbe5 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Mon, 28 Feb 2022 10:28:39 +0800 Subject: ALSA: spi: Add check for clk_enable() As the potential failure of the clk_enable(), it should be better to check it and return error if fails. Fixes: 3568459a5113 ("ALSA: at73c213: manage SSC clock") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220228022839.3547266-1-jiasheng@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/spi/at73c213.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 76c0e37a838c..8a2da6b1012e 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -218,7 +218,9 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) runtime->hw = snd_at73c213_playback_hw; chip->substream = substream; - clk_enable(chip->ssc->clk); + err = clk_enable(chip->ssc->clk); + if (err) + return err; return 0; } @@ -776,7 +778,9 @@ static int snd_at73c213_chip_init(struct snd_at73c213 *chip) goto out; /* Enable DAC master clock. */ - clk_enable(chip->board->dac_clk); + retval = clk_enable(chip->board->dac_clk); + if (retval) + goto out; /* Initialize at73c213 on SPI bus. */ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04); @@ -889,7 +893,9 @@ static int snd_at73c213_dev_init(struct snd_card *card, chip->card = card; chip->irq = -1; - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->ssc->clk); + if (retval) + return retval; retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); if (retval) { @@ -1008,7 +1014,9 @@ static int snd_at73c213_remove(struct spi_device *spi) int retval; /* Stop playback. */ - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->ssc->clk); + if (retval) + goto out; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); clk_disable(chip->ssc->clk); @@ -1088,9 +1096,16 @@ static int snd_at73c213_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_at73c213 *chip = card->private_data; + int retval; - clk_enable(chip->board->dac_clk); - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->board->dac_clk); + if (retval) + return retval; + retval = clk_enable(chip->ssc->clk); + if (retval) { + clk_disable(chip->board->dac_clk); + return retval; + } ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); return 0; -- cgit v1.2.3 From d248b2771f543417dea7f9af8be05190594e0621 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Mon, 28 Feb 2022 13:02:52 +0800 Subject: sound: core: remove initialise static variables to 0 Initializing the static variable to 0 causes the following error when exec checkpatch: ERROR: do not initialise statics to 0 FILE: sound/sound_core.c:142: static int preclaim_oss = 0; In addition, considering the following way of writing 139: #ifdef config_sound_oss_core_preclaim 140: Static int preclaim_oss = 1; 141: #ELSE 142: Static int preclaim_oss = 0; 143: #ENDIF We can optimize it by IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM), so modified it to static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM); Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220228050253.1649-1-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/sound_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/sound_core.c b/sound/sound_core.c index 90d118cd9164..aa4a57e488e5 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -136,11 +136,7 @@ struct sound_unit * All these clutters are scheduled to be removed along with * sound-slot/service-* module aliases. */ -#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM -static int preclaim_oss = 1; -#else -static int preclaim_oss = 0; -#endif +static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM); module_param(preclaim_oss, int, 0444); -- cgit v1.2.3 From e52b78f890675132989aaa298e1e1077b6bf9325 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Mon, 28 Feb 2022 13:02:53 +0800 Subject: sound: core: Remove redundant variable and return the last statement Return the result from file->f_op->open() directly instead of taking this in another redundant variable. Make the typical return the last statement, return early and reduce the indentation too. Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220228050253.1649-2-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/sound_core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/sound_core.c b/sound/sound_core.c index aa4a57e488e5..3332fe321737 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -577,20 +577,20 @@ static int soundcore_open(struct inode *inode, struct file *file) new_fops = fops_get(s->unit_fops); } spin_unlock(&sound_loader_lock); - if (new_fops) { - /* - * We rely upon the fact that we can't be unloaded while the - * subdriver is there. - */ - int err = 0; - replace_fops(file, new_fops); - if (file->f_op->open) - err = file->f_op->open(inode,file); + if (!new_fops) + return -ENODEV; - return err; - } - return -ENODEV; + /* + * We rely upon the fact that we can't be unloaded while the + * subdriver is there. + */ + replace_fops(file, new_fops); + + if (!file->f_op->open) + return -ENODEV; + + return file->f_op->open(inode, file); } MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); -- cgit v1.2.3 From 8dd55245836119ee3636543b6c2597efd78e643d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 28 Feb 2022 14:42:35 +0000 Subject: ASoC: codecs: wsa881x: add runtime pm support WSA SoundWire Controller does not support Clock stop and performs a soft reset on suspend resume path. Its recommended that WSA881x codecs connected to this are also reset using a hard reset during suspend resume. So this codec driver performs a hard reset during suspend resume cycle. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220228144235.24208-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 0222370ff95d..616b26c70c3b 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -198,6 +199,7 @@ #define WSA881X_OCP_CTL_TIMER_SEC 2 #define WSA881X_OCP_CTL_TEMP_CELSIUS 25 #define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 +#define WSA881X_PROBE_TIMEOUT 1000 #define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -747,6 +749,12 @@ static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, unsigned int mask = (1 << fls(max)) - 1; int val, ret, min_gain, max_gain; + ret = pm_runtime_get_sync(comp->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(comp->dev); + return ret; + } + max_gain = (max - ucontrol->value.integer.value[0]) & mask; /* * Gain has to set incrementally in 4 steps @@ -773,6 +781,9 @@ static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, usleep_range(1000, 1010); } + pm_runtime_mark_last_busy(comp->dev); + pm_runtime_put_autosuspend(comp->dev); + return 1; } @@ -1101,6 +1112,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { struct wsa881x_priv *wsa881x; + struct device *dev = &pdev->dev; wsa881x = devm_kzalloc(&pdev->dev, sizeof(*wsa881x), GFP_KERNEL); if (!wsa881x) @@ -1132,12 +1144,52 @@ static int wsa881x_probe(struct sdw_slave *pdev, return PTR_ERR(wsa881x->regmap); } + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return devm_snd_soc_register_component(&pdev->dev, &wsa881x_component_drv, wsa881x_dais, ARRAY_SIZE(wsa881x_dais)); } +static int __maybe_unused wsa881x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); + + gpiod_direction_output(wsa881x->sd_n, 0); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa881x_runtime_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); + + gpiod_direction_output(wsa881x->sd_n, 1); + + wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(WSA881X_PROBE_TIMEOUT)); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa881x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa881x_runtime_suspend, wsa881x_runtime_resume, NULL) +}; + static const struct sdw_device_id wsa881x_slave_id[] = { SDW_SLAVE_ENTRY(0x0217, 0x2010, 0), SDW_SLAVE_ENTRY(0x0217, 0x2110, 0), @@ -1151,6 +1203,7 @@ static struct sdw_driver wsa881x_codec_driver = { .id_table = wsa881x_slave_id, .driver = { .name = "wsa881x-codec", + .pm = &wsa881x_pm_ops, } }; module_sdw_driver(wsa881x_codec_driver); -- cgit v1.2.3 From 76f22f4dcae645ea468811f9d30ec04f9ffaa1ea Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sun, 27 Feb 2022 22:52:04 +0800 Subject: Documentation: sound: fix typo in control-names.rst change 'cannel' to 'channel' Signed-off-by: Sunrisepeak Link: https://lore.kernel.org/r/20220227145204.16600-1-speakshen@163.com Signed-off-by: Takashi Iwai --- Documentation/sound/designs/control-names.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/sound/designs/control-names.rst b/Documentation/sound/designs/control-names.rst index 7fedd0f33cd9..765ff9b5b7d9 100644 --- a/Documentation/sound/designs/control-names.rst +++ b/Documentation/sound/designs/control-names.rst @@ -34,7 +34,7 @@ CHANNEL Front front left/right channels Surround rear left/right in 4.0/5.1 surround CLFE C/LFE channels -Center center cannel +Center center channel LFE LFE channel Side side left/right for 7.1 surround ============ ================================================== -- cgit v1.2.3 From a6264056b39ee0c478e1d73bfc40f61a8cf3673f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 1 Mar 2022 13:48:56 -0600 Subject: ASoC: soc-acpi: remove sof_fw_filename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've been using a default firmware name for each PCI/ACPI/OF platform for a while. The machine-specific sof_fw_filename is in practice not different from the default, and newer devices don't set this field, so let's remove the redundant definitions. When OEMs modify the base firmware, they can keep the same firmware name but store the file in a separate directory. Reviewed-by: Bard Liao Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 2 -- sound/soc/intel/common/soc-acpi-intel-adl-match.c | 13 ------------- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 6 ------ sound/soc/intel/common/soc-acpi-intel-byt-match.c | 11 ----------- sound/soc/intel/common/soc-acpi-intel-cht-match.c | 12 ------------ sound/soc/intel/common/soc-acpi-intel-cml-match.c | 11 ----------- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 4 ---- sound/soc/intel/common/soc-acpi-intel-ehl-match.c | 1 - sound/soc/intel/common/soc-acpi-intel-glk-match.c | 6 ------ sound/soc/intel/common/soc-acpi-intel-hda-match.c | 2 -- sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c | 5 ----- sound/soc/intel/common/soc-acpi-intel-icl-match.c | 5 ----- sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 7 ------- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 7 ------- sound/soc/sof/intel/hda.c | 5 +---- sound/soc/sof/intel/pci-tng.c | 1 - 16 files changed, 1 insertion(+), 97 deletions(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index ac0893df9c76..fdb536d699ff 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -142,7 +142,6 @@ struct snd_soc_acpi_link_adr { * audio codecs whose presence if checked with ACPI * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time - * @sof_fw_filename: Sound Open Firmware file name, if enabled * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled */ /* Descriptor for SST ASoC machine driver */ @@ -158,7 +157,6 @@ struct snd_soc_acpi_mach { const void *quirk_data; void *pdata; struct snd_soc_acpi_mach_params mach_params; - const char *sof_fw_filename; const char *sof_tplg_filename; }; 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 20257f547275..86444e331d80 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -390,7 +390,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_mx98373_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg", }, { @@ -398,7 +397,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_mx98357_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98357a_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98357a-rt5682.tplg", }, { @@ -406,7 +404,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_mx98360_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98360a_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", }, { @@ -414,7 +411,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_rt1019p_nau8825", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt1019p_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg", }, { @@ -422,7 +418,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_max98373_nau8825", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg", }, { @@ -430,13 +425,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_mx98360a_nau8825", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98360a_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg", }, { .id = "10508825", .drv_name = "sof_nau8825", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-nau8825.tplg", }, { @@ -444,13 +437,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_max98390_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98390_amp, - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", }, { .comp_ids = &adl_rt5682_rt5682s_hp, .drv_name = "adl_rt5682", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, {}, @@ -481,21 +472,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .link_mask = 0xF, /* 4 active links required */ .links = adl_sdw_rt711_link2_rt1316_link01_rt714_link3, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01-rt714-l3.tplg", }, { .link_mask = 0xC, /* rt1316 on link2 & rt714 on link3 */ .links = adl_sdw_rt1316_link2_rt714_link3, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg", }, { .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */ .links = adl_sdw_rt1316_link12_rt714_link0, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-rt1316-l12-rt714-l0.tplg", }, { @@ -514,7 +502,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .link_mask = 0x5, /* rt5682 on link0 & 2xmax98373 on link 2 */ .links = adl_chromebook_base, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-sdw-max98373-rt5682.tplg", }, {}, diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 342d34052204..718947068956 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -51,7 +51,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-rt298.tplg", }, { @@ -60,32 +59,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-da7219.tplg", }, { .id = "104C5122", .drv_name = "sof_pcm512x", - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-pcm512x.tplg", }, { .id = "1AEC8804", .drv_name = "sof-wm8804", - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-wm8804.tplg", }, { .id = "INT34C3", .drv_name = "bxt_tdf8532", .machine_quirk = apl_quirk, - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-tdf8532.tplg", }, { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-es8336.tplg", }, {}, diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 142000991813..daa51885dc7f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -91,7 +91,6 @@ static struct snd_soc_acpi_mach byt_rt5672 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5670.tplg", }; @@ -100,7 +99,6 @@ static struct snd_soc_acpi_mach byt_pov_p1006w = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5651.tplg", }; @@ -147,7 +145,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5640.tplg", }, { @@ -155,7 +152,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5651.tplg", }, { @@ -163,7 +159,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_wm5102", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_wm5102", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-wm5102.tplg", }, { @@ -171,7 +166,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-da7213.tplg", }, { @@ -179,13 +173,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_es8316", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-es8316.tplg", }, { .id = "10EC5682", .drv_name = "sof_rt5682", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5682.tplg", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -194,7 +186,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5645.tplg", }, /* use CHT driver to Baytrail Chromebooks */ @@ -203,7 +194,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-max98090.tplg", }, { @@ -211,7 +201,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_cx2072x", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_cx2072x", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-cx2072x.tplg", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index c60a5e8e7bc9..6beb00858c33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -35,7 +35,6 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5645.tplg", }; @@ -79,7 +78,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5670.tplg", }, { @@ -87,7 +85,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5645.tplg", }, { @@ -95,7 +92,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-max98090.tplg", }, { @@ -103,7 +99,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-nau8824", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-nau8824.tplg", }, { @@ -111,7 +106,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-da7213.tplg", }, { @@ -119,7 +113,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-es8316.tplg", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -129,13 +122,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5640.tplg", }, { .id = "10EC5682", .drv_name = "sof_rt5682", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5682.tplg", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -144,7 +135,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5651.tplg", }, { @@ -152,13 +142,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_cx2072x", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_cx2072x", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-cx2072x.tplg", }, { .id = "104C5122", .drv_name = "sof_pcm512x", - .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-src-50khz-pcm512x.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index 4eebc79d4b48..d033474f8768 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -40,7 +40,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .drv_name = "cml_rt1011_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1011_spk_codecs, - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg", }, { @@ -48,7 +47,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .drv_name = "cml_rt1015_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015_spk_codecs, - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg", }, { @@ -56,13 +54,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .drv_name = "sof_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &max98357a_spk_codecs, - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg", }, { .id = "10EC5682", .drv_name = "sof_rt5682", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt5682.tplg", }, { @@ -70,7 +66,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .drv_name = "cml_da7219_mx98357a", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &max98357a_spk_codecs, - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg", }, { @@ -78,13 +73,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .drv_name = "cml_da7219_mx98357a", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &max98390_spk_codecs, - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-da7219-max98390.tplg", }, { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-es8336.tplg", }, {}, @@ -283,14 +276,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { .link_mask = 0xF, /* 4 active links required */ .links = cml_3_in_1_default, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", }, { .link_mask = 0xF, /* 4 active links required */ .links = cml_3_in_1_sdca, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1316-rt714.tplg", }, { @@ -302,14 +293,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { .link_mask = 0xF, .links = cml_3_in_1_mono_amp, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", }, { .link_mask = 0x2, /* RT700 connected on Link1 */ .links = cml_rvp, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt700.tplg", }, {} diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 94b650767e11..8d3e8c3b589b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -21,7 +21,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", .pdata = &cnl_pdata, - .sof_fw_filename = "sof-cnl.ri", .sof_tplg_filename = "sof-cnl-rt274.tplg", }, {}, @@ -58,21 +57,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = { .link_mask = BIT(2), .links = up_extreme_rt5682_2, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cnl.ri", .sof_tplg_filename = "sof-cnl-rt5682-sdw2.tplg" }, { .link_mask = GENMASK(3, 0), .links = sdw_mockup_headset_2amps_mic, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cnl.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = sdw_mockup_headset_1amp_mic, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-cnl.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", }, {} diff --git a/sound/soc/intel/common/soc-acpi-intel-ehl-match.c b/sound/soc/intel/common/soc-acpi-intel-ehl-match.c index 6222708a98e7..84639c41a268 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ehl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ehl-match.c @@ -14,7 +14,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[] = { { .id = "10EC5660", .drv_name = "ehl_rt5660", - .sof_fw_filename = "sof-ehl.ri", .sof_tplg_filename = "sof-ehl-rt5660.tplg", }, {}, diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 8492b7e2a945..c5ca077c7ac9 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -19,7 +19,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-alc298.tplg", }, { @@ -28,7 +27,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-da7219.tplg", }, { @@ -37,7 +35,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-rt5682.tplg", }, { @@ -45,7 +42,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .drv_name = "glk_rt5682_max98357a", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-rt5682.tplg", }, { @@ -54,13 +50,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-cs42l42.tplg", }, { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-es8336.tplg", }, {}, diff --git a/sound/soc/intel/common/soc-acpi-intel-hda-match.c b/sound/soc/intel/common/soc-acpi-intel-hda-match.c index aa9cb522aac9..2017fd0d676f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hda-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hda-match.c @@ -21,8 +21,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[] = { /* .fw_filename is dynamically set in skylake driver */ - /* .sof_fw_filename is dynamically set in sof/intel driver */ - .sof_tplg_filename = "sof-hda-generic.tplg", /* diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index fe343a95b5ff..0441df97b260 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -14,7 +14,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "sof-hsw.ri", .sof_tplg_filename = "sof-hsw.tplg", }, {} @@ -26,28 +25,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "sof-bdw.ri", .sof_tplg_filename = "sof-bdw-rt286.tplg", }, { .id = "10EC5650", .drv_name = "bdw-rt5650", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "sof-bdw.ri", .sof_tplg_filename = "sof-bdw-rt5650.tplg", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "sof-bdw.ri", .sof_tplg_filename = "sof-bdw-rt5677.tplg", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "sof-bdw.ri", .sof_tplg_filename = "sof-bdw-rt5640.tplg", }, {} diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 768ed538c4ea..b032bc07de8b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -20,13 +20,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { .drv_name = "icl_rt274", .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &icl_pdata, - .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt274.tplg", }, { .id = "10EC5682", .drv_name = "sof_rt5682", - .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt5682.tplg", }, {}, @@ -165,21 +163,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = { .link_mask = 0xF, /* 4 active links required */ .links = icl_3_in_1_default, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg", }, { .link_mask = 0xB, /* 3 active links required */ .links = icl_3_in_1_mono_amp, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715-mono.tplg", }, { .link_mask = 0x1, /* rt700 connected on link0 */ .links = icl_rvp, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-icl.ri", .sof_tplg_filename = "sof-icl-rt700.tplg", }, {}, diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 278ec196da7b..a2da5cad520c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -43,7 +43,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "DLGS7219", .drv_name = "sof_da7219_mx98373", - .sof_fw_filename = "sof-jsl.ri", .sof_tplg_filename = "sof-jsl-da7219.tplg", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &jsl_7219_98373_codecs, @@ -51,13 +50,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "DLGS7219", .drv_name = "sof_da7219_mx98360a", - .sof_fw_filename = "sof-jsl.ri", .sof_tplg_filename = "sof-jsl-da7219-mx98360a.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, .drv_name = "jsl_rt5682_rt1015", - .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", @@ -65,7 +62,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .comp_ids = &rt5682_rt5682s_hp, .drv_name = "jsl_rt5682_rt1015p", - .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015p_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", @@ -73,7 +69,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .comp_ids = &rt5682_rt5682s_hp, .drv_name = "jsl_rt5682_mx98360", - .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg", @@ -81,7 +76,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "10134242", .drv_name = "jsl_cs4242_mx98360a", - .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-cs42l42-mx98360a.tplg", @@ -89,7 +83,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_fw_filename = "sof-jsl.ri", .sof_tplg_filename = "sof-jsl-es8336.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 da31bb3cca17..daff01466c05 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -369,7 +369,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .drv_name = "tgl_mx98357_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_codecs, - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, { @@ -377,7 +376,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .drv_name = "tgl_mx98373_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_max98373_amp, - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg", }, { @@ -385,13 +383,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .drv_name = "tgl_rt1011_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_rt1011_amp, - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg", }, { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-es8336.tplg", }, {}, @@ -405,21 +401,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { .link_mask = GENMASK(3, 0), .links = sdw_mockup_headset_2amps_mic, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg", }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = sdw_mockup_headset_1amp_mic, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt711-rt1308-mono-rt715.tplg", }, { .link_mask = BIT(0) | BIT(1) | BIT(2), .links = sdw_mockup_mic_headset_1amp, .drv_name = "sof_sdw", - .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg", }, { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 659fe9d1a542..e42b45722e9d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1313,10 +1313,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); - if (mach->sof_fw_filename) - pdata->fw_filename = mach->sof_fw_filename; - else - pdata->fw_filename = pdata->desc->default_fw_filename; + pdata->fw_filename = pdata->desc->default_fw_filename; pdata->tplg_filename = mach->sof_tplg_filename; /* diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index f8c841caa362..7d5062f8076e 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -25,7 +25,6 @@ static struct snd_soc_acpi_mach sof_tng_machines[] = { { .id = "INT343A", .drv_name = "edison", - .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt.tplg", }, {} -- cgit v1.2.3 From f1eebb3bf707b267bd8ed945d00a81c8ca31bd73 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 1 Mar 2022 13:48:57 -0600 Subject: ASoC: Intel: boards: fix spelling in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit copy/paste spelling issues with platforms and buttons. Reviewed-by: Ranjani Sridharan Reviewed-by: FRED OH Reviewed-by: Péter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5650.c | 2 +- sound/soc/intel/boards/bdw-rt5677.c | 2 +- sound/soc/intel/boards/broadwell.c | 2 +- sound/soc/intel/boards/bxt_da7219_max98357a.c | 2 +- sound/soc/intel/boards/bxt_rt298.c | 2 +- sound/soc/intel/boards/bytcht_cx2072x.c | 2 +- sound/soc/intel/boards/bytcht_da7213.c | 2 +- sound/soc/intel/boards/bytcht_es8316.c | 2 +- sound/soc/intel/boards/bytcr_rt5640.c | 2 +- sound/soc/intel/boards/bytcr_rt5651.c | 2 +- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 4 ++-- sound/soc/intel/boards/cht_bsw_nau8824.c | 4 ++-- sound/soc/intel/boards/cht_bsw_rt5645.c | 2 +- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 +- sound/soc/intel/boards/glk_rt5682_max98357a.c | 2 +- sound/soc/intel/boards/haswell.c | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index 6cba5552f7a2..bc0eab1c304a 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -299,7 +299,7 @@ static int bdw_rt5650_probe(struct platform_device *pdev) if (!bdw_rt5650) return -ENOMEM; - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card, mach->mach_params.platform); diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 119c441f4c10..071557fada29 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -426,7 +426,7 @@ static int bdw_rt5677_probe(struct platform_device *pdev) if (!bdw_rt5677) return -ENOMEM; - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5677_card, mach->mach_params.platform); diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 618d0645ed8d..d37c74fd1a3c 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -292,7 +292,7 @@ static int broadwell_audio_probe(struct platform_device *pdev) broadwell_rt286.dev = &pdev->dev; - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286, mach->mach_params.platform); diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index b768d9b8ec02..9bc7b88e346b 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -825,7 +825,7 @@ static int broxton_audio_probe(struct platform_device *pdev) } } - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 920e575b4314..05e833076499 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -628,7 +628,7 @@ static int broxton_audio_probe(struct platform_device *pdev) card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, ctx); - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index ffd497a5b5a5..96d3201efbbd 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -257,7 +257,7 @@ static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name; } - /* override plaform name, if required */ + /* override platform name, if required */ ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, mach->mach_params.platform); if (ret) diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index fae1e7e785b0..eb19bf16afad 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -260,7 +260,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev) dailink[dai_index].codecs->name = codec_name; } - /* override plaform name, if required */ + /* override platform name, if required */ platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name); diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 9d86fea51a7d..f4bf26e802a2 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -497,7 +497,7 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) return -ENXIO; } - /* override plaform name, if required */ + /* override platform name, if required */ byt_cht_es8316_card.dev = dev; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 2ace32c03ec9..d76a505052fb 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -1764,7 +1764,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_card.long_name = byt_rt5640_long_name; #endif - /* override plaform name, if required */ + /* override platform name, if required */ platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5640_card, diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 5e9c53dadbc7..39348d2b242f 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -1088,7 +1088,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) byt_rt5651_card.long_name = byt_rt5651_long_name; #endif - /* override plaform name, if required */ + /* override platform name, if required */ platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5651_card, diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 1bc21434c9de..b3d7a0725ef2 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -296,7 +296,7 @@ static int cht_max98090_headset_init(struct snd_soc_component *component) int ret; /* - * TI supports 4 butons headset detection + * TI supports 4 buttons headset detection * KEY_MEDIA * KEY_VOICECOMMAND * KEY_VOLUMEUP @@ -558,7 +558,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) dev_dbg(dev, "Unable to add GPIO mapping table\n"); } - /* override plaform name, if required */ + /* override platform name, if required */ snd_soc_card_cht.dev = &pdev->dev; mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index bad32d2bdf89..da6c659de266 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -100,7 +100,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_component *component = codec_dai->component; int ret, jack_type; - /* NAU88L24 supports 4 butons headset detection + /* NAU88L24 supports 4 buttons headset detection * KEY_PLAYPAUSE * KEY_VOICECOMMAND * KEY_VOLUMEUP @@ -257,7 +257,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) return -ENOMEM; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); - /* override plaform name, if required */ + /* override platform name, if required */ snd_soc_card_cht.dev = &pdev->dev; mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index e182012d0c60..c21561c6a464 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -653,7 +653,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) cht_dailink[dai_index].cpus->dai_name = "ssp0-port"; - /* override plaform name, if required */ + /* override platform name, if required */ platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(card, diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 26eb8ad0d262..9882aeb24d33 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -483,7 +483,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) drv->use_ssp0 = true; } - /* override plaform name, if required */ + /* override platform name, if required */ snd_soc_card_cht.dev = &pdev->dev; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index bad3829e52ca..e4bfb0fe5f12 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -638,7 +638,7 @@ static int geminilake_audio_probe(struct platform_device *pdev) card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, ctx); - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 36e136acbef5..aa61e101f793 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -175,7 +175,7 @@ static int haswell_audio_probe(struct platform_device *pdev) haswell_rt5640.dev = &pdev->dev; - /* override plaform name, if required */ + /* override platform name, if required */ mach = pdev->dev.platform_data; ret = snd_soc_fixup_dai_links_platform_name(&haswell_rt5640, mach->mach_params.platform); -- cgit v1.2.3 From da793fb0f56c0a53d0d461d80d9c1936a39afc30 Mon Sep 17 00:00:00 2001 From: "balamurugan.c" Date: Tue, 1 Mar 2022 13:48:58 -0600 Subject: ASoC: Intel: add RT1308 I2S machine driver and HDMI-in capture via I2S support. Adding separate I2S machine driver for RT1308. Adding support for HDMI-In capture via I2S in slave mode configuration. Reviewed-by: Ranjani Sridharan Signed-off-by: balamurugan.c Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 12 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/sof_rt1308.c | 305 ++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 12 + 4 files changed, 331 insertions(+) create mode 100644 sound/soc/intel/boards/sof_rt1308.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 34ccefcc30c7..ad0664ca4915 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -599,6 +599,18 @@ config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH endif ## SND_SOC_SOF_JASPERLAKE +config SND_SOC_INTEL_SOF_RT1308_MACH + tristate "SOF with RT1308 in I2S Mode" + depends on I2C && ACPI && GPIOLIB + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT1308 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Tigerlake platforms + with RT1308 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + if SND_SOC_SOF_ELKHARTLAKE config SND_SOC_INTEL_EHL_RT5660_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 3ea273d27168..9f044290b420 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -35,6 +35,7 @@ snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o snd-soc-ehl-rt5660-objs := ehl_rt5660.o +snd-soc-sof-rt1308-objs := sof_rt1308.o snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_max98373.o \ sof_sdw_rt1308.o sof_sdw_rt1316.o \ @@ -79,6 +80,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o obj-$(CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH) += snd-soc-ehl-rt5660.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_RT1308_MACH) += snd-soc-sof-rt1308.o # common modules snd-soc-intel-hda-dsp-common-objs := hda_dsp_common.o diff --git a/sound/soc/intel/boards/sof_rt1308.c b/sound/soc/intel/boards/sof_rt1308.c new file mode 100644 index 000000000000..7e33c49b3531 --- /dev/null +++ b/sound/soc/intel/boards/sof_rt1308.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. + +/* + * sof_rt1308.c - ASoc Machine driver for Intel platforms + * with RT1308 codec. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt1308.h" + +#define SOF_RT1308_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) +#define SOF_RT1308_SSP_CODEC_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) + +/* Default: SSP2 */ +static unsigned long sof_rt1308_quirk = SOF_RT1308_SSP_CODEC(2); + +static const struct snd_soc_dapm_widget sof_rt1308_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_kcontrol_new sof_rt1308_controls[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static const struct snd_soc_dapm_route sof_rt1308_dapm_routes[] = { + { "Speakers", NULL, "SPOL" }, + { "Speakers", NULL, "SPOR" }, + + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +static struct snd_soc_card sof_rt1308_card = { + .name = "rt1308", + .owner = THIS_MODULE, + .controls = sof_rt1308_controls, + .num_controls = ARRAY_SIZE(sof_rt1308_controls), + .dapm_widgets = sof_rt1308_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sof_rt1308_dapm_widgets), + .dapm_routes = sof_rt1308_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sof_rt1308_dapm_routes), + .fully_routed = true, +}; + +static int sof_rt1308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int clk_id, clk_freq, pll_out; + int ret; + + clk_id = RT1308_PLL_S_MCLK; + /* get the tplg configured mclk. */ + clk_freq = sof_dai_get_mclk(rtd); + + pll_out = params_rate(params) * 512; + + /* Set rt1308 pll */ + ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + if (ret < 0) { + dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", ret); + return ret; + } + + /* Set rt1308 sysclk */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", ret); + + return ret; +} + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +static struct snd_soc_dai_link_component rt1308_component[] = { + { + .name = "i2c-10EC1308:00", + .dai_name = "rt1308-aif", + } +}; + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component dummy_component[] = { + { + .name = "snd-soc-dummy", + .dai_name = "snd-soc-dummy-dai", + } +}; + +/* machine stream operations */ +static const struct snd_soc_ops sof_rt1308_ops = { + .hw_params = sof_rt1308_hw_params, +}; + +static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, + int ssp_codec, + int dmic_be_num) +{ + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link *links; + int i, id = 0; + + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + sof_rt1308_card.num_links, GFP_KERNEL); + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * + sof_rt1308_card.num_links, GFP_KERNEL); + if (!links || !cpus) + return NULL; + + /* HDMI-In SSP */ + if (sof_rt1308_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) { + int num_of_hdmi_ssp = (sof_rt1308_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + + for (i = 1; i <= num_of_hdmi_ssp; i++) { + int port = (i == 1 ? (sof_rt1308_quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >> + SOF_HDMI_CAPTURE_1_SSP_SHIFT : + (sof_rt1308_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) + return NULL; + links[id].id = id; + links[id].codecs = dummy_component; + links[id].num_codecs = ARRAY_SIZE(dummy_component); + 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 SSP */ + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); + if (!links[id].name) + return NULL; + + links[id].id = id; + links[id].codecs = rt1308_component; + links[id].num_codecs = ARRAY_SIZE(rt1308_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].ops = &sof_rt1308_ops; + links[id].dpcm_playback = 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++; + + /* dmic */ + if (dmic_be_num > 0) { + /* at least we have dmic01 */ + links[id].name = "dmic01"; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = "DMIC01 Pin"; + if (dmic_be_num > 1) { + /* set up 2 BE links at most */ + links[id + 1].name = "dmic16k"; + links[id + 1].cpus = &cpus[id + 1]; + links[id + 1].cpus->dai_name = "DMIC16k Pin"; + dmic_be_num = 2; + } + } + + for (i = 0; i < dmic_be_num; i++) { + links[id].id = id; + links[id].num_cpus = 1; + links[id].codecs = dmic_component; + links[id].num_codecs = ARRAY_SIZE(dmic_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].ignore_suspend = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + id++; + } + + return links; +} + +static int sof_rt1308_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_links; + struct snd_soc_acpi_mach *mach; + int dmic_be_num; + int ret, ssp_codec; + + if (pdev->id_entry && pdev->id_entry->driver_data) + sof_rt1308_quirk = (unsigned long)pdev->id_entry->driver_data; + + mach = pdev->dev.platform_data; + + dmic_be_num = mach->mach_params.dmic_num; + + ssp_codec = sof_rt1308_quirk & SOF_RT1308_SSP_CODEC_MASK; + + /* set number of dai links */ + sof_rt1308_card.num_links = 1 + dmic_be_num; + + if (sof_rt1308_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) + sof_rt1308_card.num_links += (sof_rt1308_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, dmic_be_num); + if (!dai_links) + return -ENOMEM; + + sof_rt1308_card.dai_link = dai_links; + + sof_rt1308_card.dev = &pdev->dev; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&sof_rt1308_card, + mach->mach_params.platform); + if (ret) + return ret; + + snd_soc_card_set_drvdata(&sof_rt1308_card, NULL); + + return devm_snd_soc_register_card(&pdev->dev, &sof_rt1308_card); +} + +static const struct platform_device_id board_ids[] = { + { + .name = "sof_rt1308", + }, + { + .name = "tgl_rt1308_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_RT1308_SSP_CODEC(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), + }, + { } +}; +MODULE_DEVICE_TABLE(platform, board_ids); + +static struct platform_driver sof_rt1308_driver = { + .probe = sof_rt1308_probe, + .driver = { + .name = "sof_rt1308", + .pm = &snd_soc_pm_ops, + }, + .id_table = board_ids, +}; +module_platform_driver(sof_rt1308_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) SOF + RT1308 Machine driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sof_rt1308"); 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 daff01466c05..224b54d35c7a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -363,6 +363,11 @@ static const struct snd_soc_acpi_codecs tgl_rt5682_rt5682s_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs tgl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .comp_ids = &tgl_rt5682_rt5682s_hp, @@ -390,6 +395,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-tgl-es8336.tplg", }, + { + .id = "10EC1308", + .drv_name = "tgl_rt1308_hdmi_ssp", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &tgl_lt6911_hdmi, + .sof_tplg_filename = "sof-tgl-rt1308-ssp2-hdmi-ssp15.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); -- cgit v1.2.3 From e1d5e13324020c4b405e63cae34560c7992bec2e Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 1 Mar 2022 13:48:59 -0600 Subject: ASoC: Intel: boards: create sof-realtek-common module Move sof_realtek_common.o to a dedicated module like the module to support maxim amplifiers. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 4 ++++ sound/soc/intel/boards/Makefile | 7 +++++-- sound/soc/intel/boards/sof_realtek_common.c | 9 +++++++++ sound/soc/intel/boards/sof_rt5682.c | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ad0664ca4915..cdf94b09c372 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -32,6 +32,9 @@ config SND_SOC_INTEL_HDA_DSP_COMMON config SND_SOC_INTEL_SOF_MAXIM_COMMON tristate +config SND_SOC_INTEL_SOF_REALTEK_COMMON + tristate + if SND_SOC_INTEL_CATPT config SND_SOC_INTEL_HASWELL_MACH @@ -477,6 +480,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH select SND_SOC_HDAC_HDMI select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_MAXIM_COMMON + select SND_SOC_INTEL_SOF_REALTEK_COMMON help This adds support for ASoC machine driver for SOF platforms with rt5682 codec. diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 9f044290b420..b2966020e7ed 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -19,10 +19,10 @@ snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o -snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o +snd-soc-sof_rt5682-objs := sof_rt5682.o snd-soc-sof_cs42l42-objs := sof_cs42l42.o snd-soc-sof_es8336-objs := sof_es8336.o -snd-soc-sof_nau8825-objs := sof_nau8825.o sof_realtek_common.o +snd-soc-sof_nau8825-objs := sof_nau8825.o snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o @@ -88,3 +88,6 @@ obj-$(CONFIG_SND_SOC_INTEL_HDA_DSP_COMMON) += snd-soc-intel-hda-dsp-common.o snd-soc-intel-sof-maxim-common-objs += sof_maxim_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_MAXIM_COMMON) += snd-soc-intel-sof-maxim-common.o + +snd-soc-intel-sof-realtek-common-objs += sof_realtek_common.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_REALTEK_COMMON) += snd-soc-intel-sof-realtek-common.o diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index 4cf131310ad3..669e44c73c17 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -132,12 +132,14 @@ void sof_rt1011_dai_link(struct snd_soc_dai_link *link) link->init = rt1011_init; link->ops = &rt1011_ops; } +EXPORT_SYMBOL_NS(sof_rt1011_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); void sof_rt1011_codec_conf(struct snd_soc_card *card) { card->codec_conf = rt1011_codec_confs; card->num_configs = ARRAY_SIZE(rt1011_codec_confs); } +EXPORT_SYMBOL_NS(sof_rt1011_codec_conf, SND_SOC_INTEL_SOF_REALTEK_COMMON); /* * rt1015: i2c mode driver for ALC1015 and ALC1015Q @@ -233,6 +235,7 @@ void sof_rt1015p_dai_link(struct snd_soc_dai_link *link) link->init = rt1015p_init; link->ops = &rt1015p_ops; } +EXPORT_SYMBOL_NS(sof_rt1015p_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); void sof_rt1015p_codec_conf(struct snd_soc_card *card) { @@ -242,6 +245,7 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card) card->codec_conf = rt1015p_codec_confs; card->num_configs = ARRAY_SIZE(rt1015p_codec_confs); } +EXPORT_SYMBOL_NS(sof_rt1015p_codec_conf, SND_SOC_INTEL_SOF_REALTEK_COMMON); /* * RT1015 audio amplifier @@ -343,6 +347,7 @@ void sof_rt1015_codec_conf(struct snd_soc_card *card) card->codec_conf = rt1015_amp_conf; card->num_configs = ARRAY_SIZE(rt1015_amp_conf); } +EXPORT_SYMBOL_NS(sof_rt1015_codec_conf, SND_SOC_INTEL_SOF_REALTEK_COMMON); void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) { @@ -354,3 +359,7 @@ void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) if (fs == 100) rt1015_ops.hw_params = rt1015_hw_params_pll_and_tdm; } +EXPORT_SYMBOL_NS(sof_rt1015_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); + +MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 2fcd22272900..ebec4d15edaa 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -1081,3 +1081,4 @@ MODULE_AUTHOR("Mac Chiang "); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); -- cgit v1.2.3 From 024979b67b392569dde3f9294f9b66651d2c0a93 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 1 Mar 2022 13:49:00 -0600 Subject: ASoC: Intel: sof_rt1308: move rt1308 code to common module Move the code related to rt1308 dai link to the realtek common module. It creates a clean base to add more amplifier support to this machine driver in the future. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 1 + sound/soc/intel/boards/sof_realtek_common.c | 98 +++++++++++++++++++++++++++++ sound/soc/intel/boards/sof_realtek_common.h | 4 ++ sound/soc/intel/boards/sof_rt1308.c | 70 ++++----------------- 4 files changed, 114 insertions(+), 59 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cdf94b09c372..d96ebc335249 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -609,6 +609,7 @@ config SND_SOC_INTEL_SOF_RT1308_MACH depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT1308 select SND_SOC_DMIC + select SND_SOC_INTEL_SOF_REALTEK_COMMON help This adds support for ASoC machine driver for Tigerlake platforms with RT1308 I2S audio codec. diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index 669e44c73c17..a2bcbeee0216 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -10,9 +10,11 @@ #include #include #include +#include #include #include "../../codecs/rt1011.h" #include "../../codecs/rt1015.h" +#include "../../codecs/rt1308.h" #include "sof_realtek_common.h" /* @@ -361,5 +363,101 @@ void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) } EXPORT_SYMBOL_NS(sof_rt1015_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); +/* + * RT1308 audio amplifier + */ +static const struct snd_kcontrol_new rt1308_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speakers", NULL), +}; + +static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { + /* speaker */ + {"Speakers", NULL, "SPOL"}, + {"Speakers", NULL, "SPOR"}, +}; + +static struct snd_soc_dai_link_component rt1308_components[] = { + { + .name = RT1308_DEV0_NAME, + .dai_name = RT1308_CODEC_DAI, + } +}; + +static int rt1308_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_dapm_widgets, + ARRAY_SIZE(rt1308_dapm_widgets)); + if (ret) { + dev_err(rtd->dev, "fail to add dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt1308_kcontrols, + ARRAY_SIZE(rt1308_kcontrols)); + if (ret) { + dev_err(rtd->dev, "fail to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_dapm_routes, + ARRAY_SIZE(rt1308_dapm_routes)); + + if (ret) + dev_err(rtd->dev, "fail to add dapm routes, ret %d\n", ret); + + return ret; +} + +static int rt1308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int clk_id, clk_freq, pll_out; + int ret; + + clk_id = RT1308_PLL_S_MCLK; + /* get the tplg configured mclk. */ + clk_freq = sof_dai_get_mclk(rtd); + + pll_out = params_rate(params) * 512; + + /* Set rt1308 pll */ + ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + if (ret < 0) { + dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", ret); + return ret; + } + + /* Set rt1308 sysclk */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops rt1308_ops = { + .hw_params = rt1308_hw_params, +}; + +void sof_rt1308_dai_link(struct snd_soc_dai_link *link) +{ + link->codecs = rt1308_components; + link->num_codecs = ARRAY_SIZE(rt1308_components); + link->init = rt1308_init; + link->ops = &rt1308_ops; +} +EXPORT_SYMBOL_NS(sof_rt1308_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); + MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h index 228ac9c08430..e0a5518e8dd2 100644 --- a/sound/soc/intel/boards/sof_realtek_common.h +++ b/sound/soc/intel/boards/sof_realtek_common.h @@ -35,4 +35,8 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card); void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs); void sof_rt1015_codec_conf(struct snd_soc_card *card); +#define RT1308_CODEC_DAI "rt1308-aif" +#define RT1308_DEV0_NAME "i2c-10EC1308:00" +void sof_rt1308_dai_link(struct snd_soc_dai_link *link); + #endif /* __SOF_REALTEK_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_rt1308.c b/sound/soc/intel/boards/sof_rt1308.c index 7e33c49b3531..971ab53236a5 100644 --- a/sound/soc/intel/boards/sof_rt1308.c +++ b/sound/soc/intel/boards/sof_rt1308.c @@ -16,7 +16,7 @@ #include #include #include -#include "../../codecs/rt1308.h" +#include "sof_realtek_common.h" #define SOF_RT1308_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) #define SOF_RT1308_SSP_CODEC_MASK (GENMASK(3, 0)) @@ -38,22 +38,16 @@ #define SOF_HDMI_CAPTURE_2_SSP(quirk) \ (((quirk) << SOF_HDMI_CAPTURE_2_SSP_SHIFT) & SOF_HDMI_CAPTURE_2_SSP_MASK) +#define SOF_RT1308_SPEAKER_AMP_PRESENT BIT(13) + /* Default: SSP2 */ static unsigned long sof_rt1308_quirk = SOF_RT1308_SSP_CODEC(2); static const struct snd_soc_dapm_widget sof_rt1308_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; -static const struct snd_kcontrol_new sof_rt1308_controls[] = { - SOC_DAPM_PIN_SWITCH("Speakers"), -}; - static const struct snd_soc_dapm_route sof_rt1308_dapm_routes[] = { - { "Speakers", NULL, "SPOL" }, - { "Speakers", NULL, "SPOR" }, - /* digital mics */ {"DMic", NULL, "SoC DMIC"}, }; @@ -61,8 +55,6 @@ static const struct snd_soc_dapm_route sof_rt1308_dapm_routes[] = { static struct snd_soc_card sof_rt1308_card = { .name = "rt1308", .owner = THIS_MODULE, - .controls = sof_rt1308_controls, - .num_controls = ARRAY_SIZE(sof_rt1308_controls), .dapm_widgets = sof_rt1308_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sof_rt1308_dapm_widgets), .dapm_routes = sof_rt1308_dapm_routes, @@ -70,37 +62,6 @@ static struct snd_soc_card sof_rt1308_card = { .fully_routed = true, }; -static int sof_rt1308_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int clk_id, clk_freq, pll_out; - int ret; - - clk_id = RT1308_PLL_S_MCLK; - /* get the tplg configured mclk. */ - clk_freq = sof_dai_get_mclk(rtd); - - pll_out = params_rate(params) * 512; - - /* Set rt1308 pll */ - ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); - if (ret < 0) { - dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", ret); - return ret; - } - - /* Set rt1308 sysclk */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out, - SND_SOC_CLOCK_IN); - if (ret < 0) - dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", ret); - - return ret; -} - static struct snd_soc_dai_link_component platform_component[] = { { /* name might be overridden during probe */ @@ -108,13 +69,6 @@ static struct snd_soc_dai_link_component platform_component[] = { } }; -static struct snd_soc_dai_link_component rt1308_component[] = { - { - .name = "i2c-10EC1308:00", - .dai_name = "rt1308-aif", - } -}; - static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -129,11 +83,6 @@ static struct snd_soc_dai_link_component dummy_component[] = { } }; -/* machine stream operations */ -static const struct snd_soc_ops sof_rt1308_ops = { - .hw_params = sof_rt1308_hw_params, -}; - static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, int ssp_codec, int dmic_be_num) @@ -186,11 +135,11 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, return NULL; links[id].id = id; - links[id].codecs = rt1308_component; - links[id].num_codecs = ARRAY_SIZE(rt1308_component); + if (sof_rt1308_quirk & SOF_RT1308_SPEAKER_AMP_PRESENT) { + sof_rt1308_dai_link(&links[id]); + } links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].ops = &sof_rt1308_ops; links[id].dpcm_playback = 1; links[id].no_pcm = 1; links[id].cpus = &cpus[id]; @@ -284,7 +233,8 @@ static const struct platform_device_id board_ids[] = { 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_SSP_HDMI_CAPTURE_PRESENT | + SOF_RT1308_SPEAKER_AMP_PRESENT), }, { } }; @@ -301,5 +251,7 @@ static struct platform_driver sof_rt1308_driver = { module_platform_driver(sof_rt1308_driver); MODULE_DESCRIPTION("ASoC Intel(R) SOF + RT1308 Machine driver"); +MODULE_AUTHOR("balamurugan.c "); +MODULE_AUTHOR("Brent Lu "); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:sof_rt1308"); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); -- cgit v1.2.3 From 709ec7bec6b34ee136fff4b1b5265baaae7319a3 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 1 Mar 2022 13:49:01 -0600 Subject: ASoC: Intel: cirrus-common: support cs35l41 amplifier Implement cs35l41 support code in this common module so it could be shared between multiple SOF machine drivers. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 3 + sound/soc/intel/boards/Makefile | 3 + sound/soc/intel/boards/sof_cirrus_common.c | 163 +++++++++++++++++++++++++++++ sound/soc/intel/boards/sof_cirrus_common.h | 25 +++++ 4 files changed, 194 insertions(+) create mode 100644 sound/soc/intel/boards/sof_cirrus_common.c create mode 100644 sound/soc/intel/boards/sof_cirrus_common.h diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d96ebc335249..f29f9b731ed9 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -35,6 +35,9 @@ config SND_SOC_INTEL_SOF_MAXIM_COMMON config SND_SOC_INTEL_SOF_REALTEK_COMMON tristate +config SND_SOC_INTEL_SOF_CIRRUS_COMMON + tristate + if SND_SOC_INTEL_CATPT config SND_SOC_INTEL_HASWELL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index b2966020e7ed..d0ef71b7af6e 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -91,3 +91,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_MAXIM_COMMON) += snd-soc-intel-sof-maxim-common.o snd-soc-intel-sof-realtek-common-objs += sof_realtek_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_REALTEK_COMMON) += snd-soc-intel-sof-realtek-common.o + +snd-soc-intel-sof-cirrus-common-objs += sof_cirrus_common.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_CIRRUS_COMMON) += snd-soc-intel-sof-cirrus-common.o diff --git a/sound/soc/intel/boards/sof_cirrus_common.c b/sound/soc/intel/boards/sof_cirrus_common.c new file mode 100644 index 000000000000..e71d74ec1b0b --- /dev/null +++ b/sound/soc/intel/boards/sof_cirrus_common.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file defines data structures and functions used in Machine + * Driver for Intel platforms with Cirrus Logic Codecs. + * + * Copyright 2022 Intel Corporation. + */ +#include +#include +#include "../../codecs/cs35l41.h" +#include "sof_cirrus_common.h" + +/* + * Cirrus Logic CS35L41/CS35L53 + */ +static const struct snd_kcontrol_new cs35l41_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("WL Spk"), + SOC_DAPM_PIN_SWITCH("WR Spk"), + SOC_DAPM_PIN_SWITCH("TL Spk"), + SOC_DAPM_PIN_SWITCH("TR Spk"), +}; + +static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { + SND_SOC_DAPM_SPK("WL Spk", NULL), + SND_SOC_DAPM_SPK("WR Spk", NULL), + SND_SOC_DAPM_SPK("TL Spk", NULL), + SND_SOC_DAPM_SPK("TR Spk", NULL), +}; + +static const struct snd_soc_dapm_route cs35l41_dapm_routes[] = { + /* speaker */ + {"WL Spk", NULL, "WL SPK"}, + {"WR Spk", NULL, "WR SPK"}, + {"TL Spk", NULL, "TL SPK"}, + {"TR Spk", NULL, "TR SPK"}, +}; + +static struct snd_soc_dai_link_component cs35l41_components[] = { + { + .name = CS35L41_DEV0_NAME, + .dai_name = CS35L41_CODEC_DAI, + }, + { + .name = CS35L41_DEV1_NAME, + .dai_name = CS35L41_CODEC_DAI, + }, + { + .name = CS35L41_DEV2_NAME, + .dai_name = CS35L41_CODEC_DAI, + }, + { + .name = CS35L41_DEV3_NAME, + .dai_name = CS35L41_CODEC_DAI, + }, +}; + +static struct snd_soc_codec_conf cs35l41_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(CS35L41_DEV0_NAME), + .name_prefix = "WL", + }, + { + .dlc = COMP_CODEC_CONF(CS35L41_DEV1_NAME), + .name_prefix = "WR", + }, + { + .dlc = COMP_CODEC_CONF(CS35L41_DEV2_NAME), + .name_prefix = "TL", + }, + { + .dlc = COMP_CODEC_CONF(CS35L41_DEV3_NAME), + .name_prefix = "TR", + }, +}; + +static int cs35l41_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, cs35l41_dapm_widgets, + ARRAY_SIZE(cs35l41_dapm_widgets)); + if (ret) { + dev_err(rtd->dev, "fail to add dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, cs35l41_kcontrols, + ARRAY_SIZE(cs35l41_kcontrols)); + if (ret) { + dev_err(rtd->dev, "fail to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, cs35l41_dapm_routes, + ARRAY_SIZE(cs35l41_dapm_routes)); + + if (ret) + dev_err(rtd->dev, "fail to add dapm routes, ret %d\n", ret); + + return ret; +} + +static int cs35l41_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int clk_freq, i, ret; + + clk_freq = sof_dai_get_bclk(rtd); /* BCLK freq */ + + if (clk_freq <= 0) { + dev_err(rtd->dev, "fail to get bclk freq, ret %d\n", clk_freq); + return -EINVAL; + } + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + /* call dai driver's set_sysclk() callback */ + ret = snd_soc_dai_set_sysclk(codec_dai, CS35L41_CLKID_SCLK, + clk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set sysclk, ret %d\n", + ret); + return ret; + } + + /* call component driver's set_sysclk() callback */ + ret = snd_soc_component_set_sysclk(codec_dai->component, + CS35L41_CLKID_SCLK, 0, + clk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set component sysclk, ret %d\n", + ret); + return ret; + } + } + + return 0; +} + +static const struct snd_soc_ops cs35l41_ops = { + .hw_params = cs35l41_hw_params, +}; + +void cs35l41_set_dai_link(struct snd_soc_dai_link *link) +{ + link->codecs = cs35l41_components; + link->num_codecs = ARRAY_SIZE(cs35l41_components); + link->init = cs35l41_init; + link->ops = &cs35l41_ops; +} +EXPORT_SYMBOL_NS(cs35l41_set_dai_link, SND_SOC_INTEL_SOF_CIRRUS_COMMON); + +void cs35l41_set_codec_conf(struct snd_soc_card *card) +{ + card->codec_conf = cs35l41_codec_conf; + card->num_configs = ARRAY_SIZE(cs35l41_codec_conf); +} +EXPORT_SYMBOL_NS(cs35l41_set_codec_conf, SND_SOC_INTEL_SOF_CIRRUS_COMMON); + +MODULE_DESCRIPTION("ASoC Intel SOF Cirrus Logic helpers"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_cirrus_common.h b/sound/soc/intel/boards/sof_cirrus_common.h new file mode 100644 index 000000000000..ca438c12c386 --- /dev/null +++ b/sound/soc/intel/boards/sof_cirrus_common.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with Cirrus Logic Codecs. + * + * Copyright 2022 Intel Corporation. + */ +#ifndef __SOF_CIRRUS_COMMON_H +#define __SOF_CIRRUS_COMMON_H + +#include + +/* + * Cirrus Logic CS35L41/CS35L53 + */ +#define CS35L41_CODEC_DAI "cs35l41-pcm" +#define CS35L41_DEV0_NAME "i2c-CSC3541:00" +#define CS35L41_DEV1_NAME "i2c-CSC3541:01" +#define CS35L41_DEV2_NAME "i2c-CSC3541:02" +#define CS35L41_DEV3_NAME "i2c-CSC3541:03" + +void cs35l41_set_dai_link(struct snd_soc_dai_link *link); +void cs35l41_set_codec_conf(struct snd_soc_card *card); + +#endif /* __SOF_CIRRUS_COMMON_H */ -- cgit v1.2.3 From 2fe14ff61bd6d4fabe313435dd378b5a38eb6102 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 1 Mar 2022 13:49:02 -0600 Subject: ASoC: Intel: sof_ssp_amp: rename driver and support cs35l41 amplifier Add support of CS35L41 amplifier to the machine driver, as well as the support of HDMI playback and BT offload DAI Link. Rename the driver to a generic name to support different amplifiers from different vendors. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 12 +- sound/soc/intel/boards/Makefile | 4 +- sound/soc/intel/boards/sof_rt1308.c | 257 ------------ sound/soc/intel/boards/sof_ssp_amp.c | 483 ++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-adl-match.c | 6 + 5 files changed, 499 insertions(+), 263 deletions(-) delete mode 100644 sound/soc/intel/boards/sof_rt1308.c create mode 100644 sound/soc/intel/boards/sof_ssp_amp.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index f29f9b731ed9..da59504d2322 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -606,16 +606,20 @@ config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH endif ## SND_SOC_SOF_JASPERLAKE -config SND_SOC_INTEL_SOF_RT1308_MACH - tristate "SOF with RT1308 in I2S Mode" +config SND_SOC_INTEL_SOF_SSP_AMP_MACH + tristate "SOF with amplifiers in I2S Mode" depends on I2C && ACPI && GPIOLIB depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT1308 + select SND_SOC_CS35L41_I2C select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_REALTEK_COMMON + select SND_SOC_INTEL_SOF_CIRRUS_COMMON help - This adds support for ASoC machine driver for Tigerlake platforms - with RT1308 I2S audio codec. + This adds support for ASoC machine driver for SOF platforms + with RT1308/CS35L41 I2S audio codec. Say Y if you have such a device. If unsure select "N". diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index d0ef71b7af6e..40c0c3d1c500 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -35,7 +35,7 @@ snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o snd-soc-ehl-rt5660-objs := ehl_rt5660.o -snd-soc-sof-rt1308-objs := sof_rt1308.o +snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_max98373.o \ sof_sdw_rt1308.o sof_sdw_rt1316.o \ @@ -80,7 +80,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o obj-$(CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH) += snd-soc-ehl-rt5660.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o -obj-$(CONFIG_SND_SOC_INTEL_SOF_RT1308_MACH) += snd-soc-sof-rt1308.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_SSP_AMP_MACH) += snd-soc-sof-ssp-amp.o # common modules snd-soc-intel-hda-dsp-common-objs := hda_dsp_common.o diff --git a/sound/soc/intel/boards/sof_rt1308.c b/sound/soc/intel/boards/sof_rt1308.c deleted file mode 100644 index 971ab53236a5..000000000000 --- a/sound/soc/intel/boards/sof_rt1308.c +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// Copyright(c) 2022 Intel Corporation. All rights reserved. - -/* - * sof_rt1308.c - ASoc Machine driver for Intel platforms - * with RT1308 codec. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sof_realtek_common.h" - -#define SOF_RT1308_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) -#define SOF_RT1308_SSP_CODEC_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_RT1308_SPEAKER_AMP_PRESENT BIT(13) - -/* Default: SSP2 */ -static unsigned long sof_rt1308_quirk = SOF_RT1308_SSP_CODEC(2); - -static const struct snd_soc_dapm_widget sof_rt1308_dapm_widgets[] = { - SND_SOC_DAPM_MIC("SoC DMIC", NULL), -}; - -static const struct snd_soc_dapm_route sof_rt1308_dapm_routes[] = { - /* digital mics */ - {"DMic", NULL, "SoC DMIC"}, -}; - -static struct snd_soc_card sof_rt1308_card = { - .name = "rt1308", - .owner = THIS_MODULE, - .dapm_widgets = sof_rt1308_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sof_rt1308_dapm_widgets), - .dapm_routes = sof_rt1308_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sof_rt1308_dapm_routes), - .fully_routed = true, -}; - -static struct snd_soc_dai_link_component platform_component[] = { - { - /* name might be overridden during probe */ - .name = "0000:00:1f.3" - } -}; - -static struct snd_soc_dai_link_component dmic_component[] = { - { - .name = "dmic-codec", - .dai_name = "dmic-hifi", - } -}; - -static struct snd_soc_dai_link_component dummy_component[] = { - { - .name = "snd-soc-dummy", - .dai_name = "snd-soc-dummy-dai", - } -}; - -static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, - int ssp_codec, - int dmic_be_num) -{ - struct snd_soc_dai_link_component *cpus; - struct snd_soc_dai_link *links; - int i, id = 0; - - links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * - sof_rt1308_card.num_links, GFP_KERNEL); - cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * - sof_rt1308_card.num_links, GFP_KERNEL); - if (!links || !cpus) - return NULL; - - /* HDMI-In SSP */ - if (sof_rt1308_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) { - int num_of_hdmi_ssp = (sof_rt1308_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; - - for (i = 1; i <= num_of_hdmi_ssp; i++) { - int port = (i == 1 ? (sof_rt1308_quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >> - SOF_HDMI_CAPTURE_1_SSP_SHIFT : - (sof_rt1308_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) - return NULL; - links[id].id = id; - links[id].codecs = dummy_component; - links[id].num_codecs = ARRAY_SIZE(dummy_component); - 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 SSP */ - links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); - if (!links[id].name) - return NULL; - - links[id].id = id; - if (sof_rt1308_quirk & SOF_RT1308_SPEAKER_AMP_PRESENT) { - sof_rt1308_dai_link(&links[id]); - } - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].dpcm_playback = 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++; - - /* dmic */ - if (dmic_be_num > 0) { - /* at least we have dmic01 */ - links[id].name = "dmic01"; - links[id].cpus = &cpus[id]; - links[id].cpus->dai_name = "DMIC01 Pin"; - if (dmic_be_num > 1) { - /* set up 2 BE links at most */ - links[id + 1].name = "dmic16k"; - links[id + 1].cpus = &cpus[id + 1]; - links[id + 1].cpus->dai_name = "DMIC16k Pin"; - dmic_be_num = 2; - } - } - - for (i = 0; i < dmic_be_num; i++) { - links[id].id = id; - links[id].num_cpus = 1; - links[id].codecs = dmic_component; - links[id].num_codecs = ARRAY_SIZE(dmic_component); - links[id].platforms = platform_component; - links[id].num_platforms = ARRAY_SIZE(platform_component); - links[id].ignore_suspend = 1; - links[id].dpcm_capture = 1; - links[id].no_pcm = 1; - id++; - } - - return links; -} - -static int sof_rt1308_probe(struct platform_device *pdev) -{ - struct snd_soc_dai_link *dai_links; - struct snd_soc_acpi_mach *mach; - int dmic_be_num; - int ret, ssp_codec; - - if (pdev->id_entry && pdev->id_entry->driver_data) - sof_rt1308_quirk = (unsigned long)pdev->id_entry->driver_data; - - mach = pdev->dev.platform_data; - - dmic_be_num = mach->mach_params.dmic_num; - - ssp_codec = sof_rt1308_quirk & SOF_RT1308_SSP_CODEC_MASK; - - /* set number of dai links */ - sof_rt1308_card.num_links = 1 + dmic_be_num; - - if (sof_rt1308_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) - sof_rt1308_card.num_links += (sof_rt1308_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >> - SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; - - dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, dmic_be_num); - if (!dai_links) - return -ENOMEM; - - sof_rt1308_card.dai_link = dai_links; - - sof_rt1308_card.dev = &pdev->dev; - - /* set platform name for each dailink */ - ret = snd_soc_fixup_dai_links_platform_name(&sof_rt1308_card, - mach->mach_params.platform); - if (ret) - return ret; - - snd_soc_card_set_drvdata(&sof_rt1308_card, NULL); - - return devm_snd_soc_register_card(&pdev->dev, &sof_rt1308_card); -} - -static const struct platform_device_id board_ids[] = { - { - .name = "sof_rt1308", - }, - { - .name = "tgl_rt1308_hdmi_ssp", - .driver_data = (kernel_ulong_t)(SOF_RT1308_SSP_CODEC(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_RT1308_SPEAKER_AMP_PRESENT), - }, - { } -}; -MODULE_DEVICE_TABLE(platform, board_ids); - -static struct platform_driver sof_rt1308_driver = { - .probe = sof_rt1308_probe, - .driver = { - .name = "sof_rt1308", - .pm = &snd_soc_pm_ops, - }, - .id_table = board_ids, -}; -module_platform_driver(sof_rt1308_driver); - -MODULE_DESCRIPTION("ASoC Intel(R) SOF + RT1308 Machine driver"); -MODULE_AUTHOR("balamurugan.c "); -MODULE_AUTHOR("Brent Lu "); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c new file mode 100644 index 000000000000..88530e9de543 --- /dev/null +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. + +/* + * sof_ssp_amp.c - ASoc Machine driver for Intel platforms + * with RT1308/CS35L41 codec. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" +#include "sof_realtek_common.h" +#include "sof_cirrus_common.h" + +#define NAME_SIZE 32 + +/* SSP port ID for speaker amplifier */ +#define SOF_AMPLIFIER_SSP(quirk) ((quirk) & GENMASK(3, 0)) +#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) + +/* HDMI playback */ +#define SOF_HDMI_PLAYBACK_PRESENT BIT(13) +#define SOF_NO_OF_HDMI_PLAYBACK_SHIFT 14 +#define SOF_NO_OF_HDMI_PLAYBACK_MASK (GENMASK(16, 14)) +#define SOF_NO_OF_HDMI_PLAYBACK(quirk) \ + (((quirk) << SOF_NO_OF_HDMI_PLAYBACK_SHIFT) & SOF_NO_OF_HDMI_PLAYBACK_MASK) + +/* BT audio offload */ +#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(17) +#define SOF_BT_OFFLOAD_SSP_SHIFT 18 +#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(20, 18)) +#define SOF_BT_OFFLOAD_SSP(quirk) \ + (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) + +/* Speaker amplifiers */ +#define SOF_RT1308_SPEAKER_AMP_PRESENT BIT(21) +#define SOF_CS35L41_SPEAKER_AMP_PRESENT BIT(22) + +/* Default: SSP2 */ +static unsigned long sof_ssp_amp_quirk = SOF_AMPLIFIER_SSP(2); + +struct sof_hdmi_pcm { + struct list_head head; + struct snd_soc_jack sof_hdmi; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct sof_card_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + bool idisp_codec; +}; + +static const struct snd_soc_dapm_widget sof_ssp_amp_dapm_widgets[] = { + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route sof_ssp_amp_dapm_routes[] = { + /* digital mics */ + {"DMic", NULL, "SoC DMIC"}, +}; + +static int sof_card_late_probe(struct snd_soc_card *card) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[NAME_SIZE]; + struct sof_hdmi_pcm *pcm; + int err; + int i = 0; + + if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT)) + return 0; + + /* HDMI is not supported by SOF on Baytrail/CherryTrail */ + if (!ctx->idisp_codec) + return 0; + + if (list_empty(&ctx->hdmi_pcm_list)) + return -EINVAL; + + if (ctx->common_hdmi_codec_drv) { + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, + head); + component = pcm->codec_dai->component; + return hda_dsp_hdmi_build_controls(card, component); + } + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &pcm->sof_hdmi, + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &pcm->sof_hdmi); + if (err < 0) + return err; + + i++; + } + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +static struct snd_soc_card sof_ssp_amp_card = { + .name = "ssp_amp", + .owner = THIS_MODULE, + .dapm_widgets = sof_ssp_amp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sof_ssp_amp_dapm_widgets), + .dapm_routes = sof_ssp_amp_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sof_ssp_amp_dapm_routes), + .fully_routed = true, + .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" + } +}; + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component dummy_component[] = { + { + .name = "snd-soc-dummy", + .dai_name = "snd-soc-dummy-dai", + } +}; + +static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0); + struct sof_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define IDISP_CODEC_MASK 0x4 + +static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, + int ssp_codec, + int dmic_be_num, + int hdmi_num, + bool idisp_codec) +{ + struct snd_soc_dai_link_component *idisp_components; + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link *links; + int i, id = 0; + + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + sof_ssp_amp_card.num_links, GFP_KERNEL); + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) * + sof_ssp_amp_card.num_links, GFP_KERNEL); + if (!links || !cpus) + 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; + + 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) + return NULL; + links[id].id = id; + links[id].codecs = dummy_component; + links[id].num_codecs = ARRAY_SIZE(dummy_component); + 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 SSP */ + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); + if (!links[id].name) + return NULL; + + links[id].id = id; + if (sof_ssp_amp_quirk & SOF_RT1308_SPEAKER_AMP_PRESENT) { + sof_rt1308_dai_link(&links[id]); + } else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) { + cs35l41_set_dai_link(&links[id]); + } + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_playback = 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++; + + /* dmic */ + if (dmic_be_num > 0) { + /* at least we have dmic01 */ + links[id].name = "dmic01"; + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = "DMIC01 Pin"; + if (dmic_be_num > 1) { + /* set up 2 BE links at most */ + links[id + 1].name = "dmic16k"; + links[id + 1].cpus = &cpus[id + 1]; + links[id + 1].cpus->dai_name = "DMIC16k Pin"; + dmic_be_num = 2; + } + } + + for (i = 0; i < dmic_be_num; i++) { + links[id].id = id; + links[id].num_cpus = 1; + links[id].codecs = dmic_component; + links[id].num_codecs = ARRAY_SIZE(dmic_component); + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].ignore_suspend = 1; + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + id++; + } + + /* HDMI playback */ + if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) { + /* HDMI */ + if (hdmi_num > 0) { + idisp_components = devm_kzalloc(dev, + sizeof(struct snd_soc_dai_link_component) * + hdmi_num, GFP_KERNEL); + if (!idisp_components) + goto devm_err; + } + for (i = 1; i <= hdmi_num; i++) { + links[id].name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d", i); + if (!links[id].name) + goto devm_err; + + links[id].id = id; + links[id].cpus = &cpus[id]; + links[id].num_cpus = 1; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "iDisp%d Pin", i); + if (!links[id].cpus->dai_name) + goto devm_err; + + if (idisp_codec) { + idisp_components[i - 1].name = "ehdaudio0D2"; + idisp_components[i - 1].dai_name = devm_kasprintf(dev, + GFP_KERNEL, + "intel-hdmi-hifi%d", + i); + if (!idisp_components[i - 1].dai_name) + goto devm_err; + } else { + idisp_components[i - 1].name = "snd-soc-dummy"; + idisp_components[i - 1].dai_name = "snd-soc-dummy-dai"; + } + + links[id].codecs = &idisp_components[i - 1]; + links[id].num_codecs = 1; + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].init = sof_hdmi_init; + links[id].dpcm_playback = 1; + links[id].no_pcm = 1; + id++; + } + } + + /* BT audio offload */ + if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { + 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 = dummy_component; + links[id].num_codecs = ARRAY_SIZE(dummy_component); + 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; + id++; + } + + return links; +devm_err: + return NULL; +} + +static int sof_ssp_amp_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_links; + struct snd_soc_acpi_mach *mach; + struct sof_card_private *ctx; + int dmic_be_num, hdmi_num = 0; + int ret, ssp_codec; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (pdev->id_entry && pdev->id_entry->driver_data) + sof_ssp_amp_quirk = (unsigned long)pdev->id_entry->driver_data; + + mach = pdev->dev.platform_data; + + dmic_be_num = mach->mach_params.dmic_num; + + ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK; + + /* set number of dai links */ + sof_ssp_amp_card.num_links = 1 + dmic_be_num; + + 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 (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) { + hdmi_num = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_PLAYBACK_MASK) >> + SOF_NO_OF_HDMI_PLAYBACK_SHIFT; + /* default number of HDMI DAI's */ + if (!hdmi_num) + hdmi_num = 3; + + if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) + ctx->idisp_codec = true; + + sof_ssp_amp_card.num_links += hdmi_num; + } + + if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) + sof_ssp_amp_card.num_links++; + + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, dmic_be_num, hdmi_num, ctx->idisp_codec); + if (!dai_links) + return -ENOMEM; + + sof_ssp_amp_card.dai_link = dai_links; + + /* update codec_conf */ + if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) { + cs35l41_set_codec_conf(&sof_ssp_amp_card); + } + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + sof_ssp_amp_card.dev = &pdev->dev; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&sof_ssp_amp_card, + mach->mach_params.platform); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(&sof_ssp_amp_card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &sof_ssp_amp_card); +} + +static const struct platform_device_id board_ids[] = { + { + .name = "sof_ssp_amp", + }, + { + .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_RT1308_SPEAKER_AMP_PRESENT), + }, + { + .name = "adl_cs35l41", + .driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(1) | + SOF_NO_OF_HDMI_PLAYBACK(4) | + SOF_HDMI_PLAYBACK_PRESENT | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT | + SOF_CS35L41_SPEAKER_AMP_PRESENT), + }, + { } +}; +MODULE_DEVICE_TABLE(platform, board_ids); + +static struct platform_driver sof_ssp_amp_driver = { + .probe = sof_ssp_amp_probe, + .driver = { + .name = "sof_ssp_amp", + .pm = &snd_soc_pm_ops, + }, + .id_table = board_ids, +}; +module_platform_driver(sof_ssp_amp_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) SOF Amplifier Machine driver"); +MODULE_AUTHOR("balamurugan.c "); +MODULE_AUTHOR("Brent Lu "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_REALTEK_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_CIRRUS_COMMON); 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 86444e331d80..f120d7e2aecb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -444,6 +444,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .drv_name = "adl_rt5682", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, + /* place amp-only boards in the end of table */ + { + .id = "CSC3541", + .drv_name = "adl_cs35l41", + .sof_tplg_filename = "sof-adl-cs35l41.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); -- cgit v1.2.3 From c4dcd7100c26881b1095d5b2651d61190fc5f247 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Tue, 1 Mar 2022 13:49:03 -0600 Subject: ASoC: Intel: soc-acpi: add entries in ADL match table Support configuration with SoundWire RT1316 amplifiers on link0 and link1, and RT711 on link2 for headphone/headset. This product does not support local microphones. Reviewed-by: Bard Liao Signed-off-by: Libin Yang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220301194903.60859-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-adl-match.c | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) 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 f120d7e2aecb..7c89a974b59f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -260,6 +260,25 @@ static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01_rt71 {} }; +static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt711_sdca_2_adr), + .adr_d = rt711_sdca_2_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1316_0_group2_adr), + .adr_d = rt1316_0_group2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group2_adr), + .adr_d = rt1316_1_group2_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] = { { .mask = BIT(1), @@ -480,6 +499,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01-rt714-l3.tplg", }, + { + .link_mask = 0x7, /* rt1316 on link0 and link1 & rt711 on link2*/ + .links = adl_sdw_rt711_link2_rt1316_link01, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01.tplg", + }, { .link_mask = 0xC, /* rt1316 on link2 & rt714 on link3 */ .links = adl_sdw_rt1316_link2_rt714_link3, -- cgit v1.2.3 From 2ecf362d220317debf5da376e0390e9f7a3f7b29 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 1 Mar 2022 16:17:17 +0800 Subject: ASoC: mxs-saif: Handle errors for clk_enable As the potential failure of the clk_enable(), it should be better to check it, like mxs_saif_trigger(). Fixes: d0ba4c014934 ("ASoC: mxs-saif: set a base clock rate for EXTMASTER mode work") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220301081717.3727190-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 6a2d24d48964..879c1221a809 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -455,7 +455,10 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, * basic clock which should be fast enough for the internal * logic. */ - clk_enable(saif->clk); + ret = clk_enable(saif->clk); + if (ret) + return ret; + ret = clk_set_rate(saif->clk, 24000000); clk_disable(saif->clk); if (ret) -- cgit v1.2.3 From f9e2ca0640e59d19af0ff285ee5591ed39069b09 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 1 Mar 2022 17:06:37 +0800 Subject: ASoC: atmel_ssc_dai: Handle errors for clk_enable As the potential failure of the clk_enable(), it should be better to check it and return error if fals. Fixes: cbaadf0f90d6 ("ASoC: atmel_ssc_dai: refactor the startup and shutdown") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220301090637.3776558-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/atmel/atmel_ssc_dai.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 26e2bc690d86..c1dea8d62416 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -280,7 +280,10 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug("atmel_ssc_dai: Starting clock\n"); - clk_enable(ssc_p->ssc->clk); + ret = clk_enable(ssc_p->ssc->clk); + if (ret) + return ret; + ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); /* Reset the SSC unless initialized to keep it in a clean state */ -- cgit v1.2.3 From a2253ec7aef2c942630ecbe3380690bd3a704a94 Mon Sep 17 00:00:00 2001 From: Zhen Ni Date: Wed, 2 Mar 2022 16:15:02 +0800 Subject: ASoC: amd: use asoc_substream_to_rtd() Uses asoc_substream_to_rtd() helper. Signed-off-by: Zhen Ni Link: https://lore.kernel.org/r/20220302081502.25367-1-nizhen@uniontech.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-mach.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 25b5166b23f8..28238af538d2 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -101,7 +101,7 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = { static int acp5x_8821_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card); @@ -119,7 +119,7 @@ static int acp5x_8821_startup(struct snd_pcm_substream *substream) static int acp5x_nau8821_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_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, @@ -141,7 +141,7 @@ static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream, static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(card); @@ -158,7 +158,7 @@ static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream) static int acp5x_cs35l41_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_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai; int ret, i; -- cgit v1.2.3 From 45ea97d74313bae681328b0c36fa348036777644 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 1 Mar 2022 16:47:42 +0800 Subject: ASoC: dwc-i2s: Handle errors for clk_enable As the potential failure of the clk_enable(), it should be better to check it, as same as clk_prepare_enable(). Fixes: c9afc1834e81 ("ASoC: dwc: Disallow building designware_pcm as a module") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220301084742.3751939-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/dwc/dwc-i2s.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 5cb58929090d..1edac3e10f34 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -403,9 +403,13 @@ static int dw_i2s_runtime_suspend(struct device *dev) static int dw_i2s_runtime_resume(struct device *dev) { struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); + int ret; - if (dw_dev->capability & DW_I2S_MASTER) - clk_enable(dw_dev->clk); + if (dw_dev->capability & DW_I2S_MASTER) { + ret = clk_enable(dw_dev->clk); + if (ret) + return ret; + } return 0; } @@ -422,10 +426,13 @@ static int dw_i2s_resume(struct snd_soc_component *component) { struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component); struct snd_soc_dai *dai; - int stream; + int stream, ret; - if (dev->capability & DW_I2S_MASTER) - clk_enable(dev->clk); + if (dev->capability & DW_I2S_MASTER) { + ret = clk_enable(dev->clk); + if (ret) + return ret; + } for_each_component_dais(component, dai) { for_each_pcm_streams(stream) -- cgit v1.2.3 From 300689fb04b3f23c1ac1abfe960b48ec414df597 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 1 Mar 2022 12:34:46 +0100 Subject: ASoC: soc-generic-dmaengine-pcm: set period_bytes_min based on maxburst In dmaengine_pcm_set_runtime_hwparams() period_bytes_min is hardcoded to 256. For some applications that may be too big. This patch changes that to calculate the value based on dma_data->maxburst. The correct value would be maxburst multiplied by the address width of the hardware FIFO. Unfortunately the address width is dynamically calculated based on the stream parameters and is not known at open time, so the worst case is chosen here which is 8 bytes, the maximum that is supported by dmaengine drivers. Not all drivers may set a maxburst value, so we fall back to the previously used hardcoded value of 256 bytes. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220301113446.1053171-1-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 8659cb1794f1..285441d6aeed 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -132,7 +132,9 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, SNDRV_PCM_INFO_INTERLEAVED; hw.periods_min = 2; hw.periods_max = UINT_MAX; - hw.period_bytes_min = 256; + hw.period_bytes_min = dma_data->maxburst * DMA_SLAVE_BUSWIDTH_8_BYTES; + if (!hw.period_bytes_min) + hw.period_bytes_min = 256; hw.period_bytes_max = dma_get_max_seg_size(dma_dev); hw.buffer_bytes_max = SIZE_MAX; hw.fifo_size = dma_data->fifo_size; -- cgit v1.2.3 From de2c6f98817fa5decb9b7d3b3a8a3ab864c10588 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Fri, 15 Oct 2021 08:13:53 +0000 Subject: ASoC: soc-compress: prevent the potentially use of null pointer There is one call trace that snd_soc_register_card() ->snd_soc_bind_card()->soc_init_pcm_runtime() ->snd_soc_dai_compress_new()->snd_soc_new_compress(). In the trace the 'codec_dai' transfers from card->dai_link, and we can see from the snd_soc_add_pcm_runtime() in snd_soc_bind_card() that, if value of card->dai_link->num_codecs is 0, then 'codec_dai' could be null pointer caused by index out of bound in 'asoc_rtd_to_codec(rtd, 0)'. And snd_soc_register_card() is called by various platforms. Therefore, it is better to add the check in the case of misusing. And because 'cpu_dai' has already checked in soc_init_pcm_runtime(), there is no need to check again. Adding the check as follow, then if 'codec_dai' is null, snd_soc_new_compress() will not pass through the check 'if (playback + capture != 1)', avoiding the leftover use of 'codec_dai'. Fixes: 467fece ("ASoC: soc-dai: move snd_soc_dai_stream_valid() to soc-dai.c") Signed-off-by: Jiasheng Jiang Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/1634285633-529368-1-git-send-email-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 8e2494a9f3a7..f4b376a71be8 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -568,12 +568,14 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) } /* check client and interface hw capabilities */ - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) - playback = 1; - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) - capture = 1; + if (codec_dai) { + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) + playback = 1; + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) + capture = 1; + } /* * Compress devices are unidirectional so only one of the directions -- cgit v1.2.3 From d5dd781bcc81aa31b62310927f25cfa2574450f1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 1 Mar 2022 11:11:04 +0300 Subject: ASoC: qcom: Fix error code in lpass_platform_copy() The copy_to/from_user() functions return the number of bytes remaining to be copied. This function needs to return negative error codes because snd_soc_pcm_component_copy_user() treats positive returns as success in soc_component_ret(). Fixes: 7d7209557b67 ("ASoC: qcom: Add support for codec dma driver") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220301081104.GB17375@kili Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 1ce0878665ca..6f58f246476e 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1229,15 +1229,19 @@ static int lpass_platform_copy(struct snd_soc_component *component, channel * (rt->dma_bytes / rt->channels)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (is_cdc_dma_port(dai_id)) + if (is_cdc_dma_port(dai_id)) { ret = copy_from_user_toio(dma_buf, buf, bytes); - else - ret = copy_from_user((void __force *)dma_buf, buf, bytes); + } else { + if (copy_from_user((void __force *)dma_buf, buf, bytes)) + ret = -EFAULT; + } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (is_cdc_dma_port(dai_id)) + if (is_cdc_dma_port(dai_id)) { ret = copy_to_user_fromio(buf, dma_buf, bytes); - else - ret = copy_to_user(buf, (void __force *)dma_buf, bytes); + } else { + if (copy_to_user(buf, (void __force *)dma_buf, bytes)) + ret = -EFAULT; + } } return ret; -- cgit v1.2.3 From e94769900f4302b4034945e5d9ec8262a2f5e086 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Wed, 2 Mar 2022 17:43:51 +0800 Subject: ASoC: hdac_hda: Avoid unexpected match when pcm_name is "Analog" pcm name can be "Analog" and "Alt Analog", cpcm->name can be "Analog Codec DAI" and "Alt Analog Codec DAI". When pcm_name is "Analog", "Analog Codec DAI" and "Alt Analog Codec DAI" are both satisfy the 'if (strstr(cpcm->name, pcm_name))' condition, which may cause the returned cpcm to be "Alt Analog Codec DAI". Even if we get the pcm name by id, and "Analog Codec DAI" goes into the loop before "Alt Analog Codec DAI", but I still think we'd better have multiple insurances against unexpected return values. After, we can correctly return the expected result even if other relevant places are changed. Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220302094351.3487-1-tangmeng@uniontech.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index de5955db0a5f..402e049dfcd7 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -363,8 +363,13 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, } list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { - if (strstr(cpcm->name, pcm_name)) + if (strstr(cpcm->name, pcm_name)) { + if (strcmp(pcm_name, "Analog") == 0) { + if (strstr(cpcm->name, "Alt Analog")) + continue; + } return cpcm; + } } dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); -- cgit v1.2.3 From 8f2b025abc31bc15d38657d1286d7470bbbd5efa Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Wed, 2 Mar 2022 09:35:33 +0800 Subject: ASoC: bt-sco: fix bt-sco-pcm-wb dai widget don't connect to the endpoint This patch fix the second dai driver's dai widget can't connect to the endpoint. Because "bt-sco-pcm" and "bt-sco-pcm-wb" dai driver have the same stream_name, so it will cause they have the same widget name. Therefor it will just create only one route when do snd_soc_dapm_add_route that only find the widget through the widget name. Signed-off-by: Jiaxin Yu Link: https://lore.kernel.org/r/20220302013533.29068-1-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/codecs/bt-sco.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 4d286844e3c8..cf17b9741bd8 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -13,11 +13,15 @@ static const struct snd_soc_dapm_widget bt_sco_widgets[] = { SND_SOC_DAPM_INPUT("RX"), SND_SOC_DAPM_OUTPUT("TX"), + SND_SOC_DAPM_AIF_IN("BT_SCO_RX", "Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0, + SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_dapm_route bt_sco_routes[] = { - { "Capture", NULL, "RX" }, - { "TX", NULL, "Playback" }, + { "BT_SCO_TX", NULL, "RX" }, + { "TX", NULL, "BT_SCO_RX" }, }; static struct snd_soc_dai_driver bt_sco_dai[] = { -- cgit v1.2.3 From 3cffb26fbb5202c6a4bee4b99d6bef5623003fbb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 2 Mar 2022 17:07:28 +0000 Subject: ALSA: echoaudio: remove redundant assignment to variable bytes The variable bytes is being assigned a value that is never read, it is being re-assigned inside a following if block. The assignment is redundant and can be removed. Cleans up clang scan build warning: sound/pci/echoaudio/midi.c:211:9: warning: Although the value stored to 'bytes' is used in the enclosing expression, the value is never actually read from 'bytes' [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20220302170728.1094633-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index cb72d27e809e..7be5c3327b16 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -208,7 +208,7 @@ static void snd_echo_midi_output_write(struct timer_list *t) /* No interrupts are involved: we have to check at regular intervals if the card's output buffer has room for new data. */ - sent = bytes = 0; + sent = 0; spin_lock_irqsave(&chip->lock, flags); chip->midi_full = 0; if (!snd_rawmidi_transmit_empty(chip->midi_out)) { -- cgit v1.2.3 From d7f15befac809ba365742464e1b0bebf07149c58 Mon Sep 17 00:00:00 2001 From: Xiaoke Wang Date: Fri, 4 Mar 2022 16:38:20 +0800 Subject: ALSA: lola: add a check for the return of vmalloc() vmalloc() is a memory allocation function which can return NULL when some internal memory errors happen. So it is better to check the return of it to catch the error in time. Signed-off-by: Xiaoke Wang Link: https://lore.kernel.org/r/tencent_4221FC4089F6DF01C48F192E5784038BA205@qq.com Signed-off-by: Takashi Iwai --- sound/pci/lola/lola_mixer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index e2c8f1417001..6b162489cb5f 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -121,6 +121,8 @@ int lola_init_mixer_widget(struct lola *chip, int nid) /* reserve memory to copy mixer data for sleep mode transitions */ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array)); + if (!chip->mixer.array_saved) + return -ENOMEM; /* mixer matrix sources are physical input data and play streams */ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams; -- cgit v1.2.3 From bf0cd60b7e33cf221fbe1114e4acb2c828b0af0d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 4 Mar 2022 21:56:47 +0900 Subject: ALSA: firewire-lib: fix uninitialized flag for AV/C deferred transaction AV/C deferred transaction was supported at a commit 00a7bb81c20f ("ALSA: firewire-lib: Add support for deferred transaction") while 'deferrable' flag can be uninitialized for non-control/notify AV/C transactions. UBSAN reports it: kernel: ================================================================================ kernel: UBSAN: invalid-load in /build/linux-aa0B4d/linux-5.15.0/sound/firewire/fcp.c:363:9 kernel: load of value 158 is not a valid value for type '_Bool' kernel: CPU: 3 PID: 182227 Comm: irq/35-firewire Tainted: P OE 5.15.0-18-generic #18-Ubuntu kernel: Hardware name: Gigabyte Technology Co., Ltd. AX370-Gaming 5/AX370-Gaming 5, BIOS F42b 08/01/2019 kernel: Call Trace: kernel: kernel: show_stack+0x52/0x58 kernel: dump_stack_lvl+0x4a/0x5f kernel: dump_stack+0x10/0x12 kernel: ubsan_epilogue+0x9/0x45 kernel: __ubsan_handle_load_invalid_value.cold+0x44/0x49 kernel: fcp_response.part.0.cold+0x1a/0x2b [snd_firewire_lib] kernel: fcp_response+0x28/0x30 [snd_firewire_lib] kernel: fw_core_handle_request+0x230/0x3d0 [firewire_core] kernel: handle_ar_packet+0x1d9/0x200 [firewire_ohci] kernel: ? handle_ar_packet+0x1d9/0x200 [firewire_ohci] kernel: ? transmit_complete_callback+0x9f/0x120 [firewire_core] kernel: ar_context_tasklet+0xa8/0x2e0 [firewire_ohci] kernel: tasklet_action_common.constprop.0+0xea/0xf0 kernel: tasklet_action+0x22/0x30 kernel: __do_softirq+0xd9/0x2e3 kernel: ? irq_finalize_oneshot.part.0+0xf0/0xf0 kernel: do_softirq+0x75/0xa0 kernel: kernel: kernel: __local_bh_enable_ip+0x50/0x60 kernel: irq_forced_thread_fn+0x7e/0x90 kernel: irq_thread+0xba/0x190 kernel: ? irq_thread_fn+0x60/0x60 kernel: kthread+0x11e/0x140 kernel: ? irq_thread_check_affinity+0xf0/0xf0 kernel: ? set_kthread_struct+0x50/0x50 kernel: ret_from_fork+0x22/0x30 kernel: kernel: ================================================================================ This commit fixes the bug. The bug has no disadvantage for the non- control/notify AV/C transactions since the flag has an effect for AV/C response with INTERIM (0x0f) status which is not used for the transactions in AV/C general specification. Fixes: 00a7bb81c20f ("ALSA: firewire-lib: Add support for deferred transaction") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20220304125647.78430-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- sound/firewire/fcp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index bbfbebf4affb..df44dd5dc4b2 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -240,9 +240,7 @@ int fcp_avc_transaction(struct fw_unit *unit, t.response_match_bytes = response_match_bytes; t.state = STATE_PENDING; init_waitqueue_head(&t.wait); - - if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03) - t.deferrable = true; + t.deferrable = (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03); spin_lock_irq(&transactions_lock); list_add_tail(&t.list, &transactions); -- cgit v1.2.3 From 0c20fce13e6e111463e3a15ce3cf6713fe518388 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 4 Mar 2022 10:08:40 -0700 Subject: ALSA: hda/realtek: Add quirk for Clevo NP70PNJ Fixes headset detection on Clevo NP70PNJ. Signed-off-by: Tim Crawford Cc: Link: https://lore.kernel.org/r/20220304170840.3351-1-tcrawford@system76.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3a42457984e9..bd926763fa47 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9103,6 +9103,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), -- cgit v1.2.3 From cd94df1795418056a19ff4cb44eadfc18ac99a57 Mon Sep 17 00:00:00 2001 From: Reza Jahanbakhshi Date: Fri, 4 Mar 2022 22:23:02 +0100 Subject: ALSA: usb-audio: add mapping for new Corsair Virtuoso SE New device id for Corsair Virtuoso SE RGB Wireless that currently is not in the mixer_map. This entry in the mixer_map is necessary in order to label its mixer appropriately and allow userspace to pick the correct volume controls. For instance, my own Corsair Virtuoso SE RGB Wireless headset has this new ID and consequently, the sidetone and volume are not working correctly without this change. > sudo lsusb -v | grep -i corsair Bus 007 Device 011: ID 1b1c:0a40 Corsair CORSAIR VIRTUOSO SE Wireless Gam idVendor 0x1b1c Corsair iManufacturer 1 Corsair iProduct 2 CORSAIR VIRTUOSO SE Wireless Gaming Headset Signed-off-by: Reza Jahanbakhshi Cc: Link: https://lore.kernel.org/r/20220304212303.195949-1-reza.jahanbakhshi@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 96991ddf5055..64f5544d0a0a 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -542,6 +542,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x05a7, 0x40fa), .map = bose_soundlink_map, }, + { + /* Corsair Virtuoso SE Latest (wired mode) */ + .id = USB_ID(0x1b1c, 0x0a3f), + .map = corsair_virtuoso_map, + }, + { + /* Corsair Virtuoso SE Latest (wireless mode) */ + .id = USB_ID(0x1b1c, 0x0a40), + .map = corsair_virtuoso_map, + }, { /* Corsair Virtuoso SE (wired mode) */ .id = USB_ID(0x1b1c, 0x0a3d), -- cgit v1.2.3 From fc4cf4293f0da3985ca66edc2cf067531d933c42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 5 Mar 2022 09:33:08 +0100 Subject: ALSA: x86: Use standard mmap helper for Intel HDMI LPE audio Intel HDMI LPE audio driver has its own mmap callback that mimics with the noncached page attributes, but this is rather superfluous and can be replaced with the standard helper, as the device is only for playback and the write-cache should suffice. This patch drops the own code and just uses the standard helper. Link: https://lore.kernel.org/r/20220305083308.15718-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/x86/intel_hdmi_audio.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 4a3ff6468aa7..b00634663346 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1253,18 +1253,6 @@ static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream) return len; } -/* - * ALSA PCM mmap callback - */ -static int had_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return remap_pfn_range(vma, vma->vm_start, - substream->runtime->dma_addr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} - /* * ALSA PCM ops */ @@ -1276,7 +1264,6 @@ static const struct snd_pcm_ops had_pcm_ops = { .trigger = had_pcm_trigger, .sync_stop = had_pcm_sync_stop, .pointer = had_pcm_pointer, - .mmap = had_pcm_mmap, }; /* process mode change of the running stream; called in mutex */ -- cgit v1.2.3 From 03a7895ee701e873c88c06bdb830ff40adb2be73 Mon Sep 17 00:00:00 2001 From: David Rhodes Date: Thu, 3 Mar 2022 17:30:40 +0000 Subject: ASoC: cs35l41: Fix GPIO2 configuration Fix GPIO2 polarity and direction configuration Fixes: fe1024d50477b ("ASoC: cs35l41: Combine adjacent register writes") Signed-off-by: David Rhodes Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220303173059.269657-2-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 77a017694645..90c91b00288b 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1035,8 +1035,8 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) regmap_update_bits(cs35l41->regmap, CS35L41_GPIO2_CTRL1, CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK, - irq_gpio_cfg1->irq_pol_inv << CS35L41_GPIO_POL_SHIFT | - !irq_gpio_cfg1->irq_out_en << CS35L41_GPIO_DIR_SHIFT); + irq_gpio_cfg2->irq_pol_inv << CS35L41_GPIO_POL_SHIFT | + !irq_gpio_cfg2->irq_out_en << CS35L41_GPIO_DIR_SHIFT); regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO1_CTRL_MASK | CS35L41_GPIO2_CTRL_MASK, -- cgit v1.2.3 From 16639d39bdf577168d3fe34315917a94365c8d19 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 3 Mar 2022 17:30:41 +0000 Subject: ASoC: cs35l41: Fix max number of TX channels This device only has 4 TX channels. Fixes: fe1024d50477b ("ASoC: cs35l41: Combine adjacent register writes") Signed-off-by: Lucas Tanure Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220303173059.269657-3-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 90c91b00288b..f3787d77f892 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1091,7 +1091,7 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { .capture = { .stream_name = "AMP Capture", .channels_min = 1, - .channels_max = 8, + .channels_max = 4, .rates = SNDRV_PCM_RATE_KNOT, .formats = CS35L41_TX_FORMATS, }, -- cgit v1.2.3 From 5e02fb590e83684f63217f93a9cdeabd6a925f9c Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 3 Mar 2022 17:30:42 +0000 Subject: ASoC: cs35l41: Fix DSP mbox start command and global enable order Global enable must happen before CSPL_MBOX_CMD_RESUME command is sent. Move it to PRE_PMU as both events use SND_SOC_DAPM_OUT_DRV_E macro. Signed-off-by: Lucas Tanure Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220303173059.269657-4-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index f3787d77f892..05de94fd2e55 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -573,7 +573,7 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, int ret = 0; switch (event) { - case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMU: regmap_multi_reg_write_bypassed(cs35l41->regmap, cs35l41_pup_patch, ARRAY_SIZE(cs35l41_pup_patch)); @@ -649,7 +649,7 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0, cs35l41_main_amp_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux), SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), -- cgit v1.2.3 From d66c57c5ff8a24859fe7506d290d0b705c2576c0 Mon Sep 17 00:00:00 2001 From: Gongjun Song Date: Fri, 4 Mar 2022 14:57:24 -0600 Subject: ASoC: SOF: Intel: pci-tgl: add RPL-S support Add PCI DID for Intel Raptor Lake S. Reviewed-by: Kai Vehmanen Signed-off-by: Gongjun Song Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-tgl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index fd46210f1730..feaec251adc8 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -110,6 +110,8 @@ static const struct pci_device_id sof_pci_ids[] = { .driver_data = (unsigned long)&ehl_desc}, { PCI_DEVICE(0x8086, 0x7ad0), /* ADL-S */ .driver_data = (unsigned long)&adls_desc}, + { PCI_DEVICE(0x8086, 0x7a50), /* RPL-S */ + .driver_data = (unsigned long)&adls_desc}, { PCI_DEVICE(0x8086, 0x51c8), /* ADL-P */ .driver_data = (unsigned long)&adl_desc}, { PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */ -- cgit v1.2.3 From edca0623f6d7928b312780d4e885258ca9e562fe Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 4 Mar 2022 14:57:25 -0600 Subject: ASoC: SOF: amd: acp-pcm: Take buffer information directly from runtime Instead of using the values from ipc_params, take them directly from substream->runtime. This is in preparation of making the platform hw_params callback to be IPC agnostic. Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-pcm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 5b23830cb1f3..b49cc55980ae 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -19,13 +19,14 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params) { - struct acp_dsp_stream *stream = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct acp_dsp_stream *stream = runtime->private_data; unsigned int buf_offset, index; u32 size; int ret; - size = ipc_params->buffer.size; - stream->num_pages = ipc_params->buffer.pages; + size = runtime->dma_bytes; + stream->num_pages = PFN_UP(runtime->dma_bytes); stream->dmab = substream->runtime->dma_buffer_p; ret = acp_dsp_stream_config(sdev, stream); -- cgit v1.2.3 From 9c2611b2a620f90219f85e4b40bbe3e26ab81e2c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 4 Mar 2022 14:57:26 -0600 Subject: ASoC: SOF: amd: Do not set ipc_pcm_params ops as it is optional The ipc_pcm_params() ops implementation for AMD is a NOP and since the callback is marked now as optional, it can be dropped along with the empty function. Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-ipc.c | 8 -------- sound/soc/sof/amd/acp.h | 2 -- sound/soc/sof/amd/renoir.c | 1 - 3 files changed, 11 deletions(-) diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index e132223b4c66..cd5af3d85002 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -170,14 +170,6 @@ int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *sub } EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON); -int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - /* TODO: Implement stream hw params to validate stream offset */ - return 0; -} -EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON); - int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) { return ACP_SCRATCH_MEMORY_ADDRESS; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 7ceb8bee0d8f..8ed4e338467f 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -185,8 +185,6 @@ int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); -int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index c3ecb9e9d5ba..409fd57448b8 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -150,7 +150,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { /*IPC */ .send_msg = acp_sof_ipc_send_msg, .ipc_msg_data = acp_sof_ipc_msg_data, - .ipc_pcm_params = acp_sof_ipc_pcm_params, .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset, .irq_thread = acp_sof_ipc_irq_thread, .fw_ready = sof_fw_ready, -- cgit v1.2.3 From b7485ec850591ad62fde0526bd7fdc56cdc04efd Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Fri, 4 Mar 2022 14:57:27 -0600 Subject: ASoC: SOF: amd: Flush cache after ATU_BASE_ADDR_GRP register update ACP_SRAM_PTE block has cache that needs to be flushed after every PTE updates. This patch updates ACPAXI2AXI_ATU_CTRL register to flush cache after updating PTE with stream physical address. Reviewed-by: Ranjani Sridharan Signed-off-by: Ajit Kumar Pandey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c index f2837bfbdb20..b3ca4a90dbf8 100644 --- a/sound/soc/sof/amd/acp-stream.c +++ b/sound/soc/sof/amd/acp-stream.c @@ -115,6 +115,9 @@ int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *strea offset += 8; } + /* Flush ATU Cache after PTE Update */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); + return 0; } -- cgit v1.2.3 From dc0d4ed26dd2166b47c29d6a9829ac798e62a0fc Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Fri, 4 Mar 2022 14:57:28 -0600 Subject: ASoC: SOF: amd: Use semaphore register to synchronize ipc's irq Add lock and unlock around ipc irq handling code using hw semaphore register that exhibit special property for register read calls. As host and DSP firmware uses few shared registers, there is a possible race condition around those shared registers values. This lock ensure synchronization between Firmware and host ipc interrupts. Reviewed-by: Ranjani Sridharan Signed-off-by: Ajit Kumar Pandey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 1 + sound/soc/sof/amd/acp-ipc.c | 14 ++++++++++++++ sound/soc/sof/amd/acp.c | 15 ++++++++++++++- sound/soc/sof/amd/acp.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 63f13c111b24..40fbf11facba 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -61,6 +61,7 @@ #define ACP_DSP_SW_INTR_STAT 0x1818 #define ACP_SW_INTR_TRIG 0x181C #define ACP_ERROR_STATUS 0x18C4 +#define ACP_AXI2DAGB_SEM_0 0x1880 /* Registers from ACP_SHA block */ #define ACP_SHA_DSP_FW_QUALIFIER 0x1C70 diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index cd5af3d85002..9fcd2535fd3b 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -62,12 +62,26 @@ int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct acp_dev_data *adata = sdev->pdata->hw_pdata; unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box); + unsigned int count = ACP_HW_SEM_RETRY_COUNT; + + while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) { + /* Wait until acquired HW Semaphore Lock or timeout*/ + count--; + if (!count) { + dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__); + return -EINVAL; + } + }; acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size); acp_ipc_host_msg_set(sdev); /* Trigger host to dsp interrupt for the msg */ acpbus_trigger_host_to_dsp_swintr(adata); + + /* Unlock or Release HW Semaphore */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0); + return 0; } EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index fe9b7dc5bc86..ba8b6427b59f 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -273,7 +273,7 @@ static int acp_memory_init(struct snd_sof_dev *sdev) static irqreturn_t acp_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; - unsigned int val; + unsigned int val, count = ACP_HW_SEM_RETRY_COUNT; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT); if (val & ACP_SHA_STAT) { @@ -284,9 +284,22 @@ static irqreturn_t acp_irq_thread(int irq, void *context) val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT); if (val & ACP_DSP_TO_HOST_IRQ) { + while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) { + /* 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; + } + }; + sof_ops(sdev)->irq_thread(irq, sdev); val |= ACP_DSP_TO_HOST_IRQ; snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val); + + /* Unlock or Release HW Semaphore */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0); + return IRQ_HANDLED; } diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 8ed4e338467f..db1030d36811 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -17,6 +17,7 @@ #define ACP_DSP_BAR 0 +#define ACP_HW_SEM_RETRY_COUNT 10 #define ACP_REG_POLL_INTERVAL 500 #define ACP_REG_POLL_TIMEOUT_US 2000 #define ACP_DMA_COMPLETE_TIMEOUT_US 5000 -- cgit v1.2.3 From 7cf467ac9cf33f0975095f080a79f6ec6d9be5b6 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Fri, 4 Mar 2022 14:57:29 -0600 Subject: ASoC: SOF: amd: Move group register configuration to acp-loader We are using PTE_GRP1 for DMA operations to load firmware binaries but we are enabling PTE_GRP and flushing ATU cache much before in probe callbacks. This can cause issue if we try to load firmware runtime during system resume as probe callback will not be invoked hence PTE_GRP will not be enabled. Moreover it makes more sense to flush the cache after register configuration. Move PTE group register configuration to acp-loader within pre_fw_run callback to avoid such issue. Reviewed-by: Ranjani Sridharan Signed-off-by: Ajit Kumar Pandey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 9 +++++++++ sound/soc/sof/amd/acp.c | 14 -------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 2dc15ae38155..7ca51e0f3b1b 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -127,6 +127,12 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev return; } + /* Group Enable */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1, + ACP_SRAM_PTE_OFFSET | BIT(31)); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1, + PAGE_SIZE_4K_ENABLE); + for (page_idx = 0; page_idx < num_pages; page_idx++) { low = lower_32_bits(addr); high = upper_32_bits(addr); @@ -136,6 +142,9 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev offset += 8; addr += PAGE_SIZE; } + + /* Flush ATU Cache after PTE Update */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); } /* pre fw run operations */ diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index ba8b6427b59f..66ca05545be2 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -36,19 +36,6 @@ static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data) return 0; } -static void configure_acp_groupregisters(struct acp_dev_data *adata) -{ - struct snd_sof_dev *sdev = adata->dev; - - /* Group Enable */ - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1, - ACP_SRAM_PTE_OFFSET | BIT(31)); - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1, - PAGE_SIZE_4K_ENABLE); - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); -} - static void init_dma_descriptor(struct acp_dev_data *adata) { struct snd_sof_dev *sdev = adata->dev; @@ -264,7 +251,6 @@ static int acp_memory_init(struct snd_sof_dev *sdev) snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL, ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK); - configure_acp_groupregisters(adata); init_dma_descriptor(adata); return 0; -- cgit v1.2.3 From 8e85cab858562734b9d323f392ba9956bbdc133c Mon Sep 17 00:00:00 2001 From: Ajit Kumar Pandey Date: Fri, 4 Mar 2022 14:57:30 -0600 Subject: ASoC: SOF: amd: Increase ACP_HW_SEM_RETRY_COUNT value Host is trying to acquire semaphore lock based on HW_SEM_RETRY_COUNT value which is set to 10 by default. So host will loop for 10 times trying to acquire lock before giving error msg "Failed to acquire HW lock". Though this loop count of 10 is good enough with most of the times but we have observed such failure msg in very few cases(~5 %). Increase ACP_HW_SEM_RETRY_COUNT to avoid such issue and loop for a significant time period before throwing error. We're setting newer loop count to quite higher value of 10K but it's very unlikely that it will loop for this count, since for most of the cases lock will get acquired at much lesser loop iterations. Reviewed-by: Ranjani Sridharan Signed-off-by: Ajit Kumar Pandey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index db1030d36811..f550a5010a91 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -17,7 +17,7 @@ #define ACP_DSP_BAR 0 -#define ACP_HW_SEM_RETRY_COUNT 10 +#define ACP_HW_SEM_RETRY_COUNT 10000 #define ACP_REG_POLL_INTERVAL 500 #define ACP_REG_POLL_TIMEOUT_US 2000 #define ACP_DMA_COMPLETE_TIMEOUT_US 5000 -- cgit v1.2.3 From 4aaa06b227f737da5c10feb93a6b203920d5a1e7 Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Fri, 4 Mar 2022 14:57:31 -0600 Subject: ASoC: SOF: fix 32 signed bit overflow Shifting in a signed 32bit container past the signed bit is technically undefined behaviour. Fix by using unsigned types. Found via cppcheck. Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Signed-off-by: Curtis Malainey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/header.h | 2 +- include/uapi/sound/sof/abi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index b97a76bcb655..b22e925c70e2 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -31,7 +31,7 @@ /* Global Message - Generic */ #define SOF_GLB_TYPE_SHIFT 28 -#define SOF_GLB_TYPE_MASK (0xfL << SOF_GLB_TYPE_SHIFT) +#define SOF_GLB_TYPE_MASK (0xfUL << SOF_GLB_TYPE_SHIFT) #define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT) /* Command Message - Generic */ diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index f4232d289a22..e052653a6e4c 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -27,7 +27,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 #define SOF_ABI_MINOR 19 -#define SOF_ABI_PATCH 0 +#define SOF_ABI_PATCH 1 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ #define SOF_ABI_MAJOR_SHIFT 24 -- cgit v1.2.3 From 9188812539d1d9a13dac690c95ec657259859ba4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 4 Mar 2022 14:57:32 -0600 Subject: ASoC: SOF: debug: clarify operator precedence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cppcheck warning: for '&' and '?'. [clarifyCalculation] char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; ^ sound/soc/sof/debug.c:398:46: style: Clarify calculation precedence Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 145fd0d1e166..7b1139961a99 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -395,7 +395,7 @@ static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *lev void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) { - char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; + char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) -- cgit v1.2.3 From 0f33105bb2f77c870542d5bc08cf94b8c4e26f36 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 4 Mar 2022 14:57:33 -0600 Subject: ASoC: SOF: Intel: hda: clarify operator precedence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cppcheck warning sound/soc/sof/intel/hda.c:545:46: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304205733.62233-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e42b45722e9d..a99e6608f0b6 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -534,7 +534,7 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *le void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { - char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; + char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; -- cgit v1.2.3 From 26e5366dd30569a469e1a87998b866b814deccf8 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Feb 2022 17:27:53 +0000 Subject: ASoC: dt-bindings: audio-graph-port: Add dai-tdm-slot-width-map Some audio hardware cannot support a fixed slot width for all sample widths, or a slot width equal to the sample width for all sample widths. This is usually due either to limitations of the audio serial port or system clocking restrictions. This property allows setting a mapping of sample widths and the corresponding tdm slot widths. The slot count is also provided for each slot width - although this would almost always be the same for all slot widths this allows for possibly adding extra padding slots to maintain a fixed bitclock frequency. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220228172754.453783-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/audio-graph-port.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 476dcb49ece6..5c368674d11a 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -71,4 +71,24 @@ patternProperties: description: CPU to Codec rate channels. $ref: /schemas/types.yaml#/definitions/uint32 + dai-tdm-slot-width-map: + description: Mapping of sample widths to slot widths. For hardware + that cannot support a fixed slot width or a slot width always + equal to sample width. A matrix of one or more 3-tuples. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - + description: Sample width in bits + minimum: 8 + maximum: 64 + - + description: Slot width in bits + minimum: 8 + maximum: 256 + - + description: Slot count + minimum: 1 + maximum: 64 + additionalProperties: true -- cgit v1.2.3 From 1e974e5b82b3d75069b50445cd248cee0199654e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Feb 2022 17:27:54 +0000 Subject: ASoC: audio_graph_card2: Add support for variable slot widths Some audio hardware cannot support the same slot width for all sample widths, or a slot width equal to the sample width for all sample widths. This is usually due either to limitations of the audio serial port or system clocking restrictions. A typical example would be: - 16-bit samples in 16-bit slots - 24-bit samples in 32-bit slots The new dai-tdm-slot-width-map property allows setting a mapping of sample widths and the corresponding tdm slot widths and slot counts. Although the slot count is usually the same for all cases this does allow for adding padding slots to maintain the same bitclk frequency. The property is added to each endpoint node that needs the component DAI to be told the TDM slot width and count. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220228172754.453783-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 11 ++++ sound/soc/generic/audio-graph-card2.c | 4 ++ sound/soc/generic/simple-card-utils.c | 97 +++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 5ee269c59aac..8faa649f712b 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -16,6 +16,12 @@ #define asoc_simple_init_mic(card, sjack, prefix) \ asoc_simple_init_jack(card, sjack, 0, prefix, NULL) +struct asoc_simple_tdm_width_map { + u8 sample_bits; + u8 slot_count; + u16 slot_width; +}; + struct asoc_simple_dai { const char *name; unsigned int sysclk; @@ -26,6 +32,8 @@ struct asoc_simple_dai { unsigned int rx_slot_mask; struct clk *clk; bool clk_fixed; + struct asoc_simple_tdm_width_map *tdm_width_map; + int n_tdm_widths; }; struct asoc_simple_data { @@ -132,6 +140,9 @@ int asoc_simple_parse_daifmt(struct device *dev, struct device_node *codec, char *prefix, unsigned int *retfmt); +int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np, + struct asoc_simple_dai *dai); + __printf(3, 4) int asoc_simple_set_dailink_name(struct device *dev, struct snd_soc_dai_link *dai_link, diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index c3947347dda3..c0f3907a01fd 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -503,6 +503,10 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, if (ret < 0) return ret; + ret = asoc_simple_parse_tdm_width_map(dev, ep, dai); + if (ret < 0) + return ret; + ret = asoc_simple_parse_clk(dev, ep, dai, dlc); if (ret < 0) return ret; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a4babfb63175..14c8b3a74c2d 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -12,6 +12,7 @@ #include #include #include +#include #include void asoc_simple_convert_fixup(struct asoc_simple_data *data, @@ -87,6 +88,51 @@ int asoc_simple_parse_daifmt(struct device *dev, } EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt); +int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np, + struct asoc_simple_dai *dai) +{ + u32 *array_values, *p; + int n, i, ret; + + if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) + return 0; + + n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32)); + if (n % 3) { + dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n"); + return -EINVAL; + } + + dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL); + if (!dai->tdm_width_map) + return -ENOMEM; + + array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL); + if (!array_values) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n); + if (ret < 0) { + dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret); + goto out; + } + + p = array_values; + for (i = 0; i < n / 3; ++i) { + dai->tdm_width_map[i].sample_bits = *p++; + dai->tdm_width_map[i].slot_width = *p++; + dai->tdm_width_map[i].slot_count = *p++; + } + + dai->n_tdm_widths = i; + ret = 0; +out: + kfree(array_values); + + return ret; +} +EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map); + int asoc_simple_set_dailink_name(struct device *dev, struct snd_soc_dai_link *dai_link, const char *fmt, ...) @@ -309,6 +355,42 @@ static int asoc_simple_set_clk_rate(struct device *dev, return clk_set_rate(simple_dai->clk, rate); } +static int asoc_simple_set_tdm(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai, + struct snd_pcm_hw_params *params) +{ + int sample_bits = params_width(params); + int slot_width = simple_dai->slot_width; + int slot_count = simple_dai->slots; + int i, ret; + + if (!simple_dai || !simple_dai->tdm_width_map) + return 0; + + if (slot_width == 0) + slot_width = sample_bits; + + for (i = 0; i < simple_dai->n_tdm_widths; ++i) { + if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) { + slot_width = simple_dai->tdm_width_map[i].slot_width; + slot_count = simple_dai->tdm_width_map[i].slot_count; + break; + } + } + + ret = snd_soc_dai_set_tdm_slot(dai, + simple_dai->tx_slot_mask, + simple_dai->rx_slot_mask, + slot_count, + slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret); + return ret; + } + + return 0; +} + int asoc_simple_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -362,6 +444,21 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, return ret; } } + + for_each_prop_dai_codec(props, i, pdai) { + sdai = asoc_rtd_to_codec(rtd, i); + ret = asoc_simple_set_tdm(sdai, pdai, params); + if (ret < 0) + return ret; + } + + for_each_prop_dai_cpu(props, i, pdai) { + sdai = asoc_rtd_to_cpu(rtd, i); + ret = asoc_simple_set_tdm(sdai, pdai, params); + if (ret < 0) + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_hw_params); -- cgit v1.2.3 From b3284430615c27ca441967f99fbde957b434e092 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Fri, 4 Mar 2022 11:24:51 +0100 Subject: ASoC: dt-bindings: Add schema for "awinic,aw8738" Add a DT schema for describing Awinic AW8738 audio amplifiers. They are fairly simple and controlled using a single GPIO. The number of pulses during power up selects one of a few pre-defined operation modes. This can be used to configure the speaker-guard function (primarily the power limit for the amplifier). Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20220304102452.26856-2-stephan@gerhold.net Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/awinic,aw8738.yaml | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw8738.yaml diff --git a/Documentation/devicetree/bindings/sound/awinic,aw8738.yaml b/Documentation/devicetree/bindings/sound/awinic,aw8738.yaml new file mode 100644 index 000000000000..dce86dafe382 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw8738.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw8738.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW8738 Audio Amplifier + +maintainers: + - Stephan Gerhold + +description: + The Awinic AW8738 is a simple audio amplifier with different operation modes + (set using one-wire pulse control). The mode configures the speaker-guard + function (primarily the power limit for the amplifier). + +allOf: + - $ref: name-prefix.yaml# + +properties: + compatible: + const: awinic,aw8738 + + mode-gpios: + description: + GPIO used for one-wire pulse control. The pin is typically called SHDN + (active-low), but this is misleading since it is actually more than + just a simple shutdown/enable control. + maxItems: 1 + + awinic,mode: + description: Operation mode (number of pulses for one-wire pulse control) + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 7 + + sound-name-prefix: true + +required: + - compatible + - mode-gpios + - awinic,mode + +additionalProperties: false + +examples: + - | + #include + audio-amplifier { + compatible = "awinic,aw8738"; + mode-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; + awinic,mode = <5>; + sound-name-prefix = "Speaker Amp"; + }; -- cgit v1.2.3 From 6b4528b5532f84f385b4e756637698cf4ae211f3 Mon Sep 17 00:00:00 2001 From: Jonathan Albrieux Date: Fri, 4 Mar 2022 11:24:52 +0100 Subject: ASoC: codecs: Add Awinic AW8738 audio amplifier driver The Awinic AW8738 is a simple audio amplifier using a single GPIO. The main difference to simple-amplifier is that there is a "one-wire pulse control" that allows configuring the amplifier to one of a few pre-defined modes. This can be used to configure the speaker-guard function (primarily the power limit for the amplifier). Add a simple driver that allows setting it up in the device tree with a specified mode number. Signed-off-by: Jonathan Albrieux Co-developed-by: Stephan Gerhold Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20220304102452.26856-3-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 10 +++++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw8738.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 sound/soc/codecs/aw8738.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4de029ae377c..f46a22660103 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -53,6 +53,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK5558 imply SND_SOC_ALC5623 imply SND_SOC_ALC5632 + imply SND_SOC_AW8738 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CQ0093VC @@ -579,6 +580,15 @@ config SND_SOC_ALC5632 tristate depends on I2C +config SND_SOC_AW8738 + tristate "Awinic AW8738 Audio Amplifier" + select GPIOLIB + help + Enable support for the Awinic AW8738 audio amplifier (or similar). + The driver supports simple audio amplifiers similar to + SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the + operation mode using the Awinic-specific one-wire pulse control. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c3c6059a5f8a..8637e9e869e3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -45,6 +45,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-ak5386-objs := ak5386.o snd-soc-ak5558-objs := ak5558.o snd-soc-arizona-objs := arizona.o arizona-jack.o +snd-soc-aw8738-objs := aw8738.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-cpcap-objs := cpcap.o @@ -388,6 +389,7 @@ obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o +obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o diff --git a/sound/soc/codecs/aw8738.c b/sound/soc/codecs/aw8738.c new file mode 100644 index 000000000000..0fe8af160319 --- /dev/null +++ b/sound/soc/codecs/aw8738.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +struct aw8738_priv { + struct gpio_desc *gpiod_mode; + unsigned int mode; +}; + +static int aw8738_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct aw8738_priv *aw = snd_soc_component_get_drvdata(c); + int i; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + for (i = 0; i < aw->mode; i++) { + gpiod_set_value_cansleep(aw->gpiod_mode, 0); + udelay(2); + gpiod_set_value_cansleep(aw->gpiod_mode, 1); + udelay(2); + } + msleep(40); + break; + case SND_SOC_DAPM_PRE_PMD: + gpiod_set_value_cansleep(aw->gpiod_mode, 0); + usleep_range(1000, 2000); + break; + default: + WARN(1, "Unexpected event"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget aw8738_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, aw8738_drv_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route aw8738_dapm_routes[] = { + { "DRV", NULL, "IN" }, + { "OUT", NULL, "DRV" }, +}; + +static const struct snd_soc_component_driver aw8738_component_driver = { + .dapm_widgets = aw8738_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw8738_dapm_widgets), + .dapm_routes = aw8738_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aw8738_dapm_routes), +}; + +static int aw8738_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aw8738_priv *aw; + int ret; + + aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL); + if (!aw) + return -ENOMEM; + platform_set_drvdata(pdev, aw); + + aw->gpiod_mode = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW); + if (IS_ERR(aw->gpiod_mode)) + return dev_err_probe(dev, PTR_ERR(aw->gpiod_mode), + "Failed to get 'mode' gpio"); + + ret = device_property_read_u32(dev, "awinic,mode", &aw->mode); + if (ret) + return -EINVAL; + + return devm_snd_soc_register_component(&pdev->dev, + &aw8738_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id aw8738_of_match[] = { + { .compatible = "awinic,aw8738" }, + { } +}; +MODULE_DEVICE_TABLE(of, aw8738_of_match); +#endif + +static struct platform_driver aw8738_driver = { + .probe = aw8738_probe, + .driver = { + .name = "aw8738", + .of_match_table = of_match_ptr(aw8738_of_match), + }, +}; +module_platform_driver(aw8738_driver); + +MODULE_DESCRIPTION("Awinic AW8738 Amplifier Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bd393e2ecc30bde95fcfab23af8246d1724e25cd Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 2 Mar 2022 09:34:22 +0100 Subject: ASoC: fsl_sai: Drop unnecessary defines The fsl_sai driver has FSL_FMT_TRANSMITTER and FSL_FMT_RECEIVER defines which are used in a single function only then are then only translated into a bool 'tx' variable. Drop the defines and pass the boolean value directly to fsl_sai_set_dai_sysclk_tr(). No functional change. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-2-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 16 ++++++---------- sound/soc/fsl/fsl_sai.h | 3 --- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index c2d423d1d87e..9b43f94f672f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -167,11 +167,10 @@ static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai, } static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int fsl_dir) + int clk_id, unsigned int freq, bool tx) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); unsigned int ofs = sai->soc_data->reg_offset; - bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0; switch (clk_id) { @@ -205,15 +204,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, if (dir == SND_SOC_CLOCK_IN) return 0; - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, - FSL_FMT_TRANSMITTER); + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); return ret; } - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, - FSL_FMT_RECEIVER); + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false); if (ret) dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); @@ -221,11 +218,10 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, } static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, - unsigned int fmt, int fsl_dir) + unsigned int fmt, bool tx) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); unsigned int ofs = sai->soc_data->reg_offset; - bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0, val_cr4 = 0; if (!sai->is_lsb_first) @@ -332,13 +328,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { int ret; - ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); return ret; } - ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false); if (ret) dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 410f6e6a9137..b30d6113ff08 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -201,9 +201,6 @@ #define FSL_SAI_REC_SYN BIT(4) #define FSL_SAI_USE_I2S_SLAVE BIT(5) -#define FSL_FMT_TRANSMITTER 0 -#define FSL_FMT_RECEIVER 1 - /* SAI clock sources */ #define FSL_SAI_CLK_BUS 0 #define FSL_SAI_CLK_MAST1 1 -- cgit v1.2.3 From cb00b4c18f89aa8ffb847cd033467f0958c025a0 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 2 Mar 2022 09:34:23 +0100 Subject: ASoC: fsl_sai: simplify irq return value Instead of using a boolean "irq_none" to describe the interrupt handlers return value use a variable of type irqreturn_t and return it directly. No functional change. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-3-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9b43f94f672f..b815e868567b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -62,7 +62,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) unsigned int ofs = sai->soc_data->reg_offset; struct device *dev = &sai->pdev->dev; u32 flags, xcsr, mask; - bool irq_none = true; + irqreturn_t iret = IRQ_NONE; /* * Both IRQ status bits and IRQ mask bits are in the xCSR but @@ -76,7 +76,7 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) flags = xcsr & mask; if (flags) - irq_none = false; + iret = IRQ_HANDLED; else goto irq_rx; @@ -110,7 +110,7 @@ irq_rx: flags = xcsr & mask; if (flags) - irq_none = false; + iret = IRQ_HANDLED; else goto out; @@ -139,10 +139,7 @@ irq_rx: regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr); out: - if (irq_none) - return IRQ_NONE; - else - return IRQ_HANDLED; + return iret; } static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, -- cgit v1.2.3 From 814c9fc46fb987bdb3af093e4818c86e62db4fa5 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Wed, 2 Mar 2022 09:34:24 +0100 Subject: ASoC: fsl_sai: simplify register poking in fsl_sai_set_bclk Depending on SAI synchronization mode, the same value is either written to FSL_SAI_TCR2 or FSL_SAI_RCR2 or nothing is written at all. As the computation is the same either way, factor it out to make it clearer what the difference is. No functional change. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-4-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index b815e868567b..218282c2e86f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -341,7 +341,7 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); - unsigned int ofs = sai->soc_data->reg_offset; + unsigned int reg, ofs = sai->soc_data->reg_offset; unsigned long clk_rate; u32 savediv = 0, ratio, savesub = freq; int adir = tx ? RX : TX; @@ -401,6 +401,9 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; } + dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", + sai->mclk_id[tx], savediv, savesub); + /* * 1) For Asynchronous mode, we must set RCR2 register for capture, and * set TCR2 register for playback. @@ -411,22 +414,16 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) * 4) For Tx and Rx are both Synchronous with another SAI, we just * ignore it. */ - if (fsl_sai_dir_is_synced(sai, adir)) { - regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), - FSL_SAI_CR2_MSEL_MASK, - FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), - FSL_SAI_CR2_DIV_MASK, savediv - 1); - } else if (!sai->synchronous[dir]) { - regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), - FSL_SAI_CR2_MSEL_MASK, - FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), - FSL_SAI_CR2_DIV_MASK, savediv - 1); - } + if (fsl_sai_dir_is_synced(sai, adir)) + reg = FSL_SAI_xCR2(!tx, ofs); + else if (!sai->synchronous[dir]) + reg = FSL_SAI_xCR2(tx, ofs); + else + return 0; - dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", - sai->mclk_id[tx], savediv, savesub); + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK, + FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK, savediv - 1); return 0; } -- cgit v1.2.3 From 99c1e74f25d435c1c714b8ee0dceb7ebb7d2f2f1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 2 Mar 2022 09:34:25 +0100 Subject: ASoC: fsl_sai: store full version instead of major/minor The driver tests for the hardware revision being newer than 3.1 with (sai->verid.major >= 3 && sai->verid.minor >= 1). The result is obviously wrong for hardware revision 4.0. Fix this by storing the full version in a single variable and comparing to that one. No practical change at the moment as there is no 4.0 ip version currently. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-5-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 8 +++----- sound/soc/fsl/fsl_sai.h | 6 ++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 218282c2e86f..c9341c25edf1 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -962,10 +962,8 @@ static int fsl_sai_check_version(struct device *dev) dev_dbg(dev, "VERID: 0x%016X\n", val); - sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >> - FSL_SAI_VERID_MAJOR_SHIFT; - sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >> - FSL_SAI_VERID_MINOR_SHIFT; + sai->verid.version = val & + (FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK); sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK; ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val); @@ -1137,7 +1135,7 @@ static int fsl_sai_probe(struct platform_device *pdev) /* Select MCLK direction */ if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && - sai->verid.major >= 3 && sai->verid.minor >= 1) { + sai->verid.version >= 0x0301) { regmap_update_bits(sai->regmap, FSL_SAI_MCTL, FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); } diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index b30d6113ff08..7310fd02cc3c 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -227,15 +227,13 @@ struct fsl_sai_soc_data { /** * struct fsl_sai_verid - version id data - * @major: major version number - * @minor: minor version number + * @version: version number * @feature: feature specification number * 0000000000000000b - Standard feature set * 0000000000000000b - Standard feature set */ struct fsl_sai_verid { - u32 major; - u32 minor; + u32 version; u32 feature; }; -- cgit v1.2.3 From c56359f4f2adfb81cf7cbea1e8ef9bfc59dbd4ec Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 2 Mar 2022 09:34:26 +0100 Subject: ASoC: fsl_sai: Use better variable names "ret" is normally used as a variable name for return values. In fsl_sai_set_bclk() it stores the difference between the desired rate and the rate we can archieve, so rename it to "diff". Also rename "savesub" to "bestdiff" as that stores the best difference we have found. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-6-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index c9341c25edf1..02b4cffa8455 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -343,11 +343,10 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); unsigned int reg, ofs = sai->soc_data->reg_offset; unsigned long clk_rate; - u32 savediv = 0, ratio, savesub = freq; + u32 savediv = 0, ratio, bestdiff = freq; int adir = tx ? RX : TX; int dir = tx ? TX : RX; u32 id; - int ret = 0; /* Don't apply to consumer mode */ if (sai->is_consumer_mode) @@ -361,19 +360,21 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0; for (; id < FSL_SAI_MCLK_MAX; id++) { + int diff; + clk_rate = clk_get_rate(sai->mclk_clk[id]); if (!clk_rate) continue; ratio = clk_rate / freq; - ret = clk_rate - ratio * freq; + diff = clk_rate - ratio * freq; /* * Drop the source that can not be * divided into the required rate. */ - if (ret != 0 && clk_rate / ret < 1000) + if (diff != 0 && clk_rate / diff < 1000) continue; dev_dbg(dai->dev, @@ -385,13 +386,13 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) else continue; - if (ret < savesub) { + if (diff < bestdiff) { savediv = ratio; sai->mclk_id[tx] = id; - savesub = ret; + bestdiff = diff; } - if (ret == 0) + if (diff == 0) break; } @@ -402,7 +403,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) } dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", - sai->mclk_id[tx], savediv, savesub); + sai->mclk_id[tx], savediv, bestdiff); /* * 1) For Asynchronous mode, we must set RCR2 register for capture, and -- cgit v1.2.3 From 1d4cbdf7bf2eaa794528250a29aed08f1df7f837 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 2 Mar 2022 09:34:27 +0100 Subject: ASoC: fsl_sai: use DIV_ROUND_CLOSEST() to calculate divider In fsl_sai_set_bclk() we want to calculate the divider that gets us closest to the desired frequency, so use DIV_ROUND_CLOSEST() instead of just doing a clk_rate/freq. Also discard invalid ratios earlier. Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-7-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 02b4cffa8455..761fa8229a09 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -366,9 +366,11 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) if (!clk_rate) continue; - ratio = clk_rate / freq; + ratio = DIV_ROUND_CLOSEST(clk_rate, freq); + if (!ratio || ratio > 512 || ratio & 1) + continue; - diff = clk_rate - ratio * freq; + diff = abs((long)clk_rate - ratio * freq); /* * Drop the source that can not be @@ -381,10 +383,6 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) "ratio %d for freq %dHz based on clock %ldHz\n", ratio, freq, clk_rate); - if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512) - ratio /= 2; - else - continue; if (diff < bestdiff) { savediv = ratio; @@ -424,7 +422,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK, savediv - 1); + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK, savediv / 2 - 1); return 0; } -- cgit v1.2.3 From a50b7926d015c3b8194ab1d7c8aa86db8e4b7700 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Wed, 2 Mar 2022 09:34:28 +0100 Subject: ASoC: fsl_sai: implement 1:1 bclk:mclk ratio support With higher channel counts, we may need higher clock rates. Starting with SAI v3.1 (i.MX8MM), we can bypass the divider and get a 1:1 bclk:mclk ratio. Add the necessary support. Signed-off-by: Viorel Suman Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20220302083428.3804687-8-s.hauer@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 761fa8229a09..4650a6931a94 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -347,6 +347,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) int adir = tx ? RX : TX; int dir = tx ? TX : RX; u32 id; + bool support_1_1_ratio = sai->verid.version >= 0x0301; /* Don't apply to consumer mode */ if (sai->is_consumer_mode) @@ -367,7 +368,11 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) continue; ratio = DIV_ROUND_CLOSEST(clk_rate, freq); - if (!ratio || ratio > 512 || ratio & 1) + if (!ratio || ratio > 512) + continue; + if (ratio == 1 && !support_1_1_ratio) + continue; + else if (ratio & 1) continue; diff = abs((long)clk_rate - ratio * freq); @@ -422,7 +427,15 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK, savediv / 2 - 1); + + if (savediv == 1) + regmap_update_bits(sai->regmap, reg, + FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, + FSL_SAI_CR2_BYP); + else + regmap_update_bits(sai->regmap, reg, + FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, + savediv / 2 - 1); return 0; } -- cgit v1.2.3 From 32666b866f55a224d2f07f83594fcf37a922b6c9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 4 Mar 2022 14:45:30 -0600 Subject: ASoC: Intel: boards: remove explicit dependency on GPIOLIB when DMIC is used" This patch reverts commit 4262ddc2ad63 ("ASoC: Intel: boards: add explicit dependency on GPIOLIB when DMIC is used") and all follow-up additions of this dependency. Now that the DMIC does not depend on GPIOLIB we can simplify again. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220304204532.54675-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index da59504d2322..b0baf3fce05b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -242,7 +242,7 @@ if SND_SOC_INTEL_SKL config SND_SOC_INTEL_SKL_RT286_MACH tristate "SKL with RT286 I2S mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT286 select SND_SOC_DMIC @@ -255,7 +255,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH tristate "SKL with NAU88L25 and SSM4567 in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_NAU8825 select SND_SOC_SSM4567 @@ -269,7 +269,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH tristate "SKL with NAU88L25 and MAX98357A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_NAU8825 select SND_SOC_MAX98357A @@ -300,7 +300,7 @@ if SND_SOC_INTEL_APL config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A/MAX98390 in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON @@ -312,7 +312,7 @@ config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH config SND_SOC_INTEL_BXT_RT298_MACH tristate "Broxton with RT298 I2S mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT298 select SND_SOC_DMIC @@ -345,7 +345,7 @@ if SND_SOC_INTEL_KBL config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT5663 select SND_SOC_MAX98927 @@ -377,7 +377,7 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH tristate "KBL with DA7219 and MAX98357A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC help @@ -387,7 +387,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH tristate "KBL with DA7219 and MAX98927 in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_DA7219 select SND_SOC_MAX98927 @@ -417,7 +417,7 @@ if SND_SOC_SOF_GEMINILAKE config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH tristate "GLK with DA7219 and MAX98357A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON @@ -429,7 +429,7 @@ config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_RT5682_I2C @@ -451,7 +451,6 @@ if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "Skylake+ with HDA Codecs" depends on SND_HDA_CODEC_HDMI - depends on GPIOLIB select SND_SOC_HDAC_HDMI select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_DMIC @@ -468,7 +467,7 @@ endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_SOF_RT5682_MACH tristate "SOF with rt5682 codec in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) @@ -537,7 +536,7 @@ config SND_SOC_INTEL_SOF_ES8336_MACH config SND_SOC_INTEL_SOF_NAU8825_MACH tristate "SOF with nau8825 codec in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ (MFD_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_NAU8825 @@ -560,7 +559,7 @@ if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK) config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH tristate "CML_LP with DA7219 and MAX98357A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON help @@ -571,7 +570,7 @@ config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH tristate "CML with RT1011 and RT5682 in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_RT1011 @@ -591,7 +590,7 @@ if SND_SOC_SOF_JASPERLAKE config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH tristate "SOF with DA7219 and MAX98373/MAX98360A in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_INTEL_HDA_DSP_COMMON @@ -608,7 +607,7 @@ endif ## SND_SOC_SOF_JASPERLAKE config SND_SOC_INTEL_SOF_SSP_AMP_MACH tristate "SOF with amplifiers in I2S Mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_RT1308 select SND_SOC_CS35L41_I2C @@ -627,7 +626,7 @@ if SND_SOC_SOF_ELKHARTLAKE config SND_SOC_INTEL_EHL_RT5660_MACH tristate "EHL with RT5660 in I2S mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_RT5660 @@ -643,7 +642,7 @@ if SND_SOC_SOF_INTEL_SOUNDWIRE config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH tristate "SoundWire generic machine driver" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST depends on SOUNDWIRE -- cgit v1.2.3 From bdfc385948bf8e73629b3580941c9d810711713b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 4 Mar 2022 14:45:31 -0600 Subject: ASoC: Intel: boards: add GPIOLIB dependency where missed We have eleven machine drivers who make explicit references to gpios. Let's add the dependency. The use of 'depends on' instead of 'select' is intentional. On one side it could be argued that the GPIOs are required, but on the other it might create more issues with randconfig builds. This patch sticks with the existing direction of using 'depends' on high-level non-audio dependencies Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220304204532.54675-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b0baf3fce05b..6884ddf9edad 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -103,6 +103,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "Baytrail and Baytrail-CR with RT5640 codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_RT5640 help @@ -115,6 +116,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH tristate "Baytrail and Baytrail-CR with RT5651 codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_RT5651 help @@ -127,6 +129,7 @@ config SND_SOC_INTEL_BYTCR_WM5102_MACH tristate "Baytrail and Baytrail-CR with WM5102 codec" depends on MFD_ARIZONA && MFD_WM5102 && SPI_MASTER && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_WM5102 help @@ -139,6 +142,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH tristate "Cherrytrail & Braswell with RT5672 codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_RT5670 help @@ -163,6 +167,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH tristate "Cherrytrail & Braswell with MAX98090 & TI codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_MAX98090 select SND_SOC_TS3A227E help @@ -187,6 +192,7 @@ config SND_SOC_INTEL_BYT_CHT_CX2072X_MACH tristate "Baytrail & Cherrytrail with CX2072X codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_CX2072X help @@ -211,6 +217,7 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH tristate "Baytrail & Cherrytrail with ES8316 codec" depends on I2C && ACPI depends on X86_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_ES8316 help @@ -332,6 +339,7 @@ config SND_SOC_INTEL_SOF_WM8804_MACH tristate "SOF with Wolfson/Cirrus WM8804 codec" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_WM8804_I2C help This adds support for ASoC machine driver for Intel platforms @@ -404,6 +412,7 @@ config SND_SOC_INTEL_KBL_RT5660_MACH tristate "KBL with RT5660 in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST select SND_SOC_RT5660 select SND_SOC_HDAC_HDMI help @@ -522,8 +531,9 @@ config SND_SOC_INTEL_SOF_PCM512x_MACH config SND_SOC_INTEL_SOF_ES8336_MACH tristate "SOF with ES8336 codec in I2S mode" - depends on I2C && ACPI && GPIOLIB + depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on GPIOLIB || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_ES8316 select SND_SOC_DMIC -- cgit v1.2.3 From ce73ef6ec67104d1fcc4c5911d77ce83288a0998 Mon Sep 17 00:00:00 2001 From: Anthony I Gilea Date: Fri, 4 Mar 2022 14:45:32 -0600 Subject: ASoC: Intel: sof_sdw: fix quirks for 2022 HP Spectre x360 13" HP changed the DMI identification for 2022 devices: Product Name: HP Spectre x360 Conv 13-ap0001na Product Name: 8709 This patch relaxes the DMI_MATCH criterion to work with all versions of this product. Reviewed-by: Rander Wang Signed-off-by: Anthony I Gilea Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220304204532.54675-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/dmi-quirks.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 0ca2a3e3a02e..747983743a14 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -59,7 +59,7 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"), }, .driver_data = (void *)intel_tgl_bios, }, diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index da515eb1ddbe..1f00679b4240 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -185,7 +185,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"), }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | -- cgit v1.2.3 From 899a9a7f624b5a9d100c9ac6b3f0960981f0e4c5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Mar 2022 16:14:49 +0300 Subject: ASoC: amd: acp: Fix signedness bug in renoir_audio_probe() The "adata->i2s_irq" is unsigned so this error handling will not work. Fixes: 3304a242f45a ("ASoC: amd: Use platform_get_irq_byname() to get the interrupt") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220304131449.GC28739@kili Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-renoir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 738cf2e2b973..75c9229ece97 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -282,9 +282,10 @@ static int renoir_audio_probe(struct platform_device *pdev) if (!adata->acp_base) return -ENOMEM; - adata->i2s_irq = platform_get_irq_byname(pdev, "acp_dai_irq"); - if (adata->i2s_irq < 0) - return -ENODEV; + ret = platform_get_irq_byname(pdev, "acp_dai_irq"); + if (ret < 0) + return ret; + adata->i2s_irq = ret; adata->dev = dev; adata->dai_driver = acp_renoir_dai; -- cgit v1.2.3 From 00925272f166db31fed73f3c00c151eb5f7ce1d8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Mar 2022 16:13:35 +0300 Subject: ASoC: amd: pcm-dma: Fix signedness bug in acp_pdm_audio_probe() The "adata->pdm_irq" variable is unsigned so the error handling will not work. Fixes: 87d71a128771 ("ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220304131335.GB28739@kili Signed-off-by: Mark Brown --- sound/soc/amd/renoir/acp3x-pdm-dma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index 88a242538461..8c42345ee41e 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -399,9 +399,10 @@ static int acp_pdm_audio_probe(struct platform_device *pdev) if (!adata->acp_base) return -ENOMEM; - adata->pdm_irq = platform_get_irq(pdev, 0); - if (adata->pdm_irq < 0) - return -ENODEV; + status = platform_get_irq(pdev, 0); + if (status < 0) + return status; + adata->pdm_irq = status; adata->capture_stream = NULL; -- cgit v1.2.3 From 9a33f5632ca573e512c49fa46cc7131cbc83d4c9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Mar 2022 16:15:34 +0300 Subject: ASoC: amd: pcm-dma: Fix signedness bug in acp3x_audio_probe() The "adata->i2s_irq" variable is unsigned so this error handling code will not work. Fixes: 87d71a128771 ("ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220304131534.GD28739@kili Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index e4f8dbf0d11d..6aec11cf0a6a 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -394,9 +394,10 @@ static int acp3x_audio_probe(struct platform_device *pdev) if (!adata->acp3x_base) return -ENOMEM; - adata->i2s_irq = platform_get_irq(pdev, 0); - if (adata->i2s_irq < 0) - return -ENODEV; + status = platform_get_irq(pdev, 0); + if (status < 0) + return status; + adata->i2s_irq = status; dev_set_drvdata(&pdev->dev, adata); status = devm_snd_soc_register_component(&pdev->dev, -- cgit v1.2.3 From f590797fa3c1bccdd19e55441592a23b46aef449 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 7 Mar 2022 12:45:39 +0000 Subject: ASoC: atmel: Add missing of_node_put() in at91sam9g20ek_audio_probe This node pointer is returned by of_parse_phandle() with refcount incremented in this function. Calling of_node_put() to avoid the refcount leak. Fixes: 531f67e41dcd ("ASoC: at91sam9g20ek-wm8731: convert to dt support") Signed-off-by: Miaoqian Lin Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220307124539.1743-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/atmel/sam9g20_wm8731.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 915da92e1ec8..33e43013ff77 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -214,6 +214,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); if (!cpu_np) { dev_err(&pdev->dev, "dai and pcm info missing\n"); + of_node_put(codec_np); return -EINVAL; } at91sam9g20ek_dai.cpus->of_node = cpu_np; -- cgit v1.2.3 From db0350da8084ad549bca16cc0486c11cc70a1f9b Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Fri, 4 Mar 2022 10:38:21 +0800 Subject: ASoC: wm8350: Handle error for wm8350_register_irq As the potential failure of the wm8350_register_irq(), it should be better to check it and return error if fails. Also, use 'free_' in order to avoid the same code. Fixes: a6ba2b2dabb5 ("ASoC: Implement WM8350 headphone jack detection") Signed-off-by: Jiasheng Jiang Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220304023821.391936-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/wm8350.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 15d42ce3b21d..41504ce2a682 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1537,18 +1537,38 @@ static int wm8350_component_probe(struct snd_soc_component *component) wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); - wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, wm8350_hpl_jack_handler, 0, "Left jack detect", priv); - wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, + if (ret != 0) + goto err; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, wm8350_hpr_jack_handler, 0, "Right jack detect", priv); - wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, + if (ret != 0) + goto free_jck_det_l; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, wm8350_mic_handler, 0, "Microphone short", priv); - wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, + if (ret != 0) + goto free_jck_det_r; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, wm8350_mic_handler, 0, "Microphone detect", priv); + if (ret != 0) + goto free_micscd; return 0; + +free_micscd: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv); +free_jck_det_r: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv); +free_jck_det_l: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv); +err: + return ret; } static void wm8350_component_remove(struct snd_soc_component *component) -- cgit v1.2.3 From b41d6195b2f0c5d5df009aa518958f3c46e66d9a Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Mon, 7 Mar 2022 18:21:54 +0800 Subject: ASoC: rt5682s: Stabilize the combo jack detection Changes: 1. Revise rt5682s_sar_power_mode and rt5682s_headset_detect to be more rational. 2. Manually set to the jack-unplugging state via rt5682s_headset_detect during going to suspend. Close unnecessary powers and prepare for re-detecting the CBJ during resuming. 3. Simplize rt5682s_resume. Signed-off-by: Derek Fang Link: https://lore.kernel.org/r/20220307102154.26065-1-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682s.c | 47 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index efa1016831dd..8f3d59c75aa0 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -644,8 +644,7 @@ enum { SAR_PWR_SAVING, }; -static void rt5682s_sar_power_mode(struct snd_soc_component *component, - int mode, int jd_step) +static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode) { struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); @@ -675,16 +674,17 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK, RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM); - if (!jd_step) { - snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, - RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO); - usleep_range(5000, 5500); - snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, - RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK, - RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM); - } + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO); + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK, + RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM); break; case SAR_PWR_OFF: + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK, + RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM); snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS | @@ -702,6 +702,10 @@ static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_BTN); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_EN | + RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_AUTO); snd_soc_component_write(component, RT5682S_IL_CMD_1, 0x0040); snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2, RT5682S_4BTN_IL_MASK | RT5682S_4BTN_IL_RST_MASK, @@ -718,6 +722,10 @@ static void rt5682s_disable_push_button_irq(struct snd_soc_component *component) RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS); snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS | + RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU); } /** @@ -753,7 +761,8 @@ static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_ RT5682S_OSW_L_DIS | RT5682S_OSW_R_DIS); snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE); - rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 1); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3, + RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN); snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW); usleep_range(45000, 50000); @@ -779,9 +788,8 @@ static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_ RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_EN); snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, RT5682S_SAR_SEL_MB1_2_MASK, val << RT5682S_SAR_SEL_MB1_2_SFT); - if (!snd_soc_dapm_get_pin_status(&component->dapm, "SAR")) - rt5682s_sar_power_mode(component, SAR_PWR_SAVING, 1); rt5682s_enable_push_button_irq(component); + rt5682s_sar_power_mode(component, SAR_PWR_SAVING); break; default: jack_type = SND_JACK_HEADPHONE; @@ -792,7 +800,7 @@ static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_ RT5682S_OSW_L_EN | RT5682S_OSW_R_EN); usleep_range(35000, 40000); } else { - rt5682s_sar_power_mode(component, SAR_PWR_OFF, 1); + rt5682s_sar_power_mode(component, SAR_PWR_OFF); rt5682s_disable_push_button_irq(component); snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW); @@ -1403,10 +1411,10 @@ static int sar_power_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 0); + rt5682s_sar_power_mode(component, SAR_PWR_NORMAL); break; case SND_SOC_DAPM_POST_PMD: - rt5682s_sar_power_mode(component, SAR_PWR_SAVING, 0); + rt5682s_sar_power_mode(component, SAR_PWR_SAVING); break; } @@ -2835,9 +2843,8 @@ static int rt5682s_suspend(struct snd_soc_component *component) cancel_delayed_work_sync(&rt5682s->jack_detect_work); cancel_delayed_work_sync(&rt5682s->jd_check_work); - if (rt5682s->hs_jack && rt5682s->jack_type == SND_JACK_HEADSET) - snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2, - RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS); + if (rt5682s->hs_jack) + rt5682s->jack_type = rt5682s_headset_detect(component, 0); regcache_cache_only(rt5682s->regmap, true); regcache_mark_dirty(rt5682s->regmap); @@ -2853,8 +2860,6 @@ static int rt5682s_resume(struct snd_soc_component *component) regcache_sync(rt5682s->regmap); if (rt5682s->hs_jack) { - rt5682s->jack_type = 0; - rt5682s_sar_power_mode(component, SAR_PWR_NORMAL, 0); mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work, msecs_to_jiffies(0)); } -- cgit v1.2.3 From dacf1497a8ea388cca67081dc25780c50f77fa42 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 4 Mar 2022 15:07:03 +0000 Subject: ASoC: cs35l41: Fix max number of TX channels This device only has 4 TX channels. Fixes: fe1024d50477b ("ASoC: cs35l41: Combine adjacent register writes") Signed-off-by: Lucas Tanure Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220304150721.3802-3-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 77a017694645..c90722b657c0 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1091,7 +1091,7 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { .capture = { .stream_name = "AMP Capture", .channels_min = 1, - .channels_max = 8, + .channels_max = 4, .rates = SNDRV_PCM_RATE_KNOT, .formats = CS35L41_TX_FORMATS, }, -- cgit v1.2.3 From 139cad4bde675552466da5af61b4686da9fc8008 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 4 Mar 2022 15:07:05 +0000 Subject: ASoC: cs35l41: Remove unnecessary param cs35l41_private is not used on cs35l41_handle_pdata Signed-off-by: Lucas Tanure Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220304150721.3802-5-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index c90722b657c0..e10d1276a937 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1115,9 +1115,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { .set_sysclk = cs35l41_component_set_sysclk, }; -static int cs35l41_handle_pdata(struct device *dev, - struct cs35l41_platform_data *pdata, - struct cs35l41_private *cs35l41) +static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_platform_data *pdata) { struct cs35l41_irq_cfg *irq_gpio1_config = &pdata->irq_config1; struct cs35l41_irq_cfg *irq_gpio2_config = &pdata->irq_config2; @@ -1260,7 +1258,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, if (pdata) { cs35l41->pdata = *pdata; } else { - ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->pdata, cs35l41); + ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->pdata); if (ret != 0) return ret; } -- cgit v1.2.3 From 6ed5dbba6c971fe644f5c2b4aae436b39da99f18 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 4 Mar 2022 16:09:34 +0000 Subject: ASoC: qcom: select correct WCD938X config for SC7280 SC7280 config selected WCD938X instead of WCD938X_SDW Soundwire codecs. WCD938X_SDW actually selects WCD938X, so directly selecting WCD938X results in unmet dependencies and below warning WARNING: unmet direct dependencies detected for SND_SOC_WCD938X Depends on [n]: SOUND [=y] && !UML && SND [=y] && SND_SOC [=m] && SND_SOC_WCD938X_SDW [=n] && (SOUNDWIRE [=n] || !SOUNDWIRE [=n]) Selected by [m]: - SND_SOC_SC7280 [=m] && SOUND [=y] && !UML && SND [=y] && SND_SOC [=m] && SND_SOC_QCOM [=m] && (I2C [=y] && SOUNDWIRE [=n] || COMPILE_TEST [=y]) Fix this issue by selecting WCD SoundWire codecs instead of component driver. Fixes: 57350bd41c3a ("ASoC: qcom: SC7280: Add machine driver") Reported-by: Randy Dunlap Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220304160934.32010-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 0cd0dae5c545..82f5eafb2f6c 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -193,7 +193,7 @@ config SND_SOC_SC7280 select SND_SOC_QCOM_COMMON select SND_SOC_LPASS_SC7280 select SND_SOC_MAX98357A - select SND_SOC_WCD938X + select SND_SOC_WCD938X_SDW select SND_SOC_LPASS_MACRO_COMMON select SND_SOC_LPASS_RX_MACRO select SND_SOC_LPASS_TX_MACRO -- cgit v1.2.3 From 405afed8a728f23cfaa02f75bbc8bdd6b7322123 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 2 Mar 2022 14:28:44 +0800 Subject: ASoC: fsi: Add check for clk_enable As the potential failure of the clk_enable(), it should be better to check it and return error if fails. Fixes: ab6f6d85210c ("ASoC: fsi: add master clock control functions") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220302062844.46869-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/sh/fsi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index cdf3b7f69ba7..e9a1eb6bdf66 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -816,14 +816,27 @@ static int fsi_clk_enable(struct device *dev, return ret; } - clk_enable(clock->xck); - clk_enable(clock->ick); - clk_enable(clock->div); + ret = clk_enable(clock->xck); + if (ret) + goto err; + ret = clk_enable(clock->ick); + if (ret) + goto disable_xck; + ret = clk_enable(clock->div); + if (ret) + goto disable_ick; clock->count++; } return ret; + +disable_ick: + clk_disable(clock->ick); +disable_xck: + clk_disable(clock->xck); +err: + return ret; } static int fsi_clk_disable(struct device *dev, -- cgit v1.2.3 From b6b62d942bbc4d926bcf3799ea3bcaeb105fd04f Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Thu, 3 Mar 2022 15:50:16 +0000 Subject: ASoC: wm_adsp: Expand firmware loading search options The parts supported by this driver can have product-specific firmware and tunings files. Typically these have been used on embedded systems where the manufacturer is responsible for installing the correct product-specific firmware files into /lib/firmware. However, the linux-firmware repository places all available firmwares into /lib/firmware and it is up to the driver to select the correct product-specific firmware from that directory. For example a product containing four smart amplifiers may provide firmware specific for that product and each of the amplifiers may have coefficient files containing tunings for their placement in the mechanical design. This change extends firmware (wmfw) and coefficient (bin) filenames to be of the general form: part-dspN-fwtype<-system_name<-asoc_component_prefix>>.type Where the cirrus subdirectory, system_name and asoc_component_prefix are optional. New files will be placed in the cirrus subdirectory to avoid polluting the main /lib/firmware/ location. The generic name must be searched in /lib/firmware before /lib/firmware/cirrus so that a generic file in the new location does not override existing product-specific files in the legacy location. The search order for firmware files is: - cirrus/part-dspN-fwtype-system_name-asoc_component_prefix.wmfw - cirrus/part-dspN-fwtype-system_name.wmfw - part-dspN-fwtype.wmfw - cirrus/part-dspN-fwtype.wmfw - Qualifications are added to the filename so that rightwards is more specific. - The system_name is provided by the codec driver. - The asoc_component_prefix is used to identify tunings for individual parts because it would already exist to disambiguate the controls and it makes it obvious which firmware file applies to which device. The optional coefficient file must have the same filename construction as the discovered wmfw except: - where the wmfw has only system_name then the bin file can optionally include the asoc_component_prefix. This is to allow a common wmfw for all amps but separate tunings per amp. Signed-off-by: Simon Trimmer Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20220303155016.122125-1-simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 99 ++++++++++++++++++++++++++++++++++++++++------ sound/soc/codecs/wm_adsp.h | 1 + 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index c8c8338cb401..375cb14aaccd 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -749,21 +749,48 @@ static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, } static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, - const struct firmware **firmware, - char **filename, - char *suffix) + const struct firmware **firmware, char **filename, + const char *dir, const char *system_name, + const char *asoc_component_prefix, + const char *filetype) { struct cs_dsp *cs_dsp = &dsp->cs_dsp; + char *s, c; int ret = 0; - *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", dsp->part, dsp->fwf_name, - wm_adsp_fw[dsp->fw].file, suffix); + if (system_name && asoc_component_prefix) + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part, + dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name, + asoc_component_prefix, filetype); + else if (system_name) + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, + dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name, + filetype); + else + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, dsp->fwf_name, + wm_adsp_fw[dsp->fw].file, filetype); + if (*filename == NULL) return -ENOMEM; - ret = request_firmware(firmware, *filename, cs_dsp->dev); + /* + * Make sure that filename is lower-case and any non alpha-numeric + * characters except full stop and forward slash are replaced with + * hyphens. + */ + s = *filename; + while (*s) { + c = *s; + if (isalnum(c)) + *s = tolower(c); + else if ((c != '.') && (c != '/')) + *s = '-'; + s++; + } + + ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev); if (ret != 0) { - adsp_err(dsp, "Failed to request '%s'\n", *filename); + adsp_dbg(dsp, "Failed to request '%s'\n", *filename); kfree(*filename); *filename = NULL; } @@ -771,21 +798,69 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, return ret; } +static const char *cirrus_dir = "cirrus/"; static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, const struct firmware **wmfw_firmware, char **wmfw_filename, const struct firmware **coeff_firmware, char **coeff_filename) { + const char *system_name = dsp->system_name; + const char *asoc_component_prefix = dsp->component->name_prefix; int ret = 0; - ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "wmfw"); - if (ret != 0) - return ret; + if (system_name && asoc_component_prefix) { + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, + asoc_component_prefix, "wmfw")) { + adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + asoc_component_prefix, "bin"); + return 0; + } + } - wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, "bin"); + if (system_name) { + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, + NULL, "wmfw")) { + adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); + 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"); + return 0; + } + } - return 0; + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + "", NULL, NULL, "wmfw")) { + adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + "", NULL, NULL, "bin"); + return 0; + } + + ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, NULL, NULL, "wmfw"); + if (!ret) { + adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); + 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, wm_adsp_fw[dsp->fw].file, + system_name, asoc_component_prefix); + + return -ENOENT; } static int wm_adsp_common_init(struct wm_adsp *dsp) diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 7f4fabbc6ad3..375009a65828 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -28,6 +28,7 @@ struct wm_adsp { struct cs_dsp cs_dsp; const char *part; const char *fwf_name; + const char *system_name; struct snd_soc_component *component; unsigned int sys_config_size; -- cgit v1.2.3 From 71a6254c8b8aa3dcac3a5cb1d1cc2a2d3a840bfb Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 4 Mar 2022 14:40:15 +0000 Subject: ASoC: cs42l42: Add warnings about DETECT_MODE and PLL_START DETECT_MODE and PLL_START must be zero while HP_PDN and ADC_PDN are both 1. If this condition is broken it can discharge FILT+ and it can then take up to 1 second for FILT+ to recharge. There is no workaround required for this, simply avoiding settings and sequences that would break the requirement. The driver already meets the requirement. But it is not obvious from reading the code that this requirement exists, or what is ensuring it is met. So it would not currently be obvious to someone changing the code that there is certain special behaviour that must be maintained. To avoid accidental breakage in the future: - Add comments into the register definitions to warn about this so that anyone changing the code around DETECT_MODE and PLL_START is aware of this requirement. - Add a comment where PLL_START is written to 1 to highlight the requirement and why it is satisfied. - Add a comment in cs42l42_setup_hs_type_detect() when DETECT_MODE is initialized. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20220304144015.398656-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l42.c | 13 ++++++++++++- sound/soc/codecs/cs42l42.h | 9 ++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index db6ef6cdce15..c8409d50e934 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1012,7 +1012,14 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) } } else { if (!cs42l42->stream_use) { - /* SCLK must be running before codec unmute */ + /* SCLK must be running before codec unmute. + * + * PLL must not be started with ADC and HP both off + * otherwise the FILT+ supply will not charge properly. + * DAPM widgets power-up before stream unmute so at least + * one of the "DAC" or "ADC" widgets will already have + * powered-up. + */ if (pll_ratio_table[cs42l42->pll_config].mclk_src_sel) { snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 1); @@ -1830,6 +1837,10 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) cs42l42->hs_type = CS42L42_PLUG_INVALID; + /* + * DETECT_MODE must always be 0 with ADC and HP both off otherwise the + * FILT+ supply will not charge properly. + */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, CS42L42_DETECT_MODE_MASK, 0); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 244b24d1f5e9..60d3bdf5d7c9 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -491,7 +491,10 @@ #define CS42L42_TS_UNPLUG 0 #define CS42L42_TS_TRANS 1 -/* Page 0x15 Fractional-N PLL Registers */ +/* + * NOTE: PLL_START must be 0 while both ADC_PDN=1 and HP_PDN=1. + * Otherwise it will prevent FILT+ from charging properly. + */ #define CS42L42_PLL_CTL1 (CS42L42_PAGE_15 + 0x01) #define CS42L42_PLL_START_SHIFT 0 #define CS42L42_PLL_START_MASK (1 << CS42L42_PLL_START_SHIFT) @@ -574,6 +577,10 @@ #define CS42L42_TIP_SENSE_CTRL_MASK (3 << \ CS42L42_TIP_SENSE_CTRL_SHIFT) +/* + * NOTE: DETECT_MODE must be 0 while both ADC_PDN=1 and HP_PDN=1. + * Otherwise it will prevent FILT+ from charging properly. + */ #define CS42L42_MISC_DET_CTL (CS42L42_PAGE_1B + 0x74) #define CS42L42_PDN_MIC_LVL_DET_SHIFT 0 #define CS42L42_PDN_MIC_LVL_DET_MASK (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT) -- cgit v1.2.3 From 468f252930d8fda0e4365b788b4b621fe59c05ce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Mar 2022 16:12:56 +0300 Subject: ASoC: amd: vg: fix signedness bug in acp5x_audio_probe() The "adata->i2s_irq" variable is unsigned so the error handling will not work. Fixes: 87d71a128771 ("ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220304131256.GA28739@kili Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-pcm-dma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index e4e668593b3d..31fa166df98a 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -388,9 +388,10 @@ static int acp5x_audio_probe(struct platform_device *pdev) if (!adata->acp5x_base) return -ENOMEM; - adata->i2s_irq = platform_get_irq(pdev, 0); - if (adata->i2s_irq < 0) - return -ENODEV; + status = platform_get_irq(pdev, 0); + if (status < 0) + return status; + adata->i2s_irq = status; dev_set_drvdata(&pdev->dev, adata); status = devm_snd_soc_register_component(&pdev->dev, -- cgit v1.2.3 From 9cb727506704b5323998047789fc871e64a6aa14 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Mon, 7 Mar 2022 12:32:29 -0700 Subject: ALSA: hda/realtek: Add quirk for Clevo NP50PNJ Fixes headset detection on Clevo NP50PNJ. Signed-off-by: Tim Crawford Cc: Link: https://lore.kernel.org/r/20220307193229.5141-1-tcrawford@system76.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bd926763fa47..4ddd464470fd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9103,6 +9103,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), -- cgit v1.2.3 From 441d1e10476bc9d966baf32745277a6e77c325d5 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 7 Mar 2022 01:51:04 +1030 Subject: ALSA: scarlett2: Split scarlett2_config_items[] into 3 sections scarlett2_config_items[] contains the parameters for the configuration items. The driver previously had two sets of configurations items; one for devices with no mixer, and one for devices with a mixer. This patch splits the latter into two (one set for Gen 2 devices and one set for Gen 3 devices) in preparation for a new item (standalone) which is present in both but with a different offset. Signed-off-by: Geoffrey D. Bennett Link: https://lore.kernel.org/r/20969f9ea500684e978c87067fbdc7e73de1f6ed.1646578164.git.g@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 74 ++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 7ff8a4817c67..4b1458e993e7 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -6,7 +6,7 @@ * - 6i6/18i8/18i20 Gen 2 * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 * - * Copyright (c) 2018-2021 by Geoffrey D. Bennett + * Copyright (c) 2018-2022 by Geoffrey D. Bennett * Copyright (c) 2020-2021 by Vladimir Sadovnikov * * Based on the Scarlett (Gen 1) Driver for ALSA: @@ -195,6 +195,16 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of meters (sum of output port counts) */ #define SCARLETT2_MAX_METERS 65 +/* There are three different sets of configuration parameters across + * the devices + */ +enum { + SCARLETT2_CONFIG_SET_NO_MIXER = 0, + SCARLETT2_CONFIG_SET_GEN_2 = 1, + SCARLETT2_CONFIG_SET_GEN_3 = 2, + SCARLETT2_CONFIG_SET_COUNT = 3 +}; + /* Hardware port types: * - None (no input to mux) * - Analogue I/O @@ -308,10 +318,8 @@ struct scarlett2_device_info { */ u8 has_msd_mode; - /* Gen 3 devices without a mixer have a different - * configuration set - */ - u8 has_mixer; + /* which set of configuration parameters the device uses */ + u8 config_set; /* line out hw volume is sw controlled */ u8 line_out_hw_vol; @@ -426,7 +434,7 @@ struct scarlett2_data { static const struct scarlett2_device_info s6i6_gen2_info = { .usb_id = USB_ID(0x1235, 0x8203), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .level_input_count = 2, .pad_input_count = 2, @@ -472,7 +480,7 @@ static const struct scarlett2_device_info s6i6_gen2_info = { static const struct scarlett2_device_info s18i8_gen2_info = { .usb_id = USB_ID(0x1235, 0x8204), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .level_input_count = 2, .pad_input_count = 4, @@ -521,7 +529,7 @@ static const struct scarlett2_device_info s18i8_gen2_info = { static const struct scarlett2_device_info s18i20_gen2_info = { .usb_id = USB_ID(0x1235, 0x8201), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .line_out_hw_vol = 1, .line_out_descrs = { @@ -576,6 +584,7 @@ static const struct scarlett2_device_info solo_gen3_info = { .usb_id = USB_ID(0x1235, 0x8211), .has_msd_mode = 1, + .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, @@ -588,6 +597,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = { .usb_id = USB_ID(0x1235, 0x8210), .has_msd_mode = 1, + .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, .level_input_count = 2, .air_input_count = 2, .phantom_count = 1, @@ -599,7 +609,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = { .usb_id = USB_ID(0x1235, 0x8212), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -645,7 +655,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = { .usb_id = USB_ID(0x1235, 0x8213), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -698,7 +708,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .usb_id = USB_ID(0x1235, 0x8214), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .line_out_hw_vol = 1, .has_speaker_switching = 1, .level_input_count = 2, @@ -768,7 +778,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .usb_id = USB_ID(0x1235, 0x8215), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .line_out_hw_vol = 1, .has_speaker_switching = 1, .has_talkback = 1, @@ -944,13 +954,11 @@ struct scarlett2_config { u8 activate; }; -/* scarlett2_config_items[0] is for devices without a mixer - * scarlett2_config_items[1] is for devices with a mixer - */ static const struct scarlett2_config - scarlett2_config_items[2][SCARLETT2_CONFIG_COUNT] = + scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT] + [SCARLETT2_CONFIG_COUNT] = -/* Devices without a mixer (Solo and 2i2 Gen 3) */ +/* Devices without a mixer (Gen 3 Solo and 2i2) */ { { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x04, .size = 8, .activate = 6 }, @@ -970,7 +978,27 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x09, .size = 1, .activate = 8 }, -/* Devices with a mixer (Gen 2 and all other Gen 3) */ +/* Gen 2 devices: 6i6, 18i8, 18i20 */ +}, { + [SCARLETT2_CONFIG_DIM_MUTE] = { + .offset = 0x31, .size = 8, .activate = 2 }, + + [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { + .offset = 0x34, .size = 16, .activate = 1 }, + + [SCARLETT2_CONFIG_MUTE_SWITCH] = { + .offset = 0x5c, .size = 8, .activate = 1 }, + + [SCARLETT2_CONFIG_SW_HW_SWITCH] = { + .offset = 0x66, .size = 8, .activate = 3 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x7c, .size = 8, .activate = 7 }, + + [SCARLETT2_CONFIG_PAD_SWITCH] = { + .offset = 0x84, .size = 8, .activate = 8 }, + +/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ }, { [SCARLETT2_CONFIG_DIM_MUTE] = { .offset = 0x31, .size = 8, .activate = 2 }, @@ -1175,7 +1203,7 @@ static int scarlett2_usb_get_config( struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[info->has_mixer][config_item_num]; + &scarlett2_config_items[info->config_set][config_item_num]; int size, err, i; u8 *buf_8; u8 value; @@ -1235,7 +1263,7 @@ static int scarlett2_usb_set_config( struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[info->has_mixer][config_item_num]; + &scarlett2_config_items[info->config_set][config_item_num]; struct { __le32 offset; __le32 bytes; @@ -1692,7 +1720,7 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; /* devices without a mixer also don't support reporting sync status */ - if (!private->info->has_mixer) + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, @@ -3399,7 +3427,7 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; /* devices without a mixer also don't support reporting levels */ - if (!private->info->has_mixer) + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, @@ -3632,7 +3660,7 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; /* the rest of the configuration is for devices with a mixer */ - if (!info->has_mixer) + if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; err = scarlett2_update_sync(mixer); -- cgit v1.2.3 From 604b388419d08032c07aff8946ee19c073b59189 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 7 Mar 2022 01:51:39 +1030 Subject: ALSA: scarlett2: Add support for the internal "standalone" switch The Focusrite Scarlett Gen 2/3 interfaces with internal mixers have a "standalone" mode. When the interface is not connected to a USB host and standalone mode is enabled, the interface will pass audio as previously configured. This patch adds an ALSA control to allow enabling/disabling that mode. Signed-off-by: Geoffrey D. Bennett Link: https://lore.kernel.org/r/cd88871c5e77abd5c23a4758a1f2ec9fd427fd69.1646578164.git.g@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 97 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 4b1458e993e7..69a2cd429ee2 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -60,6 +60,7 @@ * - phantom power, direct monitor, speaker switching, and talkback * controls * - disable/enable MSD mode + * - disable/enable standalone mode * * * /--------------\ 18chn 20chn /--------------\ @@ -411,6 +412,7 @@ struct scarlett2_data { u8 talkback_switch; u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; u8 msd_switch; + u8 standalone_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; @@ -936,13 +938,14 @@ enum { SCARLETT2_CONFIG_PAD_SWITCH = 5, SCARLETT2_CONFIG_MSD_SWITCH = 6, SCARLETT2_CONFIG_AIR_SWITCH = 7, - SCARLETT2_CONFIG_PHANTOM_SWITCH = 8, - SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9, - SCARLETT2_CONFIG_DIRECT_MONITOR = 10, - SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 11, - SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 12, - SCARLETT2_CONFIG_TALKBACK_MAP = 13, - SCARLETT2_CONFIG_COUNT = 14 + SCARLETT2_CONFIG_STANDALONE_SWITCH = 8, + SCARLETT2_CONFIG_PHANTOM_SWITCH = 9, + SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10, + SCARLETT2_CONFIG_DIRECT_MONITOR = 11, + SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12, + SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13, + SCARLETT2_CONFIG_TALKBACK_MAP = 14, + SCARLETT2_CONFIG_COUNT = 15 }; /* Location, size, and activation command number for the configuration @@ -998,6 +1001,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_PAD_SWITCH] = { .offset = 0x84, .size = 8, .activate = 8 }, + [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { + .offset = 0x8d, .size = 8, .activate = 6 }, + /* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ }, { [SCARLETT2_CONFIG_DIM_MUTE] = { @@ -1021,6 +1027,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x8c, .size = 8, .activate = 8 }, + [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { + .offset = 0x95, .size = 8, .activate = 6 }, + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { .offset = 0x9c, .size = 1, .activate = 8 }, @@ -3502,6 +3511,69 @@ static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer) 0, 1, "MSD Mode Switch", NULL); } +/*** Standalone Control ***/ + +static int scarlett2_standalone_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_data *private = elem->head.mixer->private_data; + + ucontrol->value.integer.value[0] = private->standalone_switch; + return 0; +} + +static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->standalone_switch; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->standalone_switch = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, + SCARLETT2_CONFIG_STANDALONE_SWITCH, + 0, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_standalone_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_standalone_ctl_get, + .put = scarlett2_standalone_ctl_put, +}; + +static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) + return 0; + + /* Add standalone control */ + return scarlett2_add_new_ctl(mixer, &scarlett2_standalone_ctl, + 0, 1, "Standalone Switch", NULL); +} + /*** Cleanup/Suspend Callbacks ***/ static void scarlett2_private_free(struct usb_mixer_interface *mixer) @@ -3663,6 +3735,12 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, + 1, &private->standalone_switch); + if (err < 0) + return err; + err = scarlett2_update_sync(mixer); if (err < 0) return err; @@ -3985,6 +4063,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err; + /* Create the standalone control */ + err = scarlett2_add_standalone_ctl(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0) -- cgit v1.2.3 From 5187357e45c3b3d4cab3b990a893369eb8ca6f70 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:10:54 -0800 Subject: ASoC: SOF: remove snd_sof_pipeline_find() It is not used. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 16 ---------------- sound/soc/sof/sof-audio.h | 2 -- 2 files changed, 18 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9e76b796502f..ac1edb4a082d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -624,22 +624,6 @@ int sof_set_hw_params_upon_resume(struct device *dev) return snd_sof_dsp_hw_params_upon_resume(sdev); } -const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, - int pipeline_id) -{ - const struct snd_sof_widget *swidget; - - list_for_each_entry(swidget, &sdev->widget_list, list) - if (swidget->id == snd_soc_dapm_scheduler) { - const struct sof_ipc_pipe_new *pipeline = - swidget->private; - if (pipeline->pipeline_id == pipeline_id) - return pipeline; - } - - return NULL; -} - int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { struct sof_ipc_fw_version *v = &sdev->fw_ready.version; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index f3009e6b91a1..107e0ef93e16 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -222,8 +222,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, int *direction); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, unsigned int pcm_id); -const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, - int pipeline_id); void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); void snd_sof_pcm_init_elapsed_work(struct work_struct *work); -- cgit v1.2.3 From 80df2226268f039326f9daebd25047cf15e22497 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:10:55 -0800 Subject: ASoC: SOF: simplify snd_sof_device_remove() The commit "ASoC: SOF: core: Unregister machine driver before IPC and debugfs" moved the call to unregister the machine driver to be done before freeing the IPC. With this change, we can safely move the call to snd_sof_remove() inside the same if() block just above. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2a35d8ddf43e..95a845d26f6e 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -442,16 +442,8 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); - } - - /* - * Unregistering the machine driver results in unloading the topology. - * Some widgets, ex: scheduler, attempt to power down the core they are - * scheduled on, when they are unloaded. Therefore, the DSP must be - * removed only after the topology has been unloaded. - */ - if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) snd_sof_remove(sdev); + } /* release firmware */ snd_sof_fw_unload(sdev); -- cgit v1.2.3 From 3a790f3a7c283d94325a85ac6ee4d1458e89b645 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:10:56 -0800 Subject: ASoC: SOF: set swidget's core for scheduler widget Set the swidget's core for scheduler type widgets to match that of the pipeline core. This simplifies the flow for core get/put during widget setup/free. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 23 +++++------------------ sound/soc/sof/topology.c | 1 + 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index ac1edb4a082d..4816473cfd7d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -103,7 +103,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) .id = swidget->comp_id, }; struct sof_ipc_reply reply; - int ret, ret1, core; + int ret, ret1; if (!swidget->private) return 0; @@ -112,14 +112,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; - core = swidget->core; - switch (swidget->id) { case snd_soc_dapm_scheduler: { - const struct sof_ipc_pipe_new *pipeline = swidget->private; - - core = pipeline->core; ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; break; } @@ -149,10 +144,10 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) * disable widget core. continue to route setup status and complete flag * even if this fails and return the appropriate error */ - ret1 = snd_sof_dsp_core_put(sdev, core); + ret1 = snd_sof_dsp_core_put(sdev, swidget->core); if (ret1 < 0) { dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", - core, swidget->widget->name); + swidget->core, swidget->widget->name); if (!ret) ret = ret1; } @@ -177,7 +172,6 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) struct snd_sof_dai *dai; size_t ipc_size; int ret; - int core; /* skip if there is no private data */ if (!swidget->private) @@ -187,15 +181,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (++swidget->use_count > 1) return 0; - /* set core ID */ - core = swidget->core; - if (swidget->id == snd_soc_dapm_scheduler) { - pipeline = swidget->private; - core = pipeline->core; - } - /* enable widget core */ - ret = snd_sof_dsp_core_get(sdev, core); + ret = snd_sof_dsp_core_get(sdev, swidget->core); if (ret < 0) { dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", swidget->widget->name); @@ -275,7 +262,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) return 0; core_put: - snd_sof_dsp_core_put(sdev, core); + snd_sof_dsp_core_put(sdev, swidget->core); use_count_dec: swidget->use_count--; return ret; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1d119d1dd69d..add0b3009588 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1707,6 +1707,7 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, swidget->dynamic_pipeline_widget); + swidget->core = pipeline->core; swidget->private = pipeline; return 0; -- cgit v1.2.3 From c99b70a2d21ab744bdee7a2ea7ec8a35caba9725 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 7 Mar 2022 10:10:57 -0800 Subject: ASoC: SOF: sof-audio: removed unused function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cppcheck warning: sound/soc/sof/sof-audio.c:884:0: style: The function 'snd_sof_find_spcm_pcm_id' is never used. [unusedFunction] Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220307181111.49392-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 14 -------------- sound/soc/sof/sof-audio.h | 2 -- 2 files changed, 16 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4816473cfd7d..e39f18e824b4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -866,20 +866,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, return NULL; } -struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, - unsigned int pcm_id) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_pcm *spcm; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) - return spcm; - } - - return NULL; -} - struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) { diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 107e0ef93e16..f2f32f2065d3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -220,8 +220,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, unsigned int comp_id, int *direction); -struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, - unsigned int pcm_id); void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); void snd_sof_pcm_init_elapsed_work(struct work_struct *work); -- cgit v1.2.3 From fb763299bd8e075f1e9fb0a1cd296b0400fe8442 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:10:58 -0800 Subject: ASoC: SOF: topology: remove redundant code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the change in the commit: "ASoC: SOF: Intel: hda: assign link DMA channel at run-time", there is no need to find the CPU DAI in sof_link_hda_load(). Fixes: bdf4ad3fd01f ('ASoC: SOF: Intel: hda: assign link DMA channel at run-time') Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index add0b3009588..15ec59596300 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3140,7 +3140,6 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &cfg->priv; - struct snd_soc_dai *dai; u32 size = sizeof(*config); int ret; @@ -3161,13 +3160,6 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index, dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", config->hda.rate, config->hda.channels); - dai = snd_soc_find_dai(link->cpus); - if (!dai) { - dev_err(scomp->dev, "error: failed to find dai %s in %s", - link->cpus->dai_name, __func__); - return -EINVAL; - } - config->hda.link_dma_ch = DMA_CHAN_INVALID; ret = sof_set_dai_config(sdev, size, link, config); -- cgit v1.2.3 From 9911ce06db9dd26d62cd131318588dc039696a99 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:10:59 -0800 Subject: ASoC: SOF: topology: remove redundant code in sof_link_afe_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like a copy-paste error. The CPU DAI is never used. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 15ec59596300..c31358aa8a75 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3014,7 +3014,6 @@ static int sof_link_afe_load(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &cfg->priv; - struct snd_soc_dai *dai; u32 size = sizeof(*config); int ret; @@ -3033,12 +3032,6 @@ static int sof_link_afe_load(struct snd_soc_component *scomp, int index, dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", config->afe.rate, config->afe.channels, config->afe.format); - dai = snd_soc_find_dai(link->cpus); - if (!dai) { - dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name); - return -EINVAL; - } - config->afe.stream_id = DMA_CHAN_INVALID; ret = sof_set_dai_config(sdev, size, link, config); -- cgit v1.2.3 From 6a6b5727f8eedff8932db6056c07a81eeeca9250 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:11:00 -0800 Subject: ASoC: SOF: topology: Drop the size parameter from struct sof_topology_token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is always 0 and never used while parsing. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 163 ++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 88 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c31358aa8a75..7280e14c13bc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -473,12 +473,11 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) struct sof_topology_token { u32 token; u32 type; - int (*get_token)(void *elem, void *object, u32 offset, u32 size); + int (*get_token)(void *elem, void *object, u32 offset); u32 offset; - u32 size; }; -static int get_token_u32(void *elem, void *object, u32 offset, u32 size) +static int get_token_u32(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); @@ -487,7 +486,7 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_u16(void *elem, void *object, u32 offset, u32 size) +static int get_token_u16(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; u16 *val = (u16 *)((u8 *)object + offset); @@ -496,7 +495,7 @@ static int get_token_u16(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_uuid(void *elem, void *object, u32 offset, u32 size) +static int get_token_uuid(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_uuid_elem *velem = elem; u8 *dst = (u8 *)object + offset; @@ -506,7 +505,7 @@ static int get_token_uuid(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) +static int get_token_comp_format(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); @@ -515,7 +514,7 @@ static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) +static int get_token_dai_type(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); @@ -524,8 +523,7 @@ static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_process_type(void *elem, void *object, u32 offset, - u32 size) +static int get_token_process_type(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); @@ -537,80 +535,80 @@ static int get_token_process_type(void *elem, void *object, u32 offset, /* Buffers */ static const struct sof_topology_token buffer_tokens[] = { {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_buffer, size), 0}, + 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), 0}, + offsetof(struct sof_ipc_buffer, caps)}, }; /* DAI */ static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_comp_dai, type), 0}, + offsetof(struct sof_ipc_comp_dai, type)}, {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dai_index), 0}, + offsetof(struct sof_ipc_comp_dai, dai_index)}, {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, direction), 0}, + offsetof(struct sof_ipc_comp_dai, direction)}, }; /* BE DAI link */ static const struct sof_topology_token dai_link_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_dai_config, type), 0}, + offsetof(struct sof_ipc_dai_config, type)}, {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_config, dai_index), 0}, + offsetof(struct sof_ipc_dai_config, dai_index)}, }; /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period), 0}, + offsetof(struct sof_ipc_pipe_new, period)}, {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, priority), 0}, + offsetof(struct sof_ipc_pipe_new, priority)}, {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period_mips), 0}, + offsetof(struct sof_ipc_pipe_new, period_mips)}, {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, core), 0}, + offsetof(struct sof_ipc_pipe_new, core)}, {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, + offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, time_domain), 0}, + offsetof(struct sof_ipc_pipe_new, time_domain)}, }; static const struct sof_topology_token pipeline_tokens[] = { {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, - offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0}, + offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, }; /* volume */ static const struct sof_topology_token volume_tokens[] = { {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp), 0}, + get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp)}, {SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_volume, initial_ramp), 0}, + offsetof(struct sof_ipc_comp_volume, initial_ramp)}, }; /* SRC */ static const struct sof_topology_token src_tokens[] = { {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, source_rate), 0}, + offsetof(struct sof_ipc_comp_src, source_rate)}, {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, sink_rate), 0}, + offsetof(struct sof_ipc_comp_src, sink_rate)}, }; /* ASRC */ static const struct sof_topology_token asrc_tokens[] = { {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, source_rate), 0}, + offsetof(struct sof_ipc_comp_asrc, source_rate)}, {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, sink_rate), 0}, + offsetof(struct sof_ipc_comp_asrc, sink_rate)}, {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, asynchronous_mode), 0}, + offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, operation_mode), 0}, + offsetof(struct sof_ipc_comp_asrc, operation_mode)}, }; /* Tone */ @@ -621,62 +619,62 @@ static const struct sof_topology_token tone_tokens[] = { static const struct sof_topology_token process_tokens[] = { {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type, - offsetof(struct sof_ipc_comp_process, type), 0}, + offsetof(struct sof_ipc_comp_process, type)}, }; /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_config), 0}, + offsetof(struct sof_ipc_comp_host, dmac_config)}, }; /* PCM */ static const struct sof_topology_token stream_tokens[] = { {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, - offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible), 0}, + offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)}, {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, - offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible), 0}, + offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)}, }; /* Generic components */ static const struct sof_topology_token comp_tokens[] = { {SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_sink), 0}, + offsetof(struct sof_ipc_comp_config, periods_sink)}, {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_source), 0}, + offsetof(struct sof_ipc_comp_config, periods_source)}, {SOF_TKN_COMP_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_comp_config, frame_fmt), 0}, + offsetof(struct sof_ipc_comp_config, frame_fmt)}, }; /* SSP */ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, clks_control), 0}, + offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width), 0}, + offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, quirks), 0}, + offsetof(struct sof_ipc_dai_ssp_params, quirks)}, {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct sof_ipc_dai_ssp_params, - tdm_per_slot_padding_flag), 0}, + tdm_per_slot_padding_flag)}, {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, bclk_delay), 0}, + offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, }; @@ -684,43 +682,42 @@ static const struct sof_topology_token ssp_tokens[] = { static const struct sof_topology_token alh_tokens[] = { {SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, rate), 0}, + offsetof(struct sof_ipc_dai_alh_params, rate)}, {SOF_TKN_INTEL_ALH_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, channels), 0}, + offsetof(struct sof_ipc_dai_alh_params, channels)}, }; /* DMIC */ static const struct sof_topology_token dmic_tokens[] = { {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version), - 0}, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, {SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min), 0}, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, {SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max), 0}, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, fifo_fs), 0}, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, {SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_min), 0}, + offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, {SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_max), 0}, + offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_dmic_params, - num_pdm_active), 0}, + num_pdm_active)}, {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, fifo_bits), 0}, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time), 0}, + offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, }; @@ -728,28 +725,28 @@ static const struct sof_topology_token dmic_tokens[] = { static const struct sof_topology_token esai_tokens[] = { {SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0}, + offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, }; /* SAI */ static const struct sof_topology_token sai_tokens[] = { {SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_sai_params, mclk_id), 0}, + offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, }; /* Core tokens */ static const struct sof_topology_token core_tokens[] = { {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp, core), 0}, + offsetof(struct sof_ipc_comp, core)}, }; /* Component extended tokens */ static const struct sof_topology_token comp_ext_tokens[] = { {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, - offsetof(struct sof_ipc_comp_ext, uuid), 0}, + offsetof(struct sof_ipc_comp_ext, uuid)}, }; /* @@ -761,63 +758,56 @@ static const struct sof_topology_token comp_ext_tokens[] = { static const struct sof_topology_token dmic_pdm_tokens[] = { {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id),}, {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew), - 0}, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, }; /* HDA */ static const struct sof_topology_token hda_tokens[] = { {SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, rate), 0}, + offsetof(struct sof_ipc_dai_hda_params, rate)}, {SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, channels), 0}, + offsetof(struct sof_ipc_dai_hda_params, channels)}, }; /* Leds */ static const struct sof_topology_token led_tokens[] = { {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_led_control, use_led), 0}, + offsetof(struct snd_sof_led_control, use_led)}, {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, + get_token_u32, offsetof(struct snd_sof_led_control, direction)}, }; /* AFE */ static const struct sof_topology_token afe_tokens[] = { {SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0}, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, {SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0}, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, {SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0}, + offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, }; static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, @@ -847,8 +837,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, /* matched - now load token */ tokens[j].get_token(elem, object, - offset + tokens[j].offset, - tokens[j].size); + offset + tokens[j].offset); found++; } @@ -884,8 +873,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp, /* matched - now load token */ tokens[j].get_token(elem, object, - offset + tokens[j].offset, - tokens[j].size); + offset + tokens[j].offset); found++; } @@ -924,8 +912,7 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp, /* load token */ tokens[j].get_token(elem, object, - offset + tokens[j].offset, - tokens[j].size); + offset + tokens[j].offset); found++; } -- cgit v1.2.3 From 5ef969e2f8e199d8881ea4cd78cb86df1c67d92b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:11:01 -0800 Subject: ASoC: SOF: topology: Modify the get_token op for string tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the get_token op for string type tokens to pass the string as the argument instead of a pointer to struct snd_soc_tplg_vendor_string_elem. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7280e14c13bc..4ba46ea73c8a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -507,28 +507,25 @@ static int get_token_uuid(void *elem, void *object, u32 offset) static int get_token_comp_format(void *elem, void *object, u32 offset) { - struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); - *val = find_format(velem->string); + *val = find_format((const char *)elem); return 0; } static int get_token_dai_type(void *elem, void *object, u32 offset) { - struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); - *val = find_dai(velem->string); + *val = find_dai((const char *)elem); return 0; } static int get_token_process_type(void *elem, void *object, u32 offset) { - struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); - *val = find_process(velem->string); + *val = find_process((const char *)elem); return 0; } @@ -872,8 +869,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp, continue; /* matched - now load token */ - tokens[j].get_token(elem, object, - offset + tokens[j].offset); + tokens[j].get_token(elem->string, object, offset + tokens[j].offset); found++; } -- cgit v1.2.3 From ea7e5ee67fb71d92b0eb0be8467fd34b0e2def6c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:11:02 -0800 Subject: ASoC: SOF: topology: expose some get_token ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These will be used later on by IPC-specific code. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-10-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 5 +++++ sound/soc/sof/topology.c | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index f2f32f2065d3..feda5793b589 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -260,4 +260,9 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev struct snd_sof_pcm *spcm); int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list); +int get_token_u32(void *elem, void *object, u32 offset); +int get_token_u16(void *elem, void *object, u32 offset); +int get_token_comp_format(void *elem, void *object, u32 offset); +int get_token_dai_type(void *elem, void *object, u32 offset); +int get_token_uuid(void *elem, void *object, u32 offset); #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4ba46ea73c8a..dd1cc6e26686 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -477,7 +477,7 @@ struct sof_topology_token { u32 offset; }; -static int get_token_u32(void *elem, void *object, u32 offset) +int get_token_u32(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); @@ -486,7 +486,7 @@ static int get_token_u32(void *elem, void *object, u32 offset) return 0; } -static int get_token_u16(void *elem, void *object, u32 offset) +int get_token_u16(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; u16 *val = (u16 *)((u8 *)object + offset); @@ -495,7 +495,7 @@ static int get_token_u16(void *elem, void *object, u32 offset) return 0; } -static int get_token_uuid(void *elem, void *object, u32 offset) +int get_token_uuid(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_uuid_elem *velem = elem; u8 *dst = (u8 *)object + offset; @@ -505,7 +505,7 @@ static int get_token_uuid(void *elem, void *object, u32 offset) return 0; } -static int get_token_comp_format(void *elem, void *object, u32 offset) +int get_token_comp_format(void *elem, void *object, u32 offset) { u32 *val = (u32 *)((u8 *)object + offset); @@ -513,7 +513,7 @@ static int get_token_comp_format(void *elem, void *object, u32 offset) return 0; } -static int get_token_dai_type(void *elem, void *object, u32 offset) +int get_token_dai_type(void *elem, void *object, u32 offset) { u32 *val = (u32 *)((u8 *)object + offset); -- cgit v1.2.3 From 40bdb2fd6b151e34fdf841238627dd0cfa960093 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 7 Mar 2022 10:11:03 -0800 Subject: ASoC: SOF: change comp_dai to a pointer in struct snd_sof_dai MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will avoid having to add the extended data for DAI components during sof_widget_setup() as an extra step. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220307181111.49392-11-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 6 +++--- sound/soc/sof/sof-audio.c | 18 +++--------------- sound/soc/sof/sof-audio.h | 2 +- sound/soc/sof/topology.c | 17 +++++------------ 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index a312ed855f1a..1d04f75e6d32 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -717,7 +717,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa /* read format from topology */ snd_mask_none(fmt); - switch (dai->comp_dai.config.frame_fmt) { + switch (dai->comp_dai->config.frame_fmt) { case SOF_IPC_FRAME_S16_LE: snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); break; @@ -752,10 +752,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa break; case SOF_DAI_INTEL_DMIC: /* DMIC only supports 16 or 32 bit formats */ - if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + if (dai->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { dev_err(component->dev, "error: invalid fmt %d for DAI type %d\n", - dai->comp_dai.config.frame_fmt, + dai->comp_dai->config.frame_fmt, dai->dai_config->type); } break; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index e39f18e824b4..b49d8e348077 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -170,7 +170,6 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) struct sof_ipc_cmd_hdr *hdr; struct sof_ipc_comp *comp; struct snd_sof_dai *dai; - size_t ipc_size; int ret; /* skip if there is no private data */ @@ -192,23 +191,12 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) switch (swidget->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: - ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext); - comp = kzalloc(ipc_size, GFP_KERNEL); - if (!comp) { - ret = -ENOMEM; - goto core_put; - } - dai = swidget->private; + comp = &dai->comp_dai->comp; dai->configured = false; - memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai)); - /* append extended data to the end of the component */ - memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext, - sizeof(swidget->comp_ext)); - - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r)); - kfree(comp); + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai->comp_dai, comp->hdr.size, + &r, sizeof(r)); if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index feda5793b589..a8eeffc12b24 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -134,7 +134,7 @@ struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; - struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_comp_dai *comp_dai; int number_configs; int current_config; bool configured; /* DAI configured during BE hw_params */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index dd1cc6e26686..41927e99ace2 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1478,7 +1478,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (ret != 0) { dev_err(scomp->dev, "error: parse dai tokens failed %d\n", le32_to_cpu(private->size)); - goto finish; + return ret; } ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens, @@ -1487,7 +1487,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (ret != 0) { dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", private->size); - goto finish; + return ret; } dev_dbg(scomp->dev, "dai %s: type %d index %d\n", @@ -1496,17 +1496,9 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (dai) { dai->scomp = scomp; - - /* - * copy only the sof_ipc_comp_dai to avoid collapsing - * the snd_sof_dai, the extended data is kept in the - * snd_sof_widget. - */ - memcpy(&dai->comp_dai, comp_dai, sizeof(*comp_dai)); + dai->comp_dai = comp_dai; } -finish: - kfree(comp_dai); return ret; } @@ -2429,6 +2421,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, dai = swidget->private; if (dai) { + kfree(dai->comp_dai); /* free dai config */ kfree(dai->dai_config); list_del(&dai->list); @@ -2668,7 +2661,7 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, * dai_index. */ for (i = 0; i < num_conf; i++) - config[i].dai_index = dai->comp_dai.dai_index; + config[i].dai_index = dai->comp_dai->dai_index; dev_dbg(sdev->dev, "set DAI config for %s index %d\n", dai->name, config[curr_conf].dai_index); -- cgit v1.2.3 From b0bfaf0544d08d093d6211d7ef8816fb0b5b6c75 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 01:39:48 +0000 Subject: ASoC: atmel: Fix error handling in snd_proto_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This function only calls of_node_put() in the regular path. And it will cause refcount leak in error paths. Fix this by calling of_node_put() in error handling too. Fixes: a45f8853a5f9 ("ASoC: Add driver for PROTO Audio CODEC (with a WM8731)") Signed-off-by: Miaoqian Lin Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220308013949.20323-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/atmel/mikroe-proto.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index 627564c18c27..ce46d8a0b7e4 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -115,7 +115,8 @@ static int snd_proto_probe(struct platform_device *pdev) cpu_np = of_parse_phandle(np, "i2s-controller", 0); if (!cpu_np) { dev_err(&pdev->dev, "i2s-controller missing\n"); - return -EINVAL; + ret = -EINVAL; + goto put_codec_node; } dai->cpus->of_node = cpu_np; dai->platforms->of_node = cpu_np; @@ -125,7 +126,8 @@ static int snd_proto_probe(struct platform_device *pdev) &bitclkmaster, &framemaster); if (bitclkmaster != framemaster) { dev_err(&pdev->dev, "Must be the same bitclock and frame master\n"); - return -EINVAL; + ret = -EINVAL; + goto put_cpu_node; } if (bitclkmaster) { if (codec_np == bitclkmaster) @@ -136,18 +138,20 @@ static int snd_proto_probe(struct platform_device *pdev) dai_fmt |= snd_soc_daifmt_parse_clock_provider_as_flag(np, NULL); } - of_node_put(bitclkmaster); - of_node_put(framemaster); - dai->dai_fmt = dai_fmt; - - of_node_put(codec_np); - of_node_put(cpu_np); + dai->dai_fmt = dai_fmt; ret = snd_soc_register_card(&snd_proto); if (ret) dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + +put_cpu_node: + of_node_put(bitclkmaster); + of_node_put(framemaster); + of_node_put(cpu_np); +put_codec_node: + of_node_put(codec_np); return ret; } -- cgit v1.2.3 From 51996ca26fc7b5dbeea80eddba0e8a4ece6af459 Mon Sep 17 00:00:00 2001 From: Lianjie Zhang Date: Mon, 7 Mar 2022 23:19:39 +0800 Subject: ASoC: Intel: catpt: use asoc_substream_to_rtd() Now we can use asoc_substream_to_rtd() macro, let's use it. Signed-off-by: Lianjie Zhang Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220307151939.32870-1-zhanglianjie@uniontech.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 939a9b801dec..a26000cd5ceb 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -74,7 +74,7 @@ static struct catpt_stream_template *catpt_topology[] = { static struct catpt_stream_template * catpt_get_stream_template(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = substream->private_data; + struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); enum catpt_stream_type type; @@ -593,7 +593,7 @@ static int catpt_component_pcm_construct(struct snd_soc_component *component, static int catpt_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = substream->private_data; + struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream); if (!rtm->dai_link->no_pcm) snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); @@ -604,7 +604,7 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtm = substream->private_data; + struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); struct catpt_stream_runtime *stream; struct catpt_dev *cdev = dev_get_drvdata(component->dev); -- cgit v1.2.3 From 5ea14bf62ef4501c13f56fce75f6752cf643748f Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Mon, 7 Mar 2022 11:30:56 +0800 Subject: ASoC: mediatek: mt8183: support wb bt audio Use "bt-sco-pcm-wb" codec dai driver for wb bt audio. Signed-off-by: Jiaxin Yu Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20220307033056.11463-1-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index b0ec5ebd4f2d..889f9e4a96aa 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -260,7 +260,7 @@ SND_SOC_DAILINK_DEFS(pcm2, SND_SOC_DAILINK_DEFS(i2s0, DAILINK_COMP_ARRAY(COMP_CPU("I2S0")), - DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm")), + DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm-wb")), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(i2s1, @@ -291,7 +291,7 @@ SND_SOC_DAILINK_DEFS(i2s3_rt1015p, SND_SOC_DAILINK_DEFS(i2s5, DAILINK_COMP_ARRAY(COMP_CPU("I2S5")), - DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm")), + DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm-wb")), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(tdm, @@ -508,7 +508,6 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .ignore_suspend = 1, - .be_hw_params_fixup = mt8183_i2s_hw_params_fixup, .ops = &mt8183_mt6358_i2s_ops, SND_SOC_DAILINK_REG(i2s0), }, @@ -541,7 +540,6 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .ignore_suspend = 1, - .be_hw_params_fixup = mt8183_i2s_hw_params_fixup, .ops = &mt8183_mt6358_i2s_ops, SND_SOC_DAILINK_REG(i2s5), }, -- cgit v1.2.3 From 954e615497cc95cd918bdfe6590abdfbaa068842 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 5 Mar 2022 20:37:05 +0800 Subject: ASoC: amd: acp5x-pcm-dma: Fix signedness bug In acp5x_audio_probe() platform_get_irq() may return error, but i2s_irq now is unsigned int so the error handling is never triggered. Fixes: 87d71a128771 ("ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20220305123705.3708-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h index fe5e1fa98974..3d4ce323669e 100644 --- a/sound/soc/amd/vangogh/acp5x.h +++ b/sound/soc/amd/vangogh/acp5x.h @@ -85,7 +85,7 @@ struct i2s_dev_data { bool tdm_mode; bool master_mode; - unsigned int i2s_irq; + int i2s_irq; u16 i2s_instance; u32 tdm_fmt; void __iomem *acp5x_base; -- cgit v1.2.3 From 6f6f28bf5d8e070c1e4a10d62d2a1af264683042 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 5 Mar 2022 20:36:13 +0800 Subject: ASoC: amd: acp3x: Fix signedness bug in acp3x In acp3x_audio_probe() platform_get_irq() may return error, but i2s_irq now is unsigned int so the error handling is never triggered. Fixes: 87d71a128771 ("ASoC: amd: pcm-dma: Use platform_get_irq() to get the interrupt") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20220305123613.6324-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index c3f0c8b7545d..7702f628ecd6 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -87,7 +87,7 @@ struct acp3x_platform_info { struct i2s_dev_data { bool tdm_mode; - unsigned int i2s_irq; + int i2s_irq; u16 i2s_instance; u32 tdm_fmt; u32 substream_type; -- cgit v1.2.3 From f725d20579807a68afbe5dba69e78b8fa05f5ef0 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 7 Mar 2022 08:35:52 +0000 Subject: ASoC: rockchip: i2s: Fix missing clk_disable_unprepare() in rockchip_i2s_probe Fix the missing clk_disable_unprepare() before return from rockchip_i2s_probe() in the error handling case. Fixes: 01605ad12875 ("ASoC: rockchip-i2s: enable "hclk" for rockchip I2S controller") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220307083553.26009-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index a6d7656c206e..4ce5d2579387 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -716,19 +716,23 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); if (IS_ERR(i2s->mclk)) { dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); - return PTR_ERR(i2s->mclk); + ret = PTR_ERR(i2s->mclk); + goto err_clk; } regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(regs)) - return PTR_ERR(regs); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_clk; + } i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &rockchip_i2s_regmap_config); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "Failed to initialise managed register map\n"); - return PTR_ERR(i2s->regmap); + ret = PTR_ERR(i2s->regmap); + goto err_clk; } i2s->bclk_ratio = 64; @@ -768,7 +772,8 @@ err_suspend: i2s_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - +err_clk: + clk_disable_unprepare(i2s->hclk); return ret; } -- cgit v1.2.3 From 5575f7f49134c7386a684335c9007737c606d3b5 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 02:33:23 +0000 Subject: ASoC: SOF: Add missing of_node_put() in imx8m_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. Fixes: afb93d716533 ("ASoC: SOF: imx: Add i.MX8M HW support") Signed-off-by: Miaoqian Lin Reviewed-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220308023325.31702-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8m.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 788e77bcb603..60251486b24b 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -224,6 +224,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev) } ret = of_address_to_resource(res_node, 0, &res); + of_node_put(res_node); if (ret) { dev_err(&pdev->dev, "failed to get reserved region address\n"); goto exit_pdev_unregister; -- cgit v1.2.3 From e45ac7831ff3e2934d58cce319c17c8ec763c95c Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 01:52:22 +0000 Subject: ASoC: mediatek: mt8192-mt6359: Fix error handling in mt8192_mt6359_dev_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This function only calls of_node_put() in the regular path. And it will cause refcount leak in error paths. Fix this by calling of_node_put() in error handling too. Fixes: 4e28491a7a19 ("ASoC: mediatek: mt8192-mt6359: fix device_node leak") Signed-off-by: Miaoqian Lin Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20220308015224.23585-1-linmq006@gmail.com Signed-off-by: Mark Brown --- .../soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index f7daad1bfe1e..ee91569c0911 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1116,8 +1116,10 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) } card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); - if (!card) - return -EINVAL; + if (!card) { + ret = -EINVAL; + goto put_platform_node; + } card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, @@ -1159,20 +1161,24 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + if (!priv) { + ret = -ENOMEM; + goto put_hdmi_codec; + } snd_soc_card_set_drvdata(card, priv); ret = mt8192_afe_gpio_init(&pdev->dev); if (ret) { dev_err(&pdev->dev, "init gpio error %d\n", ret); - return ret; + goto put_hdmi_codec; } ret = devm_snd_soc_register_card(&pdev->dev, card); - of_node_put(platform_node); +put_hdmi_codec: of_node_put(hdmi_codec); +put_platform_node: + of_node_put(platform_node); return ret; } -- cgit v1.2.3 From a6b44a2518a08348bd0f0401e4d2b99233bbabc2 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 7 Mar 2022 09:01:30 +0000 Subject: ASoC: rk817: Fix missing clk_disable_unprepare() in rk817_platform_probe Fix the missing clk_disable_unprepare() before return from rk817_platform_probe() in the error handling case. Fixes: 0d6a04da9b25 ("ASoC: Add Rockchip rk817 audio CODEC support") Signed-off-by: Miaoqian Lin Tested-by: Chris Morgan Link: https://lore.kernel.org/r/20220307090146.4104-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/rk817_codec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index 03f24edfe4f6..8fffe378618d 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -508,12 +508,14 @@ static int rk817_platform_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "%s() register codec error %d\n", __func__, ret); - goto err_; + goto err_clk; } return 0; -err_: +err_clk: + clk_disable_unprepare(rk817_codec_data->mclk); +err_: return ret; } -- cgit v1.2.3 From 9a1e13440a4f2e7566fd4c5eae6a53e6400e08a4 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Mon, 7 Mar 2022 14:21:57 +0200 Subject: ASoC: dmaengine: do not use a NULL prepare_slave_config() callback Even if struct snd_dmaengine_pcm_config is used, prepare_slave_config() callback might not be set. Check if this callback is set before using it. Fixes: fa654e085300 ("ASoC: dmaengine-pcm: Provide default config") Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220307122202.2251639-2-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 285441d6aeed..2ab2ddc1294d 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -86,10 +86,10 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, memset(&slave_config, 0, sizeof(slave_config)); - if (!pcm->config) - prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; - else + if (pcm->config && pcm->config->prepare_slave_config) prepare_slave_config = pcm->config->prepare_slave_config; + else + prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; if (prepare_slave_config) { int ret = prepare_slave_config(substream, params, &slave_config); -- cgit v1.2.3 From 015044e9610c8523794ea6cb55d5388bc00ba96a Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Mon, 7 Mar 2022 14:21:58 +0200 Subject: ASoC: dt-bindings: Document Microchip's PDMC Add DT bindings for the new Microchip PDMC embedded in sama7g5 SoCs. Signed-off-by: Codrin Ciubotariu Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220307122202.2251639-3-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/microchip,pdmc.yaml | 100 +++++++++++++++++++++ include/dt-bindings/sound/microchip,pdmc.h | 13 +++ 2 files changed, 113 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/microchip,pdmc.yaml create mode 100644 include/dt-bindings/sound/microchip,pdmc.h diff --git a/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml b/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml new file mode 100644 index 000000000000..04414eb4ada9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/microchip,pdmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Pulse Density Microphone Controller + +maintainers: + - Codrin Ciubotariu + +description: + The Microchip Pulse Density Microphone Controller (PDMC) interfaces up to 4 + digital microphones having Pulse Density Modulated (PDM) outputs. + +properties: + compatible: + const: microchip,sama7g5-pdmc + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Peripheral Bus Clock + - description: Generic Clock + + clock-names: + items: + - const: pclk + - const: gclk + + dmas: + description: RX DMA Channel + maxItems: 1 + + dma-names: + const: rx + + microchip,mic-pos: + description: | + Position of PDM microphones on the DS line and the sampling edge (rising + or falling) of the CLK line. A microphone is represented as a pair of DS + line and the sampling edge. The first microphone is mapped to channel 0, + the second to channel 1, etc. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: value for DS line + - description: value for sampling edge + anyOf: + - enum: + - [0, 0] + - [0, 1] + - [1, 0] + - [1, 1] + minItems: 1 + maxItems: 4 + uniqueItems: true + +required: + - compatible + - reg + - "#sound-dai-cells" + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - microchip,mic-pos + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + pdmc: sound@e1608000 { + compatible = "microchip,sama7g5-pdmc"; + reg = <0xe1608000 0x4000>; + #sound-dai-cells = <0>; + interrupts = ; + dmas = <&dma0 AT91_XDMAC_DT_PERID(37)>; + dma-names = "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 68>, <&pmc PMC_TYPE_GCK 68>; + clock-names = "pclk", "gclk"; + microchip,mic-pos = , + , + , + ; + }; diff --git a/include/dt-bindings/sound/microchip,pdmc.h b/include/dt-bindings/sound/microchip,pdmc.h new file mode 100644 index 000000000000..96cde94ce74f --- /dev/null +++ b/include/dt-bindings/sound/microchip,pdmc.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_BINDINGS_MICROCHIP_PDMC_H__ +#define __DT_BINDINGS_MICROCHIP_PDMC_H__ + +/* PDM microphone's pin placement */ +#define MCHP_PDMC_DS0 0 +#define MCHP_PDMC_DS1 1 + +/* PDM microphone clock edge sampling */ +#define MCHP_PDMC_CLK_POSITIVE 0 +#define MCHP_PDMC_CLK_NEGATIVE 1 + +#endif /* __DT_BINDINGS_MICROCHIP_PDMC_H__ */ -- cgit v1.2.3 From 50291652af5269813baa6024eb0e81b5f0bbb451 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Mon, 7 Mar 2022 14:21:59 +0200 Subject: ASoC: atmel: mchp-pdmc: add PDMC driver The Pulse Density Microphone Controller (PDMC) interfaces up to 4 digital microphones having Pulse Density Modulated (PDM) outputs. It generates a single clock line and samples 1 or 2 data lines. The signal path includes an audio grade programmable decimation filter and outputs 24-bit audio words on the APB bus. Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220307122202.2251639-4-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 16 + sound/soc/atmel/Makefile | 2 + sound/soc/atmel/mchp-pdmc.c | 1084 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1102 insertions(+) create mode 100644 sound/soc/atmel/mchp-pdmc.c diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 8617793ed955..795c0b0b527a 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -160,4 +160,20 @@ config SND_MCHP_SOC_SPDIFRX This S/PDIF RX driver is compliant with IEC-60958 standard and includes programmable User Data and Channel Status fields. + +config SND_MCHP_SOC_PDMC + tristate "Microchip ASoC driver for boards using PDMC" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for Microchip ASoC PDMC driver on the + following Microchip platforms: + - sama7g5 + + The Pulse Density Microphone Controller (PDMC) interfaces up to 4 digital + microphones PDM outputs. It generates a single clock line and samples 1 or + 2 data lines. The signal path includes an audio grade programmable + decimation filter and outputs 24-bit audio words. + endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 016188397210..043097a08ea8 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -7,6 +7,7 @@ snd-soc-atmel-i2s-objs := atmel-i2s.o snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o snd-soc-mchp-spdifrx-objs := mchp-spdifrx.o +snd-soc-mchp-pdmc-objs := mchp-pdmc.o # pdc and dma need to both be built-in if any user of # ssc is built-in. @@ -21,6 +22,7 @@ obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o +obj-$(CONFIG_SND_MCHP_SOC_PDMC) += snd-soc-mchp-pdmc.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c new file mode 100644 index 000000000000..c44636f6207d --- /dev/null +++ b/sound/soc/atmel/mchp-pdmc.c @@ -0,0 +1,1084 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces +// +// Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * ---- PDMC Register map ---- + */ +#define MCHP_PDMC_CR 0x00 /* Control Register */ +#define MCHP_PDMC_MR 0x04 /* Mode Register */ +#define MCHP_PDMC_CFGR 0x08 /* Configuration Register */ +#define MCHP_PDMC_RHR 0x0C /* Receive Holding Register */ +#define MCHP_PDMC_IER 0x14 /* Interrupt Enable Register */ +#define MCHP_PDMC_IDR 0x18 /* Interrupt Disable Register */ +#define MCHP_PDMC_IMR 0x1C /* Interrupt Mask Register */ +#define MCHP_PDMC_ISR 0x20 /* Interrupt Status Register */ +#define MCHP_PDMC_VER 0x50 /* Version Register */ + +/* + * ---- Control Register (Write-only) ---- + */ +#define MCHP_PDMC_CR_SWRST BIT(0) /* Software Reset */ + +/* + * ---- Mode Register (Read/Write) ---- + */ +#define MCHP_PDMC_MR_PDMCEN_MASK GENMASK(3, 0) +#define MCHP_PDMC_MR_PDMCEN(ch) (BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK) + +#define MCHP_PDMC_MR_OSR_MASK GENMASK(17, 16) +#define MCHP_PDMC_MR_OSR64 (1 << 16) +#define MCHP_PDMC_MR_OSR128 (2 << 16) +#define MCHP_PDMC_MR_OSR256 (3 << 16) + +#define MCHP_PDMC_MR_SINCORDER_MASK GENMASK(23, 20) +#define MCHP_PDMC_MR_SINCORDER(order) (((order) << 20) & \ + MCHP_PDMC_MR_SINCORDER_MASK) + +#define MCHP_PDMC_MR_SINC_OSR_MASK GENMASK(27, 24) +#define MCHP_PDMC_MR_SINC_OSR_DIS (0 << 24) +#define MCHP_PDMC_MR_SINC_OSR_8 (1 << 24) +#define MCHP_PDMC_MR_SINC_OSR_16 (2 << 24) +#define MCHP_PDMC_MR_SINC_OSR_32 (3 << 24) +#define MCHP_PDMC_MR_SINC_OSR_64 (4 << 24) +#define MCHP_PDMC_MR_SINC_OSR_128 (5 << 24) +#define MCHP_PDMC_MR_SINC_OSR_256 (6 << 24) + +#define MCHP_PDMC_MR_CHUNK_MASK GENMASK(31, 28) +#define MCHP_PDMC_MR_CHUNK(chunk) (((chunk) << 28) & \ + MCHP_PDMC_MR_CHUNK_MASK) + +/* + * ---- Configuration Register (Read/Write) ---- + */ +#define MCHP_PDMC_CFGR_BSSEL_MASK (BIT(0) | BIT(2) | BIT(4) | BIT(6)) +#define MCHP_PDMC_CFGR_BSSEL(ch) BIT((ch) * 2) + +#define MCHP_PDMC_CFGR_PDMSEL_MASK (BIT(16) | BIT(18) | BIT(20) | BIT(22)) +#define MCHP_PDMC_CFGR_PDMSEL(ch) BIT((ch) * 2 + 16) + +/* + * ---- Interrupt Enable/Disable/Mask/Status Registers ---- + */ +#define MCHP_PDMC_IR_RXRDY BIT(0) +#define MCHP_PDMC_IR_RXEMPTY BIT(1) +#define MCHP_PDMC_IR_RXFULL BIT(2) +#define MCHP_PDMC_IR_RXCHUNK BIT(3) +#define MCHP_PDMC_IR_RXUDR BIT(4) +#define MCHP_PDMC_IR_RXOVR BIT(5) + +/* + * ---- Version Register (Read-only) ---- + */ +#define MCHP_PDMC_VER_VERSION GENMASK(11, 0) + +#define MCHP_PDMC_MAX_CHANNELS 4 +#define MCHP_PDMC_DS_NO 2 +#define MCHP_PDMC_EDGE_NO 2 + +struct mic_map { + int ds_pos; + int clk_edge; +}; + +struct mchp_pdmc_chmap { + struct snd_pcm_chmap_elem *chmap; + struct mchp_pdmc *dd; + struct snd_pcm *pcm; + struct snd_kcontrol *kctl; +}; + +struct mchp_pdmc { + struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS]; + struct device *dev; + struct snd_dmaengine_dai_dma_data addr; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + u32 pdmcen; + int mic_no; + int sinc_order; + bool audio_filter_en; + u8 gclk_enabled:1; +}; + +static const char *const mchp_pdmc_sinc_filter_order_text[] = { + "1", "2", "3", "4", "5" +}; + +static const unsigned int mchp_pdmc_sinc_filter_order_values[] = { + 1, 2, 3, 4, 5, +}; + +static const struct soc_enum mchp_pdmc_sinc_filter_order_enum = { + .items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text), + .texts = mchp_pdmc_sinc_filter_order_text, + .values = mchp_pdmc_sinc_filter_order_values, +}; + +static int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int item; + + item = snd_soc_enum_val_to_item(e, dd->sinc_order); + uvalue->value.enumerated.item[0] = item; + + return 0; +} + +static int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = uvalue->value.enumerated.item; + unsigned int val; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + if (val == dd->sinc_order) + return 0; + + dd->sinc_order = val; + + return 1; +} + +static int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); + + uvalue->value.integer.value[0] = !!dd->audio_filter_en; + + return 0; +} + +static int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); + bool af = uvalue->value.integer.value ? true : false; + + if (dd->audio_filter_en == af) + return 0; + + dd->audio_filter_en = af; + + return 1; +} + +static int mchp_pdmc_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = info->dd->mic_no; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_RR; /* maxmimum 4 channels */ + return 0; +} + +static inline struct snd_pcm_substream * +mchp_pdmc_chmap_substream(struct mchp_pdmc_chmap *info, unsigned int idx) +{ + struct snd_pcm_substream *s; + + for (s = info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; s; s = s->next) + if (s->number == idx) + return s; + return NULL; +} + +static struct snd_pcm_chmap_elem *mchp_pdmc_chmap_get(struct snd_pcm_substream *substream, + struct mchp_pdmc_chmap *ch_info) +{ + struct snd_pcm_chmap_elem *map; + + for (map = ch_info->chmap; map->channels; map++) { + if (map->channels == substream->runtime->channels) + return map; + } + return NULL; +} + +static int mchp_pdmc_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = info->dd; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + const struct snd_pcm_chmap_elem *map; + int i; + u32 cfgr_val = 0; + + if (!info->chmap) + return -EINVAL; + substream = mchp_pdmc_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no); + if (!substream->runtime) + return 0; /* no channels set */ + + map = mchp_pdmc_chmap_get(substream, info); + if (!map) + return -EINVAL; + + for (i = 0; i < map->channels; i++) { + int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO : + map->map[i] - SNDRV_CHMAP_FL; + + /* make sure the reported channel map is the real one, so write the map */ + if (dd->channel_mic_map[map_idx].ds_pos) + cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); + if (dd->channel_mic_map[map_idx].clk_edge) + cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); + + ucontrol->value.integer.value[i] = map->map[i]; + } + + regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val); + + return 0; +} + +static int mchp_pdmc_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); + struct mchp_pdmc *dd = info->dd; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct snd_pcm_chmap_elem *map; + u32 cfgr_val = 0; + int i; + + if (!info->chmap) + return -EINVAL; + substream = mchp_pdmc_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + + map = mchp_pdmc_chmap_get(substream, info); + if (!map) + return -EINVAL; + + for (i = 0; i < map->channels; i++) { + int map_idx; + + map->map[i] = ucontrol->value.integer.value[i]; + map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO : + map->map[i] - SNDRV_CHMAP_FL; + + /* configure IP for the desired channel map */ + if (dd->channel_mic_map[map_idx].ds_pos) + cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); + if (dd->channel_mic_map[map_idx].clk_edge) + cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); + } + + regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val); + + return 0; +} + +static void mchp_pdmc_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) +{ + struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); + + info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = NULL; + kfree(info); +} + +static int mchp_pdmc_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); + const struct snd_pcm_chmap_elem *map; + unsigned int __user *dst; + int c, count = 0; + + if (!info->chmap) + return -EINVAL; + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (map = info->chmap; map->channels; map++) { + int chs_bytes = map->channels * 4; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + for (c = 0; c < map->channels; c++) { + if (put_user(map->map[c], dst)) + return -EFAULT; + dst++; + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static const struct snd_kcontrol_new mchp_pdmc_snd_controls[] = { + SOC_SINGLE_BOOL_EXT("Audio Filter", 0, &mchp_pdmc_af_get, &mchp_pdmc_af_put), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SINC Filter Order", + .info = snd_soc_info_enum_double, + .get = mchp_pdmc_sinc_order_get, + .put = mchp_pdmc_sinc_order_put, + .private_value = (unsigned long)&mchp_pdmc_sinc_filter_order_enum, + }, +}; + +static int mchp_pdmc_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_soc_add_component_controls(component, mchp_pdmc_snd_controls, + ARRAY_SIZE(mchp_pdmc_snd_controls)); +} + +static int mchp_pdmc_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + int i; + + /* remove controls that can't be changed at runtime */ + for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) { + const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i]; + struct snd_ctl_elem_id id; + struct snd_kcontrol *kctl; + int err; + + if (component->name_prefix) + snprintf(id.name, sizeof(id.name), "%s %s", component->name_prefix, + control->name); + else + strscpy(id.name, control->name, sizeof(id.name)); + + id.numid = 0; + id.iface = control->iface; + id.device = control->device; + id.subdevice = control->subdevice; + id.index = control->index; + kctl = snd_ctl_find_id(component->card->snd_card, &id); + if (!kctl) { + dev_err(component->dev, "Failed to find %s\n", control->name); + continue; + } + err = snd_ctl_remove(component->card->snd_card, kctl); + if (err < 0) { + dev_err(component->dev, "%d: Failed to remove %s\n", err, + control->name); + continue; + } + } + + return 0; +} + +static const struct snd_soc_component_driver mchp_pdmc_dai_component = { + .name = "mchp-pdmc", + .controls = mchp_pdmc_snd_controls, + .num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls), + .open = &mchp_pdmc_open, + .close = &mchp_pdmc_close, +}; + +static const unsigned int mchp_pdmc_1mic[] = {1}; +static const unsigned int mchp_pdmc_2mic[] = {1, 2}; +static const unsigned int mchp_pdmc_3mic[] = {1, 2, 3}; +static const unsigned int mchp_pdmc_4mic[] = {1, 2, 3, 4}; + +static const struct snd_pcm_hw_constraint_list mchp_pdmc_chan_constr[] = { + { + .list = mchp_pdmc_1mic, + .count = ARRAY_SIZE(mchp_pdmc_1mic), + }, + { + .list = mchp_pdmc_2mic, + .count = ARRAY_SIZE(mchp_pdmc_2mic), + }, + { + .list = mchp_pdmc_3mic, + .count = ARRAY_SIZE(mchp_pdmc_3mic), + }, + { + .list = mchp_pdmc_4mic, + .count = ARRAY_SIZE(mchp_pdmc_4mic), + }, +}; + +static int mchp_pdmc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(dd->pclk); + if (ret) { + dev_err(dd->dev, "failed to enable the peripheral clock: %d\n", ret); + return ret; + } + + regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST); + + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mchp_pdmc_chan_constr[dd->mic_no - 1]); + + return 0; +} + +static void mchp_pdmc_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(dd->pclk); +} + +static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, NULL, &dd->addr); + + return 0; +} + +static int mchp_pdmc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + unsigned int fmt_master = fmt & SND_SOC_DAIFMT_MASTER_MASK; + unsigned int fmt_format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* IP needs to be bitclock master */ + if (fmt_master != SND_SOC_DAIFMT_CBS_CFS && + fmt_master != SND_SOC_DAIFMT_CBS_CFM) + return -EINVAL; + + /* IP supports only PDM interface */ + if (fmt_format != SND_SOC_DAIFMT_PDM) + return -EINVAL; + + return 0; +} + +static u32 mchp_pdmc_mr_set_osr(int audio_filter_en, unsigned int osr) +{ + if (audio_filter_en) { + switch (osr) { + case 64: + return MCHP_PDMC_MR_OSR64; + case 128: + return MCHP_PDMC_MR_OSR128; + case 256: + return MCHP_PDMC_MR_OSR256; + } + } else { + switch (osr) { + case 8: + return MCHP_PDMC_MR_SINC_OSR_8; + case 16: + return MCHP_PDMC_MR_SINC_OSR_16; + case 32: + return MCHP_PDMC_MR_SINC_OSR_32; + case 64: + return MCHP_PDMC_MR_SINC_OSR_64; + case 128: + return MCHP_PDMC_MR_SINC_OSR_128; + case 256: + return MCHP_PDMC_MR_SINC_OSR_256; + } + } + return 0; +} + +static inline int mchp_pdmc_period_to_maxburst(int period_size) +{ + if (!(period_size % 8)) + return 8; + if (!(period_size % 4)) + return 4; + if (!(period_size % 2)) + return 2; + return 1; +} + +static struct snd_pcm_chmap_elem mchp_pdmc_std_chmaps[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_MONO } }, + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 3, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + unsigned long gclk_rate = 0; + unsigned long best_diff_rate = ~0UL; + unsigned int channels = params_channels(params); + unsigned int osr = 0, osr_start; + unsigned int fs = params_rate(params); + u32 mr_val = 0; + u32 cfgr_val = 0; + int i; + int ret; + + dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + __func__, params_rate(params), params_format(params), + params_width(params), params_channels(params)); + + if (channels > dd->mic_no) { + dev_err(comp->dev, "more channels %u than microphones %d\n", + channels, dd->mic_no); + return -EINVAL; + } + + dd->pdmcen = 0; + for (i = 0; i < channels; i++) { + dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i); + if (dd->channel_mic_map[i].ds_pos) + cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); + if (dd->channel_mic_map[i].clk_edge) + cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); + } + + if (dd->gclk_enabled) { + clk_disable_unprepare(dd->gclk); + dd->gclk_enabled = 0; + } + + for (osr_start = dd->audio_filter_en ? 64 : 8; + osr_start <= 256 && best_diff_rate; osr_start *= 2) { + long round_rate; + unsigned long diff_rate; + + round_rate = clk_round_rate(dd->gclk, + (unsigned long)fs * 16 * osr_start); + if (round_rate < 0) + continue; + diff_rate = abs((fs * 16 * osr_start) - round_rate); + if (diff_rate < best_diff_rate) { + best_diff_rate = diff_rate; + osr = osr_start; + gclk_rate = fs * 16 * osr; + } + } + if (!gclk_rate) { + dev_err(comp->dev, "invalid sampling rate: %u\n", fs); + return -EINVAL; + } + + /* set the rate */ + ret = clk_set_rate(dd->gclk, gclk_rate); + if (ret) { + dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n", + gclk_rate, ret); + return ret; + } + + mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr); + + mr_val |= MCHP_PDMC_MR_SINCORDER(dd->sinc_order); + + dd->addr.maxburst = mchp_pdmc_period_to_maxburst(snd_pcm_lib_period_bytes(substream)); + mr_val |= MCHP_PDMC_MR_CHUNK(dd->addr.maxburst); + dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst); + + clk_prepare_enable(dd->gclk); + dd->gclk_enabled = 1; + + snd_soc_component_update_bits(comp, MCHP_PDMC_MR, + MCHP_PDMC_MR_OSR_MASK | + MCHP_PDMC_MR_SINCORDER_MASK | + MCHP_PDMC_MR_SINC_OSR_MASK | + MCHP_PDMC_MR_CHUNK_MASK, mr_val); + + snd_soc_component_write(comp, MCHP_PDMC_CFGR, cfgr_val); + + return 0; +} + +static int mchp_pdmc_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + + if (dd->gclk_enabled) { + clk_disable_unprepare(dd->gclk); + dd->gclk_enabled = 0; + } + + return 0; +} + +static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *cpu = dai->component; +#ifdef DEBUG + u32 val; +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Enable overrun and underrun error interrupts */ + regmap_write(dd->regmap, MCHP_PDMC_IER, + MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, + MCHP_PDMC_MR_PDMCEN_MASK, + dd->pdmcen); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* Disable overrun and underrun error interrupts */ + regmap_write(dd->regmap, MCHP_PDMC_IDR, + MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, + MCHP_PDMC_MR_PDMCEN_MASK, 0); + break; + default: + return -EINVAL; + } + +#ifdef DEBUG + regmap_read(dd->regmap, MCHP_PDMC_MR, &val); + dev_dbg(dd->dev, "MR (0x%02x): 0x%08x\n", MCHP_PDMC_MR, val); + regmap_read(dd->regmap, MCHP_PDMC_CFGR, &val); + dev_dbg(dd->dev, "CFGR (0x%02x): 0x%08x\n", MCHP_PDMC_CFGR, val); + regmap_read(dd->regmap, MCHP_PDMC_IMR, &val); + dev_dbg(dd->dev, "IMR (0x%02x): 0x%08x\n", MCHP_PDMC_IMR, val); +#endif + + return 0; +} + +static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .set_fmt = mchp_pdmc_set_fmt, + .startup = mchp_pdmc_startup, + .shutdown = mchp_pdmc_shutdown, + .hw_params = mchp_pdmc_hw_params, + .hw_free = mchp_pdmc_hw_free, + .trigger = mchp_pdmc_trigger, +}; + +static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd) +{ + struct mchp_pdmc_chmap *info; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .info = mchp_pdmc_chmap_ctl_info, + .get = mchp_pdmc_chmap_ctl_get, + .put = mchp_pdmc_chmap_ctl_put, + .tlv.c = mchp_pdmc_chmap_ctl_tlv, + }; + int err; + + if (WARN_ON(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl)) + return -EBUSY; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->pcm = pcm; + info->dd = dd; + info->chmap = mchp_pdmc_std_chmaps; + knew.name = "Capture Channel Map"; + knew.device = pcm->device; + knew.count = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; + info->kctl = snd_ctl_new1(&knew, info); + if (!info->kctl) { + kfree(info); + return -ENOMEM; + } + info->kctl->private_free = mchp_pdmc_chmap_ctl_private_free; + err = snd_ctl_add(pcm->card, info->kctl); + if (err < 0) + return err; + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = info->kctl; + return 0; +} + +static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd); + if (ret < 0) { + dev_err(dd->dev, "failed to add channel map controls: %d\n", ret); + return ret; + } + + return 0; +} + +static struct snd_soc_dai_driver mchp_pdmc_dai = { + .probe = mchp_pdmc_dai_probe, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &mchp_pdmc_dai_ops, + .pcm_new = &mchp_pdmc_pcm_new, +}; + +/* PDMC interrupt handler */ +static irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id) +{ + struct mchp_pdmc *dd = (struct mchp_pdmc *)dev_id; + u32 isr, msr, pending; + irqreturn_t ret = IRQ_NONE; + + regmap_read(dd->regmap, MCHP_PDMC_ISR, &isr); + regmap_read(dd->regmap, MCHP_PDMC_IMR, &msr); + + pending = isr & msr; + dev_dbg(dd->dev, "ISR (0x%02x): 0x%08x, IMR (0x%02x): 0x%08x, pending: 0x%08x\n", + MCHP_PDMC_ISR, isr, MCHP_PDMC_IMR, msr, pending); + if (!pending) + return IRQ_NONE; + + if (pending & MCHP_PDMC_IR_RXUDR) { + dev_warn(dd->dev, "underrun detected\n"); + regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR); + ret = IRQ_HANDLED; + } + if (pending & MCHP_PDMC_IR_RXOVR) { + dev_warn(dd->dev, "overrun detected\n"); + regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR); + ret = IRQ_HANDLED; + } + + return ret; +} + +/* regmap configuration */ +static bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MCHP_PDMC_MR: + case MCHP_PDMC_CFGR: + case MCHP_PDMC_IMR: + case MCHP_PDMC_ISR: + case MCHP_PDMC_VER: + return true; + default: + return false; + } +} + +static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MCHP_PDMC_CR: + case MCHP_PDMC_MR: + case MCHP_PDMC_CFGR: + case MCHP_PDMC_IER: + case MCHP_PDMC_IDR: + return true; + default: + return false; + } +} + +static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MCHP_PDMC_RHR: + case MCHP_PDMC_ISR: + return true; + default: + return false; + } +} + +static const struct regmap_config mchp_pdmc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = MCHP_PDMC_VER, + .readable_reg = mchp_pdmc_readable_reg, + .writeable_reg = mchp_pdmc_writeable_reg, + .precious_reg = mchp_pdmc_precious_reg, +}; + +static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) +{ + struct device_node *np = dd->dev->of_node; + bool mic_ch[MCHP_PDMC_DS_NO][MCHP_PDMC_EDGE_NO] = {0}; + int i; + int ret; + + if (!np) { + dev_err(dd->dev, "device node not found\n"); + return -EINVAL; + } + + dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos"); + if (dd->mic_no < 0) { + dev_err(dd->dev, "failed to get mchp,mic-pos: %d", + dd->mic_no); + return dd->mic_no; + } + if (!dd->mic_no || dd->mic_no % 2 || + dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) { + dev_err(dd->dev, "invalid array length for mchp,mic-pos: %d", + dd->mic_no); + return -EINVAL; + } + + dd->mic_no /= 2; + + dev_info(dd->dev, "%d PDM microchopnes declared\n", dd->mic_no); + + /* by default, we consider the order of microphones in mchp,mic-pos to + * be the same with the channel mapping; 1st microphone channel 0, 2nd + * microphone channel 1, etc. + */ + for (i = 0; i < dd->mic_no; i++) { + int ds; + int edge; + + ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2, + &ds); + if (ret) { + dev_err(dd->dev, + "failed to get value no %d value from microchip,mic-pos: %d", + i * 2, ret); + return ret; + } + if (ds >= MCHP_PDMC_DS_NO) { + dev_err(dd->dev, + "invalid DS index in microchip,mic-pos array: %d", + ds); + return -EINVAL; + } + + ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1, + &edge); + if (ret) { + dev_err(dd->dev, + "failed to get value no %d value from microchip,mic-pos: %d", + i * 2 + 1, ret); + return ret; + } + + if (edge != MCHP_PDMC_CLK_POSITIVE && + edge != MCHP_PDMC_CLK_NEGATIVE) { + dev_err(dd->dev, + "invalid edge in microchip,mic-pos array: %d", edge); + return -EINVAL; + } + if (mic_ch[ds][edge]) { + dev_err(dd->dev, + "duplicated mic (DS %d, edge %d) in microchip,mic-pos array", + ds, edge); + return -EINVAL; + } + mic_ch[ds][edge] = true; + dd->channel_mic_map[i].ds_pos = ds; + dd->channel_mic_map[i].clk_edge = edge; + } + + return 0; +} + +/* used to clean the channel index found on RHR's MSB */ +static int mchp_pdmc_process(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u8 *dma_ptr = runtime->dma_area + hwoff + + channel * (runtime->dma_bytes / runtime->channels); + u8 *dma_ptr_end = dma_ptr + bytes; + unsigned int sample_size = samples_to_bytes(runtime, 1); + + for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size) + *dma_ptr = 0; + + return 0; +} + +static struct snd_dmaengine_pcm_config mchp_pdmc_config = { + .process = mchp_pdmc_process, +}; + +static int mchp_pdmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mchp_pdmc *dd; + struct resource *res; + void __iomem *io_base; + u32 version; + int irq; + int ret; + + dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->dev = &pdev->dev; + ret = mchp_pdmc_dt_init(dd); + if (ret < 0) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get irq: %d\n", irq); + return irq; + } + + dd->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dd->pclk)) { + ret = PTR_ERR(dd->pclk); + dev_err(dev, "failed to get peripheral clock: %d\n", ret); + return ret; + } + + dd->gclk = devm_clk_get(dev, "gclk"); + if (IS_ERR(dd->gclk)) { + ret = PTR_ERR(dd->gclk); + dev_err(dev, "failed to get GCK: %d\n", ret); + return ret; + } + + io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(io_base)) { + ret = PTR_ERR(io_base); + dev_err(dev, "failed to remap register memory: %d\n", ret); + return ret; + } + + dd->regmap = devm_regmap_init_mmio(dev, io_base, + &mchp_pdmc_regmap_config); + if (IS_ERR(dd->regmap)) { + ret = PTR_ERR(dd->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0, + dev_name(&pdev->dev), (void *)dd); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + /* by default audio filter is enabled and the SINC Filter order + * will be set to the recommended value, 3 + */ + dd->audio_filter_en = true; + dd->sinc_order = 3; + + dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR; + platform_set_drvdata(pdev, dd); + + /* register platform */ + ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0); + if (ret) { + dev_err(dev, "could not register platform: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component, + &mchp_pdmc_dai, 1); + if (ret) { + dev_err(dev, "could not register CPU DAI: %d\n", ret); + return ret; + } + + /* print IP version */ + regmap_read(dd->regmap, MCHP_PDMC_VER, &version); + dev_info(dd->dev, "hw version: %#lx\n", + version & MCHP_PDMC_VER_VERSION); + + return 0; +} + +static const struct of_device_id mchp_pdmc_of_match[] = { + { + .compatible = "microchip,sama7g5-pdmc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match); + +static struct platform_driver mchp_pdmc_driver = { + .driver = { + .name = "mchp-pdmc", + .of_match_table = of_match_ptr(mchp_pdmc_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = mchp_pdmc_probe, +}; +module_platform_driver(mchp_pdmc_driver); + +MODULE_DESCRIPTION("Microchip PDMC driver under ALSA SoC architecture"); +MODULE_AUTHOR("Codrin Ciubotariu "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 6ae0a4d8fec551ec581d620f0eb1fe31f755551c Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 02:01:44 +0000 Subject: ASoC: mxs: Fix error handling in mxs_sgtl5000_probe This function only calls of_node_put() in the regular path. And it will cause refcount leak in error paths. For example, when codec_np is NULL, saif_np[0] and saif_np[1] are not NULL, it will cause leaks. of_node_put() will check if the node pointer is NULL, so we can call it directly to release the refcount of regular pointers. Fixes: e968194b45c4 ("ASoC: mxs: add device tree support for mxs-sgtl5000") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220308020146.26496-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 2412dc7e65d4..746f40938675 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -118,6 +118,9 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) codec_np = of_parse_phandle(np, "audio-codec", 0); if (!saif_np[0] || !saif_np[1] || !codec_np) { dev_err(&pdev->dev, "phandle missing or invalid\n"); + of_node_put(codec_np); + of_node_put(saif_np[0]); + of_node_put(saif_np[1]); return -EINVAL; } -- cgit v1.2.3 From 7cacfa4a7b0dcf8f10dda5327957ee13db1c455f Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 8 Mar 2022 16:13:22 +0200 Subject: ALSA: hda: Add AlderLake-PS variant PCI ID Add HD Audio PCI ID for a variant of Intel AlderLake-P. Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20220308141322.880775-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 5af7a1e18013..0a83eb6b88b1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2499,6 +2499,8 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-P */ { PCI_DEVICE(0x8086, 0x51c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51c9), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, { PCI_DEVICE(0x8086, 0x51cd), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Alderlake-M */ -- cgit v1.2.3 From 7a976552a4f264f777be236c0c83263975512f1a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:37 -0800 Subject: ASoC: SOF: make struct snd_sof_widget IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse the UUID token and save it in the new uuid field in struct snd_sof_widget. struct sof_ipc_comp_ext is no longer needed. So remove it too. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-12-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/topology.h | 8 -------- sound/soc/sof/sof-audio.h | 3 +-- sound/soc/sof/topology.c | 21 ++++++++++----------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index adee6afd1490..88560281d420 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -87,9 +87,6 @@ struct sof_ipc_comp { */ #define SOF_BUF_UNDERRUN_PERMITTED BIT(1) -/* the UUID size in bytes, shared between FW and host */ -#define SOF_UUID_SIZE 16 - /* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */ struct sof_ipc_buffer { struct sof_ipc_comp comp; @@ -303,9 +300,4 @@ enum sof_event_types { SOF_KEYWORD_DETECT_DAPM_EVENT, }; -/* extended data struct for UUID components */ -struct sof_ipc_comp_ext { - uint8_t uuid[SOF_UUID_SIZE]; -} __packed; - #endif diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a8eeffc12b24..10330d826d40 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -110,8 +110,7 @@ struct snd_sof_widget { struct list_head list; /* list in sdev widget list */ struct snd_sof_widget *pipe_widget; - /* extended data for UUID components */ - struct sof_ipc_comp_ext comp_ext; + const guid_t uuid; void *private; /* core does not touch this */ }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 41927e99ace2..111ff0f77be4 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -743,7 +743,7 @@ static const struct sof_topology_token core_tokens[] = { static const struct sof_topology_token comp_ext_tokens[] = { {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, - offsetof(struct sof_ipc_comp_ext, uuid)}, + offsetof(struct snd_sof_widget, uuid)}, }; /* @@ -1419,16 +1419,16 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, * * Return: The pointer to the new allocated component, NULL if failed. */ -static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, - size_t *ipc_size, int index) +static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, + int index) { - u8 nil_uuid[SOF_UUID_SIZE] = {0}; struct sof_ipc_comp *comp; size_t total_size = *ipc_size; + size_t ext_size = sizeof(swidget->uuid); /* only non-zero UUID is valid */ - if (memcmp(&swidget->comp_ext, nil_uuid, SOF_UUID_SIZE)) - total_size += sizeof(swidget->comp_ext); + if (!guid_is_null(&swidget->uuid)) + total_size += ext_size; comp = kzalloc(total_size, GFP_KERNEL); if (!comp) @@ -1444,8 +1444,8 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, /* handle the extended data if needed */ if (total_size > *ipc_size) { /* append extended data to the end of the component */ - memcpy((u8 *)comp + *ipc_size, &swidget->comp_ext, sizeof(swidget->comp_ext)); - comp->ext_data_length = sizeof(swidget->comp_ext); + memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); + comp->ext_data_length = ext_size; } /* update ipc_size and return */ @@ -2276,9 +2276,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->core = comp.core; - ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens, - ARRAY_SIZE(comp_ext_tokens), tw->priv.array, - le32_to_cpu(tw->priv.size)); + ret = sof_parse_tokens(scomp, swidget, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), + tw->priv.array, le32_to_cpu(tw->priv.size)); if (ret != 0) { dev_err(scomp->dev, "error: parsing comp_ext_tokens failed %d\n", ret); -- cgit v1.2.3 From 2b4b383f85baa493039a1fd80ca3f428f9504a54 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:38 -0800 Subject: ASoC: SOF: topology: make sof_route_load() IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IPC structure can be set up using the fields in struct snd_sof_route when the pipeline connections are established. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-13-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 15 ++++++--------- sound/soc/sof/topology.c | 17 ----------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b49d8e348077..8fccfbb339a3 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -259,28 +259,25 @@ EXPORT_SYMBOL(sof_widget_setup); static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { - struct sof_ipc_pipe_comp_connect *connect; + struct sof_ipc_pipe_comp_connect connect; struct sof_ipc_reply reply; int ret; - /* skip if there's no private data */ - if (!sroute->private) - return 0; - /* nothing to do if route is already set up */ if (sroute->setup) return 0; - connect = sroute->private; + connect.hdr.size = sizeof(connect); + connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + connect.source_id = sroute->src_widget->comp_id; + connect.sink_id = sroute->sink_widget->comp_id; dev_dbg(sdev->dev, "setting up route %s -> %s\n", sroute->src_widget->widget->name, sroute->sink_widget->widget->name); /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), + ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), &reply, sizeof(reply)); if (ret < 0) { dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 111ff0f77be4..42260d0b9740 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3322,7 +3322,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_pipe_comp_connect *connect; struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; @@ -3334,16 +3333,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, return -ENOMEM; sroute->scomp = scomp; - - connect = kzalloc(sizeof(*connect), GFP_KERNEL); - if (!connect) { - kfree(sroute); - return -ENOMEM; - } - - connect->hdr.size = sizeof(*connect); - connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; - dev_dbg(scomp->dev, "sink %s control %s source %s\n", route->sink, route->control ? route->control : "none", route->source); @@ -3367,8 +3356,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, source_swidget->id == snd_soc_dapm_output) goto err; - connect->source_id = source_swidget->comp_id; - /* sink component */ sink_swidget = snd_sof_find_swidget(scomp, (char *)route->sink); if (!sink_swidget) { @@ -3386,8 +3373,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sink_swidget->id == snd_soc_dapm_output) goto err; - connect->sink_id = sink_swidget->comp_id; - /* * For virtual routes, both sink and source are not * buffer. Since only buffer linked to component is supported by @@ -3402,7 +3387,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, } else { sroute->route = route; dobj->private = sroute; - sroute->private = connect; sroute->src_widget = source_swidget; sroute->sink_widget = sink_swidget; @@ -3413,7 +3397,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, } err: - kfree(connect); kfree(sroute); return ret; } -- cgit v1.2.3 From c7b655ade96a282a4304d68dcb120fcb61f19db2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:39 -0800 Subject: ASoC: SOF: Add a tuples array to struct snd_sof_widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 2 new fields to snd_sof_widget to store an array of tuples defined by struct snd_sof_tuple and the number of tuples. When the topology gets parsed, the tuples associated with a widget will be stored in this array and will be used to construct the IPC structure depending on the IPC version. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-14-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 10330d826d40..2a1da9e13279 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,6 +30,18 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +/** struct snd_sof_tuple - Tuple info + * @token: Token ID + * @value: union of a string or a u32 values + */ +struct snd_sof_tuple { + u32 token; + union { + u32 v; + const char *s; + } value; +}; + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; @@ -112,6 +124,9 @@ struct snd_sof_widget { const guid_t uuid; + int num_tuples; + struct snd_sof_tuple *tuples; + void *private; /* core does not touch this */ }; -- cgit v1.2.3 From 38a9a06794fe052dab57177f0320f44124e6cef9 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:40 -0800 Subject: ASoC: SOF: topology: Modify signature for token parsing functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the signature for sof_parse_uuid_tokens(), sof_parse_word_tokens() and sof_parse_string_tokens() to reorder the arguments to be more intuitive and rename the count arg to num_tokens. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-15-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 78 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 42260d0b9740..8b2c01f45b72 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -807,12 +807,21 @@ static const struct sof_topology_token afe_tokens[] = { offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, }; +/** + * sof_parse_uuid_tokens - Parse multiple sets of UUID tokens + * @scomp: pointer to soc component + * @object: target ipc struct for parsed values + * @offset: offset within the object pointer + * @tokens: array of struct sof_topology_token containing the tokens to be matched + * @num_tokens: number of tokens in tokens array + * @array: source pointer to consecutive vendor arrays in topology + * + * This function parses multiple sets of string type tokens in vendor arrays + */ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, - void *object, - const struct sof_topology_token *tokens, - int count, - struct snd_soc_tplg_vendor_array *array, - size_t offset) + void *object, size_t offset, + const struct sof_topology_token *tokens, int num_tokens, + struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_uuid_elem *elem; int found = 0; @@ -823,7 +832,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, elem = &array->uuid[i]; /* search for token */ - for (j = 0; j < count; j++) { + for (j = 0; j < num_tokens; j++) { /* match token type */ if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) continue; @@ -843,12 +852,21 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, return found; } +/** + * sof_parse_string_tokens - Parse multiple sets of tokens + * @scomp: pointer to soc component + * @object: target ipc struct for parsed values + * @offset: offset within the object pointer + * @tokens: array of struct sof_topology_token containing the tokens to be matched + * @num_tokens: number of tokens in tokens array + * @array: source pointer to consecutive vendor arrays in topology + * + * This function parses multiple sets of string type tokens in vendor arrays + */ static int sof_parse_string_tokens(struct snd_soc_component *scomp, - void *object, - const struct sof_topology_token *tokens, - int count, - struct snd_soc_tplg_vendor_array *array, - size_t offset) + void *object, int offset, + const struct sof_topology_token *tokens, int num_tokens, + struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_string_elem *elem; int found = 0; @@ -859,7 +877,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp, elem = &array->string[i]; /* search for token */ - for (j = 0; j < count; j++) { + for (j = 0; j < num_tokens; j++) { /* match token type */ if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) continue; @@ -878,12 +896,21 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp, return found; } +/** + * sof_parse_word_tokens - Parse multiple sets of tokens + * @scomp: pointer to soc component + * @object: target ipc struct for parsed values + * @offset: offset within the object pointer + * @tokens: array of struct sof_topology_token containing the tokens to be matched + * @num_tokens: number of tokens in tokens array + * @array: source pointer to consecutive vendor arrays in topology + * + * This function parses multiple sets of word type tokens in vendor arrays + */ static int sof_parse_word_tokens(struct snd_soc_component *scomp, - void *object, - const struct sof_topology_token *tokens, - int count, - struct snd_soc_tplg_vendor_array *array, - size_t offset) + void *object, int offset, + const struct sof_topology_token *tokens, int num_tokens, + struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_value_elem *elem; int found = 0; @@ -894,7 +921,7 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp, elem = &array->value[i]; /* search for token */ - for (j = 0; j < count; j++) { + for (j = 0; j < num_tokens; j++) { /* match token type */ if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || @@ -907,8 +934,7 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp, continue; /* load token */ - tokens[j].get_token(elem, object, - offset + tokens[j].offset); + tokens[j].get_token(elem, object, offset + tokens[j].offset); found++; } @@ -964,19 +990,19 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, /* call correct parser depending on type */ switch (le32_to_cpu(array->type)) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: - found += sof_parse_uuid_tokens(scomp, object, tokens, - count, array, offset); + found += sof_parse_uuid_tokens(scomp, object, offset, tokens, count, + array); break; case SND_SOC_TPLG_TUPLE_TYPE_STRING: - found += sof_parse_string_tokens(scomp, object, tokens, - count, array, offset); + found += sof_parse_string_tokens(scomp, object, offset, tokens, count, + array); break; case SND_SOC_TPLG_TUPLE_TYPE_BOOL: case SND_SOC_TPLG_TUPLE_TYPE_BYTE: case SND_SOC_TPLG_TUPLE_TYPE_WORD: case SND_SOC_TPLG_TUPLE_TYPE_SHORT: - found += sof_parse_word_tokens(scomp, object, tokens, - count, array, offset); + found += sof_parse_word_tokens(scomp, object, offset, tokens, count, + array); break; default: dev_err(scomp->dev, "error: unknown token type %d\n", -- cgit v1.2.3 From e0974a382e7c4e0962edebbd8e0d3185c928ea85 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:41 -0800 Subject: ASoC: SOF: topology: Rename arguments in sof_parse_token_sets() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the priv_size arg to array_size and clarify the arguments in the comments. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-16-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 8b2c01f45b72..0683b7d812eb 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -949,27 +949,26 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp, * @object: target ipc struct for parsed values * @tokens: token definition array describing what tokens to parse * @count: number of tokens in definition array - * @array: source pointer to consecutive vendor arrays to be parsed - * @priv_size: total size of the consecutive source arrays - * @sets: number of similar token sets to be parsed, 1 set has count elements + * @array: source pointer to consecutive vendor arrays in topology + * @array_size: total size of @array + * @token_instance_num: number of times the same tokens needs to be parsed i.e. the function + * looks for @token_instance_num of each token in the @tokens * @object_size: offset to next target ipc struct with multiple sets * * This function parses multiple sets of tokens in vendor arrays into * consecutive ipc structs. */ static int sof_parse_token_sets(struct snd_soc_component *scomp, - void *object, - const struct sof_topology_token *tokens, - int count, - struct snd_soc_tplg_vendor_array *array, - int priv_size, int sets, size_t object_size) + void *object, const struct sof_topology_token *tokens, + int count, struct snd_soc_tplg_vendor_array *array, + int array_size, int token_instance_num, size_t object_size) { size_t offset = 0; int found = 0; int total = 0; int asize; - while (priv_size > 0 && total < count * sets) { + while (array_size > 0 && total < count * token_instance_num) { asize = le32_to_cpu(array->size); /* validate asize */ @@ -980,8 +979,8 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, } /* make sure there is enough data before parsing */ - priv_size -= asize; - if (priv_size < 0) { + array_size -= asize; + if (array_size < 0) { dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; -- cgit v1.2.3 From 5f8333f62fcada65a85aac53c6a39eb6c4f0bb4e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:42 -0800 Subject: ASoC: SOF: topology: Rename arguments in sof_parse_tokens() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the count and priv_size arguments in sof_parse_tokens() and add comments to clarify the arguments. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-17-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 0683b7d812eb..07edb8d8652c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1024,12 +1024,23 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, return 0; } -static int sof_parse_tokens(struct snd_soc_component *scomp, - void *object, - const struct sof_topology_token *tokens, - int count, +/** + * sof_parse_tokens - Parse one set of tokens + * @scomp: pointer to soc component + * @object: target ipc struct for parsed values + * @tokens: token definition array describing what tokens to parse + * @num_tokens: number of tokens in definition array + * @array: source pointer to consecutive vendor arrays in topology + * @array_size: total size of @array + * + * This function parses a single set of tokens in vendor arrays into + * consecutive ipc structs. + */ +static int sof_parse_tokens(struct snd_soc_component *scomp, void *object, + const struct sof_topology_token *tokens, int num_tokens, struct snd_soc_tplg_vendor_array *array, - int priv_size) + int array_size) + { /* * sof_parse_tokens is used when topology contains only a single set of @@ -1037,8 +1048,8 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, * sof_parse_token_sets are sets = 1 (only 1 set) and * object_size = 0 (irrelevant). */ - return sof_parse_token_sets(scomp, object, tokens, count, array, - priv_size, 1, 0); + return sof_parse_token_sets(scomp, object, tokens, num_tokens, array, + array_size, 1, 0); } static void sof_dbg_comp_config(struct snd_soc_component *scomp, -- cgit v1.2.3 From 839e484f9e173309d599e1281eb7221e07f41814 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 8 Mar 2022 08:43:43 -0800 Subject: ASoC: SOF: make struct snd_sof_dai IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the comp_dai and dai_config members of struct snd_sof_dai and replace it with a void *private field. Introduce a new struct sof_dai_private_data that will contain the pointer to these two fields. The topology parser will populate this structure and save it as part of the "private" member in snd_sof_dai. Change all users of these fields to use the private member instead. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220308164344.577647-18-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 5 +++ sound/soc/sof/intel/hda-dai.c | 27 +++++++++++---- sound/soc/sof/intel/hda.c | 39 +++++++++++++++++----- sound/soc/sof/pcm.c | 77 ++++++++++++++++++++++--------------------- sound/soc/sof/sof-audio.c | 30 ++++++++++------- sound/soc/sof/sof-audio.h | 3 +- sound/soc/sof/topology.c | 38 ++++++++++++++------- 7 files changed, 143 insertions(+), 76 deletions(-) diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 59ee50ac7705..a818a0f0a226 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -116,4 +116,9 @@ struct sof_ipc_dai_config { }; } __packed; +struct sof_dai_private_data { + struct sof_ipc_comp_dai *comp_dai; + struct sof_ipc_dai_config *dai_config; +}; + #endif diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 75063140ed0c..9b78eea8d76b 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -167,6 +167,7 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg int channel) { struct snd_sof_widget *swidget = w->dobj.private; + struct sof_dai_private_data *private; struct sof_ipc_dai_config *config; struct snd_sof_dai *sof_dai; @@ -175,12 +176,19 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg sof_dai = swidget->private; - if (!sof_dai || !sof_dai->dai_config) { - dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name); + if (!sof_dai || !sof_dai->private) { + dev_err(swidget->scomp->dev, "%s: No private data for DAI %s\n", __func__, + w->name); return NULL; } - config = &sof_dai->dai_config[sof_dai->current_config]; + private = sof_dai->private; + if (!private->dai_config) { + dev_err(swidget->scomp->dev, "%s: No config for DAI %s\n", __func__, w->name); + return NULL; + } + + config = &private->dai_config[sof_dai->current_config]; /* update config with stream tag */ config->hda.link_dma_ch = channel; @@ -294,6 +302,7 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private; struct sof_ipc_dai_config *config; struct snd_sof_dai *sof_dai; struct sof_ipc_reply reply; @@ -301,12 +310,18 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) sof_dai = swidget->private; - if (!sof_dai || !sof_dai->dai_config) { - dev_err(sdev->dev, "No config for DAI %s\n", w->name); + if (!sof_dai || !sof_dai->private) { + dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); + return -EINVAL; + } + + private = sof_dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); return -EINVAL; } - config = &sof_dai->dai_config[sof_dai->current_config]; + config = &private->dai_config[sof_dai->current_config]; /* set PAUSE command flag */ config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index a99e6608f0b6..0112097fbba4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -47,14 +47,21 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_ struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct sof_ipc_dai_config *config; + struct sof_dai_private_data *private; struct snd_sof_dai *sof_dai; struct sof_ipc_reply reply; int ret; sof_dai = swidget->private; - if (!sof_dai || !sof_dai->dai_config) { - dev_err(sdev->dev, "No config for DAI %s\n", w->name); + if (!sof_dai || !sof_dai->private) { + dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); + return -EINVAL; + } + + private = sof_dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); return -EINVAL; } @@ -65,7 +72,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_ return ret; } - config = &sof_dai->dai_config[sof_dai->current_config]; + config = &private->dai_config[sof_dai->current_config]; /* * For static pipelines, the DAI widget would already be set up and calling @@ -101,6 +108,7 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private; struct sof_ipc_dai_config *config; struct snd_sof_dai *sof_dai; struct sof_ipc_reply reply; @@ -108,8 +116,14 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f sof_dai = swidget->private; - if (!sof_dai || !sof_dai->dai_config) { - dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name); + if (!sof_dai || !sof_dai->private) { + dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); + return -EINVAL; + } + + private = sof_dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); return -EINVAL; } @@ -117,7 +131,7 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f if (!sof_dai->configured) return 0; - config = &sof_dai->dai_config[sof_dai->current_config]; + config = &private->dai_config[sof_dai->current_config]; /* set HW_FREE flag along with any quirks */ config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | @@ -154,6 +168,7 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, int link_id, int alh_stream_id, int dai_id, bool setup) { struct snd_sof_widget *swidget = w->dobj.private; + struct sof_dai_private_data *private; struct sof_ipc_dai_config *config; struct snd_sof_dai *sof_dai; @@ -164,12 +179,18 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, sof_dai = swidget->private; - if (!sof_dai || !sof_dai->dai_config) { - dev_err(sdev->dev, "error: No config for DAI %s\n", w->name); + if (!sof_dai || !sof_dai->private) { + dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); + return -EINVAL; + } + + private = sof_dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); return -EINVAL; } - config = &sof_dai->dai_config[sof_dai->current_config]; + config = &private->dai_config[sof_dai->current_config]; /* update config with link and stream ID */ config->dai_index = (link_id << 8) | dai_id; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1d04f75e6d32..4628bc642fda 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -670,7 +670,9 @@ static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char if (!dai->name || strcmp(link_name, dai->name)) continue; for (i = 0; i < dai->number_configs; i++) { - config = &dai->dai_config[i]; + struct sof_dai_private_data *private = dai->private; + + config = &private->dai_config[i]; if (config->ssp.fsync_rate == params_rate(params)) { dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); dai->current_config = i; @@ -693,6 +695,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private = dai->private; struct snd_soc_dpcm *dpcm; /* no topology exists for this BE, try a common configuration */ @@ -717,7 +720,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa /* read format from topology */ snd_mask_none(fmt); - switch (dai->comp_dai->config.frame_fmt) { + switch (private->comp_dai->config.frame_fmt) { case SOF_IPC_FRAME_S16_LE: snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); break; @@ -733,15 +736,15 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa } /* read rate and channels from topology */ - switch (dai->dai_config->type) { + switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: /* search for config to pcm params match, if not found use default */ ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - rate->min = dai->dai_config[dai->current_config].ssp.fsync_rate; - rate->max = dai->dai_config[dai->current_config].ssp.fsync_rate; - channels->min = dai->dai_config[dai->current_config].ssp.tdm_slots; - channels->max = dai->dai_config[dai->current_config].ssp.tdm_slots; + rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; + rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; + channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; + channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -752,11 +755,11 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa break; case SOF_DAI_INTEL_DMIC: /* DMIC only supports 16 or 32 bit formats */ - if (dai->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { dev_err(component->dev, "error: invalid fmt %d for DAI type %d\n", - dai->comp_dai->config.frame_fmt, - dai->dai_config->type); + private->comp_dai->config.frame_fmt, + private->dai_config->type); } break; case SOF_DAI_INTEL_HDA: @@ -776,14 +779,14 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa * Dai could run with different channel count compared with * front end, so get dai channel count from topology */ - channels->min = dai->dai_config->alh.channels; - channels->max = dai->dai_config->alh.channels; + channels->min = private->dai_config->alh.channels; + channels->max = private->dai_config->alh.channels; break; case SOF_DAI_IMX_ESAI: - rate->min = dai->dai_config->esai.fsync_rate; - rate->max = dai->dai_config->esai.fsync_rate; - channels->min = dai->dai_config->esai.tdm_slots; - channels->max = dai->dai_config->esai.tdm_slots; + rate->min = private->dai_config->esai.fsync_rate; + rate->max = private->dai_config->esai.fsync_rate; + channels->min = private->dai_config->esai.tdm_slots; + channels->max = private->dai_config->esai.tdm_slots; dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -792,10 +795,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min, channels->max); break; case SOF_DAI_MEDIATEK_AFE: - rate->min = dai->dai_config->afe.rate; - rate->max = dai->dai_config->afe.rate; - channels->min = dai->dai_config->afe.channels; - channels->max = dai->dai_config->afe.channels; + rate->min = private->dai_config->afe.rate; + rate->max = private->dai_config->afe.rate; + channels->min = private->dai_config->afe.channels; + channels->max = private->dai_config->afe.channels; dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -804,10 +807,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min, channels->max); break; case SOF_DAI_IMX_SAI: - rate->min = dai->dai_config->sai.fsync_rate; - rate->max = dai->dai_config->sai.fsync_rate; - channels->min = dai->dai_config->sai.tdm_slots; - channels->max = dai->dai_config->sai.tdm_slots; + rate->min = private->dai_config->sai.fsync_rate; + rate->max = private->dai_config->sai.fsync_rate; + channels->min = private->dai_config->sai.tdm_slots; + channels->max = private->dai_config->sai.tdm_slots; dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -816,10 +819,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min, channels->max); break; case SOF_DAI_AMD_BT: - rate->min = dai->dai_config->acpbt.fsync_rate; - rate->max = dai->dai_config->acpbt.fsync_rate; - channels->min = dai->dai_config->acpbt.tdm_slots; - channels->max = dai->dai_config->acpbt.tdm_slots; + rate->min = private->dai_config->acpbt.fsync_rate; + rate->max = private->dai_config->acpbt.fsync_rate; + channels->min = private->dai_config->acpbt.tdm_slots; + channels->max = private->dai_config->acpbt.tdm_slots; dev_dbg(component->dev, "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -828,10 +831,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min, channels->max); break; case SOF_DAI_AMD_SP: - rate->min = dai->dai_config->acpsp.fsync_rate; - rate->max = dai->dai_config->acpsp.fsync_rate; - channels->min = dai->dai_config->acpsp.tdm_slots; - channels->max = dai->dai_config->acpsp.tdm_slots; + rate->min = private->dai_config->acpsp.fsync_rate; + rate->max = private->dai_config->acpsp.fsync_rate; + channels->min = private->dai_config->acpsp.tdm_slots; + channels->max = private->dai_config->acpsp.tdm_slots; dev_dbg(component->dev, "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -840,10 +843,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min, channels->max); break; case SOF_DAI_AMD_DMIC: - rate->min = dai->dai_config->acpdmic.fsync_rate; - rate->max = dai->dai_config->acpdmic.fsync_rate; - channels->min = dai->dai_config->acpdmic.tdm_slots; - channels->max = dai->dai_config->acpdmic.tdm_slots; + rate->min = private->dai_config->acpdmic.fsync_rate; + rate->max = private->dai_config->acpdmic.fsync_rate; + channels->min = private->dai_config->acpdmic.tdm_slots; + channels->max = private->dai_config->acpdmic.tdm_slots; dev_dbg(component->dev, "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -853,7 +856,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa break; default: dev_err(component->dev, "error: invalid DAI type %d\n", - dai->dai_config->type); + private->dai_config->type); break; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 8fccfbb339a3..15c36a51f89f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -29,11 +29,12 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai) { + struct sof_dai_private_data *private = dai->private; struct sof_ipc_dai_config *config; struct sof_ipc_reply reply; int ret; - config = &dai->dai_config[dai->current_config]; + config = &private->dai_config[dai->current_config]; if (!config) { dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name); return -EINVAL; @@ -191,12 +192,16 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) switch (swidget->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: + { + struct sof_dai_private_data *dai_data; + dai = swidget->private; - comp = &dai->comp_dai->comp; + dai_data = dai->private; + comp = &dai_data->comp_dai->comp; dai->configured = false; - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai->comp_dai, comp->hdr.size, - &r, sizeof(r)); + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, + comp->hdr.size, &r, sizeof(r)); if (ret < 0) { dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); @@ -216,6 +221,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) return ret; } break; + } case snd_soc_dapm_scheduler: pipeline = swidget->private; ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, @@ -620,12 +626,13 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) /* update DAI config. The IPC will be sent in sof_widget_setup() */ if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private = dai->private; struct sof_ipc_dai_config *config; - if (!dai || !dai->dai_config) + if (!dai || !private || !private->dai_config) continue; - config = dai->dai_config; + config = private->dai_config; /* * The link DMA channel would be invalidated for running * streams but not for streams that were in the PAUSED @@ -911,18 +918,19 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); + struct sof_dai_private_data *private = dai->private; /* use the tplg configured mclk if existed */ - if (!dai || !dai->dai_config) + if (!dai || !private || !private->dai_config) return 0; - switch (dai->dai_config->type) { + switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: switch (clk_type) { case SOF_DAI_CLK_INTEL_SSP_MCLK: - return dai->dai_config->ssp.mclk_rate; + return private->dai_config->ssp.mclk_rate; case SOF_DAI_CLK_INTEL_SSP_BCLK: - return dai->dai_config->ssp.bclk_rate; + return private->dai_config->ssp.bclk_rate; default: dev_err(rtd->dev, "fail to get SSP clk %d rate\n", clk_type); @@ -932,7 +940,7 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) default: /* not yet implemented for platforms other than the above */ dev_err(rtd->dev, "DAI type %d not supported yet!\n", - dai->dai_config->type); + private->dai_config->type); return -EINVAL; } } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 2a1da9e13279..450ee9977c55 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -148,12 +148,11 @@ struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; - struct sof_ipc_comp_dai *comp_dai; int number_configs; int current_config; bool configured; /* DAI configured during BE hw_params */ - struct sof_ipc_dai_config *dai_config; struct list_head list; /* list in sdev dai list */ + void *private; }; /* diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 07edb8d8652c..afd9eda67631 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1495,14 +1495,21 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, struct snd_sof_dai *dai) { struct snd_soc_tplg_private *private = &tw->priv; + struct sof_dai_private_data *dai_data; struct sof_ipc_comp_dai *comp_dai; size_t ipc_size = sizeof(*comp_dai); int ret; + dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + comp_dai = (struct sof_ipc_comp_dai *) sof_comp_alloc(swidget, &ipc_size, index); - if (!comp_dai) - return -ENOMEM; + if (!comp_dai) { + ret = -ENOMEM; + goto free; + } /* configure dai IPC message */ comp_dai->comp.type = SOF_COMP_DAI; @@ -1514,7 +1521,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (ret != 0) { dev_err(scomp->dev, "error: parse dai tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto free; } ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens, @@ -1523,7 +1530,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (ret != 0) { dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", private->size); - return ret; + goto free; } dev_dbg(scomp->dev, "dai %s: type %d index %d\n", @@ -1532,9 +1539,14 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (dai) { dai->scomp = scomp; - dai->comp_dai = comp_dai; + dai_data->comp_dai = comp_dai; + dai->private = dai_data; } + return 0; + +free: + kfree(dai_data); return ret; } @@ -2456,9 +2468,11 @@ static int sof_widget_unload(struct snd_soc_component *scomp, dai = swidget->private; if (dai) { - kfree(dai->comp_dai); - /* free dai config */ - kfree(dai->dai_config); + struct sof_dai_private_data *dai_data = dai->private; + + kfree(dai_data->comp_dai); + kfree(dai_data->dai_config); + kfree(dai_data); list_del(&dai->list); } break; @@ -2680,11 +2694,13 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, struct sof_ipc_dai_config *config, int num_conf, int curr_conf) { + struct sof_dai_private_data *dai_data; struct snd_sof_dai *dai; int found = 0; int i; list_for_each_entry(dai, &sdev->dai_list, list) { + dai_data = dai->private; if (!dai->name) continue; @@ -2696,15 +2712,15 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, * dai_index. */ for (i = 0; i < num_conf; i++) - config[i].dai_index = dai->comp_dai->dai_index; + config[i].dai_index = dai_data->comp_dai->dai_index; dev_dbg(sdev->dev, "set DAI config for %s index %d\n", dai->name, config[curr_conf].dai_index); dai->number_configs = num_conf; dai->current_config = curr_conf; - dai->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL); - if (!dai->dai_config) + dai_data->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL); + if (!dai_data->dai_config) return -ENOMEM; found = 1; -- cgit v1.2.3 From f535880b2d32fcb85d99a81483d44c8df23d23cf Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 8 Mar 2022 08:43:44 -0800 Subject: ASoC: SOF: move definition of snd_sof_ipc to header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move definition of struct snd_sof_ipc to the header file so it can be shared with new IPC versions. Signed-off-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220308164344.577647-19-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 12 ------------ sound/soc/sof/sof-priv.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index c729bb7bf8c8..34084e0008f1 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -27,18 +27,6 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf); * IPC message Tx/Rx message handling. */ -/* SOF generic IPC data */ -struct snd_sof_ipc { - struct snd_sof_dev *sdev; - - /* protects messages and the disable flag */ - struct mutex tx_mutex; - /* disables further sending of ipc's */ - bool disable_ipc_tx; - - struct snd_sof_ipc_msg msg; -}; - struct sof_ipc_ctrl_data_params { size_t msg_bytes; size_t hdr_bytes; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 2e19ac619ad5..7f0514db4d06 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -345,6 +345,18 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; +/* SOF generic IPC data */ +struct snd_sof_ipc { + struct snd_sof_dev *sdev; + + /* protects messages and the disable flag */ + struct mutex tx_mutex; + /* disables further sending of ipc's */ + bool disable_ipc_tx; + + struct snd_sof_ipc_msg msg; +}; + /* * SOF Device Level. */ -- cgit v1.2.3 From 1174442b82b6cf13328a5f9574fac3655c58ae06 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:51 -0600 Subject: ASoC: soc-acpi: fix kernel-doc descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing dmic_num mention and clarify that 'links' mean 'SoundWire links', not to be used for other links. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index fdb536d699ff..a8fd62c00f91 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -60,9 +60,10 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @acpi_ipc_irq_index: used for BYT-CR detection * @platform: string used for HDAudio codec support * @codec_mask: used for HDAudio support + * @dmic_num: number of SoC- or chipset-attached PDM digital microphones * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver - * @link_mask: links enabled on the board - * @links: array of link _ADR descriptors, null terminated + * @link_mask: SoundWire links enabled on the board + * @links: array of SoundWire link _ADR descriptors, null terminated * @num_dai_drivers: number of elements in @dai_drivers * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode */ -- cgit v1.2.3 From 679aa83a0fb70dcbf9e97cbdfd573e6fc8bf9b1a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:52 -0600 Subject: ASoC: soc-acpi: add information on I2S/TDM link mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform driver may have information on which I2S/TDM link(s) to enable in the machine driver. In the case of Intel devices, this may be extracted from NHLT tables in platform firmware. This link information is necessary to make sure machine driver and topology are aligned. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index a8fd62c00f91..093bbe7f0e1f 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -64,6 +64,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver * @link_mask: SoundWire links enabled on the board * @links: array of SoundWire link _ADR descriptors, null terminated + * @i2s_link_mask: I2S/TDM links enabled on the board * @num_dai_drivers: number of elements in @dai_drivers * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode */ @@ -75,6 +76,7 @@ struct snd_soc_acpi_mach_params { bool common_hdmi_codec_drv; u32 link_mask; const struct snd_soc_acpi_link_adr *links; + u32 i2s_link_mask; u32 num_dai_drivers; struct snd_soc_dai_driver *dai_drivers; }; -- cgit v1.2.3 From 92c1b7c0f780f0084f7b114be316ae4e182676e5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:53 -0600 Subject: ASoC: SOF: Intel: hda: retrieve DMIC number for I2S boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently extract the DMIC number only for HDaudio or SoundWire platforms. For I2S/TDM platforms, this wasn't necessary until now, but with devices with ES8336 we need to find a solution to detect dmics more reliably than with a DMI quirk. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index a99e6608f0b6..711d14a821bb 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -432,11 +432,9 @@ static char *hda_model; module_param(hda_model, charp, 0444); MODULE_PARM_DESC(hda_model, "Use the given HDA board model."); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) -static int hda_dmic_num = -1; -module_param_named(dmic_num, hda_dmic_num, int, 0444); +static int dmic_num_override = -1; +module_param_named(dmic_num, dmic_num_override, int, 0444); MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number"); -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) static bool hda_codec_use_common_hdmi = IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI); @@ -644,24 +642,35 @@ static int hda_init(struct snd_sof_dev *sdev) return ret; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) - -static int check_nhlt_dmic(struct snd_sof_dev *sdev) +static int check_dmic_num(struct snd_sof_dev *sdev) { struct nhlt_acpi_table *nhlt; - int dmic_num; + int dmic_num = 0; nhlt = intel_nhlt_init(sdev->dev); if (nhlt) { dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt); intel_nhlt_free(nhlt); - if (dmic_num >= 1 && dmic_num <= 4) - return dmic_num; } - return 0; + /* allow for module parameter override */ + if (dmic_num_override != -1) { + dev_dbg(sdev->dev, + "overriding DMICs detected in NHLT tables %d by kernel param %d\n", + dmic_num, dmic_num_override); + dmic_num = dmic_num_override; + } + + if (dmic_num < 0 || dmic_num > 4) { + dev_dbg(sdev->dev, "invalid dmic_number %d\n", dmic_num); + dmic_num = 0; + } + + return dmic_num; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + static const char *fixup_tplg_name(struct snd_sof_dev *sdev, const char *sof_tplg_filename, const char *idisp_str, @@ -697,16 +706,8 @@ static int dmic_topology_fixup(struct snd_sof_dev *sdev, const char *dmic_str; int dmic_num; - /* first check NHLT for DMICs */ - dmic_num = check_nhlt_dmic(sdev); - - /* allow for module parameter override */ - if (hda_dmic_num != -1) { - dev_dbg(sdev->dev, - "overriding DMICs detected in NHLT tables %d by kernel param %d\n", - dmic_num, hda_dmic_num); - dmic_num = hda_dmic_num; - } + /* first check for DMICs (using NHLT or module parameter) */ + dmic_num = check_dmic_num(sdev); switch (dmic_num) { case 1: @@ -1383,6 +1384,9 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) if (!sof_pdata->tplg_filename) sof_pdata->tplg_filename = mach->sof_tplg_filename; + /* report to machine driver if any DMICs are found */ + mach->mach_params.dmic_num = check_dmic_num(sdev); + if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; -- cgit v1.2.3 From 0c470db0399e17310ed2ba54dd1c25cfa16ce0d3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:54 -0600 Subject: ALSA: intel-nhlt: add helper to detect SSP link mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NHLT information can be used to figure out which SSPs are enabled in a platform. The 'SSP' link type is too broad for machine drivers, since it can cover the Bluetooth sideband and the analog audio codec connections, so this helper exposes a parameter to filter with the device type (DEVICE_I2S refers to analog audio codec in NHLT parlance). The helper returns a mask, since more than one SSP may be used for analog audio, e.g. the NHLT spec describes the use of SSP0 for amplifiers and SSP1 for headset codec. Note that if more than one bit is set, it's impossible to determine which SSP is connected to what external component. Additional platform-specific information based on e.g. DMI quirks would still be required in the machine driver to configure the relevant dailinks. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20220308192610.392950-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/intel-nhlt.h | 22 +++++++++++++++------- sound/hda/intel-nhlt.c | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index 089a760d36eb..6fb2d5e378fd 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -18,6 +18,13 @@ enum nhlt_link_type { NHLT_LINK_INVALID }; +enum nhlt_device_type { + NHLT_DEVICE_BT = 0, + NHLT_DEVICE_DMIC = 1, + NHLT_DEVICE_I2S = 4, + NHLT_DEVICE_INVALID +}; + #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) struct wav_fmt { @@ -41,13 +48,6 @@ struct wav_fmt_ext { u8 sub_fmt[16]; } __packed; -enum nhlt_device_type { - NHLT_DEVICE_BT = 0, - NHLT_DEVICE_DMIC = 1, - NHLT_DEVICE_I2S = 4, - NHLT_DEVICE_INVALID -}; - struct nhlt_specific_cfg { u32 size; u8 caps[]; @@ -133,6 +133,9 @@ void intel_nhlt_free(struct nhlt_acpi_table *addr); int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt); bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type); + +int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type); + struct nhlt_specific_cfg * intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, u32 bus_id, u8 link_type, u8 vbps, u8 bps, @@ -163,6 +166,11 @@ static inline bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, return false; } +static inline int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type) +{ + return 0; +} + static inline struct nhlt_specific_cfg * intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, u32 bus_id, u8 link_type, u8 vbps, u8 bps, diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index 128476aa7c61..4063da378283 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -130,6 +130,28 @@ bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type) } EXPORT_SYMBOL(intel_nhlt_has_endpoint_type); +int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type) +{ + struct nhlt_endpoint *epnt; + int ssp_mask = 0; + int i; + + if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S)) + return 0; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) { + /* for SSP the virtual bus id is the SSP port */ + ssp_mask |= BIT(epnt->virtual_bus_id); + } + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return ssp_mask; +} +EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask); + static struct nhlt_specific_cfg * nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch, u32 rate, u8 vbps, u8 bps) -- cgit v1.2.3 From bd015f633b05a3d4f88a3d7099746b2819a523f5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:55 -0600 Subject: ASoC: SOF: Intel: hda: report SSP link mask to machine driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For devices designed for Windows, the SSP information should be listed in the NHLT, and when present can be used to set quirks automatically in the machine driver. The NHLT information exposes BT and analog audio connections separately, for now we are only interested in the analog audio parts. The use of dev_info() for the SSP mask is intentional so that we can immediately flag devices with an ES8336 codec. Since NHLT is not used for recent Chromebooks these messages should be rare. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 711d14a821bb..eebb3b318d79 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -669,6 +669,25 @@ static int check_dmic_num(struct snd_sof_dev *sdev) return dmic_num; } +static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev) +{ + struct nhlt_acpi_table *nhlt; + int ssp_mask = 0; + + nhlt = intel_nhlt_init(sdev->dev); + if (!nhlt) + return ssp_mask; + + if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP)) { + ssp_mask = intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S); + if (ssp_mask) + dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask); + } + intel_nhlt_free(nhlt); + + return ssp_mask; +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) static const char *fixup_tplg_name(struct snd_sof_dev *sdev, @@ -1391,6 +1410,9 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; } + + /* report SSP link mask to machine driver */ + mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev); } /* -- cgit v1.2.3 From 4694b8382d6b79bcf95995757419d279a3ab375b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:56 -0600 Subject: ASoC: Intel: soc-acpi: quirk topology filename dynamically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different topology filenames may be required depending on which SSP is used, and whether or not digital mics are present. This patch adds a tplg_quirk_mask and in the case of the SOF driver adds the relevant configurations. This is a short-term solution to the ES8336 support issues. In a long-term solution, we would need an interface where the machine driver or platform driver have the ability to alter the topology hard-coded low-level hardware support, e.g. by substituting an interface for another, or disabling an interface that is not supported on a given skew. BugLink: https://github.com/thesofproject/linux/issues/3248 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 20 +++++++++ sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 5 ++- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 5 ++- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 5 ++- sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 5 ++- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 5 ++- sound/soc/sof/intel/hda.c | 52 +++++++++++++++++++++++ 7 files changed, 92 insertions(+), 5 deletions(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 093bbe7f0e1f..d33cf8df14b1 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -125,6 +125,24 @@ struct snd_soc_acpi_link_adr { const struct snd_soc_acpi_adr_device *adr_d; }; +/* + * when set the topology uses the -ssp suffix, where N is determined based on + * BIOS or DMI information + */ +#define SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER BIT(0) + +/* + * when more than one SSP is reported in the link mask, use the most significant. + * This choice was found to be valid on platforms with ES8336 codecs. + */ +#define SND_SOC_ACPI_TPLG_INTEL_SSP_MSB BIT(1) + +/* + * when set the topology uses the -dmicch suffix, where N is determined based on + * BIOS or DMI information + */ +#define SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER BIT(2) + /** * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are * related to the hardware, except for the firmware and topology file names. @@ -146,6 +164,7 @@ struct snd_soc_acpi_link_adr { * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled + * @tplg_quirk_mask: quirks to select different topology files dynamically */ /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { @@ -161,6 +180,7 @@ struct snd_soc_acpi_mach { void *pdata; struct snd_soc_acpi_mach_params mach_params; const char *sof_tplg_filename; + const u32 tplg_quirk_mask; }; #define SND_SOC_ACPI_MAX_CODECS 3 diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 718947068956..0a2d0874dc4f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -80,7 +80,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_tplg_filename = "sof-apl-es8336.tplg", + .sof_tplg_filename = "sof-apl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index d033474f8768..f75fa1b551d7 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -78,7 +78,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_tplg_filename = "sof-cml-es8336.tplg", + .sof_tplg_filename = "sof-cml-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index c5ca077c7ac9..d494860b8190 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -55,7 +55,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_tplg_filename = "sof-glk-es8336.tplg", + .sof_tplg_filename = "sof-glk-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index a2da5cad520c..53c42a4e1694 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -83,7 +83,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_tplg_filename = "sof-jsl-es8336.tplg", + .sof_tplg_filename = "sof-jsl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, {}, }; 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 224b54d35c7a..8bf14295deb0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -393,7 +393,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .id = "ESSX8336", .drv_name = "sof-essx8336", - .sof_tplg_filename = "sof-tgl-es8336.tplg", + .sof_tplg_filename = "sof-tgl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, { .id = "10EC1308", diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index eebb3b318d79..07d8686632a5 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1393,9 +1393,12 @@ struct snd_soc_acpi_mach *hda_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_soc_acpi_mach *mach; + const char *tplg_filename; mach = snd_soc_acpi_find_machine(desc->machines); if (mach) { + bool add_extension = false; + /* * If tplg file name is overridden, use it instead of * the one set in mach table @@ -1406,6 +1409,21 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) /* report to machine driver if any DMICs are found */ mach->mach_params.dmic_num = check_dmic_num(sdev); + if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER && + mach->mach_params.dmic_num) { + tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s%s%d%s", + sof_pdata->tplg_filename, + "-dmic", + mach->mach_params.dmic_num, + "ch"); + if (!tplg_filename) + return NULL; + + sof_pdata->tplg_filename = tplg_filename; + add_extension = true; + } + if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; @@ -1413,6 +1431,40 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) /* report SSP link mask to machine driver */ mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev); + + if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER && + mach->mach_params.i2s_link_mask) { + int ssp_num; + + if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && + !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) + dev_warn(sdev->dev, "More than one SSP exposed by NHLT, choosing MSB\n"); + + /* fls returns 1-based results, SSPs indices are 0-based */ + ssp_num = fls(mach->mach_params.i2s_link_mask) - 1; + + tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s%s%d", + sof_pdata->tplg_filename, + "-ssp", + ssp_num); + if (!tplg_filename) + return NULL; + + sof_pdata->tplg_filename = tplg_filename; + add_extension = true; + } + + if (add_extension) { + tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s%s", + sof_pdata->tplg_filename, + ".tplg"); + if (!tplg_filename) + return NULL; + + sof_pdata->tplg_filename = tplg_filename; + } } /* -- cgit v1.2.3 From de24d97fb845ffd2229811ee256438e42b5a8d12 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:57 -0600 Subject: ALSA: intel-dsp-config: add more ACPI HIDs for ES83x6 devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only saw ESSX8336 so far, but now with reports of 'ESSX8326' we need to expand to a full list. Let's reuse the 'snd_soc_acpi_codecs' structure to store the information. Reported-by: anthony tonitch Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20220308192610.392950-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/hda/intel-dsp-config.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 4fb90ceb4053..b9b7bf5a5553 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -11,6 +11,7 @@ #include #include #include +#include static int dsp_driver; @@ -31,7 +32,12 @@ struct config_entry { u16 device; u8 acpi_hid[ACPI_ID_LEN]; const struct dmi_system_id *dmi_table; - u8 codec_hid[ACPI_ID_LEN]; + const struct snd_soc_acpi_codecs *codec_hid; +}; + +static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; /* @@ -77,7 +83,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0x5a98, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) @@ -163,7 +169,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0x3198, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, #endif @@ -251,7 +257,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0x02c8, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, @@ -280,7 +286,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0x06c8, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, @@ -327,7 +333,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0x4dc8, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, @@ -353,7 +359,7 @@ static const struct config_entry config_table[] = { { .flags = FLAG_SOF, .device = 0xa0c8, - .codec_hid = "ESSX8336", + .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, @@ -414,8 +420,15 @@ static const struct config_entry *snd_intel_dsp_find_config continue; if (table->dmi_table && !dmi_check_system(table->dmi_table)) continue; - if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1)) - continue; + if (table->codec_hid) { + int i; + + for (i = 0; i < table->codec_hid->num_codecs; i++) + if (acpi_dev_present(table->codec_hid->codecs[i], NULL, -1)) + break; + if (i == table->codec_hid->num_codecs) + continue; + } return table; } return NULL; -- cgit v1.2.3 From 1cedb6eabf0f2dd8285d3bb0ce1abd2369529084 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:58 -0600 Subject: ASoC: Intel: soc-acpi: add more ACPI HIDs for ES83x6 devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only saw ESSX8336 so far, but now with reports of 'ESSX8326' we need to expand to a full list. Let's reuse the 'snd_soc_acpi_codecs' structure to store the information. Note that ES8326 will need a dedicated codec driver, but the plan is to use the same machine driver for all Everest Audio devices. Reported-by: anthony tonitch Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 7 ++++++- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 7 ++++++- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 7 ++++++- sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 7 ++++++- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 7 ++++++- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 0a2d0874dc4f..f99cf6c794dc 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -41,6 +41,11 @@ static struct snd_soc_acpi_mach *apl_quirk(void *arg) return mach; } +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static const struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -78,7 +83,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "sof-apl-tdf8532.tplg", }, { - .id = "ESSX8336", + .comp_ids = &essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-apl-es8336", /* the tplg suffix is added at run time */ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index f75fa1b551d7..5eab17820532 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -9,6 +9,11 @@ #include #include +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static const struct snd_soc_acpi_codecs rt1011_spk_codecs = { .num_codecs = 1, .codecs = {"10EC1011"} @@ -76,7 +81,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .sof_tplg_filename = "sof-cml-da7219-max98390.tplg", }, { - .id = "ESSX8336", + .comp_ids = &essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-cml-es8336", /* the tplg suffix is added at run time */ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index d494860b8190..387e73100884 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -9,6 +9,11 @@ #include #include +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static const struct snd_soc_acpi_codecs glk_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -53,7 +58,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .sof_tplg_filename = "sof-glk-cs42l42.tplg", }, { - .id = "ESSX8336", + .comp_ids = &essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-glk-es8336", /* the tplg suffix is added at run time */ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 53c42a4e1694..b95c4b2cda94 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -9,6 +9,11 @@ #include #include +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static const struct snd_soc_acpi_codecs jsl_7219_98373_codecs = { .num_codecs = 1, .codecs = {"MX98373"} @@ -81,7 +86,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .sof_tplg_filename = "sof-jsl-cs42l42-mx98360a.tplg", }, { - .id = "ESSX8336", + .comp_ids = &essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-jsl-es8336", /* the tplg suffix is added at run time */ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | 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 8bf14295deb0..6edc9b7108cd 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -10,6 +10,11 @@ #include #include "soc-acpi-intel-sdw-mockup-match.h" +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static const struct snd_soc_acpi_codecs tgl_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -391,7 +396,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg", }, { - .id = "ESSX8336", + .comp_ids = &essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-tgl-es8336", /* the tplg suffix is added at run time */ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | -- cgit v1.2.3 From cded07a2dccd5493696a3adce175f01e413423c6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:25:59 -0600 Subject: ALSA: intel-dspconfig: add ES8336 support for CNL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're missing this check for the CNL PCI id Reported-by: Nikolai Kostrigin Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20220308192610.392950-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/hda/intel-dsp-config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index b9b7bf5a5553..70fd8b13938e 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -199,6 +199,11 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SOF, + .device = 0x09dc8, + .codec_hid = &essx_83x6, + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x9dc8, -- cgit v1.2.3 From b3d6a07236ebf6e0111adb957d69bebccf4f0a19 Mon Sep 17 00:00:00 2001 From: Nikolai Kostrigin Date: Tue, 8 Mar 2022 13:26:00 -0600 Subject: ASoC: Intel: soc-acpi: add ESSX8336 support on Cannon Lake machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the support for all ES83x6 devices Signed-off-by: Nikolai Kostrigin Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 8d3e8c3b589b..3df89e4511da 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -11,6 +11,11 @@ #include "../skylake/skl.h" #include "soc-acpi-intel-sdw-mockup-match.h" +static const struct snd_soc_acpi_codecs essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; @@ -23,6 +28,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .pdata = &cnl_pdata, .sof_tplg_filename = "sof-cnl-rt274.tplg", }, + { + .comp_ids = &essx_83x6, + .drv_name = "sof-essx8336", + /* cnl and cml are identical */ + .sof_tplg_filename = "sof-cml-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); -- cgit v1.2.3 From 5a6cfba5553b4f315b3d12b423e56a7fb9e8e0be Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:01 -0600 Subject: ASoC: Intel: sof_es8336: make gpio optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not fail if the GPIO used for speakers is not present, at least the headphone, headset and internal mics should work. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-12-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index e6d599f0cd26..eb792bd911fa 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -523,11 +523,10 @@ static int sof_es8336_probe(struct platform_device *pdev) if (ret) dev_warn(codec_dev, "unable to add GPIO mapping table\n"); - priv->gpio_pa = gpiod_get(codec_dev, "pa-enable", GPIOD_OUT_LOW); + priv->gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW); if (IS_ERR(priv->gpio_pa)) { - ret = PTR_ERR(priv->gpio_pa); - dev_err(codec_dev, "%s, could not get pa-enable: %d\n", - __func__, ret); + ret = dev_err_probe(dev, PTR_ERR(priv->gpio_pa), + "could not get pa-enable GPIO\n"); goto err; } -- cgit v1.2.3 From 42302b205f03c74c0226bbc79dca48448dd11d48 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:02 -0600 Subject: ASoC: Intel: sof_es8336: get codec device with ACPI instead of bus search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have an existing 'adev' handle from which we can find the codec device, no need for an I2C bus search. This change aligns this driver will all other I2S-based machine drivers. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index eb792bd911fa..bb4a3491d71c 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -515,9 +515,10 @@ static int sof_es8336_probe(struct platform_device *pdev) return ret; /* get speaker enable GPIO */ - codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name); + codec_dev = acpi_get_first_physical_node(adev); if (!codec_dev) return -EPROBE_DEFER; + priv->codec_dev = get_device(codec_dev); ret = devm_acpi_dev_add_driver_gpios(codec_dev, gpio_mapping); if (ret) @@ -530,7 +531,6 @@ static int sof_es8336_probe(struct platform_device *pdev) goto err; } - priv->codec_dev = codec_dev; INIT_LIST_HEAD(&priv->hdmi_pcm_list); snd_soc_card_set_drvdata(card, priv); -- cgit v1.2.3 From 1b5283483a782f6560999d8d5965b1874d104812 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:03 -0600 Subject: ASoC: Intel: Revert "ASoC: Intel: sof_es8336: add quirk for Huawei D15 2021" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ce6a70bfce21bb4edb7c0f29ecfb0522fa34ab71. The next patch will add run-time detection of the required SSP and this hard-coded quirk is not needed. Acked-by: Mauro Carvalho Chehab Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index bb4a3491d71c..3376bd360a03 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -247,14 +247,6 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { SOF_ES8336_TGL_GPIO_QUIRK | SOF_ES8336_ENABLE_DMIC) }, - { - .callback = sof_es8336_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), - DMI_MATCH(DMI_BOARD_NAME, "BOHB-WAX9-PCB-B2"), - }, - .driver_data = (void *)SOF_ES8336_SSP_CODEC(0) - }, {} }; -- cgit v1.2.3 From 651c304df7f6e3fbb4779527efa3eb128ef91329 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:04 -0600 Subject: ASoC: Intel: sof_es8336: use NHLT information to set dmic and SSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we see a proliferation of devices with various configurations, we want to automatically set the DMIC and SSP information. This patch relies on the information extracted from NHLT and partially reverts existing DMI quirks added by commit a164137ce91a ("ASoC: Intel: add machine driver for SOF+ES8336") Note that NHLT can report multiple SSPs, choosing from the ssp_link_mask in an MSB-first manner was found experimentally to work fine. The only thing that cannot be detected is the GPIO type, and users may want to use the quirk override parameter if the 'wrong' solution is provided. Tested-by: Mauro Carvalho Chehab Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 56 +++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 3376bd360a03..1a8680470577 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -228,24 +228,25 @@ static int sof_es8336_quirk_cb(const struct dmi_system_id *id) return 1; } +/* + * this table should only be used to add GPIO or jack-detection quirks + * that cannot be detected from ACPI tables. The SSP and DMIC + * information are providing by the platform driver and are aligned + * with the topology used. + * + * If the GPIO support is missing, the quirk parameter can be used to + * enable speakers. In that case it's recommended to keep the SSP and DMIC + * information consistent, overriding the SSP and DMIC can only be done + * if the topology file is modified as well. + */ static const struct dmi_system_id sof_es8336_quirk_table[] = { - { - .callback = sof_es8336_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "CHUWI Innovation And Technology"), - DMI_MATCH(DMI_BOARD_NAME, "Hi10 X"), - }, - .driver_data = (void *)SOF_ES8336_SSP_CODEC(2) - }, { .callback = sof_es8336_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IP3 tech"), DMI_MATCH(DMI_BOARD_NAME, "WN1"), }, - .driver_data = (void *)(SOF_ES8336_SSP_CODEC(0) | - SOF_ES8336_TGL_GPIO_QUIRK | - SOF_ES8336_ENABLE_DMIC) + .driver_data = (void *)(SOF_ES8336_TGL_GPIO_QUIRK) }, {} }; @@ -470,11 +471,33 @@ static int sof_es8336_probe(struct platform_device *pdev) card = &sof_es8336_card; card->dev = dev; - if (!dmi_check_system(sof_es8336_quirk_table)) - quirk = SOF_ES8336_SSP_CODEC(2); + /* check GPIO DMI quirks */ + dmi_check_system(sof_es8336_quirk_table); - if (quirk & SOF_ES8336_ENABLE_DMIC) - dmic_be_num = 2; + if (!mach->mach_params.i2s_link_mask) { + dev_warn(dev, "No I2S link information provided, using SSP0. This may need to be modified with the quirk module parameter\n"); + } else { + /* + * Set configuration based on platform NHLT. + * In this machine driver, we can only support one SSP for the + * ES8336 link, the else-if below are intentional. + * In some cases multiple SSPs can be reported by NHLT, starting MSB-first + * seems to pick the right connection. + */ + unsigned long ssp = 0; + + if (mach->mach_params.i2s_link_mask & BIT(2)) + ssp = SOF_ES8336_SSP_CODEC(2); + else if (mach->mach_params.i2s_link_mask & BIT(1)) + ssp = SOF_ES8336_SSP_CODEC(1); + else if (mach->mach_params.i2s_link_mask & BIT(0)) + ssp = SOF_ES8336_SSP_CODEC(0); + + quirk |= ssp; + } + + if (mach->mach_params.dmic_num) + quirk |= SOF_ES8336_ENABLE_DMIC; if (quirk_override != -1) { dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n", @@ -483,6 +506,9 @@ static int sof_es8336_probe(struct platform_device *pdev) } log_quirks(dev); + if (quirk & SOF_ES8336_ENABLE_DMIC) + dmic_be_num = 2; + sof_es8336_card.num_links += dmic_be_num + hdmi_num; dai_links = sof_card_dai_links_create(dev, SOF_ES8336_SSP_CODEC(quirk), -- cgit v1.2.3 From 9c818d849192491a8799b1cb14ca0f7aead4fb09 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:05 -0600 Subject: ASoC: Intel: sof_es8336: log all quirks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only logged the SSP quirk, make sure the GPIO and DMIC quirks are exposed. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-16-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 1a8680470577..e2daa57902af 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -63,7 +63,12 @@ static const struct acpi_gpio_mapping *gpio_mapping = acpi_es8336_gpios; static void log_quirks(struct device *dev) { - dev_info(dev, "quirk SSP%ld", SOF_ES8336_SSP_CODEC(quirk)); + dev_info(dev, "quirk mask %#lx\n", quirk); + dev_info(dev, "quirk SSP%ld\n", SOF_ES8336_SSP_CODEC(quirk)); + if (quirk & SOF_ES8336_ENABLE_DMIC) + dev_info(dev, "quirk DMIC enabled\n"); + if (quirk & SOF_ES8336_TGL_GPIO_QUIRK) + dev_info(dev, "quirk TGL GPIO enabled\n"); } static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w, -- cgit v1.2.3 From d94c11a9b0e8620d7cf0e6d8e60d685cdb24c475 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:06 -0600 Subject: ASoC: Intel: sof_es8336: move comment to the right place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional code was added and the comment on the speaker GPIO needs to be moved before we actually try to get the GPIO. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index e2daa57902af..d7dff61c9ba8 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -537,12 +537,12 @@ static int sof_es8336_probe(struct platform_device *pdev) if (ret) return ret; - /* get speaker enable GPIO */ codec_dev = acpi_get_first_physical_node(adev); if (!codec_dev) return -EPROBE_DEFER; priv->codec_dev = get_device(codec_dev); + /* get speaker enable GPIO */ ret = devm_acpi_dev_add_driver_gpios(codec_dev, gpio_mapping); if (ret) dev_warn(codec_dev, "unable to add GPIO mapping table\n"); -- cgit v1.2.3 From 8e5db49182415b8bfce3b5843fc87e49c83c02aa Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:07 -0600 Subject: ASoC: Intel: sof_es8336: add support for JD inverted quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The codec driver exposes a set of properties that can be set from the machine driver - as done in bytcht_es8316.c Start by adding the JD_INVERTED quirk. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-18-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 40 ++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index d7dff61c9ba8..932a80e62bc8 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -21,11 +21,15 @@ #include #include "hda_dsp_common.h" +/* jd-inv + terminating entry */ +#define MAX_NO_PROPS 2 + #define SOF_ES8336_SSP_CODEC(quirk) ((quirk) & GENMASK(3, 0)) #define SOF_ES8336_SSP_CODEC_MASK (GENMASK(3, 0)) #define SOF_ES8336_TGL_GPIO_QUIRK BIT(4) #define SOF_ES8336_ENABLE_DMIC BIT(5) +#define SOF_ES8336_JD_INVERTED BIT(6) static unsigned long quirk; @@ -69,6 +73,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk DMIC enabled\n"); if (quirk & SOF_ES8336_TGL_GPIO_QUIRK) dev_info(dev, "quirk TGL GPIO enabled\n"); + if (quirk & SOF_ES8336_JD_INVERTED) + dev_info(dev, "quirk JD inverted enabled\n"); } static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w, @@ -461,10 +467,13 @@ static int sof_es8336_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct snd_soc_card *card; struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; + struct property_entry props[MAX_NO_PROPS] = {}; struct sof_es8336_private *priv; + struct fwnode_handle *fwnode; struct acpi_device *adev; struct snd_soc_dai_link *dai_links; struct device *codec_dev; + unsigned int cnt = 0; int dmic_be_num = 0; int hdmi_num = 3; int ret; @@ -530,6 +539,9 @@ static int sof_es8336_probe(struct platform_device *pdev) "i2c-%s", acpi_dev_name(adev)); put_device(&adev->dev); dai_links[0].codecs->name = codec_name; + } else { + dev_err(dev, "Error cannot find '%s' dev\n", mach->id); + return -ENXIO; } ret = snd_soc_fixup_dai_links_platform_name(&sof_es8336_card, @@ -542,6 +554,26 @@ static int sof_es8336_probe(struct platform_device *pdev) return -EPROBE_DEFER; priv->codec_dev = get_device(codec_dev); + if (quirk & SOF_ES8336_JD_INVERTED) + props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted"); + + if (cnt) { + fwnode = fwnode_create_software_node(props, NULL); + if (IS_ERR(fwnode)) { + put_device(codec_dev); + return PTR_ERR(fwnode); + } + + ret = device_add_software_node(codec_dev, to_software_node(fwnode)); + + fwnode_handle_put(fwnode); + + if (ret) { + put_device(codec_dev); + return ret; + } + } + /* get speaker enable GPIO */ ret = devm_acpi_dev_add_driver_gpios(codec_dev, gpio_mapping); if (ret) @@ -551,7 +583,7 @@ static int sof_es8336_probe(struct platform_device *pdev) if (IS_ERR(priv->gpio_pa)) { ret = dev_err_probe(dev, PTR_ERR(priv->gpio_pa), "could not get pa-enable GPIO\n"); - goto err; + goto err_put_codec; } INIT_LIST_HEAD(&priv->hdmi_pcm_list); @@ -562,12 +594,13 @@ static int sof_es8336_probe(struct platform_device *pdev) if (ret) { gpiod_put(priv->gpio_pa); dev_err(dev, "snd_soc_register_card failed: %d\n", ret); - goto err; + goto err_put_codec; } platform_set_drvdata(pdev, &sof_es8336_card); return 0; -err: +err_put_codec: + device_remove_software_node(priv->codec_dev); put_device(codec_dev); return ret; } @@ -578,6 +611,7 @@ static int sof_es8336_remove(struct platform_device *pdev) struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card); gpiod_put(priv->gpio_pa); + device_remove_software_node(priv->codec_dev); put_device(priv->codec_dev); return 0; -- cgit v1.2.3 From 70b519e5cade92bddf837bd3941f905b44232b05 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:08 -0600 Subject: ASoC: Intel: sof_es8336: extend machine driver to support ES8326 codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ES8326 requires a different codec driver than ES8316/8336, fixup the codec name and dai name depending on the ACPI _HID exposed in the DSDT. Also add the select in Kconfig Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-19-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 3 ++- sound/soc/intel/boards/sof_es8336.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6884ddf9edad..a62785893bec 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -530,12 +530,13 @@ config SND_SOC_INTEL_SOF_PCM512x_MACH If unsure select "N". config SND_SOC_INTEL_SOF_ES8336_MACH - tristate "SOF with ES8336 codec in I2S mode" + tristate "SOF with ES8336 or ES8326 codec in I2S mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST depends on GPIOLIB || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_ES8316 + select SND_SOC_ES8326 select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON help diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 932a80e62bc8..32f5303041b8 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -292,7 +292,7 @@ static struct snd_soc_dai_link_component platform_component[] = { } }; -SND_SOC_DAILINK_DEF(ssp1_codec, +SND_SOC_DAILINK_DEF(es8336_codec, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi"))); static struct snd_soc_dai_link_component dmic_component[] = { @@ -356,8 +356,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, goto devm_err; links[id].id = id; - links[id].codecs = ssp1_codec; - links[id].num_codecs = ARRAY_SIZE(ssp1_codec); + links[id].codecs = es8336_codec; + links[id].num_codecs = ARRAY_SIZE(es8336_codec); links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].init = sof_es8316_init; @@ -539,6 +539,10 @@ static int sof_es8336_probe(struct platform_device *pdev) "i2c-%s", acpi_dev_name(adev)); put_device(&adev->dev); dai_links[0].codecs->name = codec_name; + + /* also fixup codec dai name if relevant */ + if (!strncmp(mach->id, "ESSX8326", SND_ACPI_I2C_ID_LEN)) + dai_links[0].codecs->dai_name = "ES8326 HiFi"; } else { dev_err(dev, "Error cannot find '%s' dev\n", mach->id); return -ENXIO; -- cgit v1.2.3 From 6e13567d2fdf54ce00212cac7ecd5418648da749 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:09 -0600 Subject: ASoC: Intel: sof_es8336: add cfg-dmics component for UCM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The presence of DMICs needs to be signaled to UCM, follow the HDaudio example and use the 'cfg-dmics' component string to report the number of dmics present on the platform. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-20-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 32f5303041b8..5e0529aa4f1d 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -459,6 +459,8 @@ devm_err: return NULL; } +static char soc_components[30]; + /* i2c-:00 with HID being 8 chars */ static char codec_name[SND_ACPI_I2C_ID_LEN]; @@ -594,6 +596,12 @@ static int sof_es8336_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); + if (mach->mach_params.dmic_num > 0) { + snprintf(soc_components, sizeof(soc_components), + "cfg-dmics:%d", mach->mach_params.dmic_num); + card->components = soc_components; + } + ret = devm_snd_soc_register_card(dev, card); if (ret) { gpiod_put(priv->gpio_pa); -- cgit v1.2.3 From fe0596a006081bc963874d4f3d38cd0b1b5e46d4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 13:26:10 -0600 Subject: ASoC: Intel: bytcht_es8316: move comment to the right place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional code was added and the comment on the speaker GPIO needs to be moved before we actually try to get the GPIO. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20220308192610.392950-21-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcht_es8316.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index f4bf26e802a2..e18371b5a771 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -535,7 +535,6 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) if (IS_ERR(priv->mclk)) return dev_err_probe(dev, PTR_ERR(priv->mclk), "clk_get pmc_plt_clk_3 failed\n"); - /* get speaker enable GPIO */ codec_dev = acpi_get_first_physical_node(adev); if (!codec_dev) return -EPROBE_DEFER; @@ -561,6 +560,7 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) } } + /* get speaker enable GPIO */ devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios); priv->speaker_en_gpio = gpiod_get_optional(codec_dev, "speaker-enable", -- cgit v1.2.3 From 8b1d3b733f3e6acaab6c6bc9968ee0e058900a7e Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 9 Mar 2022 18:38:13 +0800 Subject: ASoC: fsl_rpmsg: Remove SET_SYSTEM_SLEEP_PM_OPS callback For sound need to be continuously output at suspend with rpmsg sound card, so need to keep the clock always on at suspend, then suspend & resume callback is not needed. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1646822293-26965-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_rpmsg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index 8508bc7f239d..19fd31250883 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -297,8 +297,6 @@ static const struct dev_pm_ops fsl_rpmsg_pm_ops = { SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend, fsl_rpmsg_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) }; static struct platform_driver fsl_rpmsg_driver = { -- cgit v1.2.3 From 9779a8e61a83916ccfe7e6ddabcc7638d4bc2ae1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2022 14:23:18 -0600 Subject: ASoC: Intel: boards: fix randconfig issue on x86_64: ERROR: modpost: "sof_dai_get_bclk" [sound/soc/intel/boards/snd-soc-intel-sof-cirrus-common.ko] undefined! ERROR: modpost: "sof_dai_get_mclk" [sound/soc/intel/boards/snd-soc-intel-sof-realtek-common.ko] undefined! This comes from a missing dependency on at least ONE SOF platform being selected. This dependency exists for all other machine drivers, this was missed in the earlier reviews. Fixes: 2fe14ff61bd6 ("ASoC: Intel: sof_ssp_amp: rename driver and support cs35l41 amplifier") Reported-by: Randy Dunlap Signed-off-by: Pierre-Louis Bossart Acked-by: Randy Dunlap Tested-by: Randy Dunlap Reviewed-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220308202318.401358-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6884ddf9edad..81e012c164b0 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -615,6 +615,8 @@ config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH endif ## SND_SOC_SOF_JASPERLAKE +if SND_SOC_SOF_HDA_LINK + config SND_SOC_INTEL_SOF_SSP_AMP_MACH tristate "SOF with amplifiers in I2S Mode" depends on I2C && ACPI @@ -631,6 +633,7 @@ config SND_SOC_INTEL_SOF_SSP_AMP_MACH with RT1308/CS35L41 I2S audio codec. Say Y if you have such a device. If unsure select "N". +endif ## SND_SOC_SOF_HDA_LINK if SND_SOC_SOF_ELKHARTLAKE -- cgit v1.2.3 From c8ee0c37c045ccc1fbbe074e8af5031b36861cd2 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Wed, 9 Mar 2022 08:49:29 +0800 Subject: ASoC: SOF: amd: Remove unneeded semicolon Fix the following coccicheck warnings: ./sound/soc/sof/amd/acp.c:280:3-4: Unneeded semicolon. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20220309004929.125558-2-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 66ca05545be2..71d71c152342 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -277,7 +277,7 @@ static irqreturn_t acp_irq_thread(int irq, void *context) dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__); return IRQ_NONE; } - }; + } sof_ops(sdev)->irq_thread(irq, sdev); val |= ACP_DSP_TO_HOST_IRQ; -- cgit v1.2.3 From 5af07dad696422d48368409461a754990faa713c Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Wed, 9 Mar 2022 08:49:28 +0800 Subject: ASoC: SOF: amd: Remove unneeded semicolon Fix the following coccicheck warnings: ./sound/soc/sof/amd/acp-ipc.c:74:2-3: Unneeded semicolon. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20220309004929.125558-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 9fcd2535fd3b..e1842f037083 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -71,7 +71,7 @@ int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__); return -EINVAL; } - }; + } acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size); acp_ipc_host_msg_set(sdev); -- cgit v1.2.3 From 5bd998af5b69cf21fd4db5eaf7e9db85a4a35295 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Wed, 9 Mar 2022 17:18:43 +0800 Subject: ASoC: fsl_spdif: keep all TxClk sources by txclk array Use txclk array to keep all TxClk sources instead of keeping clocks per rate - need to do this in order to avoid multiple prepare_enable/disable_unprepare of the same clock during suspend/resume. Signed-off-by: Viorel Suman Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1646817523-26800-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 57c41b2f7d17..e0acce6b2213 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -125,7 +125,7 @@ struct fsl_spdif_priv { u16 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; - struct clk *txclk[SPDIF_TXRATE_MAX]; + struct clk *txclk[STC_TXCLK_SRC_MAX]; struct clk *rxclk; struct clk *coreclk; struct clk *sysclk; @@ -526,7 +526,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, goto clk_set_bypass; /* The S/PDIF block needs a clock of 64 * fs * txclk_df */ - ret = clk_set_rate(spdif_priv->txclk[rate], + ret = clk_set_rate(spdif_priv->txclk[clk], 64 * sample_rate * txclk_df); if (ret) { dev_err(&pdev->dev, "failed to set tx clock rate\n"); @@ -537,7 +537,7 @@ clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", (64 * sample_rate * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", - clk_get_rate(spdif_priv->txclk[rate])); + clk_get_rate(spdif_priv->txclk[clk])); /* set fs field in consumer channel status */ spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); @@ -1376,12 +1376,10 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, struct device *dev = &pdev->dev; u64 savesub = 100000, ret; struct clk *clk; - char tmp[16]; int i; for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { - sprintf(tmp, "rxtx%d", i); - clk = devm_clk_get(dev, tmp); + clk = spdif_priv->txclk[i]; if (IS_ERR(clk)) { dev_err(dev, "no rxtx%d clock in devicetree\n", i); return PTR_ERR(clk); @@ -1395,7 +1393,6 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, continue; savesub = ret; - spdif_priv->txclk[index] = clk; spdif_priv->txclk_src[index] = i; /* To quick catch a divisor, we allow a 0.1% deviation */ @@ -1407,7 +1404,7 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, spdif_priv->txclk_src[index], rate[index]); dev_dbg(dev, "use txclk df %d for %dHz sample rate\n", spdif_priv->txclk_df[index], rate[index]); - if (clk_is_match(spdif_priv->txclk[index], spdif_priv->sysclk)) + if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk)) dev_dbg(dev, "use sysclk df %d for %dHz sample rate\n", spdif_priv->sysclk_df[index], rate[index]); dev_dbg(dev, "the best rate for %dHz sample rate is %dHz\n", @@ -1423,6 +1420,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) struct resource *res; void __iomem *regs; int irq, ret, i; + char tmp[16]; spdif_priv = devm_kzalloc(&pdev->dev, sizeof(*spdif_priv), GFP_KERNEL); if (!spdif_priv) @@ -1462,8 +1460,17 @@ static int fsl_spdif_probe(struct platform_device *pdev) } } + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { + sprintf(tmp, "rxtx%d", i); + spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(spdif_priv->txclk[i])) { + dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i); + return PTR_ERR(spdif_priv->txclk[i]); + } + } + /* Get system clock for rx clock rate calculation */ - spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5"); + spdif_priv->sysclk = spdif_priv->txclk[5]; if (IS_ERR(spdif_priv->sysclk)) { dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n"); return PTR_ERR(spdif_priv->sysclk); @@ -1481,7 +1488,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "no spba clock in devicetree\n"); /* Select clock source for rx/tx clock */ - spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); + spdif_priv->rxclk = spdif_priv->txclk[1]; if (IS_ERR(spdif_priv->rxclk)) { dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n"); return PTR_ERR(spdif_priv->rxclk); @@ -1562,9 +1569,7 @@ static int fsl_spdif_runtime_suspend(struct device *dev) &spdif_priv->regcache_srpc); regcache_cache_only(spdif_priv->regmap, true); - clk_disable_unprepare(spdif_priv->rxclk); - - for (i = 0; i < SPDIF_TXRATE_MAX; i++) + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) clk_disable_unprepare(spdif_priv->txclk[i]); if (!IS_ERR(spdif_priv->spbaclk)) @@ -1594,16 +1599,12 @@ static int fsl_spdif_runtime_resume(struct device *dev) } } - for (i = 0; i < SPDIF_TXRATE_MAX; i++) { + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { ret = clk_prepare_enable(spdif_priv->txclk[i]); if (ret) goto disable_tx_clk; } - ret = clk_prepare_enable(spdif_priv->rxclk); - if (ret) - goto disable_tx_clk; - regcache_cache_only(spdif_priv->regmap, false); regcache_mark_dirty(spdif_priv->regmap); @@ -1613,12 +1614,10 @@ static int fsl_spdif_runtime_resume(struct device *dev) ret = regcache_sync(spdif_priv->regmap); if (ret) - goto disable_rx_clk; + goto disable_tx_clk; return 0; -disable_rx_clk: - clk_disable_unprepare(spdif_priv->rxclk); disable_tx_clk: for (i--; i >= 0; i--) clk_disable_unprepare(spdif_priv->txclk[i]); -- cgit v1.2.3 From 81acac8c2c88764c773811d1ec858852f81eb88a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 13:01:04 +0200 Subject: ASoC: SOF: ipc-msg-injector: Use devm_kzalloc() for the rx_buffer The rx_buffer is cleared before sending an IPC to make sure that when the /sys/kernel/debug/sof/ipc_msg_inject file is read we will have correct information in the buffer (no random or stale data). But if the user reads the file before sending any message the buffer might contain garbage which should not be interpreted. To prevent this, clear the rx_buffer on allocation. Fixes: cac0b0887e53 ("ASoC: SOF: Convert the generic IPC message injector into SOF client") Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220309110104.18370-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-client-ipc-msg-injector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c index bce103da4c49..dba6cfd7db09 100644 --- a/sound/soc/sof/sof-client-ipc-msg-injector.c +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -137,7 +137,7 @@ static int sof_msg_inject_probe(struct auxiliary_device *auxdev, return -ENOMEM; priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - priv->rx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + priv->rx_buffer = devm_kzalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); if (!priv->tx_buffer || !priv->rx_buffer) return -ENOMEM; -- cgit v1.2.3 From 31ef579d433a6bcd6b942edea372040298295acf Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 9 Mar 2022 14:05:52 +0000 Subject: ASoC: qcom: fix Kconfig for SC7280 select would force the symbol to value without checking the dependencies. In this case selecting TX and RX MACROs directly without checking its dependency on COMMON_CLK would break builds on platform which do no set COMMON_CLK. ex: WARNING: unmet direct dependencies detected for SND_SOC_LPASS_RX_MACRO Depends on [n]: SOUND [=m] && !UML && SND [=m] && SND_SOC [=m] && COMMON_CLK [=n] Selected by [m]: - SND_SOC_SC7280 [=m] && SOUND [=m] && !UML && SND [=m] && SND_SOC [=m] && SND_SOC_QCOM [=m] && I2C [=y] && SOUNDWIRE [=m] move select to imply which should enforce symbol to be set to 'n' if any dependencies are not resolved. Fixes: 57350bd41c3a ("ASoC: qcom: SC7280: Add machine driver") Reported-by: Randy Dunlap Reported-by: kernel test robot Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220309140552.8065-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 82f5eafb2f6c..28d0dfb4033c 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -195,8 +195,8 @@ config SND_SOC_SC7280 select SND_SOC_MAX98357A select SND_SOC_WCD938X_SDW select SND_SOC_LPASS_MACRO_COMMON - select SND_SOC_LPASS_RX_MACRO - select SND_SOC_LPASS_TX_MACRO + imply SND_SOC_LPASS_RX_MACRO + imply SND_SOC_LPASS_TX_MACRO help Add support for audio on Qualcomm Technologies Inc. SC7280 SoC-based systems. -- cgit v1.2.3 From 6ddf611219ba8f7c8fa0d26b39710a641e7d37a5 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 10 Mar 2022 10:37:43 +0800 Subject: ASoC: fsl_spdif: Disable TX clock when stop The TX clock source may be changed in next case, need to disable it when stop, otherwise the TX may not work after changing the clock source, error log is: aplay: pcm_write:2058: write error: Input/output error Fixes: a2388a498ad2 ("ASoC: fsl: Add S/PDIF CPU DAI driver") Signed-off-by: Shengjiu Wang Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/1646879863-27711-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index e0acce6b2213..b502e7c3c04d 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -610,6 +610,8 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; + /* Disable TX clock */ + regmap_update_bits(regmap, REG_SPDIF_STC, STC_TXCLK_ALL_EN_MASK, 0); } else { scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| -- cgit v1.2.3 From 2588a01431a85a9bb8b2eac9023181ddd714a695 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Thu, 10 Mar 2022 16:27:56 +0800 Subject: ASoC: atmel: mchp-pdmc: Remove unnecessary print function dev_err() The print function dev_err() is redundant because platform_get_irq() already prints an error. Eliminate the follow coccicheck warning: ./sound/soc/atmel/mchp-pdmc.c:991:2-9: line 991 is redundant because platform_get_irq() already prints an error. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220310082756.1183-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index c44636f6207d..7b737e3f67b7 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -987,10 +987,8 @@ static int mchp_pdmc_probe(struct platform_device *pdev) return ret; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get irq: %d\n", irq); + if (irq < 0) return irq; - } dd->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dd->pclk)) { -- cgit v1.2.3 From 3b891513f95cba3944e72c1139ea706d04f3781b Mon Sep 17 00:00:00 2001 From: Wang Wensheng Date: Thu, 10 Mar 2022 09:19:02 +0000 Subject: ASoC: imx-es8328: Fix error return code in imx_es8328_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: 7e7292dba215 ("ASoC: fsl: add imx-es8328 machine driver") Signed-off-by: Wang Wensheng Link: https://lore.kernel.org/r/20220310091902.129299-1-wangwensheng4@huawei.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-es8328.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 09c674ee79f1..168973035e35 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -87,6 +87,7 @@ static int imx_es8328_probe(struct platform_device *pdev) if (int_port > MUX_PORT_MAX || int_port == 0) { dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", MUX_PORT_MAX); + ret = -EINVAL; goto fail; } -- cgit v1.2.3 From 6b6bb5e26222021abe1c5360f43b4c2ff1dd012f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 9 Mar 2022 16:41:16 +0000 Subject: ASoC: atmel: mchp-pdmc: Fix spelling mistake "microchopnes" -> "microphones" There is a spelling mistake in a dev_info message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220309164116.178685-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index 7b737e3f67b7..1be4007875f1 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -892,7 +892,7 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) dd->mic_no /= 2; - dev_info(dd->dev, "%d PDM microchopnes declared\n", dd->mic_no); + dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no); /* by default, we consider the order of microphones in mchp,mic-pos to * be the same with the channel mapping; 1st microphone channel 0, 2nd -- cgit v1.2.3 From 327e8ba54a212f707a68670c9372747b7a32bb92 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 9 Mar 2022 20:24:39 +0200 Subject: ALSA: hda/i915 - avoid hung task timeout in i915 wait If kernel is built with hung task detection enabled and CONFIG_DEFAULT_HUNG_TASK_TIMEOUT set to less than 60 seconds, snd_hdac_i915_init() will trigger the hung task timeout in case i915 is not available and taint the kernel. Use wait_for_completion_killable_timeout() for the wait to avoid this problem. Co-developed-by: Ramalingam C Signed-off-by: Ramalingam C Signed-off-by: Kai Vehmanen Acked-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20220309182439.1053856-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 454474ac5716..efe810af28c5 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -160,8 +160,8 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!IS_ENABLED(CONFIG_MODULES) || !request_module("i915")) { /* 60s timeout */ - wait_for_completion_timeout(&acomp->master_bind_complete, - msecs_to_jiffies(60 * 1000)); + wait_for_completion_killable_timeout(&acomp->master_bind_complete, + msecs_to_jiffies(60 * 1000)); } } if (!acomp->ops) { -- cgit v1.2.3 From a174e72e2355b9025205b4b6727bf43047eac6c6 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 10 Mar 2022 11:16:47 -0600 Subject: ASoC: SOF: Intel: enable DMI L1 for playback streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add back logic to mark all playback streams as L1 compatible. Fixes: 246dd4287dfb ("ASoC: SOF: Intel: make DMI L1 selection more robust") Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Signed-off-by: Kai Vehmanen Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220310171651.249385-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index eec83ca557a1..3e77a2352b98 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -315,6 +315,7 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE; if (hda_always_enable_dmi_l1 || + direction == SNDRV_PCM_STREAM_PLAYBACK || spcm->stream[substream->stream].d0i3_compatible) flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE; -- cgit v1.2.3 From 7e4bfcf10a03981cb3056d723bb2f92eead5c0bb Mon Sep 17 00:00:00 2001 From: Weiguo Li Date: Thu, 10 Mar 2022 11:16:48 -0600 Subject: ASoC: SOF: compress: fix null check after dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "cstream" is dereferenced but null checked later. Swap their positions to avoid potential null dereference. Reviewed-by: Ranjani Sridharan Reviewed-by: Daniel Baluta Reviewed-by: Péter Ujfalusi Signed-off-by: Weiguo Li Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220310171651.249385-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/compress.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 2af8d75204e9..a8e908e50101 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -46,8 +46,8 @@ void snd_sof_compr_init_elapsed_work(struct work_struct *work) */ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_compr_runtime *crtd = cstream->runtime; + struct snd_soc_pcm_runtime *rtd; + struct snd_compr_runtime *crtd; struct snd_soc_component *component; struct snd_compr_tstamp *tstamp; struct snd_sof_pcm *spcm; @@ -55,6 +55,8 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) if (!cstream) return; + rtd = cstream->private_data; + crtd = cstream->runtime; tstamp = crtd->private_data; component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); -- cgit v1.2.3 From d8b502a7c353a63269d5ac3cfa7e5a04308df6a1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Mar 2022 11:16:49 -0600 Subject: ASoC: SOF: trace: Use proper DMA direction for the trace data buffer Buffers allocated with snd_dma_alloc_pages() will have DMA direction DMA_BIDIRECTIONAL. The trace data memory is only used for one DMA direction: DMA_FROM_DEVICE, DMA only writes there, never reads. We also need to do a sync before accessing (reading with CPU) from the trace data buffer to copy it to user space. Note: snd_dma_buffer_sync() is also used for normal playback and capture streams to make sure that the data is available for the DMA or CPU. Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220310171651.249385-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/trace.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 104388c551cb..ea8e4506d02e 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -320,6 +320,13 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, if (count > avail) count = avail; + /* + * make sure that all trace data is available for the CPU as the trace + * data buffer might be allocated from non consistent memory. + * Note: snd_dma_buffer_sync() is called for normal audio playback and + * capture streams also. + */ + snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); /* copy available trace data to debugfs */ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); if (rem) @@ -464,8 +471,9 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) } /* allocate trace data buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb); + ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, + DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, + &sdev->dmatb); if (ret < 0) { dev_err(sdev->dev, "error: can't alloc buffer for trace %d\n", ret); -- cgit v1.2.3 From 24320c55566138426ee0f9ec866dd3d656071799 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 10 Mar 2022 11:16:50 -0600 Subject: ASoC: SOF: Intel: add topology overwrite for Taniks Taniks has four max98357a on SSP2 with Demux and EQ in topology to implement a 2-way speaker system. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220310171651.249385-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-pci-dev.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 61f2afd54c3e..4c9596742844 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -75,6 +75,14 @@ static const struct dmi_system_id sof_tplg_table[] = { }, .driver_data = "sof-adl-max98360a-rt5682-2way.tplg", }, + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-AUDIO_MAX98357_ALC5682I_I2S_2WAY"), + }, + .driver_data = "sof-adl-max98357a-rt5682-2way.tplg", + }, {} }; -- cgit v1.2.3 From d7be9e33c4ad71c299fd58c5d46d4407c0b42d86 Mon Sep 17 00:00:00 2001 From: Muralidhar Reddy Date: Thu, 10 Mar 2022 11:16:51 -0600 Subject: ASoC: Intel: soc-acpi: Add entry for rt711-sdca-sdw in ADL match table RT711 sdca sdw is added with SDW0 link for ADL-PS platform. Reviewed-by: Bard Liao Signed-off-by: Muralidhar Reddy Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220310171651.249385-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-adl-match.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 7c89a974b59f..8bfe7070b84a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -359,6 +359,15 @@ static const struct snd_soc_acpi_link_adr adl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr adlps_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr adl_chromebook_base[] = { { .mask = BIT(0), @@ -529,6 +538,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-rt711.tplg", }, + { + .link_mask = 0x1, /* link0 required */ + .links = adlps_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-adl-rt711.tplg", + }, { .link_mask = 0x5, /* rt5682 on link0 & 2xmax98373 on link 2 */ .links = adl_chromebook_base, -- cgit v1.2.3 From d7bc6ddef016d851cb0ff97ae16ac98d5f3c85ee Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:11 -0800 Subject: ASoC: SOF: sof-priv: Remove stale snd_sof_ipc_stream_pcm_params() declaration The implementation for snd_sof_ipc_stream_pcm_params() does not exist, remove it from the header file. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 7f0514db4d06..4ca18331e157 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -532,8 +532,6 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev); void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev); void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); -int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, - struct sof_ipc_pcm_params *params); int snd_sof_ipc_valid(struct snd_sof_dev *sdev); int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, -- cgit v1.2.3 From 31f60a0c943d6a7e84b06686b1ed86ddadf484fa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:12 -0800 Subject: ASoC: SOF: Make pcm_hw_params snd_sof_dsp_ops callback IPC neutral Do not send IPC structure directly via pcm_hw_params to make it IPC agnostic. A new struct is created to retrieve the needed platform parameters and if there is a need it can be extended with new options. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-pcm.c | 8 +++++--- sound/soc/sof/amd/acp.h | 3 ++- sound/soc/sof/intel/hda-pcm.c | 18 ++++-------------- sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/ops.h | 6 +++--- sound/soc/sof/pcm.c | 21 ++++++++++++++++++++- sound/soc/sof/sof-priv.h | 17 ++++++++++++++++- 7 files changed, 51 insertions(+), 24 deletions(-) diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index b49cc55980ae..0ba8ae46bd76 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -17,7 +17,8 @@ #include "acp-dsp-offset.h" int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params) + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params) { struct snd_pcm_runtime *runtime = substream->runtime; struct acp_dsp_stream *stream = runtime->private_data; @@ -35,8 +36,9 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr return ret; } - ipc_params->buffer.phy_addr = stream->reg_offset; - ipc_params->stream_tag = stream->stream_tag; + platform_params->use_phy_address = true; + platform_params->phy_addr = stream->reg_offset; + platform_params->stream_tag = stream->stream_tag; /* write buffer size of stream in scratch memory */ diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index f550a5010a91..35e46fe6676a 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -201,7 +201,8 @@ int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stre int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params); + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); extern const struct snd_sof_dsp_ops sof_renoir_ops; diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index eec83ca557a1..7991407d5508 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -93,13 +93,12 @@ u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits) int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct sof_ipc_stream_params *ipc_params) + struct snd_sof_platform_stream_params *platform_params) { struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_dma_buffer *dmab; - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; int ret; u32 size, rate, bits; @@ -130,19 +129,10 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, else hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - /* update no_stream_position flag for ipc params */ - if (hda && hda->no_ipc_position) { - /* For older ABIs set host_period_bytes to zero to inform - * FW we don't want position updates. Newer versions use - * no_stream_position for this purpose. - */ - if (v->abi_version < SOF_ABI_VER(3, 10, 0)) - ipc_params->host_period_bytes = 0; - else - ipc_params->no_stream_position = 1; - } + if (hda) + platform_params->no_ipc_position = hda->no_ipc_position; - ipc_params->stream_tag = hstream->stream_tag; + platform_params->stream_tag = hstream->stream_tag; return 0; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 21e34580a403..46641d31e9c5 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -524,7 +524,7 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev, int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct sof_ipc_stream_params *ipc_params); + struct snd_sof_platform_stream_params *platform_params); int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 999a36208b11..98fa91f5927d 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -420,11 +420,11 @@ static inline int snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct sof_ipc_stream_params *ipc_params) + struct snd_sof_platform_stream_params *platform_params) { if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) - return sof_ops(sdev)->pcm_hw_params(sdev, substream, - params, ipc_params); + return sof_ops(sdev)->pcm_hw_params(sdev, substream, params, + platform_params); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 4628bc642fda..0c595d94dfce 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -162,6 +162,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_platform_stream_params platform_params = { 0 }; + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_pcm *spcm; struct sof_ipc_pcm_params pcm; struct sof_ipc_pcm_params_reply ipc_params_reply; @@ -242,12 +244,29 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, - &pcm.params); + &platform_params); if (ret < 0) { dev_err(component->dev, "error: platform hw params failed\n"); return ret; } + /* Update the IPC message with information from the platform */ + pcm.params.stream_tag = platform_params.stream_tag; + + if (platform_params.use_phy_address) + pcm.params.buffer.phy_addr = platform_params.phy_addr; + + if (platform_params.no_ipc_position) { + /* For older ABIs set host_period_bytes to zero to inform + * FW we don't want position updates. Newer versions use + * no_stream_position for this purpose. + */ + if (v->abi_version < SOF_ABI_VER(3, 10, 0)) + pcm.params.host_period_bytes = 0; + else + pcm.params.no_stream_position = 1; + } + dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); /* if this is a repeated hw_params without hw_free, skip setting up widgets */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4ca18331e157..f87c454c789e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -105,6 +105,21 @@ struct snd_soc_tplg_ops; struct snd_soc_component; struct snd_sof_pdata; +/** + * struct snd_sof_platform_stream_params - platform dependent stream parameters + * @stream_tag: Stream tag to use + * @use_phy_addr: Use the provided @phy_addr for configuration + * @phy_addr: Platform dependent address to be used, if @use_phy_addr + * is true + * @no_ipc_position: Disable position update IPC from firmware + */ +struct snd_sof_platform_stream_params { + u16 stream_tag; + bool use_phy_address; + u32 phy_addr; + bool no_ipc_position; +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU @@ -183,7 +198,7 @@ struct snd_sof_dsp_ops { int (*pcm_hw_params)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct sof_ipc_stream_params *ipc_params); /* optional */ + struct snd_sof_platform_stream_params *platform_params); /* optional */ /* host stream hw_free */ int (*pcm_hw_free)(struct snd_sof_dev *sdev, -- cgit v1.2.3 From d1b1146fc708eff661c2becb9bf78374adde6db7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:13 -0800 Subject: ASoC: SOF: pcm: Remove sof_pcm_dsp_params() wrapper Call directly for snd_sof_ipc_pcm_params() from sof_pcm_hw_params() and remove the wrapper for it. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0c595d94dfce..20459d489bfb 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -36,22 +36,6 @@ static int create_page_table(struct snd_soc_component *component, spcm->stream[stream].page_table.area, size); } -static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - struct snd_soc_component *scomp = spcm->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - - /* validate offset */ - int ret = snd_sof_ipc_pcm_params(sdev, substream, reply); - - if (ret < 0) - dev_err(scomp->dev, "error: got wrong reply for PCM %d\n", - spcm->pcm.pcm_id); - - return ret; -} - /* * sof pcm period elapse work */ @@ -285,9 +269,12 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - ret = sof_pcm_dsp_params(spcm, substream, &ipc_params_reply); - if (ret < 0) + ret = snd_sof_ipc_pcm_params(sdev, substream, &ipc_params_reply); + if (ret < 0) { + dev_err(component->dev, "%s: got wrong reply for PCM %d\n", + __func__, spcm->pcm.pcm_id); return ret; + } spcm->prepared[substream->stream] = true; -- cgit v1.2.3 From 757ce8103c9e5b83cf18a669fe38484b0be3cfaf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:14 -0800 Subject: ASoC: SOF: Introduce optional callback to configure stream data offset Each running audio stream's data have distinct start offset within the stream mailbox area from/to where the host can read/write. Instead of using the struct sof_ipc_pcm_params_reply to configure this offset, add an optional callback which is IPC agnostic. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 13 +++++++++++++ sound/soc/sof/pcm.c | 8 ++++++++ sound/soc/sof/sof-priv.h | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 98fa91f5927d..f21d4a7ac261 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -476,6 +476,19 @@ snd_sof_ipc_pcm_params(struct snd_sof_dev *sdev, return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply); } +/* host side configuration of the stream's data offset in stream mailbox area */ +static inline int +snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset) +{ + if (sof_ops(sdev) && sof_ops(sdev)->set_stream_data_offset) + return sof_ops(sdev)->set_stream_data_offset(sdev, substream, + posn_offset); + + return 0; +} + /* host stream pointer */ static inline snd_pcm_uframes_t snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 20459d489bfb..150f9ac872c6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -276,6 +276,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } + ret = snd_sof_set_stream_data_offset(sdev, substream, + ipc_params_reply.posn_offset); + if (ret < 0) { + dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", + __func__, spcm->pcm.pcm_id); + return ret; + } + spcm->prepared[substream->stream] = true; /* save pcm hw_params */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f87c454c789e..08eba51fddd7 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -226,6 +226,11 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); /* mandatory */ + /* host side configuration of the stream's data offset in stream mailbox area */ + int (*set_stream_data_offset)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset); /* optional */ + /* pre/post firmware run */ int (*pre_fw_run)(struct snd_sof_dev *sof_dev); /* optional */ int (*post_fw_run)(struct snd_sof_dev *sof_dev); /* optional */ -- cgit v1.2.3 From a6db22a68b0b2183184659d27c0a74df96f0d6d0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:15 -0800 Subject: ASoC: SOF: Mark snd_sof_dsp_ops.ipc_pcm_params() callback optional AMD is only implementing an empty function to pass the required test and it is going to be deprecated in favor of the IPC agnostic set_stream_data_offset() callback. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 3 +-- sound/soc/sof/ops.h | 5 ++++- sound/soc/sof/sof-priv.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 95a845d26f6e..9217644e2eab 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -361,8 +361,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !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(sdev)->ipc_pcm_params || - !sof_ops(sdev)->fw_ready) { + !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->fw_ready) { dev_err(dev, "error: missing mandatory ops\n"); return -EINVAL; } diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index f21d4a7ac261..aeea73efcb2f 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -473,7 +473,10 @@ snd_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply) { - return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply); + if (sof_ops(sdev) && sof_ops(sdev)->ipc_pcm_params) + return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply); + + return 0; } /* host side configuration of the stream's data offset in stream mailbox area */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 08eba51fddd7..4b235ce74dfe 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -224,7 +224,7 @@ struct snd_sof_dsp_ops { /* host configure DSP HW parameters */ int (*ipc_pcm_params)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); /* mandatory */ + const struct sof_ipc_pcm_params_reply *reply); /* optional */ /* host side configuration of the stream's data offset in stream mailbox area */ int (*set_stream_data_offset)(struct snd_sof_dev *sdev, -- cgit v1.2.3 From 9a0a809a5aaeb09458c5f0d26fac63c213b0adb6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:16 -0800 Subject: ASoC: SOF: stream-ipc: Add sof_set_stream_data_offset() Add implementation for the generic set_stream_data_offset() callback in core to be used by platforms. Convert the sof_ipc_pcm_params() to a wrapper for the new function. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 3 +++ sound/soc/sof/stream-ipc.c | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4b235ce74dfe..e6fd3910634f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -638,6 +638,9 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev, int sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); +int sof_set_stream_data_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset); int sof_stream_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 15a55851faeb..b7b96b9f5279 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -45,12 +45,11 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_ipc_msg_data); -int sof_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) +int sof_set_stream_data_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset) { struct sof_stream *stream = substream->runtime->private_data; - size_t posn_offset = reply->posn_offset; /* check if offset is overflow or it is not aligned */ if (posn_offset > sdev->stream_box.size || @@ -64,6 +63,14 @@ int sof_ipc_pcm_params(struct snd_sof_dev *sdev, return 0; } +EXPORT_SYMBOL(sof_set_stream_data_offset); + +int sof_ipc_pcm_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply) +{ + return sof_set_stream_data_offset(sdev, substream, reply->posn_offset); +} EXPORT_SYMBOL(sof_ipc_pcm_params); int sof_stream_pcm_open(struct snd_sof_dev *sdev, -- cgit v1.2.3 From 29e3aa0bb934e44c6ec0127cbe96983dc9b82b0e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:17 -0800 Subject: ASoC: SOF: Intel: hda-ipc: Add hda_set_stream_data_offset() Add implementation for the generic set_stream_data_offset() callback to be used by HDA platforms. Convert the hda_ipc_pcm_params() to a wrapper for the new function. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-ipc.c | 15 ++++++++++----- sound/soc/sof/intel/hda.h | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index a8c452144168..317c3ab7e768 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -267,14 +267,12 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev, return 0; } -int hda_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) +int hda_set_stream_data_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset) { struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_stream *hda_stream; - /* validate offset */ - size_t posn_offset = reply->posn_offset; hda_stream = container_of(hstream, struct sof_intel_hda_stream, hext_stream.hstream); @@ -291,3 +289,10 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev, return 0; } + +int hda_ipc_pcm_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply) +{ + return hda_set_stream_data_offset(sdev, substream, reply->posn_offset); +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 46641d31e9c5..1e0a6d7bde17 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -569,6 +569,9 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev, int hda_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); +int hda_set_stream_data_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + size_t posn_offset); /* * DSP IPC Operations. -- cgit v1.2.3 From cf73363e4a55579e3131f5de38c3b7b70bb4d639 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:18 -0800 Subject: ASoC: SOF: Intel: Convert to use the generic set_stream_data_offset ops Switch from the IPC dependent ipc_pcm_params() ops to the IPC neutral set_stream_data_offset(). Remove the no longer used hda_ipc_pcm_params() function as well. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/bdw.c | 2 +- sound/soc/sof/intel/byt.c | 4 ++-- sound/soc/sof/intel/cnl.c | 2 +- sound/soc/sof/intel/hda-ipc.c | 7 ------- sound/soc/sof/intel/hda.h | 3 --- sound/soc/sof/intel/icl.c | 2 +- sound/soc/sof/intel/pci-tng.c | 2 +- sound/soc/sof/intel/tgl.c | 2 +- 9 files changed, 8 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index cd8d08c17561..6721c8f95161 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -56,7 +56,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .get_window_offset = hda_dsp_ipc_get_window_offset, .ipc_msg_data = hda_ipc_msg_data, - .ipc_pcm_params = hda_ipc_pcm_params, + .set_stream_data_offset = hda_set_stream_data_offset, /* machine driver */ .machine_select = hda_machine_select, diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index d627b7498d5e..fb9682b2fe32 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -596,7 +596,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .get_window_offset = bdw_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* machine driver */ .machine_select = bdw_machine_select, diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index dcfeaedb8fd5..bb84a4aa587a 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -250,7 +250,7 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .get_window_offset = atom_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* machine driver */ .machine_select = atom_machine_select, @@ -332,7 +332,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .get_window_offset = atom_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* machine driver */ .machine_select = atom_machine_select, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index bef27e8751f2..6a96470b967f 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -274,7 +274,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .get_window_offset = hda_dsp_ipc_get_window_offset, .ipc_msg_data = hda_ipc_msg_data, - .ipc_pcm_params = hda_ipc_pcm_params, + .set_stream_data_offset = hda_set_stream_data_offset, /* machine driver */ .machine_select = hda_machine_select, diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 317c3ab7e768..0395638c43ae 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -289,10 +289,3 @@ int hda_set_stream_data_offset(struct snd_sof_dev *sdev, return 0; } - -int hda_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - return hda_set_stream_data_offset(sdev, substream, reply->posn_offset); -} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1e0a6d7bde17..13b509c9f481 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -566,9 +566,6 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, int hda_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, void *p, size_t sz); -int hda_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); int hda_set_stream_data_offset(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, size_t posn_offset); diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index f20ab60e8a52..b44a649bfc0b 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -118,7 +118,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .get_window_offset = hda_dsp_ipc_get_window_offset, .ipc_msg_data = hda_ipc_msg_data, - .ipc_pcm_params = hda_ipc_pcm_params, + .set_stream_data_offset = hda_set_stream_data_offset, /* machine driver */ .machine_select = hda_machine_select, diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 7d5062f8076e..6efef225973f 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -165,7 +165,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .get_window_offset = atom_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* machine driver */ .machine_select = atom_machine_select, diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index c7d1c244bc48..cb1c319d5bee 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -91,7 +91,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .get_window_offset = hda_dsp_ipc_get_window_offset, .ipc_msg_data = hda_ipc_msg_data, - .ipc_pcm_params = hda_ipc_pcm_params, + .set_stream_data_offset = hda_set_stream_data_offset, /* machine driver */ .machine_select = hda_machine_select, -- cgit v1.2.3 From f0383aded3c6e61e044b90662bf99b3d850c5d90 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:19 -0800 Subject: ASoC: SOF: imx: Convert to use the generic set_stream_data_offset ops Switch from the IPC dependent ipc_pcm_params() ops to the IPC neutral set_stream_data_offset(). Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-10-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 4 ++-- sound/soc/sof/imx/imx8m.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index f6baecbb57fb..825bd2b9b7a1 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -509,7 +509,7 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = { .get_window_offset = imx8_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -572,7 +572,7 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = { .get_window_offset = imx8_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* module loading */ .load_module = snd_sof_parse_module_memcpy, diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 60251486b24b..803d6be6b4fb 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -435,7 +435,7 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = { .get_window_offset = imx8m_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, - .ipc_pcm_params = sof_ipc_pcm_params, + .set_stream_data_offset = sof_set_stream_data_offset, /* module loading */ .load_module = snd_sof_parse_module_memcpy, -- cgit v1.2.3 From 00f19253633710877880ad062d6cee3c13deb9a5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Mar 2022 20:27:20 -0800 Subject: ASoC: SOF: Remove ipc_pcm_params() ops All users have been converted to use the IPC agnostic set_stream_data_offsett() Remove all code related to the old API. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20220310042720.976809-11-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 13 ------------- sound/soc/sof/pcm.c | 7 ------- sound/soc/sof/sof-priv.h | 8 -------- sound/soc/sof/stream-ipc.c | 8 -------- 4 files changed, 36 deletions(-) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index aeea73efcb2f..a19474663767 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -466,19 +466,6 @@ static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev, { return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz); } - -/* host configure DSP HW parameters */ -static inline int -snd_sof_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - if (sof_ops(sdev) && sof_ops(sdev)->ipc_pcm_params) - return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply); - - return 0; -} - /* host side configuration of the stream's data offset in stream mailbox area */ static inline int snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 150f9ac872c6..1661b0bc6f12 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -269,13 +269,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - ret = snd_sof_ipc_pcm_params(sdev, substream, &ipc_params_reply); - if (ret < 0) { - dev_err(component->dev, "%s: got wrong reply for PCM %d\n", - __func__, spcm->pcm.pcm_id); - return ret; - } - ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset); if (ret < 0) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e6fd3910634f..ef5a2adae5c7 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -221,11 +221,6 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, void *p, size_t sz); /* mandatory */ - /* host configure DSP HW parameters */ - int (*ipc_pcm_params)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); /* optional */ - /* host side configuration of the stream's data offset in stream mailbox area */ int (*set_stream_data_offset)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, @@ -635,9 +630,6 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); int sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, void *p, size_t sz); -int sof_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); int sof_set_stream_data_offset(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, size_t posn_offset); diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index b7b96b9f5279..5f1ceeea893a 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -65,14 +65,6 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_set_stream_data_offset); -int sof_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - return sof_set_stream_data_offset(sdev, substream, reply->posn_offset); -} -EXPORT_SYMBOL(sof_ipc_pcm_params); - int sof_stream_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { -- cgit v1.2.3 From f67084148dac015d059c64f25e57abd0ab18946c Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Tue, 8 Mar 2022 15:24:31 +0800 Subject: ASoC: mediatek: mt8195: add reset controller Audio hardware is possibly used in the firmware stage, so resetting audio hardware before regcache records default register values is required. Signed-off-by: Trevor Wu Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220308072435.22460-2-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 550636500949..72b2c6d629b9 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "mt8195-afe-common.h" #include "mt8195-afe-clk.h" #include "mt8195-reg.h" @@ -3056,6 +3057,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_base_afe *afe; struct mt8195_afe_private *afe_priv; struct device *dev = &pdev->dev; + struct reset_control *rstc; int i, irq_id, ret; struct snd_soc_component *component; @@ -3092,6 +3094,20 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) return ret; } + /* reset controller to reset audio regs before regmap cache */ + rstc = devm_reset_control_get_exclusive(dev, "audiosys"); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + dev_err(dev, "could not get audiosys reset:%d\n", ret); + return ret; + } + + ret = reset_control_reset(rstc); + if (ret) { + dev_err(dev, "failed to trigger audio reset:%d\n", ret); + return ret; + } + spin_lock_init(&afe_priv->afe_ctrl_lock); mutex_init(&afe->irq_alloc_lock); -- cgit v1.2.3 From ee7f79a81a27c47088fe0af95788621644826d91 Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Tue, 8 Mar 2022 15:24:32 +0800 Subject: dt-bindings: mediatek: mt8195: add reset property Add required properties "resets" and "reset_names", which are used to specify audiosys hw reset for mt8195 afe driver. Signed-off-by: Trevor Wu Link: https://lore.kernel.org/r/20220308072435.22460-3-trevor.wu@mediatek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml index 6d0975b33d15..4452a4070eff 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-afe-pcm.yaml @@ -19,6 +19,12 @@ properties: interrupts: maxItems: 1 + resets: + maxItems: 1 + + reset-names: + const: audiosys + memory-region: maxItems: 1 description: | @@ -127,6 +133,8 @@ required: - compatible - reg - interrupts + - resets + - reset-names - mediatek,topckgen - power-domains - clocks @@ -144,6 +152,8 @@ examples: compatible = "mediatek,mt8195-audio"; reg = <0x10890000 0x10000>; interrupts = ; + resets = <&watchdog 14>; + reset-names = "audiosys"; mediatek,topckgen = <&topckgen>; power-domains = <&spm 7>; //MT8195_POWER_DOMAIN_AUDIO memory-region = <&snd_dma_mem_reserved>; -- cgit v1.2.3 From 886e09c77d2b48559d26928a5705a20ef3ac117d Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 11 Mar 2022 13:28:18 +0200 Subject: ASoC: mchp-spdifrx: fix typo Fix typo in log describing failure of devm_snd_dmaengine_pcm_register(). Signed-off-by: Claudiu Beznea Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220311112818.1482372-1-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdifrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index bcd4f3e4fb0f..5fc968483f2c 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -920,7 +920,7 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (err) { - dev_err(&pdev->dev, "failed to register PMC: %d\n", err); + dev_err(&pdev->dev, "failed to register PCM: %d\n", err); return err; } -- cgit v1.2.3 From 5bbe2918acccfa60de1c1a2139de9cc5441d5796 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 10 Mar 2022 16:46:39 +0530 Subject: ASoC: simple-card-utils: Don't reset clock of active DAI Playback or capture errors are seen when clock is reset during an active stage of DAI. Presently this scenario happens when DAI has both playback and capture sessions running and one of these finishes first which will be followed by clock rate reset. The remaining active session will be affected in such case. Address this problem by allowing clock rate reset to happen only when the DAI is no more active. Signed-off-by: Sameer Pujar Reviewed-by: Kuninori Morimoto Link: https://lore.kernel.org/r/1646910999-2501-1-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 14c8b3a74c2d..8e037835bc58 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -318,15 +318,19 @@ void asoc_simple_shutdown(struct snd_pcm_substream *substream) int i; for_each_prop_dai_cpu(props, i, dai) { - if (props->mclk_fs && !dai->clk_fixed) - snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, i), + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i); + + if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai)) + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN); asoc_simple_clk_disable(dai); } for_each_prop_dai_codec(props, i, dai) { - if (props->mclk_fs && !dai->clk_fixed) - snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, i), + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i); + + if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai)) + snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); asoc_simple_clk_disable(dai); -- cgit v1.2.3 From 375a347da4889f64d86e1ab7f4e6702b6e9bf299 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 7 Mar 2022 08:45:22 +0000 Subject: ASoC: msm8916-wcd-digital: Fix missing clk_disable_unprepare() in msm8916_wcd_digital_probe Fix the missing clk_disable_unprepare() before return from msm8916_wcd_digital_probe in the error handling case. Fixes: 150db8c5afa1 ("ASoC: codecs: Add msm8916-wcd digital codec") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220307084523.28687-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-digital.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index fcc10c8bc625..9ad7fc0baf07 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -1201,7 +1201,7 @@ static int msm8916_wcd_digital_probe(struct platform_device *pdev) ret = clk_prepare_enable(priv->mclk); if (ret < 0) { dev_err(dev, "failed to enable mclk %d\n", ret); - return ret; + goto err_clk; } dev_set_drvdata(dev, priv); @@ -1209,6 +1209,9 @@ static int msm8916_wcd_digital_probe(struct platform_device *pdev) return devm_snd_soc_register_component(dev, &msm8916_wcd_digital, msm8916_wcd_digital_dai, ARRAY_SIZE(msm8916_wcd_digital_dai)); +err_clk: + clk_disable_unprepare(priv->ahbclk); + return ret; } static int msm8916_wcd_digital_remove(struct platform_device *pdev) -- cgit v1.2.3 From 882bd07f564f97fca6e42ce6ce627ce24ce1ef5a Mon Sep 17 00:00:00 2001 From: huangwenhui Date: Fri, 11 Mar 2022 17:38:36 +0800 Subject: ALSA: hda/realtek - Fix headset mic problem for a HP machine with alc671 On a HP 288 Pro G8, the front mic could not be detected.In order to get it working, the pin configuration needs to be set correctly, and the ALC671_FIXUP_HP_HEADSET_MIC2 fixup needs to be applied. Signed-off-by: huangwenhui Cc: Link: https://lore.kernel.org/r/20220311093836.20754-1-huangwenhuia@uniontech.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4ddd464470fd..ba86b09a61af 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11069,6 +11069,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), -- cgit v1.2.3 From c19bd02e9029f0f75b58f4b8662527da74be8985 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:28 +0100 Subject: ALSA: hda: Add helper macros for DSP capable devices HDAudio drivers make heavy use of I/O operations. Declare a range of update, read and write helpers similar to those available for HDAudio legacy driver. These macros are used by AVS driver to improve code readability. Signed-off-by: Cezary Rojewski Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/20220311153544.136854-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/hdaudio.h | 3 +++ include/sound/hdaudio_ext.h | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 6a90ce405e60..15f15075238d 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -448,6 +449,8 @@ static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr) #define snd_hdac_reg_writel(bus, addr, val) writel(val, addr) #define snd_hdac_reg_readl(bus, addr) readl(addr) +#define snd_hdac_reg_writeq(bus, addr, val) writeq(val, addr) +#define snd_hdac_reg_readq(bus, addr) readq(addr) /* * macros for easy use diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 77123c3e4095..5a538799ff77 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -2,6 +2,8 @@ #ifndef __SOUND_HDAUDIO_EXT_H #define __SOUND_HDAUDIO_EXT_H +#include +#include #include int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, @@ -143,6 +145,54 @@ void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable); writew(((readw(addr + reg) & ~(mask)) | (val)), \ addr + reg) +#define snd_hdac_adsp_writeb(chip, reg, value) \ + snd_hdac_reg_writeb(chip, (chip)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readb(chip, reg) \ + snd_hdac_reg_readb(chip, (chip)->dsp_ba + (reg)) +#define snd_hdac_adsp_writew(chip, reg, value) \ + snd_hdac_reg_writew(chip, (chip)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readw(chip, reg) \ + snd_hdac_reg_readw(chip, (chip)->dsp_ba + (reg)) +#define snd_hdac_adsp_writel(chip, reg, value) \ + snd_hdac_reg_writel(chip, (chip)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readl(chip, reg) \ + snd_hdac_reg_readl(chip, (chip)->dsp_ba + (reg)) +#define snd_hdac_adsp_writeq(chip, reg, value) \ + snd_hdac_reg_writeq(chip, (chip)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readq(chip, reg) \ + snd_hdac_reg_readq(chip, (chip)->dsp_ba + (reg)) + +#define snd_hdac_adsp_updateb(chip, reg, mask, val) \ + snd_hdac_adsp_writeb(chip, reg, \ + (snd_hdac_adsp_readb(chip, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatew(chip, reg, mask, val) \ + snd_hdac_adsp_writew(chip, reg, \ + (snd_hdac_adsp_readw(chip, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatel(chip, reg, mask, val) \ + snd_hdac_adsp_writel(chip, reg, \ + (snd_hdac_adsp_readl(chip, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updateq(chip, reg, mask, val) \ + snd_hdac_adsp_writeq(chip, reg, \ + (snd_hdac_adsp_readq(chip, reg) & ~(mask)) | (val)) + +#define snd_hdac_adsp_readb_poll(chip, reg, val, cond, delay_us, timeout_us) \ + readb_poll_timeout((chip)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readw_poll(chip, reg, val, cond, delay_us, timeout_us) \ + readw_poll_timeout((chip)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readl_poll(chip, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout((chip)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readq_poll(chip, reg, val, cond, delay_us, timeout_us) \ + readq_poll_timeout((chip)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_stream_readb_poll(strm, reg, val, cond, delay_us, timeout_us) \ + readb_poll_timeout((strm)->sd_addr + AZX_REG_ ## reg, val, cond, \ + delay_us, timeout_us) +#define snd_hdac_stream_readl_poll(strm, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout((strm)->sd_addr + AZX_REG_ ## reg, val, cond, \ + delay_us, timeout_us) struct hdac_ext_device; -- cgit v1.2.3 From da0398099a83483014adc509a2845c88ccf672af Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:29 +0100 Subject: ASoC: Export DAI register and widget ctor and dctor functions To allow for more flexibility i.e. populating component DAIs dynamically during its initialization, without being limited to topology loading procedure, expose snd_soc_register(), snd_soc_dapm_new_dai_widgets() and snd_soc_dapm_free_widget() functions. Allows users to first check available resources e.g. number of PCMs supported by HDAudio codec before allocating the number of DAPM widgets needed. This prevents superfluous objects from being created or allows driver to adjust to situation when resources are limited. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c3039e97929a..ebb8e7a7fc29 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -429,6 +429,7 @@ struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked( const struct snd_soc_dapm_widget *widget); int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a088bc9f7dd7..ce153ac2c3ab 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2465,6 +2465,7 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); return dai; } +EXPORT_SYMBOL_GPL(snd_soc_register_dai); /** * snd_soc_unregister_dais - Unregister DAIs from the ASoC core diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b06c5682445c..b435b5c4cfb7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2484,6 +2484,12 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) kfree(path); } +/** + * snd_soc_dapm_free_widget - Free specified widget + * @w: widget to free + * + * Removes widget from all paths and frees memory occupied by it. + */ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *p, *next_p; @@ -2506,6 +2512,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) kfree_const(w->sname); kfree(w); } +EXPORT_SYMBOL_GPL(snd_soc_dapm_free_widget); void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm) { @@ -4208,6 +4215,13 @@ param_fail: return ERR_PTR(ret); } +/** + * snd_soc_dapm_new_dai_widgets - Create new DAPM widgets + * @dapm: DAPM context + * @dai: parent DAI + * + * Returns 0 on success, error code otherwise. + */ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai) { @@ -4253,6 +4267,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, return 0; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_new_dai_widgets); int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) { -- cgit v1.2.3 From 9fe51c559a862d6c884f6784de26c8f9463187b1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:30 +0100 Subject: ASoC: Intel: Introduce AVS driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare base structures and core DSP operations for the avs solution. The base structures describe PCI HDAudio bus device and platform-type differentiations. First set of operations added controls the lifecycle of any Audio DSP core: (un)powering, (un)resetting and (un)stalling. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 11 +++++ sound/soc/intel/Makefile | 1 + sound/soc/intel/avs/Makefile | 5 ++ sound/soc/intel/avs/avs.h | 71 ++++++++++++++++++++++++++ sound/soc/intel/avs/dsp.c | 107 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/registers.h | 22 +++++++++ 6 files changed, 217 insertions(+) create mode 100644 sound/soc/intel/avs/Makefile create mode 100644 sound/soc/intel/avs/avs.h create mode 100644 sound/soc/intel/avs/dsp.c create mode 100644 sound/soc/intel/avs/registers.h diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index f3a4a907b29d..e9768c4aa1a9 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -209,5 +209,16 @@ config SND_SOC_INTEL_KEEMBAY If you have a Intel Keembay platform then enable this option by saying Y or m. +config SND_SOC_INTEL_AVS + tristate "Intel AVS driver" + depends on PCI && ACPI + depends on COMMON_CLK + select SND_SOC_ACPI + select SND_HDA_EXT_CORE + help + Enable support for Intel(R) cAVS 1.5 platforms with DSP + capabilities. This includes Skylake, Kabylake, Amberlake and + Apollolake. + # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 7c5038803be7..d44b2652c707 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/ obj-$(CONFIG_SND_SOC_INTEL_CATPT) += catpt/ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_COMMON) += skylake/ obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += keembay/ +obj-$(CONFIG_SND_SOC_INTEL_AVS) += avs/ # Machine support obj-$(CONFIG_SND_SOC) += boards/ diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile new file mode 100644 index 000000000000..5f7976a95fe2 --- /dev/null +++ b/sound/soc/intel/avs/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +snd-soc-avs-objs := dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h new file mode 100644 index 000000000000..2aefd1969644 --- /dev/null +++ b/sound/soc/intel/avs/avs.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski + * Amadeusz Slawinski + */ + +#ifndef __SOUND_SOC_INTEL_AVS_H +#define __SOUND_SOC_INTEL_AVS_H + +#include +#include + +struct avs_dev; + +struct avs_dsp_ops { + int (* const power)(struct avs_dev *, u32, bool); + int (* const reset)(struct avs_dev *, u32, bool); + int (* const stall)(struct avs_dev *, u32, bool); +}; + +#define avs_dsp_op(adev, op, ...) \ + ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) + +#define avs_platattr_test(adev, attr) \ + ((adev)->spec->attributes & AVS_PLATATTR_##attr) + +/* Platform specific descriptor */ +struct avs_spec { + const char *name; + + const struct avs_dsp_ops *const dsp_ops; + + const u32 core_init_mask; /* used during DSP boot */ + const u64 attributes; /* bitmask of AVS_PLATATTR_* */ +}; + +/* + * struct avs_dev - Intel HD-Audio driver data + * + * @dev: PCI device + * @dsp_ba: DSP bar address + * @spec: platform-specific descriptor + */ +struct avs_dev { + struct hda_bus base; + struct device *dev; + + void __iomem *dsp_ba; + const struct avs_spec *spec; +}; + +/* from hda_bus to avs_dev */ +#define hda_to_avs(hda) container_of(hda, struct avs_dev, base) +/* from hdac_bus to avs_dev */ +#define hdac_to_avs(hdac) hda_to_avs(to_hda_bus(hdac)) +/* from device to avs_dev */ +#define to_avs_dev(dev) \ +({ \ + struct hdac_bus *__bus = dev_get_drvdata(dev); \ + hdac_to_avs(__bus); \ +}) + +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power); +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset); +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall); +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask); +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask); + +#endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c new file mode 100644 index 000000000000..63f5ce2698a2 --- /dev/null +++ b/sound/soc/intel/avs/dsp.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include "avs.h" +#include "registers.h" + +#define AVS_ADSPCS_INTERVAL_US 500 +#define AVS_ADSPCS_TIMEOUT_US 50000 + +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) +{ + u32 value, mask, reg; + int ret; + + mask = AVS_ADSPCS_SPA_MASK(core_mask); + value = power ? mask : 0; + + snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); + + mask = AVS_ADSPCS_CPA_MASK(core_mask); + value = power ? mask : 0; + + ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, + reg, (reg & mask) == value, + AVS_ADSPCS_INTERVAL_US, + AVS_ADSPCS_TIMEOUT_US); + if (ret) + dev_err(adev->dev, "core_mask %d power %s failed: %d\n", + core_mask, power ? "on" : "off", ret); + + return ret; +} + +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) +{ + u32 value, mask, reg; + int ret; + + mask = AVS_ADSPCS_CRST_MASK(core_mask); + value = reset ? mask : 0; + + snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); + + ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, + reg, (reg & mask) == value, + AVS_ADSPCS_INTERVAL_US, + AVS_ADSPCS_TIMEOUT_US); + if (ret) + dev_err(adev->dev, "core_mask %d %s reset failed: %d\n", + core_mask, reset ? "enter" : "exit", ret); + + return ret; +} + +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) +{ + u32 value, mask, reg; + int ret; + + mask = AVS_ADSPCS_CSTALL_MASK(core_mask); + value = stall ? mask : 0; + + snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); + + ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, + reg, (reg & mask) == value, + AVS_ADSPCS_INTERVAL_US, + AVS_ADSPCS_TIMEOUT_US); + if (ret) + dev_err(adev->dev, "core_mask %d %sstall failed: %d\n", + core_mask, stall ? "" : "un", ret); + + return ret; +} + +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask) +{ + int ret; + + ret = avs_dsp_op(adev, power, core_mask, true); + if (ret) + return ret; + + ret = avs_dsp_op(adev, reset, core_mask, false); + if (ret) + return ret; + + return avs_dsp_op(adev, stall, core_mask, false); +} + +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask) +{ + /* No error checks to allow for complete DSP shutdown. */ + avs_dsp_op(adev, stall, core_mask, true); + avs_dsp_op(adev, reset, core_mask, true); + + return avs_dsp_op(adev, power, core_mask, false); +} + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h new file mode 100644 index 000000000000..e6b49973bd8f --- /dev/null +++ b/sound/soc/intel/avs/registers.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski + * Amadeusz Slawinski + */ + +#ifndef __SOUND_SOC_INTEL_AVS_REGS_H +#define __SOUND_SOC_INTEL_AVS_REGS_H + +/* Intel HD Audio General DSP Registers */ +#define AVS_ADSP_GEN_BASE 0x0 +#define AVS_ADSP_REG_ADSPCS (AVS_ADSP_GEN_BASE + 0x04) + +#define AVS_ADSPCS_CRST_MASK(cm) (cm) +#define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8) +#define AVS_ADSPCS_SPA_MASK(cm) ((cm) << 16) +#define AVS_ADSPCS_CPA_MASK(cm) ((cm) << 24) +#define AVS_MAIN_CORE_MASK BIT(0) + +#endif /* __SOUND_SOC_INTEL_AVS_REGS_H */ -- cgit v1.2.3 From 2879516fcd6d7ec5972762dbd94859d9221e70d8 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:31 +0100 Subject: ASoC: Intel: avs: Inter process communication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the IPC between Intel audio firmware and kernel driver. The IPC allows transmission of requests, handling of responses as well as unsolicited (i.e. firmware-generated) notifications. A subscription mechanism is added to enable different parts of the driver to register for specific notifications. The part of the DSP boot process that involves sending ROM message requires an extra step - must be followed by unstall operation of MAIN_CORE. All other types of messages do not require such specific handling, so separate set of functions is provided for sending these. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/avs.h | 100 +++++++++++ sound/soc/intel/avs/ipc.c | 376 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 170 ++++++++++++++++++ sound/soc/intel/avs/registers.h | 45 +++++ 5 files changed, 692 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/ipc.c create mode 100644 sound/soc/intel/avs/messages.h diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 5f7976a95fe2..e243806dd38a 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o +snd-soc-avs-objs := dsp.o ipc.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 2aefd1969644..330898d8ce2d 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -11,13 +11,27 @@ #include #include +#include "messages.h" struct avs_dev; +/* + * struct avs_dsp_ops - Platform-specific DSP operations + * + * @power: Power on or off DSP cores + * @reset: Enter or exit reset state on DSP cores + * @stall: Stall or run DSP cores + * @irq_handler: Top half of IPC servicing + * @irq_thread: Bottom half of IPC servicing + * @int_control: Enable or disable IPC interrupts + */ struct avs_dsp_ops { int (* const power)(struct avs_dev *, u32, bool); int (* const reset)(struct avs_dev *, u32, bool); int (* const stall)(struct avs_dev *, u32, bool); + irqreturn_t (* const irq_handler)(int, void *); + irqreturn_t (* const irq_thread)(int, void *); + void (* const int_control)(struct avs_dev *, bool); }; #define avs_dsp_op(adev, op, ...) \ @@ -34,6 +48,9 @@ struct avs_spec { const u32 core_init_mask; /* used during DSP boot */ const u64 attributes; /* bitmask of AVS_PLATATTR_* */ + const u32 sram_base_offset; + const u32 sram_window_size; + const u32 rom_status; }; /* @@ -49,6 +66,9 @@ struct avs_dev { void __iomem *dsp_ba; const struct avs_spec *spec; + struct avs_ipc *ipc; + + struct completion fw_ready; }; /* from hda_bus to avs_dev */ @@ -68,4 +88,84 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall); int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask); int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask); +/* Inter Process Communication */ + +struct avs_ipc_msg { + union { + u64 header; + union avs_global_msg glb; + union avs_reply_msg rsp; + }; + void *data; + size_t size; +}; + +/* + * struct avs_ipc - DSP IPC context + * + * @dev: PCI device + * @rx: Reply message cache + * @default_timeout_ms: default message timeout in MS + * @ready: whether firmware is ready and communication is open + * @rx_completed: whether RX for previously sent TX has been received + * @rx_lock: for serializing manipulation of rx_* fields + * @msg_lock: for synchronizing request handling + * @done_completion: DONE-part of IPC i.e. ROM and ACKs from FW + * @busy_completion: BUSY-part of IPC i.e. receiving responses from FW + */ +struct avs_ipc { + struct device *dev; + + struct avs_ipc_msg rx; + u32 default_timeout_ms; + bool ready; + + bool rx_completed; + spinlock_t rx_lock; + struct mutex msg_mutex; + struct completion done_completion; + struct completion busy_completion; +}; + +#define AVS_EIPC EREMOTEIO +/* + * IPC handlers may return positive value (firmware error code) what denotes + * successful HOST <-> DSP communication yet failure to process specific request. + * + * Below macro converts returned value to linux kernel error code. + * All IPC callers MUST use it as soon as firmware error code is consumed. + */ +#define AVS_IPC_RET(ret) \ + (((ret) <= 0) ? (ret) : -AVS_EIPC) + +static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx, + const char *name, int error) +{ + /* + * If IPC channel is blocked e.g.: due to ongoing recovery, + * -EPERM error code is expected and thus it's not an actual error. + */ + if (error == -EPERM) + dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name, + tx->glb.primary, tx->glb.ext.val, error); + else + dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name, + tx->glb.primary, tx->glb.ext.val, error); +} + +irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id); +irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id); +void avs_dsp_process_response(struct avs_dev *adev, u64 header); +int avs_dsp_send_msg_timeout(struct avs_dev *adev, + struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout); +int avs_dsp_send_msg(struct avs_dev *adev, + struct avs_ipc_msg *request, struct avs_ipc_msg *reply); +int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, + struct avs_ipc_msg *request, int timeout); +int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request); +void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable); +int avs_ipc_init(struct avs_ipc *ipc, struct device *dev); +void avs_ipc_block(struct avs_ipc *ipc); + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c new file mode 100644 index 000000000000..b1986a82945d --- /dev/null +++ b/sound/soc/intel/avs/ipc.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include "avs.h" +#include "messages.h" +#include "registers.h" + +#define AVS_IPC_TIMEOUT_MS 300 + +static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) +{ + struct avs_ipc *ipc = adev->ipc; + union avs_reply_msg msg = AVS_MSG(header); + + ipc->rx.header = header; + /* Abort copying payload if request processing was unsuccessful. */ + if (!msg.status) + memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size); +} + +static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) +{ + struct avs_notify_mod_data mod_data; + union avs_notify_msg msg = AVS_MSG(header); + size_t data_size = 0; + void *data = NULL; + + /* Ignore spurious notifications until handshake is established. */ + if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) { + dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n", msg.primary); + return; + } + + /* Calculate notification payload size. */ + switch (msg.notify_msg_type) { + case AVS_NOTIFY_FW_READY: + break; + + case AVS_NOTIFY_PHRASE_DETECTED: + data_size = sizeof(struct avs_notify_voice_data); + break; + + case AVS_NOTIFY_RESOURCE_EVENT: + data_size = sizeof(struct avs_notify_res_data); + break; + + case AVS_NOTIFY_MODULE_EVENT: + /* To know the total payload size, header needs to be read first. */ + memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data)); + data_size = sizeof(mod_data) + mod_data.data_size; + break; + + default: + dev_info(adev->dev, "unknown notification: 0x%08x\n", msg.primary); + break; + } + + if (data_size) { + data = kmalloc(data_size, GFP_KERNEL); + if (!data) + return; + + memcpy_fromio(data, avs_uplink_addr(adev), data_size); + } + + /* Perform notification-specific operations. */ + switch (msg.notify_msg_type) { + case AVS_NOTIFY_FW_READY: + dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary); + adev->ipc->ready = true; + complete(&adev->fw_ready); + break; + + default: + break; + } + + kfree(data); +} + +void avs_dsp_process_response(struct avs_dev *adev, u64 header) +{ + struct avs_ipc *ipc = adev->ipc; + + /* + * Response may either be solicited - a reply for a request that has + * been sent beforehand - or unsolicited (notification). + */ + if (avs_msg_is_reply(header)) { + /* Response processing is invoked from IRQ thread. */ + spin_lock_irq(&ipc->rx_lock); + avs_dsp_receive_rx(adev, header); + ipc->rx_completed = true; + spin_unlock_irq(&ipc->rx_lock); + } else { + avs_dsp_process_notification(adev, header); + } + + complete(&ipc->busy_completion); +} + +irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + struct avs_ipc *ipc = adev->ipc; + u32 adspis, hipc_rsp, hipc_ack; + irqreturn_t ret = IRQ_NONE; + + adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); + if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC)) + return ret; + + hipc_ack = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCIE); + hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + + /* DSP acked host's request */ + if (hipc_ack & SKL_ADSP_HIPCIE_DONE) { + /* + * As an extra precaution, mask done interrupt. Code executed + * due to complete() found below does not assume any masking. + */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_DONE, 0); + + complete(&ipc->done_completion); + + /* tell DSP it has our attention */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE, + SKL_ADSP_HIPCIE_DONE, + SKL_ADSP_HIPCIE_DONE); + /* unmask done interrupt */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_DONE, + AVS_ADSP_HIPCCTL_DONE); + ret = IRQ_HANDLED; + } + + /* DSP sent new response to process */ + if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) { + /* mask busy interrupt */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_BUSY, 0); + + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + union avs_reply_msg msg; + u32 hipct, hipcte; + + hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); + + /* ensure DSP sent new response to process */ + if (!(hipct & SKL_ADSP_HIPCT_BUSY)) + return IRQ_NONE; + + msg.primary = hipct; + msg.ext.val = hipcte; + avs_dsp_process_response(adev, msg.val); + + /* tell DSP we accepted its message */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, + SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); + /* unmask busy interrupt */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); + + return IRQ_HANDLED; +} + +static bool avs_ipc_is_busy(struct avs_ipc *ipc) +{ + struct avs_dev *adev = to_avs_dev(ipc->dev); + u32 hipc_rsp; + + hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + return hipc_rsp & SKL_ADSP_HIPCT_BUSY; +} + +static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout) +{ + u32 repeats_left = 128; /* to avoid infinite looping */ + int ret; + +again: + ret = wait_for_completion_timeout(&ipc->busy_completion, msecs_to_jiffies(timeout)); + + /* DSP could be unresponsive at this point. */ + if (!ipc->ready) + return -EPERM; + + if (!ret) { + if (!avs_ipc_is_busy(ipc)) + return -ETIMEDOUT; + /* + * Firmware did its job, either notification or reply + * has been received - now wait until it's processed. + */ + wait_for_completion_killable(&ipc->busy_completion); + } + + /* Ongoing notification's bottom-half may cause early wakeup */ + spin_lock(&ipc->rx_lock); + if (!ipc->rx_completed) { + if (repeats_left) { + /* Reply delayed due to notification. */ + repeats_left--; + reinit_completion(&ipc->busy_completion); + spin_unlock(&ipc->rx_lock); + goto again; + } + + spin_unlock(&ipc->rx_lock); + return -ETIMEDOUT; + } + + spin_unlock(&ipc->rx_lock); + return 0; +} + +static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) +{ + lockdep_assert_held(&ipc->rx_lock); + + ipc->rx.header = 0; + ipc->rx.size = reply ? reply->size : 0; + ipc->rx_completed = false; + + reinit_completion(&ipc->done_completion); + reinit_completion(&ipc->busy_completion); +} + +static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx) +{ + tx->header |= SKL_ADSP_HIPCI_BUSY; + + if (tx->size) + memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); + snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32); + snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX); +} + +static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout) +{ + struct avs_ipc *ipc = adev->ipc; + int ret; + + if (!ipc->ready) + return -EPERM; + + mutex_lock(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, reply); + avs_dsp_send_tx(adev, request); + spin_unlock(&ipc->rx_lock); + + ret = avs_ipc_wait_busy_completion(ipc, timeout); + if (ret) { + if (ret == -ETIMEDOUT) { + dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret); + + avs_ipc_block(ipc); + } + goto exit; + } + + ret = ipc->rx.rsp.status; + if (reply) { + reply->header = ipc->rx.header; + if (reply->data && ipc->rx.size) + memcpy(reply->data, ipc->rx.data, reply->size); + } + +exit: + mutex_unlock(&ipc->msg_mutex); + return ret; +} + +int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout) +{ + return avs_dsp_do_send_msg(adev, request, reply, timeout); +} + +int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply) +{ + return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms); +} + +static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout) +{ + struct avs_ipc *ipc = adev->ipc; + int ret; + + mutex_lock(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, NULL); + avs_dsp_send_tx(adev, request); + spin_unlock(&ipc->rx_lock); + + /* ROM messages must be sent before main core is unstalled */ + ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false); + if (!ret) { + ret = wait_for_completion_timeout(&ipc->done_completion, msecs_to_jiffies(timeout)); + ret = ret ? 0 : -ETIMEDOUT; + } + + mutex_unlock(&ipc->msg_mutex); + + return ret; +} + +int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout) +{ + return avs_dsp_do_send_rom_msg(adev, request, timeout); +} + +int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request) +{ + return avs_dsp_send_rom_msg_timeout(adev, request, adev->ipc->default_timeout_ms); +} + +void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) +{ + u32 value, mask; + + /* + * No particular bit setting order. All of these are required + * to have a functional SW <-> FW communication. + */ + value = enable ? AVS_ADSP_ADSPIC_IPC : 0; + snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_IPC, value); + + mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY; + value = enable ? mask : 0; + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value); +} + +int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) +{ + ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!ipc->rx.data) + return -ENOMEM; + + ipc->dev = dev; + ipc->ready = false; + ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS; + init_completion(&ipc->done_completion); + init_completion(&ipc->busy_completion); + spin_lock_init(&ipc->rx_lock); + mutex_init(&ipc->msg_mutex); + + return 0; +} + +void avs_ipc_block(struct avs_ipc *ipc) +{ + ipc->ready = false; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h new file mode 100644 index 000000000000..becf336578d7 --- /dev/null +++ b/sound/soc/intel/avs/messages.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski + * Amadeusz Slawinski + */ + +#ifndef __SOUND_SOC_INTEL_AVS_MSGS_H +#define __SOUND_SOC_INTEL_AVS_MSGS_H + +struct avs_dev; + +#define AVS_MAILBOX_SIZE 4096 + +enum avs_msg_target { + AVS_FW_GEN_MSG = 0, + AVS_MOD_MSG = 1 +}; + +enum avs_msg_direction { + AVS_MSG_REQUEST = 0, + AVS_MSG_REPLY = 1 +}; + +enum avs_global_msg_type { + AVS_GLB_NOTIFICATION = 27, +}; + +union avs_global_msg { + u64 val; + struct { + union { + u32 primary; + struct { + u32 rsvd:24; + u32 global_msg_type:5; + u32 msg_direction:1; + u32 msg_target:1; + }; + }; + union { + u32 val; + } ext; + }; +} __packed; + +struct avs_tlv { + u32 type; + u32 length; + u32 value[]; +} __packed; + +union avs_module_msg { + u64 val; + struct { + union { + u32 primary; + struct { + u32 module_id:16; + u32 instance_id:8; + u32 module_msg_type:5; + u32 msg_direction:1; + u32 msg_target:1; + }; + }; + union { + u32 val; + } ext; + }; +} __packed; + +union avs_reply_msg { + u64 val; + struct { + union { + u32 primary; + struct { + u32 status:24; + u32 global_msg_type:5; + u32 msg_direction:1; + u32 msg_target:1; + }; + }; + union { + u32 val; + } ext; + }; +} __packed; + +enum avs_notify_msg_type { + AVS_NOTIFY_PHRASE_DETECTED = 4, + AVS_NOTIFY_RESOURCE_EVENT = 5, + AVS_NOTIFY_FW_READY = 8, + AVS_NOTIFY_MODULE_EVENT = 12, +}; + +union avs_notify_msg { + u64 val; + struct { + union { + u32 primary; + struct { + u32 rsvd:16; + u32 notify_msg_type:8; + u32 global_msg_type:5; + u32 msg_direction:1; + u32 msg_target:1; + }; + }; + union { + u32 val; + } ext; + }; +} __packed; + +#define AVS_MSG(hdr) { .val = hdr } + +#define AVS_GLOBAL_REQUEST(msg_type) \ +{ \ + .global_msg_type = AVS_GLB_##msg_type, \ + .msg_direction = AVS_MSG_REQUEST, \ + .msg_target = AVS_FW_GEN_MSG, \ +} + +#define AVS_MODULE_REQUEST(msg_type) \ +{ \ + .module_msg_type = AVS_MOD_##msg_type, \ + .msg_direction = AVS_MSG_REQUEST, \ + .msg_target = AVS_MOD_MSG, \ +} + +#define AVS_NOTIFICATION(msg_type) \ +{ \ + .notify_msg_type = AVS_NOTIFY_##msg_type,\ + .global_msg_type = AVS_GLB_NOTIFICATION,\ + .msg_direction = AVS_MSG_REPLY, \ + .msg_target = AVS_FW_GEN_MSG, \ +} + +#define avs_msg_is_reply(hdr) \ +({ \ + union avs_reply_msg __msg = AVS_MSG(hdr); \ + __msg.msg_direction == AVS_MSG_REPLY && \ + __msg.global_msg_type != AVS_GLB_NOTIFICATION; \ +}) + +/* Notification types */ + +struct avs_notify_voice_data { + u16 kpd_score; + u16 reserved; +} __packed; + +struct avs_notify_res_data { + u32 resource_type; + u32 resource_id; + u32 event_type; + u32 reserved; + u32 data[6]; +} __packed; + +struct avs_notify_mod_data { + u32 module_instance_id; + u32 event_id; + u32 data_size; + u32 data[]; +} __packed; + +#endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index e6b49973bd8f..775701454a28 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -12,6 +12,11 @@ /* Intel HD Audio General DSP Registers */ #define AVS_ADSP_GEN_BASE 0x0 #define AVS_ADSP_REG_ADSPCS (AVS_ADSP_GEN_BASE + 0x04) +#define AVS_ADSP_REG_ADSPIC (AVS_ADSP_GEN_BASE + 0x08) +#define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C) + +#define AVS_ADSP_ADSPIC_IPC BIT(0) +#define AVS_ADSP_ADSPIS_IPC BIT(0) #define AVS_ADSPCS_CRST_MASK(cm) (cm) #define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8) @@ -19,4 +24,44 @@ #define AVS_ADSPCS_CPA_MASK(cm) ((cm) << 24) #define AVS_MAIN_CORE_MASK BIT(0) +#define AVS_ADSP_HIPCCTL_BUSY BIT(0) +#define AVS_ADSP_HIPCCTL_DONE BIT(1) + +/* SKL Intel HD Audio Inter-Processor Communication Registers */ +#define SKL_ADSP_IPC_BASE 0x40 +#define SKL_ADSP_REG_HIPCT (SKL_ADSP_IPC_BASE + 0x00) +#define SKL_ADSP_REG_HIPCTE (SKL_ADSP_IPC_BASE + 0x04) +#define SKL_ADSP_REG_HIPCI (SKL_ADSP_IPC_BASE + 0x08) +#define SKL_ADSP_REG_HIPCIE (SKL_ADSP_IPC_BASE + 0x0C) +#define SKL_ADSP_REG_HIPCCTL (SKL_ADSP_IPC_BASE + 0x10) + +#define SKL_ADSP_HIPCI_BUSY BIT(31) +#define SKL_ADSP_HIPCIE_DONE BIT(30) +#define SKL_ADSP_HIPCT_BUSY BIT(31) + +/* Constants used when accessing SRAM, space shared with firmware */ +#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram_base_offset) +#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) +#define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) + +#define AVS_FW_REGS_SIZE PAGE_SIZE +#define AVS_FW_REGS_WINDOW 0 +/* DSP -> HOST communication window */ +#define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW +/* HOST -> DSP communication window */ +#define AVS_DOWNLINK_WINDOW 1 + +/* registry I/O helpers */ +#define avs_sram_offset(adev, window_idx) \ + ((adev)->spec->sram_base_offset + \ + (adev)->spec->sram_window_size * (window_idx)) + +#define avs_sram_addr(adev, window_idx) \ + ((adev)->dsp_ba + avs_sram_offset(adev, window_idx)) + +#define avs_uplink_addr(adev) \ + (avs_sram_addr(adev, AVS_UPLINK_WINDOW) + AVS_FW_REGS_SIZE) +#define avs_downlink_addr(adev) \ + avs_sram_addr(adev, AVS_DOWNLINK_WINDOW) + #endif /* __SOUND_SOC_INTEL_AVS_REGS_H */ -- cgit v1.2.3 From cb1eb6b5be42799d23f81ffde84ee3537365b0ed Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:32 +0100 Subject: ASoC: Intel: avs: Add code loading requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before firmware and its modules can be used, they have to be loaded. Code loading process is complex and is a combination of DMA and IPC operations. Here, IPC part is being added and accounts for CLDMA and HDA mechanisms both. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/messages.c | 65 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 22 ++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/messages.c diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index e243806dd38a..c0824f30fd3b 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o ipc.o +snd-soc-avs-objs := dsp.o ipc.o messages.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c new file mode 100644 index 000000000000..055ba74f8e0b --- /dev/null +++ b/sound/soc/intel/avs/messages.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include "avs.h" +#include "messages.h" + +#define AVS_CL_TIMEOUT_MS 5000 + +int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES); + struct avs_ipc_msg request; + int ret; + + msg.load_multi_mods.mod_cnt = num_mod_ids; + request.header = msg.val; + request.data = mod_ids; + request.size = sizeof(*mod_ids) * num_mod_ids; + + ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS); + if (ret) + avs_ipc_err(adev, &request, "load multiple modules", ret); + + return ret; +} + +int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES); + struct avs_ipc_msg request; + int ret; + + msg.load_multi_mods.mod_cnt = num_mod_ids; + request.header = msg.val; + request.data = mod_ids; + request.size = sizeof(*mod_ids) * num_mod_ids; + + ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS); + if (ret) + avs_ipc_err(adev, &request, "unload multiple modules", ret); + + return ret; +} + +int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.load_lib.dma_id = dma_id; + msg.load_lib.lib_id = lib_id; + request.header = msg.val; + + ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS); + if (ret) + avs_ipc_err(adev, &request, "load library", ret); + + return ret; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index becf336578d7..5e16aabfb6f4 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -24,6 +24,9 @@ enum avs_msg_direction { }; enum avs_global_msg_type { + AVS_GLB_LOAD_MULTIPLE_MODULES = 15, + AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16, + AVS_GLB_LOAD_LIBRARY = 24, AVS_GLB_NOTIFICATION = 27, }; @@ -38,6 +41,16 @@ union avs_global_msg { u32 msg_direction:1; u32 msg_target:1; }; + /* module loading */ + struct { + u32 mod_cnt:8; + } load_multi_mods; + /* library loading */ + struct { + u32 dma_id:5; + u32 rsvd:11; + u32 lib_id:4; + } load_lib; }; union { u32 val; @@ -84,6 +97,10 @@ union avs_reply_msg { }; union { u32 val; + /* module loading */ + struct { + u32 err_mod_id:16; + } load_multi_mods; } ext; }; } __packed; @@ -167,4 +184,9 @@ struct avs_notify_mod_data { u32 data[]; } __packed; +/* Code loading messages */ +int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); +int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); +int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From b956b27b477ae63e92240f813c1027de601df11f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:33 +0100 Subject: ASoC: Intel: avs: Add pipeline management requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pipeline represents a scheduling entity. Their existence as well as their state machine is controlled through CREATE_PIPELINE, DELETE_PIPELINE and SET_PIPELINE_STATE IPCs. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/messages.c | 76 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 48 ++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 055ba74f8e0b..3a6c2f9e9542 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -63,3 +63,79 @@ int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id) return ret; } + +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, + u8 instance_id, bool lp, u16 attributes) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.create_ppl.ppl_mem_size = req_size; + msg.create_ppl.ppl_priority = priority; + msg.create_ppl.instance_id = instance_id; + msg.ext.create_ppl.lp = lp; + msg.ext.create_ppl.attributes = attributes; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "create pipeline", ret); + + return ret; +} + +int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(DELETE_PIPELINE); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.ppl.instance_id = instance_id; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "delete pipeline", ret); + + return ret; +} + +int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id, + enum avs_pipeline_state state) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(SET_PIPELINE_STATE); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.set_ppl_state.ppl_id = instance_id; + msg.set_ppl_state.state = state; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "set pipeline state", ret); + + return ret; +} + +int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id, + enum avs_pipeline_state *state) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(GET_PIPELINE_STATE); + struct avs_ipc_msg request = {{0}}; + struct avs_ipc_msg reply = {{0}}; + int ret; + + msg.get_ppl_state.ppl_id = instance_id; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, &reply); + if (ret) { + avs_ipc_err(adev, &request, "get pipeline state", ret); + return ret; + } + + *state = reply.rsp.ext.get_ppl_state.state; + return ret; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 5e16aabfb6f4..8f18eb7935d1 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -26,6 +26,10 @@ enum avs_msg_direction { enum avs_global_msg_type { AVS_GLB_LOAD_MULTIPLE_MODULES = 15, AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16, + AVS_GLB_CREATE_PIPELINE = 17, + AVS_GLB_DELETE_PIPELINE = 18, + AVS_GLB_SET_PIPELINE_STATE = 19, + AVS_GLB_GET_PIPELINE_STATE = 20, AVS_GLB_LOAD_LIBRARY = 24, AVS_GLB_NOTIFICATION = 27, }; @@ -45,6 +49,23 @@ union avs_global_msg { struct { u32 mod_cnt:8; } load_multi_mods; + /* pipeline management */ + struct { + u32 ppl_mem_size:11; + u32 ppl_priority:5; + u32 instance_id:8; + } create_ppl; + struct { + u32 rsvd:16; + u32 instance_id:8; + } ppl; /* generic ppl request */ + struct { + u32 state:16; + u32 ppl_id:8; + } set_ppl_state; + struct { + u32 ppl_id:8; + } get_ppl_state; /* library loading */ struct { u32 dma_id:5; @@ -54,6 +75,12 @@ union avs_global_msg { }; union { u32 val; + /* pipeline management */ + struct { + u32 lp:1; /* low power flag */ + u32 rsvd:3; + u32 attributes:16; /* additional scheduling flags */ + } create_ppl; } ext; }; } __packed; @@ -101,6 +128,10 @@ union avs_reply_msg { struct { u32 err_mod_id:16; } load_multi_mods; + /* pipeline management */ + struct { + u32 state:5; + } get_ppl_state; } ext; }; } __packed; @@ -189,4 +220,21 @@ int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id); +/* Pipeline management messages */ +enum avs_pipeline_state { + AVS_PPL_STATE_INVALID, + AVS_PPL_STATE_UNINITIALIZED, + AVS_PPL_STATE_RESET, + AVS_PPL_STATE_PAUSED, + AVS_PPL_STATE_RUNNING, +}; + +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, + u8 instance_id, bool lp, u16 attributes); +int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id); +int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id, + enum avs_pipeline_state state); +int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id, + enum avs_pipeline_state *state); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From f14a1c5a9f830025dc8638303ddefd5f731ae4bc Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:34 +0100 Subject: ASoC: Intel: avs: Add module management requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firmware modules implement processing algorithms. Their lifecycle, similarly to pipelines is being controlled by IPCs: initialization, deletion and (un)binding. Modules can be configured at runtime - runtime parameters. This is done with help of LARGE_CONFIG IPCs: getter and setter. Due to firmware limitations, LARGE_CONFIG_GET handler implementation does not support retrieving payload with size larger than IPC inbox window size. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/ipc.c | 8 +- sound/soc/intel/avs/messages.c | 261 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 52 ++++++++ 3 files changed, 320 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index b1986a82945d..68aaf01edbf2 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -21,8 +21,14 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) ipc->rx.header = header; /* Abort copying payload if request processing was unsuccessful. */ - if (!msg.status) + if (!msg.status) { + /* update size in case of LARGE_CONFIG_GET */ + if (msg.msg_target == AVS_MOD_MSG && + msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET) + ipc->rx.size = msg.ext.large_config.data_off_size; + memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size); + } } static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 3a6c2f9e9542..2e775798a6c3 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski // +#include #include "avs.h" #include "messages.h" @@ -139,3 +140,263 @@ int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id, *state = reply.rsp.ext.get_ppl_state.state; return ret; } + +/* + * avs_ipc_init_instance - Initialize module instance + * + * @adev: Driver context + * @module_id: Module-type id + * @instance_id: Unique module instance id + * @ppl_id: Parent pipeline id + * @core_id: DSP core to allocate module on + * @domain: Processing domain (low latency or data processing) + * @param: Module-type specific configuration + * @param_size: Size of @param in bytes + * + * Argument verification, as well as pipeline state checks are done by the + * firmware. + * + * Note: @ppl_id and @core_id are independent of each other as single pipeline + * can be composed of module instances located on different DSP cores. + */ +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id, + u8 ppl_id, u8 core_id, u8 domain, + void *param, u32 param_size) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE); + struct avs_ipc_msg request; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + /* firmware expects size provided in dwords */ + msg.ext.init_instance.param_block_size = DIV_ROUND_UP(param_size, sizeof(u32)); + msg.ext.init_instance.ppl_instance_id = ppl_id; + msg.ext.init_instance.core_id = core_id; + msg.ext.init_instance.proc_domain = domain; + + request.header = msg.val; + request.data = param; + request.size = param_size; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "init instance", ret); + + return ret; +} + +/* + * avs_ipc_delete_instance - Delete module instance + * + * @adev: Driver context + * @module_id: Module-type id + * @instance_id: Unique module instance id + * + * Argument verification, as well as pipeline state checks are done by the + * firmware. + * + * Note: only standalone modules i.e. without a parent pipeline shall be + * deleted using this IPC message. In all other cases, pipeline owning the + * modules performs cleanup automatically when it is deleted. + */ +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "delete instance", ret); + + return ret; +} + +/* + * avs_ipc_bind - Bind two module instances + * + * @adev: Driver context + * @module_id: Source module-type id + * @instance_id: Source module instance id + * @dst_module_id: Sink module-type id + * @dst_instance_id: Sink module instance id + * @dst_queue: Sink module pin to bind @src_queue with + * @src_queue: Source module pin to bind @dst_queue with + */ +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id, + u16 dst_module_id, u8 dst_instance_id, + u8 dst_queue, u8 src_queue) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(BIND); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.ext.bind_unbind.dst_module_id = dst_module_id; + msg.ext.bind_unbind.dst_instance_id = dst_instance_id; + msg.ext.bind_unbind.dst_queue = dst_queue; + msg.ext.bind_unbind.src_queue = src_queue; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "bind modules", ret); + + return ret; +} + +/* + * avs_ipc_unbind - Unbind two module instances + * + * @adev: Driver context + * @module_id: Source module-type id + * @instance_id: Source module instance id + * @dst_module_id: Sink module-type id + * @dst_instance_id: Sink module instance id + * @dst_queue: Sink module pin to unbind @src_queue from + * @src_queue: Source module pin to unbind @dst_queue from + */ +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id, + u16 dst_module_id, u8 dst_instance_id, + u8 dst_queue, u8 src_queue) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.ext.bind_unbind.dst_module_id = dst_module_id; + msg.ext.bind_unbind.dst_instance_id = dst_instance_id; + msg.ext.bind_unbind.dst_queue = dst_queue; + msg.ext.bind_unbind.src_queue = src_queue; + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "unbind modules", ret); + + return ret; +} + +static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id, + u8 param_id, bool init_block, bool final_block, + u8 *request_data, size_t request_size, size_t off_size) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET); + struct avs_ipc_msg request; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.ext.large_config.data_off_size = off_size; + msg.ext.large_config.large_param_id = param_id; + msg.ext.large_config.final_block = final_block; + msg.ext.large_config.init_block = init_block; + + request.header = msg.val; + request.data = request_data; + request.size = request_size; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "large config set", ret); + + return ret; +} + +int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, + u8 instance_id, u8 param_id, + u8 *request, size_t request_size) +{ + size_t remaining, tx_size; + bool final; + int ret; + + remaining = request_size; + tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining); + final = (tx_size == remaining); + + /* Initial request states total payload size. */ + ret = __avs_ipc_set_large_config(adev, module_id, instance_id, + param_id, 1, final, request, tx_size, + request_size); + if (ret) + return ret; + + remaining -= tx_size; + + /* Loop the rest only when payload exceeds mailbox's size. */ + while (remaining) { + size_t offset; + + offset = request_size - remaining; + tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining); + final = (tx_size == remaining); + + ret = __avs_ipc_set_large_config(adev, module_id, instance_id, + param_id, 0, final, + request + offset, tx_size, + offset); + if (ret) + return ret; + + remaining -= tx_size; + } + + return 0; +} + +int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id, + u8 param_id, u8 *request_data, size_t request_size, + u8 **reply_data, size_t *reply_size) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_GET); + struct avs_ipc_msg request; + struct avs_ipc_msg reply = {{0}}; + size_t size; + void *buf; + int ret; + + reply.data = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!reply.data) + return -ENOMEM; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.ext.large_config.data_off_size = request_size; + msg.ext.large_config.large_param_id = param_id; + /* final_block is always 0 on request. Updated by fw on reply. */ + msg.ext.large_config.final_block = 0; + msg.ext.large_config.init_block = 1; + + request.header = msg.val; + request.data = request_data; + request.size = request_size; + reply.size = AVS_MAILBOX_SIZE; + + ret = avs_dsp_send_msg(adev, &request, &reply); + if (ret) { + avs_ipc_err(adev, &request, "large config get", ret); + kfree(reply.data); + return ret; + } + + size = reply.rsp.ext.large_config.data_off_size; + buf = krealloc(reply.data, size, GFP_KERNEL); + if (!buf) { + kfree(reply.data); + return -ENOMEM; + } + + *reply_data = buf; + *reply_size = size; + + return 0; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 8f18eb7935d1..d744fe453033 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -91,6 +91,15 @@ struct avs_tlv { u32 value[]; } __packed; +enum avs_module_msg_type { + AVS_MOD_INIT_INSTANCE = 0, + AVS_MOD_LARGE_CONFIG_GET = 3, + AVS_MOD_LARGE_CONFIG_SET = 4, + AVS_MOD_BIND = 5, + AVS_MOD_UNBIND = 6, + AVS_MOD_DELETE_INSTANCE = 11, +}; + union avs_module_msg { u64 val; struct { @@ -106,6 +115,24 @@ union avs_module_msg { }; union { u32 val; + struct { + u32 param_block_size:16; + u32 ppl_instance_id:8; + u32 core_id:4; + u32 proc_domain:1; + } init_instance; + struct { + u32 data_off_size:20; + u32 large_param_id:8; + u32 final_block:1; + u32 init_block:1; + } large_config; + struct { + u32 dst_module_id:16; + u32 dst_instance_id:8; + u32 dst_queue:3; + u32 src_queue:3; + } bind_unbind; } ext; }; } __packed; @@ -132,6 +159,13 @@ union avs_reply_msg { struct { u32 state:5; } get_ppl_state; + /* module management */ + struct { + u32 data_off_size:20; + u32 large_param_id:8; + u32 final_block:1; + u32 init_block:1; + } large_config; } ext; }; } __packed; @@ -237,4 +271,22 @@ int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id, int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id, enum avs_pipeline_state *state); +/* Module management messages */ +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id, + u8 ppl_id, u8 core_id, u8 domain, + void *param, u32 param_size); +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id); +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id, + u16 dst_module_id, u8 dst_instance_id, + u8 dst_queue, u8 src_queue); +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id, + u16 dst_module_id, u8 dst_instance_id, + u8 dst_queue, u8 src_queue); +int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, + u8 instance_id, u8 param_id, + u8 *request, size_t request_size); +int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id, + u8 param_id, u8 *request_data, size_t request_size, + u8 **reply_data, size_t *reply_size); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From 469e87ca9a2029c325362496c7b589683a382505 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:35 +0100 Subject: ASoC: Intel: avs: Add power management requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audio DSP supports low power states i.e.: transitions between D0 and D3 and D0-substates in form of D0i0 and D0i3. That process is a combination of core and IPC operations. Here, Dx and D0ix IPC handlers are added. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/messages.c | 44 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 15 ++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 2e775798a6c3..f51f18d67cfb 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -400,3 +400,47 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id return 0; } + +int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX); + struct avs_ipc_msg request; + struct avs_dxstate_info dx; + int ret; + + dx.core_mask = core_mask; + dx.dx_mask = powerup ? core_mask : 0; + request.header = msg.val; + request.data = &dx; + request.size = sizeof(dx); + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "set dx", ret); + + return ret; +} + +/* + * avs_ipc_set_d0ix - Set power gating policy (entering D0IX substates) + * + * @enable_pg: Whether to enable or disable power gating + * @streaming: Whether a stream is running when transitioning + */ +int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) +{ + union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.ext.set_d0ix.wake = enable_pg; + msg.ext.set_d0ix.streaming = streaming; + + request.header = msg.val; + + ret = avs_dsp_send_msg(adev, &request, NULL); + if (ret) + avs_ipc_err(adev, &request, "set d0ix", ret); + + return ret; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index d744fe453033..7ace78ed5980 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -97,6 +97,8 @@ enum avs_module_msg_type { AVS_MOD_LARGE_CONFIG_SET = 4, AVS_MOD_BIND = 5, AVS_MOD_UNBIND = 6, + AVS_MOD_SET_DX = 7, + AVS_MOD_SET_D0IX = 8, AVS_MOD_DELETE_INSTANCE = 11, }; @@ -133,6 +135,10 @@ union avs_module_msg { u32 dst_queue:3; u32 src_queue:3; } bind_unbind; + struct { + u32 wake:1; + u32 streaming:1; + } set_d0ix; } ext; }; } __packed; @@ -289,4 +295,13 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id u8 param_id, u8 *request_data, size_t request_size, u8 **reply_data, size_t *reply_size); +/* DSP cores and domains power management messages */ +struct avs_dxstate_info { + u32 core_mask; /* which cores are subject for power transition */ + u32 dx_mask; /* bit[n]=1 core n goes to D0, bit[n]=0 it goes to D3 */ +} __packed; + +int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup); +int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From 25f414fcdb875e4203ff43f42898367c32df4827 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:36 +0100 Subject: ASoC: Intel: avs: Add ROM requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROM requests are messages initiated by Host to alter firmware early boot process. They specify whether the next boot should be a fresh start or if IMR can be used to speed things up. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-10-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/messages.c | 18 ++++++++++++++++++ sound/soc/intel/avs/messages.h | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index f51f18d67cfb..0d71ab297e91 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -12,6 +12,24 @@ #define AVS_CL_TIMEOUT_MS 5000 +int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge) +{ + union avs_global_msg msg = AVS_GLOBAL_REQUEST(ROM_CONTROL); + struct avs_ipc_msg request = {{0}}; + int ret; + + msg.boot_cfg.rom_ctrl_msg_type = AVS_ROM_SET_BOOT_CONFIG; + msg.boot_cfg.dma_id = dma_id; + msg.boot_cfg.purge_request = purge; + request.header = msg.val; + + ret = avs_dsp_send_rom_msg(adev, &request); + if (ret) + avs_ipc_err(adev, &request, "set boot config", ret); + + return ret; +} + int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids) { union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES); diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 7ace78ed5980..c9de7f5a8f69 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -24,6 +24,7 @@ enum avs_msg_direction { }; enum avs_global_msg_type { + AVS_GLB_ROM_CONTROL = 1, AVS_GLB_LOAD_MULTIPLE_MODULES = 15, AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16, AVS_GLB_CREATE_PIPELINE = 17, @@ -45,6 +46,12 @@ union avs_global_msg { u32 msg_direction:1; u32 msg_target:1; }; + /* set boot config */ + struct { + u32 rom_ctrl_msg_type:9; + u32 dma_id:5; + u32 purge_request:1; + } boot_cfg; /* module loading */ struct { u32 mod_cnt:8; @@ -255,6 +262,13 @@ struct avs_notify_mod_data { u32 data[]; } __packed; +/* ROM messages */ +enum avs_rom_control_msg_type { + AVS_ROM_SET_BOOT_CONFIG = 0, +}; + +int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge); + /* Code loading messages */ int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids); -- cgit v1.2.3 From 3322e1688953966da15fcd0ae3183a351e241ea3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:37 +0100 Subject: ASoC: Intel: avs: Add basefw runtime-parameter requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each module may expose a range of runtime parameters. For basefw, implement handlers for: FIRMWARE_CONFIG, HARDWARE_CONFIG and MODULES_INFO. These are used by driver to dynamically allocate resources in respect to platform details, reducing number of hardcodes and code duplications that would otherwise be needed to be defined within the driver code. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-11-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/messages.c | 214 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.h | 179 ++++++++++++++++++++++++++++++++++ 2 files changed, 393 insertions(+) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 0d71ab297e91..d260b5d30c87 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -462,3 +462,217 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) return ret; } + +int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) +{ + struct avs_tlv *tlv; + size_t payload_size; + size_t offset = 0; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_FIRMWARE_CONFIG, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + while (offset < payload_size) { + tlv = (struct avs_tlv *)(payload + offset); + + switch (tlv->type) { + case AVS_FW_CFG_FW_VERSION: + memcpy(&cfg->fw_version, tlv->value, sizeof(cfg->fw_version)); + break; + + case AVS_FW_CFG_MEMORY_RECLAIMED: + cfg->memory_reclaimed = *tlv->value; + break; + + case AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ: + cfg->slow_clock_freq_hz = *tlv->value; + break; + + case AVS_FW_CFG_FAST_CLOCK_FREQ_HZ: + cfg->fast_clock_freq_hz = *tlv->value; + break; + + case AVS_FW_CFG_ALH_SUPPORT_LEVEL: + cfg->alh_support = *tlv->value; + break; + + case AVS_FW_CFG_IPC_DL_MAILBOX_BYTES: + cfg->ipc_dl_mailbox_bytes = *tlv->value; + break; + + case AVS_FW_CFG_IPC_UL_MAILBOX_BYTES: + cfg->ipc_ul_mailbox_bytes = *tlv->value; + break; + + case AVS_FW_CFG_TRACE_LOG_BYTES: + cfg->trace_log_bytes = *tlv->value; + break; + + case AVS_FW_CFG_MAX_PPL_COUNT: + cfg->max_ppl_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_ASTATE_COUNT: + cfg->max_astate_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_MODULE_PIN_COUNT: + cfg->max_module_pin_count = *tlv->value; + break; + + case AVS_FW_CFG_MODULES_COUNT: + cfg->modules_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_MOD_INST_COUNT: + cfg->max_mod_inst_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT: + cfg->max_ll_tasks_per_pri_count = *tlv->value; + break; + + case AVS_FW_CFG_LL_PRI_COUNT: + cfg->ll_pri_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_DP_TASKS_COUNT: + cfg->max_dp_tasks_count = *tlv->value; + break; + + case AVS_FW_CFG_MAX_LIBS_COUNT: + cfg->max_libs_count = *tlv->value; + break; + + case AVS_FW_CFG_XTAL_FREQ_HZ: + cfg->xtal_freq_hz = *tlv->value; + break; + + case AVS_FW_CFG_POWER_GATING_POLICY: + cfg->power_gating_policy = *tlv->value; + break; + + /* Known but not useful to us. */ + case AVS_FW_CFG_DMA_BUFFER_CONFIG: + case AVS_FW_CFG_SCHEDULER_CONFIG: + case AVS_FW_CFG_CLOCKS_CONFIG: + break; + + default: + dev_info(adev->dev, "Unrecognized fw param: %d\n", tlv->type); + break; + } + + offset += sizeof(*tlv) + tlv->length; + } + + /* No longer needed, free it as it's owned by the get_large_config() caller. */ + kfree(payload); + return ret; +} + +int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) +{ + struct avs_tlv *tlv; + size_t payload_size; + size_t size, offset = 0; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_HARDWARE_CONFIG, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + while (offset < payload_size) { + tlv = (struct avs_tlv *)(payload + offset); + + switch (tlv->type) { + case AVS_HW_CFG_AVS_VER: + cfg->avs_version = *tlv->value; + break; + + case AVS_HW_CFG_DSP_CORES: + cfg->dsp_cores = *tlv->value; + break; + + case AVS_HW_CFG_MEM_PAGE_BYTES: + cfg->mem_page_bytes = *tlv->value; + break; + + case AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES: + cfg->total_phys_mem_pages = *tlv->value; + break; + + case AVS_HW_CFG_I2S_CAPS: + cfg->i2s_caps.i2s_version = tlv->value[0]; + size = tlv->value[1]; + cfg->i2s_caps.ctrl_count = size; + if (!size) + break; + + /* Multiply to get entire array size. */ + size *= sizeof(*cfg->i2s_caps.ctrl_base_addr); + cfg->i2s_caps.ctrl_base_addr = devm_kmemdup(adev->dev, + &tlv->value[2], + size, GFP_KERNEL); + if (!cfg->i2s_caps.ctrl_base_addr) { + ret = -ENOMEM; + goto exit; + } + break; + + case AVS_HW_CFG_GATEWAY_COUNT: + cfg->gateway_count = *tlv->value; + break; + + case AVS_HW_CFG_HP_EBB_COUNT: + cfg->hp_ebb_count = *tlv->value; + break; + + case AVS_HW_CFG_LP_EBB_COUNT: + cfg->lp_ebb_count = *tlv->value; + break; + + case AVS_HW_CFG_EBB_SIZE_BYTES: + cfg->ebb_size_bytes = *tlv->value; + break; + + case AVS_HW_CFG_GPDMA_CAPS: + break; + + default: + dev_info(adev->dev, "Unrecognized hw config: %d\n", tlv->type); + break; + } + + offset += sizeof(*tlv) + tlv->length; + } + +exit: + /* No longer needed, free it as it's owned by the get_large_config() caller. */ + kfree(payload); + return ret; +} + +int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) +{ + size_t payload_size; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_MODULES_INFO, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + *info = (struct avs_mods_info *)payload; + return 0; +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index c9de7f5a8f69..2c0614eed128 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -318,4 +318,183 @@ struct avs_dxstate_info { int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup); int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming); +/* Base-firmware runtime parameters */ + +#define AVS_BASEFW_MOD_ID 0 +#define AVS_BASEFW_INST_ID 0 + +enum avs_basefw_runtime_param { + AVS_BASEFW_FIRMWARE_CONFIG = 7, + AVS_BASEFW_HARDWARE_CONFIG = 8, + AVS_BASEFW_MODULES_INFO = 9, + AVS_BASEFW_LIBRARIES_INFO = 16, +}; + +struct avs_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + +enum avs_fw_cfg_params { + AVS_FW_CFG_FW_VERSION = 0, + AVS_FW_CFG_MEMORY_RECLAIMED, + AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ, + AVS_FW_CFG_FAST_CLOCK_FREQ_HZ, + AVS_FW_CFG_DMA_BUFFER_CONFIG, + AVS_FW_CFG_ALH_SUPPORT_LEVEL, + AVS_FW_CFG_IPC_DL_MAILBOX_BYTES, + AVS_FW_CFG_IPC_UL_MAILBOX_BYTES, + AVS_FW_CFG_TRACE_LOG_BYTES, + AVS_FW_CFG_MAX_PPL_COUNT, + AVS_FW_CFG_MAX_ASTATE_COUNT, + AVS_FW_CFG_MAX_MODULE_PIN_COUNT, + AVS_FW_CFG_MODULES_COUNT, + AVS_FW_CFG_MAX_MOD_INST_COUNT, + AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT, + AVS_FW_CFG_LL_PRI_COUNT, + AVS_FW_CFG_MAX_DP_TASKS_COUNT, + AVS_FW_CFG_MAX_LIBS_COUNT, + AVS_FW_CFG_SCHEDULER_CONFIG, + AVS_FW_CFG_XTAL_FREQ_HZ, + AVS_FW_CFG_CLOCKS_CONFIG, + AVS_FW_CFG_RESERVED, + AVS_FW_CFG_POWER_GATING_POLICY, + AVS_FW_CFG_ASSERT_MODE, +}; + +struct avs_fw_cfg { + struct avs_fw_version fw_version; + u32 memory_reclaimed; + u32 slow_clock_freq_hz; + u32 fast_clock_freq_hz; + u32 alh_support; + u32 ipc_dl_mailbox_bytes; + u32 ipc_ul_mailbox_bytes; + u32 trace_log_bytes; + u32 max_ppl_count; + u32 max_astate_count; + u32 max_module_pin_count; + u32 modules_count; + u32 max_mod_inst_count; + u32 max_ll_tasks_per_pri_count; + u32 ll_pri_count; + u32 max_dp_tasks_count; + u32 max_libs_count; + u32 xtal_freq_hz; + u32 power_gating_policy; +}; + +int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg); + +enum avs_hw_cfg_params { + AVS_HW_CFG_AVS_VER, + AVS_HW_CFG_DSP_CORES, + AVS_HW_CFG_MEM_PAGE_BYTES, + AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES, + AVS_HW_CFG_I2S_CAPS, + AVS_HW_CFG_GPDMA_CAPS, + AVS_HW_CFG_GATEWAY_COUNT, + AVS_HW_CFG_HP_EBB_COUNT, + AVS_HW_CFG_LP_EBB_COUNT, + AVS_HW_CFG_EBB_SIZE_BYTES, +}; + +enum avs_iface_version { + AVS_AVS_VER_1_5 = 0x10005, + AVS_AVS_VER_1_8 = 0x10008, +}; + +enum avs_i2s_version { + AVS_I2S_VER_15_SKYLAKE = 0x00000, + AVS_I2S_VER_15_BROXTON = 0x10000, + AVS_I2S_VER_15_BROXTON_P = 0x20000, + AVS_I2S_VER_18_KBL_CNL = 0x30000, +}; + +struct avs_i2s_caps { + u32 i2s_version; + u32 ctrl_count; + u32 *ctrl_base_addr; +}; + +struct avs_hw_cfg { + u32 avs_version; + u32 dsp_cores; + u32 mem_page_bytes; + u32 total_phys_mem_pages; + struct avs_i2s_caps i2s_caps; + u32 gateway_count; + u32 hp_ebb_count; + u32 lp_ebb_count; + u32 ebb_size_bytes; +}; + +int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg); + +#define AVS_MODULE_LOAD_TYPE_BUILTIN 0 +#define AVS_MODULE_LOAD_TYPE_LOADABLE 1 +#define AVS_MODULE_STATE_LOADED BIT(0) + +struct avs_module_type { + u32 load_type:4; + u32 auto_start:1; + u32 domain_ll:1; + u32 domain_dp:1; + u32 lib_code:1; + u32 rsvd:24; +} __packed; + +union avs_segment_flags { + u32 ul; + struct { + u32 contents:1; + u32 alloc:1; + u32 load:1; + u32 readonly:1; + u32 code:1; + u32 data:1; + u32 rsvd_1:2; + u32 type:4; + u32 rsvd_2:4; + u32 length:16; + }; +} __packed; + +struct avs_segment_desc { + union avs_segment_flags flags; + u32 v_base_addr; + u32 file_offset; +} __packed; + +struct avs_module_entry { + u16 module_id; + u16 state_flags; + u8 name[8]; + guid_t uuid; + struct avs_module_type type; + u8 hash[32]; + u32 entry_point; + u16 cfg_offset; + u16 cfg_count; + u32 affinity_mask; + u16 instance_max_count; + u16 instance_bss_size; + struct avs_segment_desc segments[3]; +} __packed; + +struct avs_mods_info { + u32 count; + struct avs_module_entry entries[]; +} __packed; + +static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry) +{ + return mentry->type.load_type == AVS_MODULE_LOAD_TYPE_BUILTIN || + mentry->state_flags & AVS_MODULE_STATE_LOADED; +} + +int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From c1a427e8da9332a5832fd4a5429adede34bfa85f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:38 +0100 Subject: ASoC: Intel: avs: Firmware resources management utilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With basefw runtime parameter handlers added, implement utility functions to ease pipelines and modules allocation. IDA is enlisted to help with that. As firmware is modular and multiple binaries can be loaded on-demand depending on the streaming scenario, custom firmware caching mechanism is added. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-12-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/avs.h | 37 ++++++ sound/soc/intel/avs/utils.c | 301 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/utils.c diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index c0824f30fd3b..d9f92c5f5407 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o ipc.o messages.o +snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 330898d8ce2d..08d5ebb5bdf0 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -53,12 +53,26 @@ struct avs_spec { const u32 rom_status; }; +struct avs_fw_entry { + char *name; + const struct firmware *fw; + + struct list_head node; +}; + /* * struct avs_dev - Intel HD-Audio driver data * * @dev: PCI device * @dsp_ba: DSP bar address * @spec: platform-specific descriptor + * @fw_cfg: Firmware configuration, obtained through FW_CONFIG message + * @hw_cfg: Hardware configuration, obtained through HW_CONFIG message + * @mods_info: Available module-types, obtained through MODULES_INFO message + * @mod_idas: Module instance ID pool, one per module-type + * @modres_mutex: For synchronizing any @mods_info updates + * @ppl_ida: Pipeline instance ID pool + * @fw_list: List of libraries loaded, including base firmware */ struct avs_dev { struct hda_bus base; @@ -68,6 +82,14 @@ struct avs_dev { const struct avs_spec *spec; struct avs_ipc *ipc; + struct avs_fw_cfg fw_cfg; + struct avs_hw_cfg hw_cfg; + struct avs_mods_info *mods_info; + struct ida **mod_idas; + struct mutex modres_mutex; + struct ida ppl_ida; + struct list_head fw_list; + struct completion fw_ready; }; @@ -168,4 +190,19 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable); int avs_ipc_init(struct avs_ipc *ipc, struct device *dev); void avs_ipc_block(struct avs_ipc *ipc); +/* Firmware resources management */ + +int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry); +int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry); +int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid); +bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id); + +int avs_module_info_init(struct avs_dev *adev, bool purge); +void avs_module_info_free(struct avs_dev *adev); +int avs_module_id_alloc(struct avs_dev *adev, u16 module_id); +void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id); +int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name); +void avs_release_last_firmware(struct avs_dev *adev); +void avs_release_firmwares(struct avs_dev *adev); + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c new file mode 100644 index 000000000000..6473e3ae4c6e --- /dev/null +++ b/sound/soc/intel/avs/utils.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include "avs.h" +#include "messages.h" + +/* Caller responsible for holding adev->modres_mutex. */ +static int avs_module_entry_index(struct avs_dev *adev, const guid_t *uuid) +{ + int i; + + for (i = 0; i < adev->mods_info->count; i++) { + struct avs_module_entry *module; + + module = &adev->mods_info->entries[i]; + if (guid_equal(&module->uuid, uuid)) + return i; + } + + return -ENOENT; +} + +/* Caller responsible for holding adev->modres_mutex. */ +static int avs_module_id_entry_index(struct avs_dev *adev, u32 module_id) +{ + int i; + + for (i = 0; i < adev->mods_info->count; i++) { + struct avs_module_entry *module; + + module = &adev->mods_info->entries[i]; + if (module->module_id == module_id) + return i; + } + + return -ENOENT; +} + +int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry) +{ + int idx; + + mutex_lock(&adev->modres_mutex); + + idx = avs_module_entry_index(adev, uuid); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + + mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; +} + +int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry) +{ + int idx; + + mutex_lock(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + + mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; +} + +int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid) +{ + struct avs_module_entry module; + int ret; + + ret = avs_get_module_entry(adev, uuid, &module); + return !ret ? module.module_id : -ENOENT; +} + +bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) +{ + bool ret = false; + int idx; + + mutex_lock(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + ret = ida_is_empty(adev->mod_idas[idx]); + + mutex_unlock(&adev->modres_mutex); + return ret; +} + +/* Caller responsible for holding adev->modres_mutex. */ +static void avs_module_ida_destroy(struct avs_dev *adev) +{ + int i = adev->mods_info ? adev->mods_info->count : 0; + + while (i--) { + ida_destroy(adev->mod_idas[i]); + kfree(adev->mod_idas[i]); + } + kfree(adev->mod_idas); +} + +/* Caller responsible for holding adev->modres_mutex. */ +static int +avs_module_ida_alloc(struct avs_dev *adev, struct avs_mods_info *newinfo, bool purge) +{ + struct avs_mods_info *oldinfo = adev->mods_info; + struct ida **ida_ptrs; + u32 tocopy_count = 0; + int i; + + if (!purge && oldinfo) { + if (oldinfo->count >= newinfo->count) + dev_warn(adev->dev, "refreshing %d modules info with %d\n", + oldinfo->count, newinfo->count); + tocopy_count = oldinfo->count; + } + + ida_ptrs = kcalloc(newinfo->count, sizeof(*ida_ptrs), GFP_KERNEL); + if (!ida_ptrs) + return -ENOMEM; + + if (tocopy_count) + memcpy(ida_ptrs, adev->mod_idas, tocopy_count * sizeof(*ida_ptrs)); + + for (i = tocopy_count; i < newinfo->count; i++) { + ida_ptrs[i] = kzalloc(sizeof(**ida_ptrs), GFP_KERNEL); + if (!ida_ptrs[i]) { + while (i--) + kfree(ida_ptrs[i]); + + kfree(ida_ptrs); + return -ENOMEM; + } + + ida_init(ida_ptrs[i]); + } + + /* If old elements have been reused, don't wipe them. */ + if (tocopy_count) + kfree(adev->mod_idas); + else + avs_module_ida_destroy(adev); + + adev->mod_idas = ida_ptrs; + return 0; +} + +int avs_module_info_init(struct avs_dev *adev, bool purge) +{ + struct avs_mods_info *info; + int ret; + + ret = avs_ipc_get_modules_info(adev, &info); + if (ret) + return AVS_IPC_RET(ret); + + mutex_lock(&adev->modres_mutex); + + ret = avs_module_ida_alloc(adev, info, purge); + if (ret < 0) { + dev_err(adev->dev, "initialize module idas failed: %d\n", ret); + goto exit; + } + + /* Refresh current information with newly received table. */ + kfree(adev->mods_info); + adev->mods_info = info; + +exit: + mutex_unlock(&adev->modres_mutex); + return ret; +} + +void avs_module_info_free(struct avs_dev *adev) +{ + mutex_lock(&adev->modres_mutex); + + avs_module_ida_destroy(adev); + kfree(adev->mods_info); + adev->mods_info = NULL; + + mutex_unlock(&adev->modres_mutex); +} + +int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) +{ + int ret, idx, max_id; + + mutex_lock(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx == -ENOENT) { + dev_err(adev->dev, "invalid module id: %d", module_id); + ret = -EINVAL; + goto exit; + } + max_id = adev->mods_info->entries[idx].instance_max_count - 1; + ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); +exit: + mutex_unlock(&adev->modres_mutex); + return ret; +} + +void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) +{ + int idx; + + mutex_lock(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx == -ENOENT) { + dev_err(adev->dev, "invalid module id: %d", module_id); + goto exit; + } + + ida_free(adev->mod_idas[idx], instance_id); +exit: + mutex_unlock(&adev->modres_mutex); +} + +/* + * Once driver loads FW it should keep it in memory, so we are not affected + * by FW removal from filesystem or even worse by loading different FW at + * runtime suspend/resume. + */ +int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name) +{ + struct avs_fw_entry *entry; + int ret; + + /* first check in list if it is not already loaded */ + list_for_each_entry(entry, &adev->fw_list, node) { + if (!strcmp(name, entry->name)) { + *fw_p = entry->fw; + return 0; + } + } + + /* FW is not loaded, let's load it now and add to the list */ + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->name = kstrdup(name, GFP_KERNEL); + if (!entry->name) { + kfree(entry); + return -ENOMEM; + } + + ret = request_firmware(&entry->fw, name, adev->dev); + if (ret < 0) { + kfree(entry->name); + kfree(entry); + return ret; + } + + *fw_p = entry->fw; + + list_add_tail(&entry->node, &adev->fw_list); + + return 0; +} + +/* + * Release single FW entry, used to handle errors in functions calling + * avs_request_firmware() + */ +void avs_release_last_firmware(struct avs_dev *adev) +{ + struct avs_fw_entry *entry; + + entry = list_last_entry(&adev->fw_list, typeof(*entry), node); + + list_del(&entry->node); + release_firmware(entry->fw); + kfree(entry->name); + kfree(entry); +} + +/* + * Release all FW entries, used on driver removal + */ +void avs_release_firmwares(struct avs_dev *adev) +{ + struct avs_fw_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &adev->fw_list, node) { + list_del(&entry->node); + release_firmware(entry->fw); + kfree(entry->name); + kfree(entry); + } +} -- cgit v1.2.3 From 580a5912d1fe774f9902b614fa33e1add92ca749 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:39 +0100 Subject: ASoC: Intel: avs: Declare module configuration types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare structures and constants for all modules being part of basefw binary. These are used in streaming operations to communicate the needs of software to firmware side. While adding module types, append handler for SET_SINK_FORMAT runtime for COPIER module which allows for configuration of output pin other than the default one (0). Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-13-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/messages.c | 17 +++ sound/soc/intel/avs/messages.h | 252 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index d260b5d30c87..004da166a943 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -676,3 +676,20 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) *info = (struct avs_mods_info *)payload; return 0; } + +int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, + u8 instance_id, u32 sink_id, + const struct avs_audio_format *src_fmt, + const struct avs_audio_format *sink_fmt) +{ + struct avs_copier_sink_format cpr_fmt; + + cpr_fmt.sink_id = sink_id; + /* Firmware expects driver to resend copier's input format. */ + cpr_fmt.src_fmt = *src_fmt; + cpr_fmt.sink_fmt = *sink_fmt; + + return avs_ipc_set_large_config(adev, module_id, instance_id, + AVS_COPIER_SET_SINK_FORMAT, + (u8 *)&cpr_fmt, sizeof(cpr_fmt)); +} diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 2c0614eed128..0395dd7150eb 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -497,4 +497,256 @@ static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry) int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info); +/* Module configuration */ + +#define AVS_MIXIN_MOD_UUID \ + GUID_INIT(0x39656EB2, 0x3B71, 0x4049, 0x8D, 0x3F, 0xF9, 0x2C, 0xD5, 0xC4, 0x3C, 0x09) + +#define AVS_MIXOUT_MOD_UUID \ + GUID_INIT(0x3C56505A, 0x24D7, 0x418F, 0xBD, 0xDC, 0xC1, 0xF5, 0xA3, 0xAC, 0x2A, 0xE0) + +#define AVS_COPIER_MOD_UUID \ + GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA) + +#define AVS_KPBUFF_MOD_UUID \ + GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6) + +#define AVS_MICSEL_MOD_UUID \ + GUID_INIT(0x32FE92C1, 0x1E17, 0x4FC2, 0x97, 0x58, 0xC7, 0xF3, 0x54, 0x2E, 0x98, 0x0A) + +#define AVS_MUX_MOD_UUID \ + GUID_INIT(0x64CE6E35, 0x857A, 0x4878, 0xAC, 0xE8, 0xE2, 0xA2, 0xF4, 0x2e, 0x30, 0x69) + +#define AVS_UPDWMIX_MOD_UUID \ + GUID_INIT(0x42F8060C, 0x832F, 0x4DBF, 0xB2, 0x47, 0x51, 0xE9, 0x61, 0x99, 0x7b, 0x35) + +#define AVS_SRCINTC_MOD_UUID \ + GUID_INIT(0xE61BB28D, 0x149A, 0x4C1F, 0xB7, 0x09, 0x46, 0x82, 0x3E, 0xF5, 0xF5, 0xAE) + +#define AVS_PROBE_MOD_UUID \ + GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23, 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF) + +#define AVS_AEC_MOD_UUID \ + GUID_INIT(0x46CB87FB, 0xD2C9, 0x4970, 0x96, 0xD2, 0x6D, 0x7E, 0x61, 0x4B, 0xB6, 0x05) + +#define AVS_ASRC_MOD_UUID \ + GUID_INIT(0x66B4402D, 0xB468, 0x42F2, 0x81, 0xA7, 0xB3, 0x71, 0x21, 0x86, 0x3D, 0xD4) + +#define AVS_INTELWOV_MOD_UUID \ + GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7) + +/* channel map */ +enum avs_channel_index { + AVS_CHANNEL_LEFT = 0, + AVS_CHANNEL_RIGHT = 1, + AVS_CHANNEL_CENTER = 2, + AVS_CHANNEL_LEFT_SURROUND = 3, + AVS_CHANNEL_CENTER_SURROUND = 3, + AVS_CHANNEL_RIGHT_SURROUND = 4, + AVS_CHANNEL_LFE = 7, + AVS_CHANNEL_INVALID = 0xF, +}; + +enum avs_channel_config { + AVS_CHANNEL_CONFIG_MONO = 0, + AVS_CHANNEL_CONFIG_STEREO = 1, + AVS_CHANNEL_CONFIG_2_1 = 2, + AVS_CHANNEL_CONFIG_3_0 = 3, + AVS_CHANNEL_CONFIG_3_1 = 4, + AVS_CHANNEL_CONFIG_QUATRO = 5, + AVS_CHANNEL_CONFIG_4_0 = 6, + AVS_CHANNEL_CONFIG_5_0 = 7, + AVS_CHANNEL_CONFIG_5_1 = 8, + AVS_CHANNEL_CONFIG_DUAL_MONO = 9, + AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_0 = 10, + AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_1 = 11, + AVS_CHANNEL_CONFIG_4_CHANNEL = 12, + AVS_CHANNEL_CONFIG_INVALID +}; + +enum avs_interleaving { + AVS_INTERLEAVING_PER_CHANNEL = 0, + AVS_INTERLEAVING_PER_SAMPLE = 1, +}; + +enum avs_sample_type { + AVS_SAMPLE_TYPE_INT_MSB = 0, + AVS_SAMPLE_TYPE_INT_LSB = 1, + AVS_SAMPLE_TYPE_INT_SIGNED = 2, + AVS_SAMPLE_TYPE_INT_UNSIGNED = 3, + AVS_SAMPLE_TYPE_FLOAT = 4, +}; + +#define AVS_CHANNELS_MAX 8 +#define AVS_ALL_CHANNELS_MASK UINT_MAX + +struct avs_audio_format { + u32 sampling_freq; + u32 bit_depth; + u32 channel_map; + u32 channel_config; + u32 interleaving; + u32 num_channels:8; + u32 valid_bit_depth:8; + u32 sample_type:8; + u32 reserved:8; +} __packed; + +struct avs_modcfg_base { + u32 cpc; + u32 ibs; + u32 obs; + u32 is_pages; + struct avs_audio_format audio_fmt; +} __packed; + +struct avs_pin_format { + u32 pin_index; + u32 iobs; + struct avs_audio_format audio_fmt; +} __packed; + +struct avs_modcfg_ext { + struct avs_modcfg_base base; + u16 num_input_pins; + u16 num_output_pins; + u8 reserved[12]; + /* input pin formats followed by output ones */ + struct avs_pin_format pin_fmts[]; +} __packed; + +enum avs_dma_type { + AVS_DMA_HDA_HOST_OUTPUT = 0, + AVS_DMA_HDA_HOST_INPUT = 1, + AVS_DMA_HDA_LINK_OUTPUT = 8, + AVS_DMA_HDA_LINK_INPUT = 9, + AVS_DMA_DMIC_LINK_INPUT = 11, + AVS_DMA_I2S_LINK_OUTPUT = 12, + AVS_DMA_I2S_LINK_INPUT = 13, +}; + +union avs_virtual_index { + u8 val; + struct { + u8 time_slot:4; + u8 instance:4; + } i2s; + struct { + u8 queue_id:3; + u8 time_slot:2; + u8 instance:3; + } dmic; +} __packed; + +union avs_connector_node_id { + u32 val; + struct { + u32 vindex:8; + u32 dma_type:5; + u32 rsvd:19; + }; +} __packed; + +#define INVALID_PIPELINE_ID 0xFF +#define INVALID_NODE_ID \ + ((union avs_connector_node_id) { UINT_MAX }) + +union avs_gtw_attributes { + u32 val; + struct { + u32 lp_buffer_alloc:1; + u32 rsvd:31; + }; +} __packed; + +struct avs_copier_gtw_cfg { + union avs_connector_node_id node_id; + u32 dma_buffer_size; + u32 config_length; + struct { + union avs_gtw_attributes attrs; + u32 blob[]; + } config; +} __packed; + +struct avs_copier_cfg { + struct avs_modcfg_base base; + struct avs_audio_format out_fmt; + u32 feature_mask; + struct avs_copier_gtw_cfg gtw_cfg; +} __packed; + +struct avs_micsel_cfg { + struct avs_modcfg_base base; + struct avs_audio_format out_fmt; +} __packed; + +struct avs_mux_cfg { + struct avs_modcfg_base base; + struct avs_audio_format ref_fmt; + struct avs_audio_format out_fmt; +} __packed; + +struct avs_updown_mixer_cfg { + struct avs_modcfg_base base; + u32 out_channel_config; + u32 coefficients_select; + s32 coefficients[AVS_CHANNELS_MAX]; + u32 channel_map; +} __packed; + +struct avs_src_cfg { + struct avs_modcfg_base base; + u32 out_freq; +} __packed; + +struct avs_probe_gtw_cfg { + union avs_connector_node_id node_id; + u32 dma_buffer_size; +} __packed; + +struct avs_probe_cfg { + struct avs_modcfg_base base; + struct avs_probe_gtw_cfg gtw_cfg; +} __packed; + +struct avs_aec_cfg { + struct avs_modcfg_base base; + struct avs_audio_format ref_fmt; + struct avs_audio_format out_fmt; + u32 cpc_lp_mode; +} __packed; + +struct avs_asrc_cfg { + struct avs_modcfg_base base; + u32 out_freq; + u32 rsvd0:1; + u32 mode:1; + u32 rsvd2:2; + u32 disable_jitter_buffer:1; + u32 rsvd3:27; +} __packed; + +struct avs_wov_cfg { + struct avs_modcfg_base base; + u32 cpc_lp_mode; +} __packed; + +/* Module runtime parameters */ + +enum avs_copier_runtime_param { + AVS_COPIER_SET_SINK_FORMAT = 2, +}; + +struct avs_copier_sink_format { + u32 sink_id; + struct avs_audio_format src_fmt; + struct avs_audio_format sink_fmt; +} __packed; + +int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, + u8 instance_id, u32 sink_id, + const struct avs_audio_format *src_fmt, + const struct avs_audio_format *sink_fmt); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ -- cgit v1.2.3 From 215e67b2d2de1d8d34a53c440b9a19a732ee6fb0 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:40 +0100 Subject: ASoC: Intel: avs: Dynamic firmware resources management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap elementary DSP-core operations and resource control into more complex handlers. This is done to reduce the number of invocations of wrapped operations throughout the driver as order of operations matters - most flows involve register manipulation and IPCs combined. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-14-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 10 +++ sound/soc/intel/avs/dsp.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 08d5ebb5bdf0..17ef8b5de1df 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -89,6 +89,7 @@ struct avs_dev { struct mutex modres_mutex; struct ida ppl_ida; struct list_head fw_list; + int *core_refs; /* reference count per core */ struct completion fw_ready; }; @@ -205,4 +206,13 @@ int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, con void avs_release_last_firmware(struct avs_dev *adev); void avs_release_firmwares(struct avs_dev *adev); +int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, + u8 core_id, u8 domain, void *param, u32 param_size, + u16 *instance_id); +void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id, + u8 ppl_instance_id, u8 core_id); +int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, + bool lp, u16 attributes, u8 *instance_id); +int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id); + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c index 63f5ce2698a2..53925eae92b3 100644 --- a/sound/soc/intel/avs/dsp.c +++ b/sound/soc/intel/avs/dsp.c @@ -104,4 +104,173 @@ int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask) return avs_dsp_op(adev, power, core_mask, false); } +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask) +{ + u32 mask; + int ret; + + ret = avs_dsp_core_enable(adev, core_mask); + if (ret < 0) + return ret; + + mask = core_mask & ~AVS_MAIN_CORE_MASK; + if (!mask) + /* + * without main core, fw is dead anyway + * so setting D0 for it is futile. + */ + return 0; + + ret = avs_ipc_set_dx(adev, mask, true); + return AVS_IPC_RET(ret); +} + +static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask) +{ + int ret; + + ret = avs_ipc_set_dx(adev, core_mask, false); + if (ret) + return AVS_IPC_RET(ret); + + return avs_dsp_core_disable(adev, core_mask); +} + +static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id) +{ + u32 mask; + int ret; + + mask = BIT_MASK(core_id); + if (mask == AVS_MAIN_CORE_MASK) + /* nothing to do for main core */ + return 0; + if (core_id >= adev->hw_cfg.dsp_cores) { + ret = -EINVAL; + goto err; + } + + adev->core_refs[core_id]++; + if (adev->core_refs[core_id] == 1) { + ret = avs_dsp_enable(adev, mask); + if (ret) + goto err_enable_dsp; + } + + return 0; + +err_enable_dsp: + adev->core_refs[core_id]--; +err: + dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret); + return ret; +} + +static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id) +{ + u32 mask; + int ret; + + mask = BIT_MASK(core_id); + if (mask == AVS_MAIN_CORE_MASK) + /* nothing to do for main core */ + return 0; + if (core_id >= adev->hw_cfg.dsp_cores) { + ret = -EINVAL; + goto err; + } + + adev->core_refs[core_id]--; + if (!adev->core_refs[core_id]) { + ret = avs_dsp_disable(adev, mask); + if (ret) + goto err; + } + + return 0; +err: + dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret); + return ret; +} + +int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, + u8 core_id, u8 domain, void *param, u32 param_size, + u16 *instance_id) +{ + struct avs_module_entry mentry; + int ret, id; + + id = avs_module_id_alloc(adev, module_id); + if (id < 0) + return id; + + ret = avs_get_module_id_entry(adev, module_id, &mentry); + if (ret) + goto err_mod_entry; + + ret = avs_dsp_get_core(adev, core_id); + if (ret) + goto err_mod_entry; + + ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id, + core_id, domain, param, param_size); + if (ret) { + ret = AVS_IPC_RET(ret); + goto err_ipc; + } + + *instance_id = id; + return 0; + +err_ipc: + avs_dsp_put_core(adev, core_id); +err_mod_entry: + avs_module_id_free(adev, module_id, id); + return ret; +} + +void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id, + u8 ppl_instance_id, u8 core_id) +{ + /* Modules not owned by any pipeline need to be freed explicitly. */ + if (ppl_instance_id == INVALID_PIPELINE_ID) + avs_ipc_delete_instance(adev, module_id, instance_id); + + avs_module_id_free(adev, module_id, instance_id); + + avs_dsp_put_core(adev, core_id); +} + +int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, + bool lp, u16 attributes, u8 *instance_id) +{ + struct avs_fw_cfg *fw_cfg = &adev->fw_cfg; + int ret, id; + + id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL); + if (id < 0) + return id; + + ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes); + if (ret) { + ida_free(&adev->ppl_ida, id); + return AVS_IPC_RET(ret); + } + + *instance_id = id; + return 0; +} + +int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id) +{ + int ret; + + ret = avs_ipc_delete_pipeline(adev, instance_id); + if (ret) + ret = AVS_IPC_RET(ret); + + ida_free(&adev->ppl_ida, instance_id); + return ret; +} + MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b27f452317236b0cbaa94c4498f8241e2ad871b1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:41 +0100 Subject: ASoC: Intel: avs: General code loading flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code loading is a complex procedure and requires combined effort of DMA and IPCs. With IPCs already in place, lay out ground for specific DMA transfer operations. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-15-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/avs.h | 17 +++ sound/soc/intel/avs/core.c | 61 +++++++++++ sound/soc/intel/avs/dsp.c | 26 +++++ sound/soc/intel/avs/loader.c | 235 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/registers.h | 6 + 6 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/core.c create mode 100644 sound/soc/intel/avs/loader.c diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index d9f92c5f5407..d9c793160612 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o +snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 17ef8b5de1df..c7e9be27be1e 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -10,8 +10,11 @@ #define __SOUND_SOC_INTEL_AVS_H #include +#include #include +#include #include "messages.h" +#include "registers.h" struct avs_dev; @@ -32,6 +35,9 @@ struct avs_dsp_ops { irqreturn_t (* const irq_handler)(int, void *); irqreturn_t (* const irq_thread)(int, void *); void (* const int_control)(struct avs_dev *, bool); + int (* const load_basefw)(struct avs_dev *, struct firmware *); + int (* const load_lib)(struct avs_dev *, struct firmware *, u32); + int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32); }; #define avs_dsp_op(adev, op, ...) \ @@ -45,6 +51,7 @@ struct avs_spec { const char *name; const struct avs_dsp_ops *const dsp_ops; + struct avs_fw_version min_fw_version; /* anything below is rejected */ const u32 core_init_mask; /* used during DSP boot */ const u64 attributes; /* bitmask of AVS_PLATATTR_* */ @@ -90,6 +97,7 @@ struct avs_dev { struct ida ppl_ida; struct list_head fw_list; int *core_refs; /* reference count per core */ + char **lib_names; struct completion fw_ready; }; @@ -215,4 +223,13 @@ int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, bool lp, u16 attributes, u8 *instance_id); int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id); +/* Firmware loading */ + +void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable); +void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable); +void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable); + +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge); +int avs_dsp_first_boot_firmware(struct avs_dev *adev); + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c new file mode 100644 index 000000000000..a4d063d12fec --- /dev/null +++ b/sound/soc/intel/avs/core.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// +// Special thanks to: +// Krzysztof Hejmowski +// Michal Sienkiewicz +// Filip Proborszcz +// +// for sharing Intel AudioDSP expertise and helping shape the very +// foundation of this driver +// + +#include +#include +#include "avs.h" + +static void +avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value) +{ + struct pci_dev *pci = to_pci_dev(bus->dev); + u32 data; + + pci_read_config_dword(pci, reg, &data); + data &= ~mask; + data |= (value & mask); + pci_write_config_dword(pci, reg, data); +} + +void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable) +{ + u32 value; + + value = enable ? 0 : AZX_PGCTL_LSRMD_MASK; + avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, + AZX_PGCTL_LSRMD_MASK, value); +} + +static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable) +{ + u32 value; + + value = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0; + avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, value); +} + +void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) +{ + avs_hdac_clock_gating_enable(&adev->base.core, enable); +} + +void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) +{ + u32 value; + + value = enable ? AZX_VS_EM2_L1SEN : 0; + snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value); +} diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c index 53925eae92b3..3ff17bd22a5a 100644 --- a/sound/soc/intel/avs/dsp.c +++ b/sound/soc/intel/avs/dsp.c @@ -198,6 +198,7 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, u16 *instance_id) { struct avs_module_entry mentry; + bool was_loaded = false; int ret, id; id = avs_module_id_alloc(adev, module_id); @@ -212,6 +213,16 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, if (ret) goto err_mod_entry; + /* Load code into memory if this is the first instance. */ + if (!id && !avs_module_entry_is_loaded(&mentry)) { + ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1); + if (ret) { + dev_err(adev->dev, "load modules failed: %d\n", ret); + goto err_mod_entry; + } + was_loaded = true; + } + ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id, core_id, domain, param, param_size); if (ret) { @@ -223,6 +234,8 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, return 0; err_ipc: + if (was_loaded) + avs_dsp_op(adev, transfer_mods, false, &mentry, 1); avs_dsp_put_core(adev, core_id); err_mod_entry: avs_module_id_free(adev, module_id, id); @@ -232,12 +245,25 @@ err_mod_entry: void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id, u8 ppl_instance_id, u8 core_id) { + struct avs_module_entry mentry; + int ret; + /* Modules not owned by any pipeline need to be freed explicitly. */ if (ppl_instance_id == INVALID_PIPELINE_ID) avs_ipc_delete_instance(adev, module_id, instance_id); avs_module_id_free(adev, module_id, instance_id); + ret = avs_get_module_id_entry(adev, module_id, &mentry); + /* Unload occupied memory if this was the last instance. */ + if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) { + if (avs_is_module_ida_empty(adev, module_id)) { + ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1); + if (ret) + dev_err(adev->dev, "unload modules failed: %d\n", ret); + } + } + avs_dsp_put_core(adev, core_id); } diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c new file mode 100644 index 000000000000..96c979b44f60 --- /dev/null +++ b/sound/soc/intel/avs/loader.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include "avs.h" +#include "messages.h" +#include "registers.h" + +#define AVS_FW_INIT_TIMEOUT_MS 3000 + +#define AVS_ROOT_DIR "intel/avs" +#define AVS_BASEFW_FILENAME "dsp_basefw.bin" +#define AVS_EXT_MANIFEST_MAGIC 0x31454124 +#define SKL_MANIFEST_MAGIC 0x00000006 +#define SKL_ADSPFW_OFFSET 0x284 + +/* Occasionally, engineering (release candidate) firmware is provided for testing. */ +static bool debug_ignore_fw_version; +module_param_named(ignore_fw_version, debug_ignore_fw_version, bool, 0444); +MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes (default), 1=no"); + +#define AVS_LIB_NAME_SIZE 8 + +struct avs_fw_manifest { + u32 id; + u32 len; + char name[AVS_LIB_NAME_SIZE]; + u32 preload_page_count; + u32 img_flags; + u32 feature_mask; + struct avs_fw_version version; +} __packed; + +struct avs_fw_ext_manifest { + u32 id; + u32 len; + u16 version_major; + u16 version_minor; + u32 entries; +} __packed; + +static int avs_fw_ext_manifest_strip(struct firmware *fw) +{ + struct avs_fw_ext_manifest *man; + + if (fw->size < sizeof(*man)) + return -EINVAL; + + man = (struct avs_fw_ext_manifest *)fw->data; + if (man->id == AVS_EXT_MANIFEST_MAGIC) { + fw->data += man->len; + fw->size -= man->len; + } + + return 0; +} + +static int avs_fw_manifest_offset(struct firmware *fw) +{ + /* Header type found in first DWORD of fw binary. */ + u32 magic = *(u32 *)fw->data; + + switch (magic) { + case SKL_MANIFEST_MAGIC: + return SKL_ADSPFW_OFFSET; + default: + return -EINVAL; + } +} + +static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw, + const struct avs_fw_version *min) +{ + struct avs_fw_manifest *man; + int offset, ret; + + ret = avs_fw_ext_manifest_strip(fw); + if (ret) + return ret; + + offset = avs_fw_manifest_offset(fw); + if (offset < 0) + return offset; + + if (fw->size < offset + sizeof(*man)) + return -EINVAL; + if (!min) + return 0; + + man = (struct avs_fw_manifest *)(fw->data + offset); + if (man->version.major != min->major || + man->version.minor != min->minor || + man->version.hotfix != min->hotfix || + man->version.build < min->build) { + dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n", + man->version.major, man->version.minor, + man->version.hotfix, man->version.build, + min->major, min->minor, min->hotfix, min->build); + + if (!debug_ignore_fw_version) + return -EINVAL; + } + + return 0; +} + +static int avs_dsp_load_basefw(struct avs_dev *adev) +{ + const struct avs_fw_version *min_req; + const struct avs_spec *const spec = adev->spec; + const struct firmware *fw; + struct firmware stripped_fw; + char *filename; + int ret; + + filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name, AVS_BASEFW_FILENAME); + if (!filename) + return -ENOMEM; + + ret = avs_request_firmware(adev, &fw, filename); + kfree(filename); + if (ret < 0) { + dev_err(adev->dev, "request firmware failed: %d\n", ret); + return ret; + } + + stripped_fw = *fw; + min_req = &adev->spec->min_fw_version; + + ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req); + if (ret < 0) { + dev_err(adev->dev, "invalid firmware data: %d\n", ret); + goto release_fw; + } + + ret = avs_dsp_op(adev, load_basefw, &stripped_fw); + if (ret < 0) { + dev_err(adev->dev, "basefw load failed: %d\n", ret); + goto release_fw; + } + + ret = wait_for_completion_timeout(&adev->fw_ready, + msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); + if (!ret) { + dev_err(adev->dev, "firmware ready timeout\n"); + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + ret = -ETIMEDOUT; + goto release_fw; + } + + return 0; + +release_fw: + avs_release_last_firmware(adev); + return ret; +} + +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) +{ + int ret, i; + + /* Full boot, clear cached data except for basefw (slot 0). */ + for (i = 1; i < adev->fw_cfg.max_libs_count; i++) + memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE); + + avs_hda_clock_gating_enable(adev, false); + avs_hda_l1sen_enable(adev, false); + + ret = avs_dsp_load_basefw(adev); + + avs_hda_l1sen_enable(adev, true); + avs_hda_clock_gating_enable(adev, true); + + if (ret < 0) + return ret; + + /* With all code loaded, refresh module information. */ + ret = avs_module_info_init(adev, true); + if (ret) { + dev_err(adev->dev, "init module info failed: %d\n", ret); + return ret; + } + + return 0; +} + +int avs_dsp_first_boot_firmware(struct avs_dev *adev) +{ + int ret, i; + + ret = avs_dsp_boot_firmware(adev, true); + if (ret < 0) { + dev_err(adev->dev, "firmware boot failed: %d\n", ret); + return ret; + } + + ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg); + if (ret) { + dev_err(adev->dev, "get hw cfg failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + + ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg); + if (ret) { + dev_err(adev->dev, "get fw cfg failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + + adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores, + sizeof(*adev->core_refs), GFP_KERNEL); + adev->lib_names = devm_kcalloc(adev->dev, adev->fw_cfg.max_libs_count, + sizeof(*adev->lib_names), GFP_KERNEL); + if (!adev->core_refs || !adev->lib_names) + return -ENOMEM; + + for (i = 0; i < adev->fw_cfg.max_libs_count; i++) { + adev->lib_names[i] = devm_kzalloc(adev->dev, AVS_LIB_NAME_SIZE, GFP_KERNEL); + if (!adev->lib_names[i]) + return -ENOMEM; + } + + /* basefw always occupies slot 0 */ + strcpy(&adev->lib_names[0][0], "BASEFW"); + + ida_init(&adev->ppl_ida); + + return 0; +} diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 775701454a28..73699d46821b 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -9,6 +9,12 @@ #ifndef __SOUND_SOC_INTEL_AVS_REGS_H #define __SOUND_SOC_INTEL_AVS_REGS_H +#define AZX_PCIREG_PGCTL 0x44 +#define AZX_PCIREG_CGCTL 0x48 +#define AZX_PGCTL_LSRMD_MASK BIT(4) +#define AZX_CGCTL_MISCBDCGE_MASK BIT(6) +#define AZX_VS_EM2_L1SEN BIT(13) + /* Intel HD Audio General DSP Registers */ #define AVS_ADSP_GEN_BASE 0x0 #define AVS_ADSP_REG_ADSPCS (AVS_ADSP_GEN_BASE + 0x04) -- cgit v1.2.3 From 45864e49a05aa446b9d99c464c1a9f46956ed32c Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:42 +0100 Subject: ASoC: Intel: avs: Implement CLDMA transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKL and KBL rely on a dedicated HDAudio DMA stream for code loading and authentication. The implementation of this specific mechanism for SKL-based platforms re-uses HDAudio DMA (streaming) functions found in HDA library to avoid duplication of functionality. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-16-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 1 + sound/soc/intel/avs/cldma.c | 316 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/cldma.h | 29 ++++ sound/soc/intel/avs/registers.h | 2 + 4 files changed, 348 insertions(+) create mode 100644 sound/soc/intel/avs/cldma.c create mode 100644 sound/soc/intel/avs/cldma.h diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index d9c793160612..f842bfc5e97e 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o +snd-soc-avs-objs += cldma.o obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c new file mode 100644 index 000000000000..d100c6ba4d8a --- /dev/null +++ b/sound/soc/intel/avs/cldma.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include "cldma.h" +#include "registers.h" + +/* Stream Registers */ +#define AZX_CL_SD_BASE 0x80 +#define AZX_SD_CTL_STRM_MASK GENMASK(23, 20) +#define AZX_SD_CTL_STRM(s) (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK) +#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7) +#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK) + +/* Software Position Based FIFO Capability Registers */ +#define AZX_CL_SPBFCS 0x20 +#define AZX_REG_CL_SPBFCTL (AZX_CL_SPBFCS + 0x4) +#define AZX_REG_CL_SD_SPIB (AZX_CL_SPBFCS + 0x8) + +#define AVS_CL_OP_INTERVAL_US 3 +#define AVS_CL_OP_TIMEOUT_US 300 +#define AVS_CL_IOC_TIMEOUT_MS 300 +#define AVS_CL_STREAM_INDEX 0 + +struct hda_cldma { + struct device *dev; + struct hdac_bus *bus; + void __iomem *dsp_ba; + + unsigned int buffer_size; + unsigned int num_periods; + unsigned int stream_tag; + void __iomem *sd_addr; + + struct snd_dma_buffer dmab_data; + struct snd_dma_buffer dmab_bdl; + struct delayed_work memcpy_work; + struct completion completion; + + /* runtime */ + void *position; + unsigned int remaining; + unsigned int sd_status; +}; + +static void cldma_memcpy_work(struct work_struct *work); + +struct hda_cldma code_loader = { + .stream_tag = AVS_CL_STREAM_INDEX + 1, + .memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0), + .completion = COMPLETION_INITIALIZER(code_loader.completion), +}; + +void hda_cldma_fill(struct hda_cldma *cl) +{ + unsigned int size, offset; + + if (cl->remaining > cl->buffer_size) + size = cl->buffer_size; + else + size = cl->remaining; + + offset = snd_hdac_stream_readl(cl, CL_SD_SPIB); + if (offset + size > cl->buffer_size) { + unsigned int ss; + + ss = cl->buffer_size - offset; + memcpy(cl->dmab_data.area + offset, cl->position, ss); + offset = 0; + size -= ss; + cl->position += ss; + cl->remaining -= ss; + } + + memcpy(cl->dmab_data.area + offset, cl->position, size); + cl->position += size; + cl->remaining -= size; + + snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size); +} + +static void cldma_memcpy_work(struct work_struct *work) +{ + struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work); + int ret; + + ret = hda_cldma_start(cl); + if (ret < 0) { + dev_err(cl->dev, "cldma set RUN failed: %d\n", ret); + return; + } + + while (true) { + ret = wait_for_completion_timeout(&cl->completion, + msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS)); + if (!ret) { + dev_err(cl->dev, "cldma IOC timeout\n"); + break; + } + + if (!(cl->sd_status & SD_INT_COMPLETE)) { + dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n", + cl->sd_status); + break; + } + + if (!cl->remaining) + break; + + reinit_completion(&cl->completion); + hda_cldma_fill(cl); + /* enable CLDMA interrupt */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, + AVS_ADSP_ADSPIC_CLDMA); + } +} + +void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay) +{ + if (!cl->remaining) + return; + + reinit_completion(&cl->completion); + /* fill buffer with the first chunk before scheduling run */ + hda_cldma_fill(cl); + + schedule_delayed_work(&cl->memcpy_work, start_delay); +} + +int hda_cldma_start(struct hda_cldma *cl) +{ + unsigned int reg; + + /* enable interrupts */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, + AVS_ADSP_ADSPIC_CLDMA); + snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, + SD_INT_MASK | SD_CTL_DMA_START); + + /* await DMA engine start */ + return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START, + AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); +} + +int hda_cldma_stop(struct hda_cldma *cl) +{ + unsigned int reg; + int ret; + + /* disable interrupts */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); + snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0); + + /* await DMA engine stop */ + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START), + AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US); + cancel_delayed_work_sync(&cl->memcpy_work); + + return ret; +} + +int hda_cldma_reset(struct hda_cldma *cl) +{ + unsigned int reg; + int ret; + + ret = hda_cldma_stop(cl); + if (ret < 0) { + dev_err(cl->dev, "cldma stop failed: %d\n", ret); + return ret; + } + + snd_hdac_stream_updateb(cl, SD_CTL, 1, 1); + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & 1), AVS_CL_OP_INTERVAL_US, + AVS_CL_OP_TIMEOUT_US); + if (ret < 0) { + dev_err(cl->dev, "cldma set SRST failed: %d\n", ret); + return ret; + } + + snd_hdac_stream_updateb(cl, SD_CTL, 1, 0); + ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & 1), AVS_CL_OP_INTERVAL_US, + AVS_CL_OP_TIMEOUT_US); + if (ret < 0) { + dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret); + return ret; + } + + return 0; +} + +void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size) +{ + /* setup runtime */ + cl->position = data; + cl->remaining = size; +} + +static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size) +{ + struct snd_dma_buffer *dmab = &cl->dmab_data; + __le32 *bdl = (__le32 *)cl->dmab_bdl.area; + int remaining = cl->buffer_size; + int offset = 0; + + cl->num_periods = 0; + + while (remaining > 0) { + phys_addr_t addr; + int chunk; + + addr = snd_sgbuf_get_addr(dmab, offset); + bdl[0] = cpu_to_le32(lower_32_bits(addr)); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size); + bdl[2] = cpu_to_le32(chunk); + + remaining -= chunk; + /* set IOC only for the last entry */ + bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01); + + bdl += 4; + offset += chunk; + cl->num_periods++; + } +} + +void hda_cldma_setup(struct hda_cldma *cl) +{ + dma_addr_t bdl_addr = cl->dmab_bdl.addr; + + cldma_setup_bdle(cl, cl->buffer_size / 2); + + snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr))); + snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr)); + + snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size); + snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1); + + snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl)); + /* enable spib */ + snd_hdac_stream_writel(cl, CL_SPBFCTL, 1); +} + +static irqreturn_t cldma_irq_handler(int irq, void *dev_id) +{ + struct hda_cldma *cl = dev_id; + u32 adspis; + + adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS); + if (adspis == UINT_MAX) + return IRQ_NONE; + if (!(adspis & AVS_ADSP_ADSPIS_CLDMA)) + return IRQ_NONE; + + cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); + dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); + + /* disable CLDMA interrupt */ + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); + + complete(&cl->completion); + + return IRQ_HANDLED; +} + +int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, + unsigned int buffer_size) +{ + struct pci_dev *pci = to_pci_dev(bus->dev); + int ret; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data); + if (ret < 0) + return ret; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl); + if (ret < 0) + goto alloc_err; + + cl->dev = bus->dev; + cl->bus = bus; + cl->dsp_ba = dsp_ba; + cl->buffer_size = buffer_size; + cl->sd_addr = dsp_ba + AZX_CL_SD_BASE; + + ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA"); + if (ret < 0) { + dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret); + goto req_err; + } + + return 0; + +req_err: + snd_dma_free_pages(&cl->dmab_bdl); +alloc_err: + snd_dma_free_pages(&cl->dmab_data); + + return ret; +} + +void hda_cldma_free(struct hda_cldma *cl) +{ + struct pci_dev *pci = to_pci_dev(cl->dev); + + pci_free_irq(pci, 0, cl); + snd_dma_free_pages(&cl->dmab_data); + snd_dma_free_pages(&cl->dmab_bdl); +} diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h new file mode 100644 index 000000000000..754fcf9ee585 --- /dev/null +++ b/sound/soc/intel/avs/cldma.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H +#define __SOUND_SOC_INTEL_AVS_CLDMA_H + +#define AVS_CL_DEFAULT_BUFFER_SIZE (32 * PAGE_SIZE) + +struct hda_cldma; +extern struct hda_cldma code_loader; + +void hda_cldma_fill(struct hda_cldma *cl); +void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay); + +int hda_cldma_start(struct hda_cldma *cl); +int hda_cldma_stop(struct hda_cldma *cl); +int hda_cldma_reset(struct hda_cldma *cl); + +void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size); +void hda_cldma_setup(struct hda_cldma *cl); +int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, + unsigned int buffer_size); +void hda_cldma_free(struct hda_cldma *cl); + +#endif diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 73699d46821b..3fd02389ed2b 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -22,7 +22,9 @@ #define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C) #define AVS_ADSP_ADSPIC_IPC BIT(0) +#define AVS_ADSP_ADSPIC_CLDMA BIT(1) #define AVS_ADSP_ADSPIS_IPC BIT(0) +#define AVS_ADSP_ADSPIS_CLDMA BIT(1) #define AVS_ADSPCS_CRST_MASK(cm) (cm) #define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8) -- cgit v1.2.3 From 65794fe1a5f6f5fb10fa7b71a762158332144855 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:43 +0100 Subject: ASoC: Intel: avs: Code loading over CLDMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CLDMA transfer implemented, make use of it to shape firmware, library and module loading routines for SKL and KBL platforms. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-17-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 7 ++ sound/soc/intel/avs/loader.c | 159 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index c7e9be27be1e..9d1a3f1c7d74 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -43,6 +43,8 @@ struct avs_dsp_ops { #define avs_dsp_op(adev, op, ...) \ ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) +#define AVS_PLATATTR_CLDMA BIT_ULL(0) + #define avs_platattr_test(adev, attr) \ ((adev)->spec->attributes & AVS_PLATATTR_##attr) @@ -232,4 +234,9 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable); int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge); int avs_dsp_first_boot_firmware(struct avs_dev *adev); +int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw); +int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); +int avs_cldma_transfer_modules(struct avs_dev *adev, bool load, + struct avs_module_entry *mods, u32 num_mods); + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 96c979b44f60..2393fc5b2ba2 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -9,12 +9,24 @@ #include #include #include +#include #include "avs.h" +#include "cldma.h" #include "messages.h" #include "registers.h" +#define AVS_ROM_STS_MASK 0xFF +#define AVS_ROM_INIT_DONE 0x1 +#define SKL_ROM_BASEFW_ENTERED 0xF +#define AVS_ROM_INIT_POLLING_US 5 +#define SKL_ROM_INIT_TIMEOUT_US 1000000 + +#define AVS_FW_INIT_POLLING_US 500 +#define AVS_FW_INIT_TIMEOUT_US 3000000 #define AVS_FW_INIT_TIMEOUT_MS 3000 +#define AVS_CLDMA_START_DELAY_MS 100 + #define AVS_ROOT_DIR "intel/avs" #define AVS_BASEFW_FILENAME "dsp_basefw.bin" #define AVS_EXT_MANIFEST_MAGIC 0x31454124 @@ -111,6 +123,144 @@ static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *f return 0; } +int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) +{ + struct hda_cldma *cl = &code_loader; + unsigned int reg; + int ret; + + ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true); + if (ret < 0) + return ret; + + ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false); + if (ret < 0) + return ret; + + ret = hda_cldma_reset(cl); + if (ret < 0) { + dev_err(adev->dev, "cldma reset failed: %d\n", ret); + return ret; + } + hda_cldma_setup(cl); + + ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false); + if (ret < 0) + return ret; + + reinit_completion(&adev->fw_ready); + avs_dsp_op(adev, int_control, true); + + /* await ROM init */ + ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, + (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE, + AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US); + if (ret < 0) { + dev_err(adev->dev, "rom init timeout: %d\n", ret); + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + return ret; + } + + hda_cldma_set_data(cl, (void *)fw->data, fw->size); + /* transfer firmware */ + hda_cldma_transfer(cl, 0); + ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, + (reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED, + AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); + hda_cldma_stop(cl); + if (ret < 0) { + dev_err(adev->dev, "transfer fw failed: %d\n", ret); + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + return ret; + } + + return 0; +} + +int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) +{ + struct hda_cldma *cl = &code_loader; + int ret; + + hda_cldma_set_data(cl, (void *)lib->data, lib->size); + /* transfer modules manifest */ + hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); + + /* DMA id ignored as there is only ever one code-loader DMA */ + ret = avs_ipc_load_library(adev, 0, id); + hda_cldma_stop(cl); + + if (ret) { + ret = AVS_IPC_RET(ret); + dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret); + } + + return ret; +} + +static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry) +{ + struct hda_cldma *cl = &code_loader; + const struct firmware *mod; + char *mod_name; + int ret; + + mod_name = kasprintf(GFP_KERNEL, "%s/%s/dsp_mod_%pUL.bin", AVS_ROOT_DIR, + adev->spec->name, mentry->uuid.b); + if (!mod_name) + return -ENOMEM; + + ret = avs_request_firmware(adev, &mod, mod_name); + kfree(mod_name); + if (ret < 0) + return ret; + + hda_cldma_set_data(cl, (void *)mod->data, mod->size); + hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS)); + ret = avs_ipc_load_modules(adev, &mentry->module_id, 1); + hda_cldma_stop(cl); + + if (ret) { + dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret); + avs_release_last_firmware(adev); + return AVS_IPC_RET(ret); + } + + return 0; +} + +int avs_cldma_transfer_modules(struct avs_dev *adev, bool load, + struct avs_module_entry *mods, u32 num_mods) +{ + u16 *mod_ids; + int ret, i; + + /* Either load to DSP or unload them to free space. */ + if (load) { + for (i = 0; i < num_mods; i++) { + ret = avs_cldma_load_module(adev, &mods[i]); + if (ret) + return ret; + } + + return 0; + } + + mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL); + if (!mod_ids) + return -ENOMEM; + + for (i = 0; i < num_mods; i++) + mod_ids[i] = mods[i].module_id; + + ret = avs_ipc_unload_modules(adev, mod_ids, num_mods); + kfree(mod_ids); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} + static int avs_dsp_load_basefw(struct avs_dev *adev) { const struct avs_fw_version *min_req; @@ -195,6 +345,15 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev) { int ret, i; + if (avs_platattr_test(adev, CLDMA)) { + ret = hda_cldma_init(&code_loader, &adev->base.core, + adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); + if (ret < 0) { + dev_err(adev->dev, "cldma init failed: %d\n", ret); + return ret; + } + } + ret = avs_dsp_boot_firmware(adev, true); if (ret < 0) { dev_err(adev->dev, "firmware boot failed: %d\n", ret); -- cgit v1.2.3 From 092cf7b26a48a542d9aabfc0f517fa263102a4c1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 11 Mar 2022 16:35:44 +0100 Subject: ASoC: Intel: avs: Code loading over HDA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compared to SKL and KBL, more recent cAVS platforms are meant to re-use one of HDAudio streams during boot procedure causing CLDMA to become obsolete. Once transferred, given stream is returned to pool available for audio streaming. Module loading handler is stub as library and module code became inseparable in later firmware generations. Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220311153544.136854-18-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/avs/avs.h | 5 + sound/soc/intel/avs/loader.c | 214 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e9768c4aa1a9..d025ca0c77fa 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -215,6 +215,7 @@ config SND_SOC_INTEL_AVS depends on COMMON_CLK select SND_SOC_ACPI select SND_HDA_EXT_CORE + select SND_HDA_DSP_LOADER help Enable support for Intel(R) cAVS 1.5 platforms with DSP capabilities. This includes Skylake, Kabylake, Amberlake and diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 9d1a3f1c7d74..b48a342fd184 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -44,6 +44,7 @@ struct avs_dsp_ops { ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) #define AVS_PLATATTR_CLDMA BIT_ULL(0) +#define AVS_PLATATTR_IMR BIT_ULL(1) #define avs_platattr_test(adev, attr) \ ((adev)->spec->attributes & AVS_PLATATTR_##attr) @@ -238,5 +239,9 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw); int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); int avs_cldma_transfer_modules(struct avs_dev *adev, bool load, struct avs_module_entry *mods, u32 num_mods); +int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw); +int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); +int avs_hda_transfer_modules(struct avs_dev *adev, bool load, + struct avs_module_entry *mods, u32 num_mods); #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 2393fc5b2ba2..c47f85161d95 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "avs.h" #include "cldma.h" @@ -18,8 +19,11 @@ #define AVS_ROM_STS_MASK 0xFF #define AVS_ROM_INIT_DONE 0x1 #define SKL_ROM_BASEFW_ENTERED 0xF +#define APL_ROM_FW_ENTERED 0x5 #define AVS_ROM_INIT_POLLING_US 5 #define SKL_ROM_INIT_TIMEOUT_US 1000000 +#define APL_ROM_INIT_TIMEOUT_US 300000 +#define APL_ROM_INIT_RETRIES 3 #define AVS_FW_INIT_POLLING_US 500 #define AVS_FW_INIT_TIMEOUT_US 3000000 @@ -261,6 +265,207 @@ int avs_cldma_transfer_modules(struct avs_dev *adev, bool load, return 0; } +static int +avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) +{ + const struct avs_spec *const spec = adev->spec; + unsigned int corex_mask, reg; + int ret; + + corex_mask = spec->core_init_mask & ~AVS_MAIN_CORE_MASK; + + ret = avs_dsp_op(adev, power, spec->core_init_mask, true); + if (ret < 0) + goto err; + + ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false); + if (ret < 0) + goto err; + + reinit_completion(&adev->fw_ready); + avs_dsp_op(adev, int_control, true); + + /* set boot config */ + ret = avs_ipc_set_boot_config(adev, dma_id, purge); + if (ret) { + ret = AVS_IPC_RET(ret); + goto err; + } + + /* await ROM init */ + ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg, + (reg & 0xF) == AVS_ROM_INIT_DONE || + (reg & 0xF) == APL_ROM_FW_ENTERED, + AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); + if (ret < 0) { + dev_err(adev->dev, "rom init timeout: %d\n", ret); + goto err; + } + + /* power down non-main cores */ + if (corex_mask) { + ret = avs_dsp_op(adev, power, corex_mask, false); + if (ret < 0) + goto err; + } + + return 0; + +err: + avs_dsp_core_disable(adev, spec->core_init_mask); + return ret; +} + +static int avs_imr_load_basefw(struct avs_dev *adev) +{ + int ret; + + /* DMA id ignored when flashing from IMR as no transfer occurs. */ + ret = avs_hda_init_rom(adev, 0, false); + if (ret < 0) { + dev_err(adev->dev, "rom init failed: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&adev->fw_ready, + msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); + if (!ret) { + dev_err(adev->dev, "firmware ready timeout\n"); + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + return -ETIMEDOUT; + } + + return 0; +} + +int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) +{ + struct snd_pcm_substream substream; + struct snd_dma_buffer dmab; + struct hdac_ext_stream *estream; + struct hdac_stream *hstream; + struct hdac_bus *bus = &adev->base.core; + unsigned int sdfmt, reg; + int ret, i; + + /* configure hda dma */ + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + estream = snd_hdac_ext_stream_assign(bus, &substream, + HDAC_EXT_STREAM_TYPE_HOST); + if (!estream) + return -ENODEV; + 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); + ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab); + if (ret < 0) + goto release_stream; + + /* enable SPIB for hda stream */ + snd_hdac_ext_stream_spbcap_enable(bus, true, hstream->index); + ret = snd_hdac_ext_stream_set_spib(bus, estream, fw->size); + if (ret) + goto cleanup_resources; + + memcpy(dmab.area, fw->data, fw->size); + + for (i = 0; i < APL_ROM_INIT_RETRIES; i++) { + unsigned int dma_id = hstream->stream_tag - 1; + + ret = avs_hda_init_rom(adev, dma_id, true); + if (!ret) + break; + dev_info(adev->dev, "#%d rom init fail: %d\n", i + 1, ret); + } + if (ret < 0) + goto cleanup_resources; + + /* transfer firmware */ + snd_hdac_dsp_trigger(hstream, true); + ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg, + (reg & AVS_ROM_STS_MASK) == APL_ROM_FW_ENTERED, + AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); + snd_hdac_dsp_trigger(hstream, false); + if (ret < 0) { + dev_err(adev->dev, "transfer fw failed: %d\n", ret); + avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + } + +cleanup_resources: + /* disable SPIB for hda stream */ + snd_hdac_ext_stream_spbcap_enable(bus, false, hstream->index); + snd_hdac_ext_stream_set_spib(bus, estream, 0); + + snd_hdac_dsp_cleanup(hstream, &dmab); +release_stream: + snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); + + return ret; +} + +int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id) +{ + struct snd_pcm_substream substream; + struct snd_dma_buffer dmab; + struct hdac_ext_stream *estream; + struct hdac_stream *stream; + struct hdac_bus *bus = &adev->base.core; + unsigned int sdfmt; + int ret; + + /* configure hda dma */ + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + estream = snd_hdac_ext_stream_assign(bus, &substream, + HDAC_EXT_STREAM_TYPE_HOST); + if (!estream) + return -ENODEV; + 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); + ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab); + if (ret < 0) + goto release_stream; + + /* enable SPIB for hda stream */ + snd_hdac_ext_stream_spbcap_enable(bus, true, stream->index); + snd_hdac_ext_stream_set_spib(bus, estream, lib->size); + + memcpy(dmab.area, lib->data, lib->size); + + /* transfer firmware */ + snd_hdac_dsp_trigger(stream, true); + ret = avs_ipc_load_library(adev, stream->stream_tag - 1, id); + snd_hdac_dsp_trigger(stream, false); + if (ret) { + dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret); + ret = AVS_IPC_RET(ret); + } + + /* disable SPIB for hda stream */ + snd_hdac_ext_stream_spbcap_enable(bus, false, stream->index); + snd_hdac_ext_stream_set_spib(bus, estream, 0); + + snd_hdac_dsp_cleanup(stream, &dmab); +release_stream: + snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); + + return ret; +} + +int avs_hda_transfer_modules(struct avs_dev *adev, bool load, + struct avs_module_entry *mods, u32 num_mods) +{ + /* + * All platforms without CLDMA are equipped with IMR, + * and thus the module transferring is offloaded to DSP. + */ + return 0; +} + static int avs_dsp_load_basefw(struct avs_dev *adev) { const struct avs_fw_version *min_req; @@ -316,6 +521,15 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) { int ret, i; + /* Forgo full boot if flash from IMR succeeds. */ + if (!purge && avs_platattr_test(adev, IMR)) { + ret = avs_imr_load_basefw(adev); + if (!ret) + return 0; + + dev_dbg(adev->dev, "firmware flash from imr failed: %d\n", ret); + } + /* Full boot, clear cached data except for basefw (slot 0). */ for (i = 1; i < adev->fw_cfg.max_libs_count; i++) memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE); -- cgit v1.2.3 From 0f306cca42fe879694fb5e2382748c43dc9e0196 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 11 Mar 2022 21:14:00 +0100 Subject: ALSA: usb-audio: Add mute TLV for playback volumes on RODE NT-USB For the RODE NT-USB the lowest Playback mixer volume setting mutes the audio output. But it is not reported as such causing e.g. PulseAudio to accidentally mute the device when selecting a low volume. Fix this by applying the existing quirk for this kind of issue when the device is detected. Signed-off-by: Lars-Peter Clausen Cc: Link: https://lore.kernel.org/r/20220311201400.235892-1-lars@metafoo.de Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e447ddd6854c..d35cf54cab33 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3360,9 +3360,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, if (unitid == 7 && cval->control == UAC_FU_VOLUME) snd_dragonfly_quirk_db_scale(mixer, cval, kctl); break; - /* lowest playback value is muted on C-Media devices */ - case USB_ID(0x0d8c, 0x000c): - case USB_ID(0x0d8c, 0x0014): + /* lowest playback value is muted on some devices */ + case USB_ID(0x0d8c, 0x000c): /* C-Media */ + case USB_ID(0x0d8c, 0x0014): /* C-Media */ + case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */ if (strstr(kctl->id.name, "Playback")) cval->min_mute = 1; break; -- cgit v1.2.3 From 3baa40d4fd7ff6553325715b8a64cc8b43fd02ef Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 12 Mar 2022 11:27:02 +0100 Subject: ALSA: seq: oss: use kzalloc Use kzalloc instead of kmalloc + memset. The semantic patch that makes this change is: (https://coccinelle.gitlabpages.inria.fr/website/) // @@ expression res, size, flag; @@ - res = kmalloc(size, flag); + res = kzalloc(size, flag); ... - memset(res, 0, size); // Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/20220312102705.71413-4-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 0ee4a5081fd6..04a3376a6e66 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -66,7 +66,7 @@ snd_seq_oss_create_client(void) struct snd_seq_port_info *port; struct snd_seq_port_callback port_callback; - port = kmalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) { rc = -ENOMEM; goto __error; @@ -81,7 +81,6 @@ snd_seq_oss_create_client(void) system_client = rc; /* create annoucement receiver port */ - memset(port, 0, sizeof(*port)); strcpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ -- cgit v1.2.3 From b62c563f1cb9c216cecadb15ae32d6e571e2bc8a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 13 Mar 2022 09:56:35 +0100 Subject: ALSA: seq: oss: fix typo Fix typo in "announcement". Reported-by: Joe Perches Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/20220313085635.102123-1-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 04a3376a6e66..42d4e7535a82 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -80,7 +80,7 @@ snd_seq_oss_create_client(void) system_client = rc; - /* create annoucement receiver port */ + /* create announcement receiver port */ strcpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ -- cgit v1.2.3 From b7557267c233b55d8e8d7ba4c68cf944fe2ec02c Mon Sep 17 00:00:00 2001 From: Jason Zheng Date: Sun, 13 Mar 2022 04:22:16 -0500 Subject: ALSA: hda/realtek: Add quirk for ASUS GA402 ASUS GA402 requires a workaround to manage the routing of its 4 speakers like the other ASUS models. Add a corresponding quirk entry to fix it. Signed-off-by: Jason Zheng Cc: Link: https://lore.kernel.org/r/20220313092216.29858-1-jasonzheng2004@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ba86b09a61af..75ff7e8498b8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9020,6 +9020,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), -- cgit v1.2.3 From c14231cc04337c2c2a937db084af342ce704dbde Mon Sep 17 00:00:00 2001 From: Jonathan Teh Date: Sun, 13 Mar 2022 19:56:17 +0000 Subject: ALSA: cmipci: Restore aux vol on suspend/resume Save and restore CM_REG_AUX_VOL instead of register 0x24 twice on suspend/resume. Tested on CMI8738LX. Fixes: cb60e5f5b2b1 ("[ALSA] cmipci - Add PM support") Signed-off-by: Jonathan Teh Cc: Link: https://lore.kernel.org/r/DBAPR04MB7366CB3EA9C8521C35C56E8B920E9@DBAPR04MB7366.eurprd04.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/pci/cmipci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 9a678b5cf285..dab801d9d3b4 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -298,7 +298,6 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_MICGAINZ 0x01 /* mic boost */ #define CM_MICGAINZ_SHIFT 0 -#define CM_REG_MIXER3 0x24 #define CM_REG_AUX_VOL 0x26 #define CM_VAUXL_MASK 0xf0 #define CM_VAUXR_MASK 0x0f @@ -3265,7 +3264,7 @@ static int snd_cmipci_probe(struct pci_dev *pci, */ static const unsigned char saved_regs[] = { CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL, - CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL, + CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL, CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2, CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC, CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0, -- cgit v1.2.3 From ccb4214f7f2a8b75acf493f31128e464ee1a3536 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Thu, 10 Mar 2022 11:00:41 +0800 Subject: ASoC: soc-compress: Change the check for codec_dai It should be better to reverse the check on codec_dai and returned early in order to be easier to understand. Fixes: de2c6f98817f ("ASoC: soc-compress: prevent the potentially use of null pointer") Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Jiasheng Jiang Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20220310030041.1556323-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index f4b376a71be8..e9dd25894dc0 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -567,16 +567,19 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return -EINVAL; } - /* check client and interface hw capabilities */ - if (codec_dai) { - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) - playback = 1; - if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) - capture = 1; + if (!codec_dai) { + dev_err(rtd->card->dev, "Missing codec\n"); + return -EINVAL; } + /* check client and interface hw capabilities */ + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) + playback = 1; + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) + capture = 1; + /* * Compress devices are unidirectional so only one of the directions * should be set, check for that (xor) -- cgit v1.2.3 From 910f42bfe96783fc633196bd975731a420c7a066 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 11 Mar 2022 17:48:01 -0600 Subject: ASoC: dt-bindings: Fix patternProperties with fixed strings The simple-audio-card and renesas,rsnd bindings used 'patternProperties' with fixed strings to work-around a dtschema meta-schema limitation. This is now fixed and the schemas can be fixed to use 'properties' instead. Signed-off-by: Rob Herring Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220311234802.417610-1-robh@kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/renesas,rsnd.yaml | 16 ++++----- .../devicetree/bindings/sound/simple-card.yaml | 42 +++++++++++----------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index c2930d65728e..e17c0245f77a 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -123,9 +123,7 @@ properties: $ref: audio-graph-port.yaml# unevaluatedProperties: false -# use patternProperties to avoid naming "xxx,yyy" issue -patternProperties: - "^rcar_sound,dvc$": + rcar_sound,dvc: description: DVC subnode. type: object patternProperties: @@ -141,7 +139,7 @@ patternProperties: - dma-names additionalProperties: false - "^rcar_sound,mix$": + rcar_sound,mix: description: MIX subnode. type: object patternProperties: @@ -150,7 +148,7 @@ patternProperties: # no properties additionalProperties: false - "^rcar_sound,ctu$": + rcar_sound,ctu: description: CTU subnode. type: object patternProperties: @@ -159,7 +157,7 @@ patternProperties: # no properties additionalProperties: false - "^rcar_sound,src$": + rcar_sound,src: description: SRC subnode. type: object patternProperties: @@ -182,7 +180,7 @@ patternProperties: - dma-names additionalProperties: false - "^rcar_sound,ssiu$": + rcar_sound,ssiu: description: SSIU subnode. type: object patternProperties: @@ -202,7 +200,7 @@ patternProperties: - dma-names additionalProperties: false - "^rcar_sound,ssi$": + rcar_sound,ssi: description: SSI subnode. type: object patternProperties: @@ -239,7 +237,7 @@ patternProperties: additionalProperties: false # For DAI base - "^rcar_sound,dai$": + rcar_sound,dai: description: DAI subnode. type: object patternProperties: diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml index 00597dc4f396..b261d49b9ddb 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.yaml +++ b/Documentation/devicetree/bindings/sound/simple-card.yaml @@ -167,45 +167,45 @@ properties: description: User specified audio sound card name. $ref: /schemas/types.yaml#/definitions/string -# use patternProperties to avoid naming "xxx,yyy" issue -patternProperties: - "^simple-audio-card,widgets$": + simple-audio-card,widgets: $ref: "#/definitions/widgets" - "^simple-audio-card,routing$": + simple-audio-card,routing: $ref: "#/definitions/routing" - "^simple-audio-card,cpu(@[0-9a-f]+)?": - $ref: "#/definitions/dai" - "^simple-audio-card,codec(@[0-9a-f]+)?": - $ref: "#/definitions/dai" # common properties - "^simple-audio-card,frame-master$": + simple-audio-card,frame-master: $ref: "#/definitions/frame-master" - "^simple-audio-card,bitclock-master$": + simple-audio-card,bitclock-master: $ref: "#/definitions/bitclock-master" - "^simple-audio-card,frame-inversion$": + simple-audio-card,frame-inversion: $ref: "#/definitions/frame-inversion" - "^simple-audio-card,bitclock-inversion$": + simple-audio-card,bitclock-inversion: $ref: "#/definitions/bitclock-inversion" - "^simple-audio-card,format$": + simple-audio-card,format: $ref: "#/definitions/format" - "^simple-audio-card,mclk-fs$": + simple-audio-card,mclk-fs: $ref: "#/definitions/mclk-fs" - "^simple-audio-card,aux-devs$": + simple-audio-card,aux-devs: $ref: "#/definitions/aux-devs" - "^simple-audio-card,convert-rate$": + simple-audio-card,convert-rate: $ref: "#/definitions/convert-rate" - "^simple-audio-card,convert-channels$": + simple-audio-card,convert-channels: $ref: "#/definitions/convert-channels" - "^simple-audio-card,prefix$": + simple-audio-card,prefix: $ref: "#/definitions/prefix" - "^simple-audio-card,pin-switches$": + simple-audio-card,pin-switches: $ref: "#/definitions/pin-switches" - "^simple-audio-card,hp-det-gpio$": + simple-audio-card,hp-det-gpio: maxItems: 1 - "^simple-audio-card,mic-det-gpio$": + simple-audio-card,mic-det-gpio: maxItems: 1 +patternProperties: + "^simple-audio-card,cpu(@[0-9a-f]+)?$": + $ref: "#/definitions/dai" + "^simple-audio-card,codec(@[0-9a-f]+)?$": + $ref: "#/definitions/dai" + "^simple-audio-card,dai-link(@[0-9a-f]+)?$": description: | Container for dai-link level properties and the CPU and CODEC sub-nodes. -- cgit v1.2.3 From e6194c8d065389b9393290ecb682118fd982fa33 Mon Sep 17 00:00:00 2001 From: Andy Chi Date: Mon, 14 Mar 2022 22:21:19 +0800 Subject: ALSA: hda/realtek: fix right sounds and mute/micmute LEDs for HP machines * The HP ProBook 440/450 and EliteBook 640/650 are using ALC236 codec which used 0x02 to control mute LED and 0x01 to control micmute LED. Therefore, add a quirk to make it works. Signed-off-by: Andy Chi Link: https://lore.kernel.org/r/20220314142122.71602-1-andy.chi@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d85989f585b..d7bebe3bdea7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9017,6 +9017,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), -- cgit v1.2.3 From a8ae15ead9c9d10671c3f76cb0749dec6e571ce7 Mon Sep 17 00:00:00 2001 From: Alviro Iskandar Setiawan Date: Tue, 15 Mar 2022 00:18:48 +0000 Subject: ASoC: atmel: mchp-pdmc: Fix `-Wpointer-bool-conversion` warning In function mchp_pdmc_af_put(), Intel's kernel test robot reports the following warning: sound/soc/atmel/mchp-pdmc.c:186:34: warning: address of array \ 'uvalue->value.integer.value' will always evaluate to 'true' \ [-Wpointer-bool-conversion] This is because we are using `uvalue->value.integer.value` which its type is `long value[128];` for conditional expression and that array will always decay to a non-NULL pointer. Using a non-NULL pointer for conditional expression will always evaluate to true. Fix this by changing it to `uvalue->value.integer.value[0]` as that's what the mchp_pdmc_af_get() function sets. Reported-by: kernel test robot Reviewed-by: Codrin Ciubotariu Reviewed-by: Nathan Chancellor Signed-off-by: Alviro Iskandar Setiawan Link: https://lore.kernel.org/r/20220315001848.3763534-1-alviro.iskandar@gnuweeb.org Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index 1be4007875f1..e0aec5fe8a26 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -183,7 +183,7 @@ static int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); - bool af = uvalue->value.integer.value ? true : false; + bool af = uvalue->value.integer.value[0] ? true : false; if (dd->audio_filter_en == af) return 0; -- cgit v1.2.3 From 0af0f4633adfa211807262af4d446b09698df6c8 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:02 -0700 Subject: ASoC: SOF: Introduce struct snd_sof_dai_link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new struct for DAI links to save the DAI link information parsed from topology. Also add a list of dai_links to struct snd_sof_dev that will be populated during topology parsing. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 1 + sound/soc/sof/sof-audio.h | 21 ++++++++++++++++++++- sound/soc/sof/sof-priv.h | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9217644e2eab..e91631618bff 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -370,6 +370,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); + INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 450ee9977c55..548d443e83b0 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -96,7 +96,26 @@ struct snd_sof_control { bool comp_data_dirty; }; -struct snd_sof_widget; +/** struct snd_sof_dai_link - DAI link info + * @tuples: array of parsed tuples + * @num_tuples: number of tuples in the tuples array + * @link: Pointer to snd_soc_dai_link + * @hw_configs: Pointer to hw configs in topology + * @num_hw_configs: Number of hw configs in topology + * @default_hw_cfg_id: Default hw config ID + * @type: DAI type + * @list: item in snd_sof_dev dai_link list + */ +struct snd_sof_dai_link { + struct snd_sof_tuple *tuples; + int num_tuples; + struct snd_soc_dai_link *link; + struct snd_soc_tplg_hw_config *hw_configs; + int num_hw_configs; + int default_hw_cfg_id; + int type; + struct list_head list; +}; /* ASoC SOF DAPM widget */ struct snd_sof_widget { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ef5a2adae5c7..28d3f1ac8be8 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -441,6 +441,7 @@ struct snd_sof_dev { struct list_head kcontrol_list; struct list_head widget_list; struct list_head dai_list; + struct list_head dai_link_list; struct list_head route_list; struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ -- cgit v1.2.3 From 226abb759063ca53a32c1570d058db03b7850fdf Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:04 -0700 Subject: ASoC: SOF: IPC: Introduce IPC ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for supporting a new IPC version that will be introduced in the SOF firmware, add a new set of ops that will be version specific. For now, the IPC ops contain only the topology-related ops for setting up widgets and pipeline connections. It will be expanded later to also abstract the IPC-specific items in the component driver and DAI driver. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 36 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 548d443e83b0..79041622987f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,6 +30,42 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +struct snd_sof_widget; +struct snd_sof_route; + +/** + * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets + * @ipc_setup: Function pointer for setting up widget IPC params + * @ipc_free: Function pointer for freeing widget IPC params + * @token_list: List of token ID's that should be parsed for the widget + * @token_list_size: number of elements in token_list + * @bind_event: Function pointer for binding events to the widget + */ +struct sof_ipc_tplg_widget_ops { + int (*ipc_setup)(struct snd_sof_widget *swidget); + void (*ipc_free)(struct snd_sof_widget *swidget); + enum sof_tokens *token_list; + int token_list_size; + int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, + u16 event_type); +}; + +/** + * struct sof_ipc_tplg_ops - IPC-specific topology ops + * @widget: Array of pointers to IPC-specific ops for widgets. This should always be of size + * SND_SOF_DAPM_TYPE_COUNT i.e one per widget type. Unsupported widget types will be + * initialized to 0. + * @route_setup: Function pointer for setting up pipeline connections + * @token_list: List of all tokens supported by the IPC version. The size of the token_list + * array should be SOF_TOKEN_COUNT. The unused elements in the array will be + * initialized to 0. + */ +struct sof_ipc_tplg_ops { + const struct sof_ipc_tplg_widget_ops *widget; + int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); + const struct sof_token_info *token_list; +}; + /** struct snd_sof_tuple - Tuple info * @token: Token ID * @value: union of a string or a u32 values diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 28d3f1ac8be8..0dab5b70406e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -360,6 +360,16 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; +struct sof_ipc_tplg_ops; + +/** + * struct sof_ipc_ops - IPC-specific ops + * @tplg: Pointer to IPC-specific topology ops + */ +struct sof_ipc_ops { + const struct sof_ipc_tplg_ops *tplg; +}; + /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; @@ -370,6 +380,9 @@ struct snd_sof_ipc { bool disable_ipc_tx; struct snd_sof_ipc_msg msg; + + /* IPC ops based on version */ + const struct sof_ipc_ops *ops; }; /* -- cgit v1.2.3 From d87524bf9aab511906e7e9bab90198510c28149f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:05 -0700 Subject: ASoC: SOF: topology: Add helper function for processing tuple arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper function for processing tuple arrays and populating the IPC structure objects based on the token ID passed. Introduce a new enum representing token ID for the tokens currently used in the topology parse and a new struct sof_token_info to store the information about a token set. Finally, expose the struct snd_sof_topology token as it will be used by IPC-specific code. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 54 +++++++++++++++++++++++ sound/soc/sof/topology.c | 106 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 148 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 79041622987f..bde86e078e08 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -78,6 +78,57 @@ struct snd_sof_tuple { } value; }; +/* + * List of SOF token ID's. The order of ID's does not matter as token arrays are looked up based on + * the ID. + */ +enum sof_tokens { + SOF_PCM_TOKENS, + SOF_PIPELINE_TOKENS, + SOF_SCHED_TOKENS, + SOF_ASRC_TOKENS, + SOF_SRC_TOKENS, + SOF_COMP_TOKENS, + SOF_BUFFER_TOKENS, + SOF_VOLUME_TOKENS, + SOF_PROCESS_TOKENS, + SOF_DAI_TOKENS, + SOF_DAI_LINK_TOKENS, + SOF_HDA_TOKENS, + SOF_SSP_TOKENS, + SOF_ALH_TOKENS, + SOF_DMIC_TOKENS, + SOF_DMIC_PDM_TOKENS, + SOF_ESAI_TOKENS, + SOF_SAI_TOKENS, + SOF_AFE_TOKENS, + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + + /* this should be the last */ + SOF_TOKEN_COUNT, +}; + +/** + * struct sof_topology_token - SOF topology token definition + * @token: Token number + * @type: Token type + * @get_token: Function pointer to parse the token value and save it in a object + * @offset: Offset within an object to save the token value into + */ +struct sof_topology_token { + u32 token; + u32 type; + int (*get_token)(void *elem, void *object, u32 offset); + u32 offset; +}; + +struct sof_token_info { + const char *name; + const struct sof_topology_token *tokens; + int count; +}; + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; @@ -333,4 +384,7 @@ int get_token_u16(void *elem, void *object, u32 offset); int get_token_comp_format(void *elem, void *object, u32 offset); int get_token_dai_type(void *elem, void *object, u32 offset); int get_token_uuid(void *elem, void *object, u32 offset); +int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, + struct snd_sof_tuple *tuples, int num_tuples, + size_t object_size, int token_instance_num); #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index afd9eda67631..a127d3d2eab7 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -47,6 +47,100 @@ /* size of tplg abi in byte */ #define SOF_TPLG_ABI_SIZE 3 +/** + * sof_update_ipc_object - Parse multiple sets of tokens within the token array associated with the + * token ID. + * @scomp: pointer to SOC component + * @object: target IPC struct to save the parsed values + * @token_id: token ID for the token array to be searched + * @tuples: pointer to the tuples array + * @num_tuples: number of tuples in the tuples array + * @object_size: size of the object + * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function + * looks for @token_instance_num of each token in the token array associated + * with the @token_id + */ +int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, + struct snd_sof_tuple *tuples, int num_tuples, + size_t object_size, int token_instance_num) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_topology_token *tokens; + int i, j; + + if (token_list[token_id].count < 0) { + dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id); + return -EINVAL; + } + + /* No tokens to match */ + if (!token_list[token_id].count) + return 0; + + tokens = token_list[token_id].tokens; + if (!tokens) { + dev_err(scomp->dev, "Invalid tokens for token id: %d\n", token_id); + return -EINVAL; + } + + for (i = 0; i < token_list[token_id].count; i++) { + int offset = 0; + int num_tokens_matched = 0; + + for (j = 0; j < num_tuples; j++) { + if (tokens[i].token == tuples[j].token) { + switch (tokens[i].type) { + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + { + u32 *val = (u32 *)((u8 *)object + tokens[i].offset + + offset); + + *val = tuples[j].value.v; + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + { + u16 *val = (u16 *)((u8 *)object + tokens[i].offset + + offset); + + *val = (u16)tuples[j].value.v; + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + { + if (!tokens[i].get_token) { + dev_err(scomp->dev, + "get_token not defined for token %d in %s\n", + tokens[i].token, token_list[token_id].name); + return -EINVAL; + } + + tokens[i].get_token((void *)tuples[j].value.s, object, + tokens[i].offset + offset); + break; + } + default: + break; + } + + num_tokens_matched++; + + /* found all required sets of current token. Move to the next one */ + if (!(num_tokens_matched % token_instance_num)) + break; + + /* move to the next object */ + offset += object_size; + } + } + } + + return 0; +} + struct sof_widget_data { int ctrl_type; int ipc_cmd; @@ -465,18 +559,6 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) return SOF_COMP_NONE; } -/* - * Topology Token Parsing. - * New tokens should be added to headers and parsing tables below. - */ - -struct sof_topology_token { - u32 token; - u32 type; - int (*get_token)(void *elem, void *object, u32 offset); - u32 offset; -}; - int get_token_u32(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; -- cgit v1.2.3 From 7006d20e5e9d25c079a82e2bc0ea7e292fdea6e6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:06 -0700 Subject: ASoC: SOF: Introduce IPC3 ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the IPC ops including the topology-related IPC ops for the current version (IPC3, named after the current SOF firmware ABI major version 3.0) of IPC supported by the SOF firmware and set it as default. The topology IPC ops and the widget ops within the topology IPC ops are both mandatory. With the introduction of IPC3 ops, we define the list of tokens pertaining to the AIF_IN/AIF_OUT widgets. Then these tokens are parsed during topology parsing and saved as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the host component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 3 +- sound/soc/sof/ipc.c | 12 ++ sound/soc/sof/ipc3-topology.c | 173 ++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 2 + sound/soc/sof/topology.c | 258 ++++++++++++++++++++++++++++++++++-------- 5 files changed, 397 insertions(+), 51 deletions(-) create mode 100644 sound/soc/sof/ipc3-topology.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index a0459f06c68a..e13dab59764c 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\ + ipc3-topology.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 34084e0008f1..cf892859355a 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -1023,6 +1023,18 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) init_waitqueue_head(&msg->waitq); + /* + * Use IPC3 ops as it is the only available version now. With the addition of new IPC + * versions, this will need to be modified to use the selected version at runtime. + */ + ipc->ops = &ipc3_ops; + + /* check for mandatory ops */ + if (!ipc->ops->tplg || !ipc->ops->tplg->widget) { + dev_err(sdev->dev, "Invalid topology IPC ops\n"); + return NULL; + } + return ipc; } EXPORT_SYMBOL(snd_sof_ipc_init); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c new file mode 100644 index 000000000000..33d07b0df640 --- /dev/null +++ b/sound/soc/sof/ipc3-topology.c @@ -0,0 +1,173 @@ +// 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) 2021 Intel Corporation. All rights reserved. +// +// + +#include +#include +#include "sof-priv.h" +#include "sof-audio.h" +#include "ops.h" + +/* PCM */ +static const struct sof_topology_token pcm_tokens[] = { + {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_config)}, +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_sink)}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_source)}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt)}, +}; + +/* Core tokens */ +static const struct sof_topology_token core_tokens[] = { + {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp, core)}, +}; + +/* Component extended tokens */ +static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, + offsetof(struct snd_sof_widget, uuid)}, +}; + +static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { + [SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)}, + [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)}, + [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, + [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, +}; + +/** + * sof_comp_alloc - allocate and initialize buffer for a new component + * @swidget: pointer to struct snd_sof_widget containing extended data + * @ipc_size: IPC payload size that will be updated depending on valid + * extended data. + * @index: ID of the pipeline the component belongs to + * + * Return: The pointer to the new allocated component, NULL if failed. + */ +static void *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, + int index) +{ + struct sof_ipc_comp *comp; + size_t total_size = *ipc_size; + size_t ext_size = sizeof(swidget->uuid); + + /* only non-zero UUID is valid */ + if (!guid_is_null(&swidget->uuid)) + total_size += ext_size; + + comp = kzalloc(total_size, GFP_KERNEL); + if (!comp) + return NULL; + + /* configure comp new IPC message */ + comp->hdr.size = total_size; + comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp->id = swidget->comp_id; + comp->pipeline_id = index; + comp->core = swidget->core; + + /* handle the extended data if needed */ + if (total_size > *ipc_size) { + /* append extended data to the end of the component */ + memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); + comp->ext_data_length = ext_size; + } + + /* update ipc_size and return */ + *ipc_size = total_size; + return comp; +} + +static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config) +{ + dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", + config->periods_sink, config->periods_source, + config->frame_fmt); +} + +static int sof_ipc3_widget_setup_comp_host(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_host *host; + size_t ipc_size = sizeof(*host); + int ret; + + host = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!host) + return -ENOMEM; + swidget->private = host; + + /* configure host comp IPC message */ + host->comp.type = SOF_COMP_HOST; + host->config.hdr.size = sizeof(host->config); + + if (swidget->id == snd_soc_dapm_aif_out) + host->direction = SOF_IPC_STREAM_CAPTURE; + else + host->direction = SOF_IPC_STREAM_PLAYBACK; + + /* parse one set of pcm_tokens */ + ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*host), 1); + if (ret < 0) + goto err; + + /* parse one set of comp_tokens */ + ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(host->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &host->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) +{ + kfree(swidget->private); +} + +/* token list for each topology object */ +static enum sof_tokens host_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PCM_TOKENS, + SOF_COMP_TOKENS, +}; + +static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { + [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, + host_token_list, ARRAY_SIZE(host_token_list), NULL}, + [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, + host_token_list, ARRAY_SIZE(host_token_list), NULL}, +}; + +static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { + .widget = tplg_ipc3_widget_ops, + .token_list = ipc3_token_list, +}; + +const struct sof_ipc_ops ipc3_ops = { + .tplg = &ipc3_tplg_ops, +}; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0dab5b70406e..0b89c3e6ef21 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -370,6 +370,8 @@ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; }; +extern const struct sof_ipc_ops ipc3_ops; + /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index a127d3d2eab7..2258317d7f57 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -701,12 +701,6 @@ static const struct sof_topology_token process_tokens[] = { offsetof(struct sof_ipc_comp_process, type)}, }; -/* PCM */ -static const struct sof_topology_token pcm_tokens[] = { - {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_config)}, -}; - /* PCM */ static const struct sof_topology_token stream_tokens[] = { {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, @@ -934,6 +928,123 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, return found; } +/** + * sof_copy_tuples - Parse tokens and copy them to the @tuples array + * @sdev: pointer to struct snd_sof_dev + * @array: source pointer to consecutive vendor arrays in topology + * @array_size: size of @array + * @token_id: Token ID associated with a token array + * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function + * looks for @token_instance_num of each token in the token array associated + * with the @token_id + * @tuples: tuples array to copy the matched tuples to + * @tuples_size: size of @tuples + * @num_copied_tuples: pointer to the number of copied tuples in the tuples array + * + */ +static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array, + int array_size, u32 token_id, int token_instance_num, + struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_topology_token *tokens; + int found = 0; + int num_tokens, asize; + int i, j; + + /* nothing to do if token_list is NULL */ + if (!token_list) + return 0; + + if (!tuples || !num_copied_tuples) { + dev_err(sdev->dev, "Invalid tuples array\n"); + return -EINVAL; + } + + tokens = token_list[token_id].tokens; + num_tokens = token_list[token_id].count; + + if (!tokens) { + dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id); + return -EINVAL; + } + + /* check if there's space in the tuples array for new tokens */ + if (*num_copied_tuples >= tuples_size) { + dev_err(sdev->dev, "No space in tuples array for new tokens from %s", + token_list[token_id].name); + return -EINVAL; + } + + while (array_size > 0 && found < num_tokens * token_instance_num) { + asize = le32_to_cpu(array->size); + + /* validate asize */ + if (asize < 0) { + dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + array_size -= asize; + if (array_size < 0) { + dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* parse element by element */ + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { + /* search for token */ + for (j = 0; j < num_tokens; j++) { + /* match token type */ + if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING)) + continue; + + if (tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING) { + struct snd_soc_tplg_vendor_string_elem *elem; + + elem = &array->string[i]; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + tuples[*num_copied_tuples].token = tokens[j].token; + tuples[*num_copied_tuples].value.s = elem->string; + } else { + struct snd_soc_tplg_vendor_value_elem *elem; + + elem = &array->value[i]; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + tuples[*num_copied_tuples].token = tokens[j].token; + tuples[*num_copied_tuples].value.v = + le32_to_cpu(elem->value); + } + found++; + (*num_copied_tuples)++; + + /* stop if there's no space for any more new tuples */ + if (*num_copied_tuples == tuples_size) + return 0; + } + } + + /* next array */ + array = (struct snd_soc_tplg_vendor_array *)((u8 *)array + asize); + } + + return 0; +} + /** * sof_parse_string_tokens - Parse multiple sets of tokens * @scomp: pointer to soc component @@ -1693,56 +1804,72 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, return 0; } -/* - * PCM Topology - */ - -static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - enum sof_ipc_stream_direction dir, - struct snd_soc_tplg_dapm_widget *tw) +static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + enum sof_tokens *object_token_list, int count) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_host *host; - size_t ipc_size = sizeof(*host); - int ret; + int num_tuples = 0; + size_t size; + int ret, i; - host = (struct sof_ipc_comp_host *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!host) - return -ENOMEM; + if (count > 0 && !object_token_list) { + dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name); + return -EINVAL; + } - /* configure host comp IPC message */ - host->comp.type = SOF_COMP_HOST; - host->direction = dir; - host->config.hdr.size = sizeof(host->config); + /* calculate max size of tuples array */ + for (i = 0; i < count; i++) + num_tuples += token_list[object_token_list[i]].count; - ret = sof_parse_tokens(scomp, host, pcm_tokens, - ARRAY_SIZE(pcm_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse host tokens failed %d\n", - private->size); - goto err; - } + /* allocate memory for tuples array */ + size = sizeof(struct snd_sof_tuple) * num_tuples; + swidget->tuples = kzalloc(size, GFP_KERNEL); + if (!swidget->tuples) + return -ENOMEM; - ret = sof_parse_tokens(scomp, &host->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse host.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } + /* parse token list for widget */ + for (i = 0; i < count; i++) { + if (object_token_list[i] >= SOF_TOKEN_COUNT) { + dev_err(scomp->dev, "Invalid token id %d for widget %s\n", + object_token_list[i], swidget->widget->name); + ret = -EINVAL; + goto err; + } - dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); - sof_dbg_comp_config(scomp, &host->config); + /* parse and save UUID in swidget */ + if (object_token_list[i] == SOF_COMP_EXT_TOKENS) { + ret = sof_parse_tokens(scomp, swidget, + token_list[object_token_list[i]].tokens, + token_list[object_token_list[i]].count, + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed parsing %s for widget %s\n", + token_list[object_token_list[i]].name, + swidget->widget->name); + goto err; + } - swidget->private = host; + continue; + } + + /* copy one set of tuples per token ID into swidget->tuples */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + object_token_list[i], 1, swidget->tuples, + num_tuples, &swidget->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "Failed parsing %s for widget %s err: %d\n", + token_list[object_token_list[i]].name, swidget->widget->name, ret); + goto err; + } + } return 0; err: - kfree(host); + kfree(swidget->tuples); return ret; } @@ -2367,8 +2494,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; struct snd_sof_widget *swidget; struct snd_sof_dai *dai; + enum sof_tokens *token_list; + int token_list_size; struct sof_ipc_comp comp = { .core = SOF_DSP_PRIMARY_CORE, }; @@ -2391,6 +2522,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? tw->sname : "none"); + token_list = widget_ops[w->id].token_list; + token_list_size = widget_ops[w->id].token_list_size; + ret = sof_parse_tokens(scomp, &comp, core_tokens, ARRAY_SIZE(core_tokens), tw->priv.array, le32_to_cpu(tw->priv.size)); @@ -2448,12 +2582,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ret = sof_widget_load_pipeline(scomp, index, swidget, tw); break; case snd_soc_dapm_aif_out: - ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_CAPTURE, tw); - break; case snd_soc_dapm_aif_in: - ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_PLAYBACK, tw); + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; case snd_soc_dapm_src: ret = sof_widget_load_src(scomp, index, swidget, tw); @@ -2497,6 +2627,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, if (ret) { dev_err(scomp->dev, "error: widget event binding failed\n"); kfree(swidget->private); + kfree(swidget->tuples); kfree(swidget); return ret; } @@ -2527,6 +2658,9 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -2588,9 +2722,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp, } out: + /* free IPC related data */ + if (widget_ops[swidget->id].ipc_free) + widget_ops[swidget->id].ipc_free(swidget); + /* free private value */ kfree(swidget->private); + kfree(swidget->tuples); + /* remove and free swidget object */ list_del(&swidget->list); kfree(swidget); @@ -3597,8 +3737,26 @@ static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget, *comp_swidget; + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; int ret; + /* + * now update all widget IPC structures. If any of the ipc_setup callbacks fail, the + * topology will be removed and all widgets will be unloaded resulting in freeing all + * associated memories. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (widget_ops[swidget->id].ipc_setup) { + ret = widget_ops[swidget->id].ipc_setup(swidget); + if (ret < 0) { + dev_err(sdev->dev, "failed updating IPC struct for %s\n", + swidget->widget->name); + return ret; + } + } + } + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ list_for_each_entry(swidget, &sdev->widget_list, list) { switch (swidget->id) { -- cgit v1.2.3 From 2141b55d9174c7aa5b32da95a2caa807a018968a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:07 -0700 Subject: ASoC: SOF: topology: Make scheduler widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the scheduler widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the pipeline component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 98 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 99 ------------------------------------------- 2 files changed, 98 insertions(+), 99 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 33d07b0df640..15299ffb7b15 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -13,6 +13,28 @@ #include "sof-audio.h" #include "ops.h" +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, period)}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, priority)}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, period_mips)}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, core)}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, + {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, time_domain)}, +}; + +static const struct sof_topology_token pipeline_tokens[] = { + {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, + +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -44,6 +66,8 @@ static const struct sof_topology_token comp_ext_tokens[] = { static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)}, + [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, + [SOF_SCHED_TOKENS] = {"Scheduler tokens", sched_tokens, ARRAY_SIZE(sched_tokens)}, [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)}, [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, @@ -148,6 +172,71 @@ static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) kfree(swidget->private); } +static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_pipe_new *pipeline; + struct snd_sof_widget *comp_swidget; + int ret; + + pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); + if (!pipeline) + return -ENOMEM; + + /* configure pipeline IPC message */ + pipeline->hdr.size = sizeof(*pipeline); + pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline->pipeline_id = swidget->pipeline_id; + pipeline->comp_id = swidget->comp_id; + + swidget->private = pipeline; + + /* component at start of pipeline is our stream id */ + comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname); + if (!comp_swidget) { + dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n", + swidget->widget->name, swidget->widget->sname); + ret = -EINVAL; + goto err; + } + + pipeline->sched_id = comp_swidget->comp_id; + + /* parse one set of scheduler tokens */ + ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*pipeline), 1); + if (ret < 0) + goto err; + + /* parse one set of pipeline tokens */ + ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*swidget), 1); + if (ret < 0) + goto err; + + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) + pipeline->core = SOF_DSP_PRIMARY_CORE; + + if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) + swidget->dynamic_pipeline_widget = + sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); + + dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", + swidget->widget->name, pipeline->period, pipeline->priority, + pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, + swidget->dynamic_pipeline_widget); + + swidget->core = pipeline->core; + + return 0; + +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -156,11 +245,20 @@ static enum sof_tokens host_token_list[] = { SOF_COMP_TOKENS, }; +static enum sof_tokens pipeline_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PIPELINE_TOKENS, + SOF_SCHED_TOKENS, +}; + static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, + [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, + pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, }; static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 2258317d7f57..4dd4e98e87cf 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -637,28 +637,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* scheduling */ -static const struct sof_topology_token sched_tokens[] = { - {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period)}, - {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, priority)}, - {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period_mips)}, - {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, core)}, - {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, - {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, time_domain)}, -}; - -static const struct sof_topology_token pipeline_tokens[] = { - {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, - offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, - -}; - /* volume */ static const struct sof_topology_token volume_tokens[] = { {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, @@ -1873,81 +1851,6 @@ err: return ret; } -/* - * Pipeline Topology - */ -static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_pipe_new *pipeline; - struct snd_sof_widget *comp_swidget; - int ret; - - pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); - if (!pipeline) - return -ENOMEM; - - /* configure dai IPC message */ - pipeline->hdr.size = sizeof(*pipeline); - pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; - pipeline->pipeline_id = index; - pipeline->comp_id = swidget->comp_id; - - /* component at start of pipeline is our stream id */ - comp_swidget = snd_sof_find_swidget(scomp, tw->sname); - if (!comp_swidget) { - dev_err(scomp->dev, "error: widget %s refers to non existent widget %s\n", - tw->name, tw->sname); - ret = -EINVAL; - goto err; - } - - pipeline->sched_id = comp_swidget->comp_id; - - dev_dbg(scomp->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", - pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id); - - ret = sof_parse_tokens(scomp, pipeline, sched_tokens, - ARRAY_SIZE(sched_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse pipeline tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, swidget, pipeline_tokens, - ARRAY_SIZE(pipeline_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n", - private->size); - goto err; - } - - if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) - pipeline->core = SOF_DSP_PRIMARY_CORE; - - if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) - swidget->dynamic_pipeline_widget = - sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); - - dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", - swidget->widget->name, pipeline->period, pipeline->priority, - pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, - swidget->dynamic_pipeline_widget); - - swidget->core = pipeline->core; - swidget->private = pipeline; - - return 0; -err: - kfree(pipeline); - return ret; -} - /* * Mixer topology */ @@ -2579,8 +2482,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ret = sof_widget_load_buffer(scomp, index, swidget, tw); break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, tw); - break; case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_in: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); -- cgit v1.2.3 From 6bd0be1c29dc0c9b69636f11f80071d46e1e2285 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:08 -0700 Subject: ASoC: SOF: topology: Make buffer widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the buffer widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the buffer component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 50 +++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 52 ------------------------------------------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 15299ffb7b15..e05d6b816fac 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -13,6 +13,14 @@ #include "sof-audio.h" #include "ops.h" +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + 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)}, +}; + /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -71,6 +79,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)}, [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, + [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, }; /** @@ -237,6 +246,41 @@ err: return ret; } +static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_buffer *buffer; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + swidget->private = buffer; + + /* configure dai IPC message */ + buffer->comp.hdr.size = sizeof(*buffer); + buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer->comp.id = swidget->comp_id; + buffer->comp.type = SOF_COMP_BUFFER; + buffer->comp.pipeline_id = swidget->pipeline_id; + buffer->comp.core = swidget->core; + + /* parse one set of buffer tokens */ + ret = sof_update_ipc_object(scomp, buffer, SOF_BUFFER_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*buffer), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", + swidget->widget->name, buffer->size, buffer->caps); + + return 0; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -245,6 +289,10 @@ static enum sof_tokens host_token_list[] = { SOF_COMP_TOKENS, }; +static enum sof_tokens buffer_token_list[] = { + SOF_BUFFER_TOKENS, +}; + static enum sof_tokens pipeline_token_list[] = { SOF_CORE_TOKENS, SOF_COMP_EXT_TOKENS, @@ -257,6 +305,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY host_token_list, ARRAY_SIZE(host_token_list), NULL}, [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, + [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp, + buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4dd4e98e87cf..88214ec2df5a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -611,14 +611,6 @@ static int get_token_process_type(void *elem, void *object, u32 offset) return 0; } -/* Buffers */ -static const struct sof_topology_token buffer_tokens[] = { - {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - 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)}, -}; - /* DAI */ static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, @@ -1721,48 +1713,6 @@ free: return ret; } -/* - * Buffer topology - */ - -static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_buffer *buffer; - int ret; - - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - /* configure dai IPC message */ - buffer->comp.hdr.size = sizeof(*buffer); - buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; - buffer->comp.id = swidget->comp_id; - buffer->comp.type = SOF_COMP_BUFFER; - buffer->comp.pipeline_id = index; - buffer->comp.core = swidget->core; - - ret = sof_parse_tokens(scomp, buffer, buffer_tokens, - ARRAY_SIZE(buffer_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse buffer tokens failed %d\n", - private->size); - kfree(buffer); - return ret; - } - - dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", - swidget->widget->name, buffer->size, buffer->caps); - - swidget->private = buffer; - - return 0; -} - /* bind PCM ID to host component ID */ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) @@ -2479,8 +2429,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ret = sof_widget_load_pga(scomp, index, swidget, tw); break; case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, tw); - break; case snd_soc_dapm_scheduler: case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_in: -- cgit v1.2.3 From 8a2e4a734f5ecbc48a3227c8ad68c12a71272c79 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:09 -0700 Subject: ASoC: SOF: topology: Make pga widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the pga type widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the pga component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 78 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 91 ++++--------------------------------------- 2 files changed, 86 insertions(+), 83 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index e05d6b816fac..517ba84eb4c4 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -43,6 +43,14 @@ static const struct sof_topology_token pipeline_tokens[] = { }; +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, ramp)}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, initial_ramp)}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -80,6 +88,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, + [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, }; /** @@ -281,6 +290,66 @@ static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) return 0; } +/* + * PGA Topology + */ + +static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_comp_volume *volume; + struct snd_sof_control *scontrol; + size_t ipc_size = sizeof(*volume); + int min_step, max_step; + int ret; + + volume = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!volume) + return -ENOMEM; + + swidget->private = volume; + + /* configure volume IPC message */ + volume->comp.type = SOF_COMP_VOLUME; + volume->config.hdr.size = sizeof(volume->config); + + /* parse one set of volume tokens */ + ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*volume), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(volume->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &volume->config); + + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id && + scontrol->volume_table) { + min_step = scontrol->min_volume_step; + max_step = scontrol->max_volume_step; + volume->min_value = scontrol->volume_table[min_step]; + volume->max_value = scontrol->volume_table[max_step]; + volume->channels = scontrol->num_channels; + break; + } + } + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -300,6 +369,13 @@ static enum sof_tokens pipeline_token_list[] = { SOF_SCHED_TOKENS, }; +static enum sof_tokens pga_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_VOLUME_TOKENS, + SOF_COMP_TOKENS, +}; + static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, @@ -309,6 +385,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, + [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, + pga_token_list, ARRAY_SIZE(pga_token_list), NULL}, }; static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 88214ec2df5a..bd62658629f5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -629,15 +629,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* volume */ -static const struct sof_topology_token volume_tokens[] = { - {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp)}, - {SOF_TKN_VOLUME_RAMP_STEP_MS, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_volume, initial_ramp)}, -}; - /* SRC */ static const struct sof_topology_token src_tokens[] = { {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -1878,78 +1869,6 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, return 0; } -/* - * PGA Topology - */ - -static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_volume *volume; - struct snd_sof_control *scontrol; - size_t ipc_size = sizeof(*volume); - int min_step; - int max_step; - int ret; - - volume = (struct sof_ipc_comp_volume *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!volume) - return -ENOMEM; - - if (!le32_to_cpu(tw->num_kcontrols)) { - dev_err(scomp->dev, "error: invalid kcontrol count %d for volume\n", - tw->num_kcontrols); - ret = -EINVAL; - goto err; - } - - /* configure volume IPC message */ - volume->comp.type = SOF_COMP_VOLUME; - volume->config.hdr.size = sizeof(volume->config); - - ret = sof_parse_tokens(scomp, volume, volume_tokens, - ARRAY_SIZE(volume_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse volume tokens failed %d\n", - private->size); - goto err; - } - ret = sof_parse_tokens(scomp, &volume->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse volume.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - sof_dbg_comp_config(scomp, &volume->config); - - swidget->private = volume; - - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - if (scontrol->comp_id == swidget->comp_id && - scontrol->volume_table) { - min_step = scontrol->min_volume_step; - max_step = scontrol->max_volume_step; - volume->min_value = scontrol->volume_table[min_step]; - volume->max_value = scontrol->volume_table[max_step]; - volume->channels = scontrol->num_channels; - break; - } - } - - return 0; -err: - kfree(volume); - return ret; -} - /* * SRC Topology */ @@ -2426,8 +2345,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ret = sof_widget_load_mixer(scomp, index, swidget, tw); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, tw); - break; + if (!le32_to_cpu(tw->num_kcontrols)) { + dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", + tw->num_kcontrols); + ret = -EINVAL; + break; + } + + fallthrough; case snd_soc_dapm_buffer: case snd_soc_dapm_scheduler: case snd_soc_dapm_aif_out: -- cgit v1.2.3 From 30f4168024e91e85aff9edcef561715ed271b34c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:10 -0700 Subject: ASoC: SOF: topology: Make mixer widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the mixer widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the mixer component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-10-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 43 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 43 +------------------------------------------ 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 517ba84eb4c4..d8a91f461bf9 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -190,6 +190,40 @@ static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) kfree(swidget->private); } +static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_mixer *mixer; + size_t ipc_size = sizeof(*mixer); + int ret; + + mixer = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!mixer) + return -ENOMEM; + + swidget->private = mixer; + + /* configure mixer IPC message */ + mixer->comp.type = SOF_COMP_MIXER; + mixer->config.hdr.size = sizeof(mixer->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &mixer->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(mixer->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + + return ret; + } + + dev_dbg(scomp->dev, "loaded mixer %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &mixer->config); + + return 0; +} + static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; @@ -358,6 +392,12 @@ static enum sof_tokens host_token_list[] = { SOF_COMP_TOKENS, }; +static enum sof_tokens comp_generic_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_COMP_TOKENS, +}; + static enum sof_tokens buffer_token_list[] = { SOF_BUFFER_TOKENS, }; @@ -383,6 +423,9 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY host_token_list, ARRAY_SIZE(host_token_list), NULL}, [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp, buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, + [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index bd62658629f5..9e9a41018458 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1792,45 +1792,6 @@ err: return ret; } -/* - * Mixer topology - */ - -static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_mixer *mixer; - size_t ipc_size = sizeof(*mixer); - int ret; - - mixer = (struct sof_ipc_comp_mixer *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!mixer) - return -ENOMEM; - - /* configure mixer IPC message */ - mixer->comp.type = SOF_COMP_MIXER; - mixer->config.hdr.size = sizeof(mixer->config); - - ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse mixer.cfg tokens failed %d\n", - private->size); - kfree(mixer); - return ret; - } - - sof_dbg_comp_config(scomp, &mixer->config); - - swidget->private = mixer; - - return 0; -} - /* * Mux topology */ @@ -2341,9 +2302,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, list_add(&dai->list, &sdev->dai_list); swidget->private = dai; break; - case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, tw); - break; case snd_soc_dapm_pga: if (!le32_to_cpu(tw->num_kcontrols)) { dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", @@ -2353,6 +2311,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } fallthrough; + case snd_soc_dapm_mixer: case snd_soc_dapm_buffer: case snd_soc_dapm_scheduler: case snd_soc_dapm_aif_out: -- cgit v1.2.3 From 683b54ef603825328859d867aabf9a6d973238a2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:11 -0700 Subject: ASoC: SOF: topology: Make mux/demux widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the mux/demux widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the mux/demux component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-11-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 40 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 44 ++----------------------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index d8a91f461bf9..5501a18babaf 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -324,6 +324,41 @@ static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) return 0; } +/* + * Mux topology + */ +static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_mux *mux; + size_t ipc_size = sizeof(*mux); + int ret; + + mux = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!mux) + return -ENOMEM; + + swidget->private = mux; + + /* configure mux IPC message */ + mux->comp.type = SOF_COMP_MUX; + mux->config.hdr.size = sizeof(mux->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &mux->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(mux->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "loaded mux %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &mux->config); + + return 0; +} + /* * PGA Topology */ @@ -430,6 +465,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, pga_token_list, ARRAY_SIZE(pga_token_list), NULL}, + [snd_soc_dapm_mux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, + [snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, }; static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9e9a41018458..42da7b8e7180 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1792,44 +1792,6 @@ err: return ret; } -/* - * Mux topology - */ -static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_mux *mux; - size_t ipc_size = sizeof(*mux); - int ret; - - mux = (struct sof_ipc_comp_mux *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!mux) - return -ENOMEM; - - /* configure mux IPC message */ - mux->comp.type = SOF_COMP_MUX; - mux->config.hdr.size = sizeof(mux->config); - - ret = sof_parse_tokens(scomp, &mux->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse mux.cfg tokens failed %d\n", - private->size); - kfree(mux); - return ret; - } - - sof_dbg_comp_config(scomp, &mux->config); - - swidget->private = mux; - - return 0; -} - /* * SRC Topology */ @@ -2316,6 +2278,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_scheduler: case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_in: + case snd_soc_dapm_mux: + case snd_soc_dapm_demux: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; case snd_soc_dapm_src: @@ -2330,10 +2294,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_effect: ret = sof_widget_load_process(scomp, index, swidget, tw); break; - case snd_soc_dapm_mux: - case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, tw); - break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: case snd_soc_dapm_kcontrol: -- cgit v1.2.3 From 8d8b1293473022953ed316c3e0c723ff5a1505ac Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:12 -0700 Subject: ASoC: SOF: topology: Make src widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the src widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the src component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-12-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 59 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 64 +------------------------------------------ 2 files changed, 60 insertions(+), 63 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 5501a18babaf..44ba3190e570 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -51,6 +51,14 @@ static const struct sof_topology_token volume_tokens[] = { offsetof(struct sof_ipc_comp_volume, initial_ramp)}, }; +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, source_rate)}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, sink_rate)}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -89,6 +97,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, + [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, }; /** @@ -324,6 +333,47 @@ static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) return 0; } +static int sof_ipc3_widget_setup_comp_src(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_src *src; + size_t ipc_size = sizeof(*src); + int ret; + + src = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!src) + return -ENOMEM; + + swidget->private = src; + + /* configure src IPC message */ + src->comp.type = SOF_COMP_SRC; + src->config.hdr.size = sizeof(src->config); + + /* parse one set of src tokens */ + ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*src), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(src->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", + swidget->widget->name, src->source_rate, src->sink_rate); + sof_dbg_comp_config(scomp, &src->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + /* * Mux topology */ @@ -444,6 +494,13 @@ static enum sof_tokens pipeline_token_list[] = { SOF_SCHED_TOKENS, }; +static enum sof_tokens src_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_SRC_TOKENS, + SOF_COMP_TOKENS +}; + static enum sof_tokens pga_token_list[] = { SOF_CORE_TOKENS, SOF_COMP_EXT_TOKENS, @@ -461,6 +518,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp, comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, + [snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp, + src_token_list, ARRAY_SIZE(src_token_list), NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 42da7b8e7180..f41bf7dfbd02 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -629,14 +629,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* SRC */ -static const struct sof_topology_token src_tokens[] = { - {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, source_rate)}, - {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, sink_rate)}, -}; - /* ASRC */ static const struct sof_topology_token asrc_tokens[] = { {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -1792,58 +1784,6 @@ err: return ret; } -/* - * SRC Topology - */ - -static int sof_widget_load_src(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_src *src; - size_t ipc_size = sizeof(*src); - int ret; - - src = (struct sof_ipc_comp_src *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!src) - return -ENOMEM; - - /* configure src IPC message */ - src->comp.type = SOF_COMP_SRC; - src->config.hdr.size = sizeof(src->config); - - ret = sof_parse_tokens(scomp, src, src_tokens, - ARRAY_SIZE(src_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse src tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, &src->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse src.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", - swidget->widget->name, src->source_rate, src->sink_rate); - sof_dbg_comp_config(scomp, &src->config); - - swidget->private = src; - - return 0; -err: - kfree(src); - return ret; -} - /* * ASRC Topology */ @@ -2278,13 +2218,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_scheduler: case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_in: + case snd_soc_dapm_src: case snd_soc_dapm_mux: case snd_soc_dapm_demux: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; - case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, tw); - break; case snd_soc_dapm_asrc: ret = sof_widget_load_asrc(scomp, index, swidget, tw); break; -- cgit v1.2.3 From cb7ed49acf585d45c1140660d076b389de7d468e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:13 -0700 Subject: ASoC: SOF: topology: Make asrc widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the asrc widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the asrc component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-13-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 65 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 72 +------------------------------------------ 2 files changed, 66 insertions(+), 71 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 44ba3190e570..7dc6530efdba 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -59,6 +59,18 @@ static const struct sof_topology_token src_tokens[] = { offsetof(struct sof_ipc_comp_src, sink_rate)}, }; +/* ASRC */ +static const struct sof_topology_token asrc_tokens[] = { + {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, source_rate)}, + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, sink_rate)}, + {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, operation_mode)}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -98,6 +110,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, + [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, }; /** @@ -374,6 +387,49 @@ err: return ret; } +static int sof_ipc3_widget_setup_comp_asrc(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_asrc *asrc; + size_t ipc_size = sizeof(*asrc); + int ret; + + asrc = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!asrc) + return -ENOMEM; + + swidget->private = asrc; + + /* configure ASRC IPC message */ + asrc->comp.type = SOF_COMP_ASRC; + asrc->config.hdr.size = sizeof(asrc->config); + + /* parse one set of asrc tokens */ + ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*asrc), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d asynch %d operation %d\n", + swidget->widget->name, asrc->source_rate, asrc->sink_rate, + asrc->asynchronous_mode, asrc->operation_mode); + + sof_dbg_comp_config(scomp, &asrc->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + /* * Mux topology */ @@ -494,6 +550,13 @@ static enum sof_tokens pipeline_token_list[] = { SOF_SCHED_TOKENS, }; +static enum sof_tokens asrc_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_ASRC_TOKENS, + SOF_COMP_TOKENS, +}; + static enum sof_tokens src_token_list[] = { SOF_CORE_TOKENS, SOF_COMP_EXT_TOKENS, @@ -520,6 +583,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY NULL}, [snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp, src_token_list, ARRAY_SIZE(src_token_list), NULL}, + [snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp, + asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f41bf7dfbd02..94449ed370af 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -629,20 +629,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* ASRC */ -static const struct sof_topology_token asrc_tokens[] = { - {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, source_rate)}, - {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, sink_rate)}, - {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, - {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_comp_asrc, operation_mode)}, -}; - /* Tone */ static const struct sof_topology_token tone_tokens[] = { }; @@ -1784,60 +1770,6 @@ err: return ret; } -/* - * ASRC Topology - */ - -static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_asrc *asrc; - size_t ipc_size = sizeof(*asrc); - int ret; - - asrc = (struct sof_ipc_comp_asrc *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!asrc) - return -ENOMEM; - - /* configure ASRC IPC message */ - asrc->comp.type = SOF_COMP_ASRC; - asrc->config.hdr.size = sizeof(asrc->config); - - ret = sof_parse_tokens(scomp, asrc, asrc_tokens, - ARRAY_SIZE(asrc_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse asrc tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, &asrc->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse asrc.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d " - "asynch %d operation %d\n", - swidget->widget->name, asrc->source_rate, asrc->sink_rate, - asrc->asynchronous_mode, asrc->operation_mode); - sof_dbg_comp_config(scomp, &asrc->config); - - swidget->private = asrc; - - return 0; -err: - kfree(asrc); - return ret; -} - /* * Signal Generator Topology */ @@ -2219,13 +2151,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_in: case snd_soc_dapm_src: + case snd_soc_dapm_asrc: case snd_soc_dapm_mux: case snd_soc_dapm_demux: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; - case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, tw); - break; case snd_soc_dapm_siggen: ret = sof_widget_load_siggen(scomp, index, swidget, tw); break; -- cgit v1.2.3 From 111d66f62e9bf49ff33631c4e81efdcc4a54b88f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:14 -0700 Subject: ASoC: SOF: topology: Make siggen widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the siggen widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the tone component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-14-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 36 ++++++++++++++++++++++++++ sound/soc/sof/topology.c | 60 +------------------------------------------ 2 files changed, 37 insertions(+), 59 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 7dc6530efdba..6fef96fee5f2 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -212,6 +212,39 @@ static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) kfree(swidget->private); } +static int sof_ipc3_widget_setup_comp_tone(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_tone *tone; + size_t ipc_size = sizeof(*tone); + int ret; + + tone = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!tone) + return -ENOMEM; + + swidget->private = tone; + + /* configure siggen IPC message */ + tone->comp.type = SOF_COMP_TONE; + tone->config.hdr.size = sizeof(tone->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &tone->config, SOF_COMP_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(tone->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", + swidget->widget->name, tone->frequency, tone->amplitude); + sof_dbg_comp_config(scomp, &tone->config); + + return 0; +} + static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; @@ -585,6 +618,9 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY src_token_list, ARRAY_SIZE(src_token_list), NULL}, [snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp, asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL}, + [snd_soc_dapm_siggen] = {sof_ipc3_widget_setup_comp_tone, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 94449ed370af..f5b25ffee5b7 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -629,10 +629,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* Tone */ -static const struct sof_topology_token tone_tokens[] = { -}; - /* EFFECT */ static const struct sof_topology_token process_tokens[] = { {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, @@ -1770,58 +1766,6 @@ err: return ret; } -/* - * Signal Generator Topology - */ - -static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_tone *tone; - size_t ipc_size = sizeof(*tone); - int ret; - - tone = (struct sof_ipc_comp_tone *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!tone) - return -ENOMEM; - - /* configure siggen IPC message */ - tone->comp.type = SOF_COMP_TONE; - tone->config.hdr.size = sizeof(tone->config); - - ret = sof_parse_tokens(scomp, tone, tone_tokens, - ARRAY_SIZE(tone_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse tone tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - ret = sof_parse_tokens(scomp, &tone->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse tone.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", - swidget->widget->name, tone->frequency, tone->amplitude); - sof_dbg_comp_config(scomp, &tone->config); - - swidget->private = tone; - - return 0; -err: - kfree(tone); - return ret; -} - static int sof_get_control_data(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *widget, struct sof_widget_data *wdata, @@ -2152,13 +2096,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_aif_in: case snd_soc_dapm_src: case snd_soc_dapm_asrc: + case snd_soc_dapm_siggen: case snd_soc_dapm_mux: case snd_soc_dapm_demux: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; - case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, tw); - break; case snd_soc_dapm_effect: ret = sof_widget_load_process(scomp, index, swidget, tw); break; -- cgit v1.2.3 From f2cf24a1afa836be44dd2abdf7896b236df8d9a2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:15 -0700 Subject: ASoC: SOF: topology: Make effect widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to effect type widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the process component based on the topology widget_setup op in ipc3_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-15-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 248 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 277 ++---------------------------------------- 2 files changed, 257 insertions(+), 268 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 6fef96fee5f2..b710129374c8 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -13,6 +13,51 @@ #include "sof-audio.h" #include "ops.h" +struct sof_widget_data { + int ctrl_type; + int ipc_cmd; + struct sof_abi_hdr *pdata; + struct snd_sof_control *control; +}; + +struct sof_process_types { + const char *name; + enum sof_ipc_process_type type; + enum sof_comp_type comp_type; +}; + +static const struct sof_process_types sof_process[] = { + {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, + {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, + {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, + {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, + {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, + {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, + {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, + {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, + {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP}, +}; + +static enum sof_ipc_process_type find_process(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (strcmp(name, sof_process[i].name) == 0) + return sof_process[i].type; + } + + return SOF_PROCESS_NONE; +} + +static int get_token_process_type(void *elem, void *object, u32 offset) +{ + u32 *val = (u32 *)((u8 *)object + offset); + + *val = find_process((const char *)elem); + return 0; +} + /* Buffers */ static const struct sof_topology_token buffer_tokens[] = { {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -71,6 +116,12 @@ static const struct sof_topology_token asrc_tokens[] = { offsetof(struct sof_ipc_comp_asrc, operation_mode)}, }; +/* EFFECT */ +static const struct sof_topology_token process_tokens[] = { + {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type, + offsetof(struct sof_ipc_comp_process, type)}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -111,6 +162,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, + [SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)}, }; /** @@ -558,6 +610,193 @@ err: return ret; } +static int sof_get_control_data(struct snd_soc_component *scomp, + struct snd_soc_dapm_widget *widget, + struct sof_widget_data *wdata, size_t *size) +{ + const struct snd_kcontrol_new *kc; + struct soc_mixer_control *sm; + struct soc_bytes_ext *sbe; + struct soc_enum *se; + int i; + + *size = 0; + + for (i = 0; i < widget->num_kcontrols; i++) { + kc = &widget->kcontrol_news[i]; + + switch (widget->dobj.widget.kcontrol_type[i]) { + case SND_SOC_TPLG_TYPE_MIXER: + sm = (struct soc_mixer_control *)kc->private_value; + wdata[i].control = sm->dobj.private; + break; + case SND_SOC_TPLG_TYPE_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + wdata[i].control = sbe->dobj.private; + break; + case SND_SOC_TPLG_TYPE_ENUM: + se = (struct soc_enum *)kc->private_value; + wdata[i].control = se->dobj.private; + break; + default: + dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n", + widget->dobj.widget.kcontrol_type[i], widget->name); + return -EINVAL; + } + + if (!wdata[i].control) { + dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name); + return -EINVAL; + } + + wdata[i].pdata = wdata[i].control->control_data->data; + if (!wdata[i].pdata) + return -EINVAL; + + /* make sure data is valid - data can be updated at runtime */ + if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES && + wdata[i].pdata->magic != SOF_ABI_MAGIC) + return -EINVAL; + + *size += wdata[i].pdata->size; + + /* get data type */ + switch (wdata[i].control->control_data->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_ENUM: + case SOF_CTRL_CMD_SWITCH: + wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE; + wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + break; + case SOF_CTRL_CMD_BINARY: + wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA; + wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; + break; + default: + break; + } + } + + return 0; +} + +static int sof_process_load(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget, int type) +{ + struct snd_soc_dapm_widget *widget = swidget->widget; + struct sof_ipc_comp_process *process; + struct sof_widget_data *wdata = NULL; + size_t ipc_data_size = 0; + size_t ipc_size; + int offset = 0; + int ret; + int i; + + /* allocate struct for widget control data sizes and types */ + if (widget->num_kcontrols) { + wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL); + if (!wdata) + return -ENOMEM; + + /* get possible component controls and get size of all pdata */ + ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size); + if (ret < 0) + goto out; + } + + ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; + + /* we are exceeding max ipc size, config needs to be sent separately */ + if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { + ipc_size -= ipc_data_size; + ipc_data_size = 0; + } + + process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!process) { + ret = -ENOMEM; + goto out; + } + + swidget->private = process; + + /* configure iir IPC message */ + process->comp.type = type; + process->config.hdr.size = sizeof(process->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(process->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &process->config); + + /* + * found private data in control, so copy it. + * get possible component controls - get size of all pdata, + * then memcpy with headers + */ + if (ipc_data_size) { + for (i = 0; i < widget->num_kcontrols; i++) { + memcpy(&process->data[offset], + wdata[i].pdata->data, + wdata[i].pdata->size); + offset += wdata[i].pdata->size; + } + } + + process->size = ipc_data_size; + + kfree(wdata); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; +out: + kfree(wdata); + return ret; +} + +static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (sof_process[i].type == type) + return sof_process[i].comp_type; + } + + return SOF_COMP_NONE; +} + +/* + * Processing Component Topology - can be "effect", "codec", or general + * "processing". + */ + +static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_process config; + int ret; + + memset(&config, 0, sizeof(config)); + config.comp.core = swidget->core; + + /* parse one set of process tokens */ + ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(config), 1); + if (ret < 0) + return ret; + + /* now load process specific data and send IPC */ + return sof_process_load(scomp, swidget, find_process_comp_type(config.type)); +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -604,6 +843,13 @@ static enum sof_tokens pga_token_list[] = { SOF_COMP_TOKENS, }; +static enum sof_tokens process_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PROCESS_TOKENS, + SOF_COMP_TOKENS, +}; + static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, @@ -630,6 +876,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY [snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, + [snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp, + process_token_list, ARRAY_SIZE(process_token_list), NULL}, }; static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f5b25ffee5b7..187f7c46a42b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -141,13 +141,6 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so return 0; } -struct sof_widget_data { - int ctrl_type; - int ipc_cmd; - struct sof_abi_hdr *pdata; - struct snd_sof_control *control; -}; - /* send pcm params ipc */ static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) { @@ -517,48 +510,6 @@ static enum sof_ipc_frame find_format(const char *name) return SOF_IPC_FRAME_S32_LE; } -struct sof_process_types { - const char *name; - enum sof_ipc_process_type type; - enum sof_comp_type comp_type; -}; - -static const struct sof_process_types sof_process[] = { - {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, - {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, - {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, - {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, - {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, - {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, - {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, - {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, - {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP}, -}; - -static enum sof_ipc_process_type find_process(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sof_process); i++) { - if (strcmp(name, sof_process[i].name) == 0) - return sof_process[i].type; - } - - return SOF_PROCESS_NONE; -} - -static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sof_process); i++) { - if (sof_process[i].type == type) - return sof_process[i].comp_type; - } - - return SOF_COMP_NONE; -} - int get_token_u32(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; @@ -603,14 +554,6 @@ int get_token_dai_type(void *elem, void *object, u32 offset) return 0; } -static int get_token_process_type(void *elem, void *object, u32 offset) -{ - u32 *val = (u32 *)((u8 *)object + offset); - - *val = find_process((const char *)elem); - return 0; -} - /* DAI */ static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, @@ -629,13 +572,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, dai_index)}, }; -/* EFFECT */ -static const struct sof_topology_token process_tokens[] = { - {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, - get_token_process_type, - offsetof(struct sof_ipc_comp_process, type)}, -}; - /* PCM */ static const struct sof_topology_token stream_tokens[] = { {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, @@ -1766,207 +1702,6 @@ err: return ret; } -static int sof_get_control_data(struct snd_soc_component *scomp, - struct snd_soc_dapm_widget *widget, - struct sof_widget_data *wdata, - size_t *size) -{ - const struct snd_kcontrol_new *kc; - struct soc_mixer_control *sm; - struct soc_bytes_ext *sbe; - struct soc_enum *se; - int i; - - *size = 0; - - for (i = 0; i < widget->num_kcontrols; i++) { - kc = &widget->kcontrol_news[i]; - - switch (widget->dobj.widget.kcontrol_type[i]) { - case SND_SOC_TPLG_TYPE_MIXER: - sm = (struct soc_mixer_control *)kc->private_value; - wdata[i].control = sm->dobj.private; - break; - case SND_SOC_TPLG_TYPE_BYTES: - sbe = (struct soc_bytes_ext *)kc->private_value; - wdata[i].control = sbe->dobj.private; - break; - case SND_SOC_TPLG_TYPE_ENUM: - se = (struct soc_enum *)kc->private_value; - wdata[i].control = se->dobj.private; - break; - default: - dev_err(scomp->dev, "error: unknown kcontrol type %u in widget %s\n", - widget->dobj.widget.kcontrol_type[i], - widget->name); - return -EINVAL; - } - - if (!wdata[i].control) { - dev_err(scomp->dev, "error: no scontrol for widget %s\n", - widget->name); - return -EINVAL; - } - - wdata[i].pdata = wdata[i].control->control_data->data; - if (!wdata[i].pdata) - return -EINVAL; - - /* make sure data is valid - data can be updated at runtime */ - if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES && - wdata[i].pdata->magic != SOF_ABI_MAGIC) - return -EINVAL; - - *size += wdata[i].pdata->size; - - /* get data type */ - switch (wdata[i].control->control_data->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE; - wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - break; - case SOF_CTRL_CMD_BINARY: - wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA; - wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; - break; - default: - break; - } - } - - return 0; -} - -static int sof_process_load(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - int type) -{ - struct snd_soc_dapm_widget *widget = swidget->widget; - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_process *process; - struct sof_widget_data *wdata = NULL; - size_t ipc_data_size = 0; - size_t ipc_size; - int offset = 0; - int ret; - int i; - - /* allocate struct for widget control data sizes and types */ - if (widget->num_kcontrols) { - wdata = kcalloc(widget->num_kcontrols, - sizeof(*wdata), - GFP_KERNEL); - - if (!wdata) - return -ENOMEM; - - /* get possible component controls and get size of all pdata */ - ret = sof_get_control_data(scomp, widget, wdata, - &ipc_data_size); - - if (ret < 0) - goto out; - } - - ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; - - /* we are exceeding max ipc size, config needs to be sent separately */ - if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { - ipc_size -= ipc_data_size; - ipc_data_size = 0; - } - - process = (struct sof_ipc_comp_process *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!process) { - ret = -ENOMEM; - goto out; - } - - /* configure iir IPC message */ - process->comp.type = type; - process->config.hdr.size = sizeof(process->config); - - ret = sof_parse_tokens(scomp, &process->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse process.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - sof_dbg_comp_config(scomp, &process->config); - - /* - * found private data in control, so copy it. - * get possible component controls - get size of all pdata, - * then memcpy with headers - */ - if (ipc_data_size) { - for (i = 0; i < widget->num_kcontrols; i++) { - memcpy(&process->data[offset], - wdata[i].pdata->data, - wdata[i].pdata->size); - offset += wdata[i].pdata->size; - } - } - - process->size = ipc_data_size; - swidget->private = process; -err: - if (ret < 0) - kfree(process); -out: - kfree(wdata); - return ret; -} - -/* - * Processing Component Topology - can be "effect", "codec", or general - * "processing". - */ - -static int sof_widget_load_process(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_process config; - int ret; - - /* check we have some tokens - we need at least process type */ - if (le32_to_cpu(private->size) == 0) { - dev_err(scomp->dev, "error: process tokens not found\n"); - return -EINVAL; - } - - memset(&config, 0, sizeof(config)); - config.comp.core = swidget->core; - - /* get the process token */ - ret = sof_parse_tokens(scomp, &config, process_tokens, - ARRAY_SIZE(process_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse process tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* now load process specific data and send IPC */ - ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type)); - if (ret < 0) { - dev_err(scomp->dev, "error: process loading failed\n"); - return ret; - } - - return 0; -} - static int sof_widget_bind_event(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, u16 event_type) @@ -2080,6 +1815,15 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, list_add(&dai->list, &sdev->dai_list); swidget->private = dai; break; + case snd_soc_dapm_effect: + /* check we have some tokens - we need at least process type */ + if (le32_to_cpu(tw->priv.size) == 0) { + dev_err(scomp->dev, "error: process tokens not found\n"); + ret = -EINVAL; + break; + } + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); + break; case snd_soc_dapm_pga: if (!le32_to_cpu(tw->num_kcontrols)) { dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", @@ -2101,9 +1845,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_demux: ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; - case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, tw); - break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: case snd_soc_dapm_kcontrol: -- cgit v1.2.3 From 85ec8560893cb9aae2728dd12f59537b6247d91f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:16 -0700 Subject: ASoC: SOF: topology: Make route setup IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define and set the route_setup op for IPC3 topology ops and use it for setting up routes. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-16-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 26 +++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 48 +++++++++++++------------------------------ 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index b710129374c8..a9d55c1d1f84 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -797,6 +797,31 @@ static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget) return sof_process_load(scomp, swidget, find_process_comp_type(config.type)); } +static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) +{ + struct sof_ipc_pipe_comp_connect connect; + struct sof_ipc_reply reply; + int ret; + + connect.hdr.size = sizeof(connect); + connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + connect.source_id = sroute->src_widget->comp_id; + connect.sink_id = sroute->sink_widget->comp_id; + + dev_dbg(sdev->dev, "setting up route %s -> %s\n", + sroute->src_widget->widget->name, + sroute->sink_widget->widget->name); + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, + sroute->src_widget->widget->name, sroute->sink_widget->widget->name); + + return ret; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -882,6 +907,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, + .route_setup = sof_ipc3_route_setup, .token_list = ipc3_token_list, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 15c36a51f89f..c02dcad03b23 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -263,45 +263,15 @@ use_count_dec: } EXPORT_SYMBOL(sof_widget_setup); -static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) -{ - struct sof_ipc_pipe_comp_connect connect; - struct sof_ipc_reply reply; - int ret; - - /* nothing to do if route is already set up */ - if (sroute->setup) - return 0; - - connect.hdr.size = sizeof(connect); - connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; - connect.source_id = sroute->src_widget->comp_id; - connect.sink_id = sroute->sink_widget->comp_id; - - dev_dbg(sdev->dev, "setting up route %s -> %s\n", - sroute->src_widget->widget->name, - sroute->sink_widget->widget->name); - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), - &reply, sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret); - return ret; - } - - sroute->setup = true; - - return 0; -} - static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_widget *src_widget = wsource->dobj.private; struct snd_sof_widget *sink_widget = wsink->dobj.private; struct snd_sof_route *sroute; bool route_found = false; + int ret; /* ignore routes involving virtual widgets in topology */ switch (src_widget->id) { @@ -335,7 +305,16 @@ static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget return -EINVAL; } - return sof_route_setup_ipc(sdev, sroute); + /* nothing to do if route is already set up */ + if (sroute->setup) + return 0; + + ret = ipc_tplg_ops->route_setup(sdev, sroute); + if (ret < 0) + return ret; + + sroute->setup = true; + return 0; } static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, @@ -604,6 +583,7 @@ int sof_set_hw_params_upon_resume(struct device *dev) int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; @@ -656,7 +636,7 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) sroute->sink_widget->dynamic_pipeline_widget)) continue; - ret = sof_route_setup_ipc(sdev, sroute); + ret = ipc_tplg_ops->route_setup(sdev, sroute); if (ret < 0) { dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); return ret; -- cgit v1.2.3 From 909dadf21aae8f7e604973218907ed39e10499e6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:17 -0700 Subject: ASoC: SOF: topology: Make DAI widget parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the list of tokens pertaining to the dai_in/out widgets, parse and save them as part of the swidget tuples array. Once topology parsing is complete, these tokens will be applied to create the IPC structure for the DAI component based on the topology widget_setup op in ipc3_tplg_ops. DAI link parsing is also made IPC agnostic by parsing the list of tokens associated with all DAI types. The config will be applied to the respective DAI widgets during topology complete. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-17-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 745 +++++++++++++++++++++++++- sound/soc/sof/topology.c | 1157 ++++++----------------------------------- 2 files changed, 915 insertions(+), 987 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index a9d55c1d1f84..e6aa78d492ea 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -8,7 +8,6 @@ // #include -#include #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" @@ -66,6 +65,24 @@ static const struct sof_topology_token buffer_tokens[] = { offsetof(struct sof_ipc_buffer, caps)}, }; +/* DAI */ +static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_comp_dai, type)}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dai_index)}, + {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, direction)}, +}; + +/* BE DAI link */ +static const struct sof_topology_token dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_dai_config, type)}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_config, dai_index)}, +}; + /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -139,6 +156,107 @@ static const struct sof_topology_token comp_tokens[] = { offsetof(struct sof_ipc_comp_config, frame_fmt)}, }; +/* SSP */ +static const struct sof_topology_token ssp_tokens[] = { + {SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, + {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, + {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, + {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, quirks)}, + {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, tdm_per_slot_padding_flag)}, + {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, +}; + +/* ALH */ +static const struct sof_topology_token alh_tokens[] = { + {SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_alh_params, rate)}, + {SOF_TKN_INTEL_ALH_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_alh_params, channels)}, +}; + +/* DMIC */ +static const struct sof_topology_token dmic_tokens[] = { + {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, + {SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, + {SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, + {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, + {SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, + {SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, + {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, num_pdm_active)}, + {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, + {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, +}; + +/* ESAI */ +static const struct sof_topology_token esai_tokens[] = { + {SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, +}; + +/* SAI */ +static const struct sof_topology_token sai_tokens[] = { + {SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, +}; + +/* + * DMIC PDM Tokens + * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token + * as it increments the index while parsing the array of pdm tokens + * and determines the correct offset + */ +static const struct sof_topology_token dmic_pdm_tokens[] = { + {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id)}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, + {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, + {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, +}; + +/* HDA */ +static const struct sof_topology_token hda_tokens[] = { + {SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_hda_params, rate)}, + {SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_hda_params, channels)}, +}; + +/* AFE */ +static const struct sof_topology_token afe_tokens[] = { + {SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, + {SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, + {SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, +}; + /* Core tokens */ static const struct sof_topology_token core_tokens[] = { {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -163,6 +281,16 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, [SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)}, + [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, + [SOF_DAI_LINK_TOKENS] = {"DAI link tokens", dai_link_tokens, ARRAY_SIZE(dai_link_tokens)}, + [SOF_HDA_TOKENS] = {"HDA tokens", hda_tokens, ARRAY_SIZE(hda_tokens)}, + [SOF_SSP_TOKENS] = {"SSP tokens", ssp_tokens, ARRAY_SIZE(ssp_tokens)}, + [SOF_ALH_TOKENS] = {"ALH tokens", alh_tokens, ARRAY_SIZE(alh_tokens)}, + [SOF_DMIC_TOKENS] = {"DMIC tokens", dmic_tokens, ARRAY_SIZE(dmic_tokens)}, + [SOF_DMIC_PDM_TOKENS] = {"DMIC PDM tokens", dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)}, + [SOF_ESAI_TOKENS] = {"ESAI tokens", esai_tokens, ARRAY_SIZE(esai_tokens)}, + [SOF_SAI_TOKENS] = {"SAI tokens", sai_tokens, ARRAY_SIZE(sai_tokens)}, + [SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)}, }; /** @@ -797,6 +925,609 @@ static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget) return sof_process_load(scomp, swidget, find_process_comp_type(config.type)); } +static int sof_link_hda_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->hda, 0, sizeof(config->hda)); + config->hdr.size = size; + + /* parse one set of HDA tokens */ + ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", + config->hda.rate, config->hda.channels); + + config->hda.link_dma_ch = DMA_CHAN_INVALID; + + 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 void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + /* clock directions wrt codec */ + config->format &= ~SOF_DAI_FMT_CLOCK_PROVIDER_MASK; + if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) { + /* codec is bclk provider */ + if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) + config->format |= SOF_DAI_FMT_CBP_CFP; + else + config->format |= SOF_DAI_FMT_CBP_CFC; + } else { + /* codec is bclk consumer */ + if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) + config->format |= SOF_DAI_FMT_CBC_CFP; + else + config->format |= SOF_DAI_FMT_CBC_CFC; + } + + /* inverted clocks ? */ + config->format &= ~SOF_DAI_FMT_INV_MASK; + if (hw_config->invert_bclk) { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_IB_IF; + else + config->format |= SOF_DAI_FMT_IB_NF; + } else { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_NB_IF; + else + config->format |= SOF_DAI_FMT_NB_NF; + } +} + +static int sof_link_sai_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); + + /* init IPC */ + memset(&config->sai, 0, sizeof(config->sai)); + config->hdr.size = size; + + /* parse one set of SAI tokens */ + ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->sai.mclk_direction = hw_config->mclk_direction; + + config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(scomp->dev, + "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->sai.mclk_rate, config->sai.tdm_slot_width, + config->sai.tdm_slots, config->sai.mclk_id); + + if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for SAI%d\n", config->dai_index); + return -EINVAL; + } + + 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_esai_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); + + /* init IPC */ + memset(&config->esai, 0, sizeof(config->esai)); + config->hdr.size = size; + + /* parse one set of ESAI tokens */ + ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->esai.mclk_direction = hw_config->mclk_direction; + config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(scomp->dev, + "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->esai.mclk_rate, config->esai.tdm_slot_width, + config->esai.tdm_slots, config->esai.mclk_id); + + if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for ESAI%d\n", config->dai_index); + return -EINVAL; + } + + 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) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpdmic, 0, sizeof(config->acpdmic)); + config->hdr.size = size; + + config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", + config->dai_index, config->acpdmic.tdm_slots, + config->acpdmic.fsync_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_bt_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); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + 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); + + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + config->dai_index, config->acpbt.tdm_slots, + config->acpbt.fsync_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_sp_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); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpsp, 0, sizeof(config->acpsp)); + config->hdr.size = size; + + config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", + config->dai_index, config->acpsp.tdm_slots, + config->acpsp.fsync_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_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + config->hdr.size = size; + + /* parse the required set of AFE tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", + config->afe.rate, config->afe.channels, config->afe.format); + + config->afe.stream_id = DMA_CHAN_INVALID; + + 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_ssp_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 current_config = 0; + int i, ret; + + /* + * Parse common data, we should have 1 common data per hw_config. + */ + ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* process all possible hw configs */ + for (i = 0; i < slink->num_hw_configs; i++) { + if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id) + current_config = i; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(&hw_config[i], &config[i]); + + config[i].hdr.size = size; + + /* copy differentiating hw configs to ipc structs */ + config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate); + config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate); + config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate); + config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots); + config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width); + config[i].ssp.mclk_direction = hw_config[i].mclk_direction; + config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); + config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); + + dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n", + config[i].dai_index, config[i].format, + config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, + config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, + config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, + config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); + + /* validate SSP fsync rate and channel count */ + if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { + dev_err(scomp->dev, "Invalid fsync rate for SSP%d\n", config[i].dai_index); + return -EINVAL; + } + + if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for SSP%d\n", + config[i].dai_index); + return -EINVAL; + } + } + + dai->number_configs = slink->num_hw_configs; + dai->current_config = current_config; + private->dai_config = kmemdup(config, size * slink->num_hw_configs, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_dmic_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_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_dai_private_data *private = dai->private; + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + size_t size = sizeof(*config); + int i, ret; + + /* Ensure the entire DMIC config struct is zeros */ + memset(&config->dmic, 0, sizeof(config->dmic)); + + /* parse the required set of DMIC tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* parse the required set of DMIC PDM tokens based on number of active PDM's */ + ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS, + slink->tuples, slink->num_tuples, + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl), + config->dmic.num_pdm_active); + if (ret < 0) + return ret; + + /* set IPC header size */ + config->hdr.size = size; + + /* debug messages */ + dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", + config->dai_index, config->dmic.driver_ipc_version); + dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %d\n", + config->dmic.pdmclk_min, config->dmic.pdmclk_max, + config->dmic.duty_min); + dev_dbg(scomp->dev, "duty_max %d fifo_fs %d num_pdms active %d\n", + config->dmic.duty_max, config->dmic.fifo_fs, + config->dmic.num_pdm_active); + dev_dbg(scomp->dev, "fifo word length %d\n", config->dmic.fifo_bits); + + for (i = 0; i < config->dmic.num_pdm_active; i++) { + dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].enable_mic_a, + config->dmic.pdm[i].enable_mic_b); + dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].polarity_mic_a, + config->dmic.pdm[i].polarity_mic_b); + dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].clk_edge, + config->dmic.pdm[i].skew); + } + + /* + * this takes care of backwards compatible handling of fifo_bits_b. + * It is deprecated since firmware ABI version 3.0.1. + */ + if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) + config->dmic.fifo_bits_b = config->dmic.fifo_bits; + + 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_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* parse the required set of ALH tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* init IPC */ + config->hdr.size = size; + + /* set config for all DAI's with name matching the link name */ + 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_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private; + struct sof_ipc_comp_dai *comp_dai; + size_t ipc_size = sizeof(*comp_dai); + struct sof_ipc_dai_config *config; + struct snd_sof_dai_link *slink; + int ret; + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + dai->private = private; + + private->comp_dai = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!private->comp_dai) { + ret = -ENOMEM; + goto free; + } + + /* configure dai IPC message */ + comp_dai = private->comp_dai; + comp_dai->comp.type = SOF_COMP_DAI; + comp_dai->config.hdr.size = sizeof(comp_dai->config); + + /* parse one set of DAI tokens */ + ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*comp_dai), 1); + if (ret < 0) + goto free; + + /* update comp_tokens */ + ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(comp_dai->config), 1); + if (ret < 0) + goto free; + + dev_dbg(scomp->dev, "%s dai %s: type %d index %d\n", + __func__, swidget->widget->name, comp_dai->type, comp_dai->dai_index); + sof_dbg_comp_config(scomp, &comp_dai->config); + + /* now update DAI config */ + list_for_each_entry(slink, &sdev->dai_link_list, list) { + struct sof_ipc_dai_config common_config; + int i; + + if (strcmp(slink->link->name, dai->name)) + continue; + + /* Reserve memory for all hw configs, eventually freed by widget */ + config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL); + if (!config) { + ret = -ENOMEM; + goto free_comp; + } + + /* parse one set of DAI link tokens */ + ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS, + slink->tuples, slink->num_tuples, + sizeof(common_config), 1); + if (ret < 0) + goto free_config; + + for (i = 0; i < slink->num_hw_configs; i++) { + config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config[i].format = le32_to_cpu(slink->hw_configs[i].fmt); + config[i].type = common_config.type; + config[i].dai_index = comp_dai->dai_index; + } + + switch (common_config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_ALH: + ret = sof_link_alh_load(scomp, slink, config, dai); + break; + case SOF_DAI_IMX_SAI: + ret = sof_link_sai_load(scomp, slink, config, dai); + break; + case SOF_DAI_IMX_ESAI: + ret = sof_link_esai_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_BT: + ret = sof_link_acp_bt_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_SP: + ret = sof_link_acp_sp_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_DMIC: + ret = sof_link_acp_dmic_load(scomp, slink, config, dai); + break; + case SOF_DAI_MEDIATEK_AFE: + ret = sof_link_afe_load(scomp, slink, config, dai); + break; + default: + break; + } + if (ret < 0) { + dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name); + goto free_config; + } + + kfree(config); + } + + return 0; +free_config: + kfree(config); +free_comp: + kfree(comp_dai); +free: + kfree(private); + dai->private = NULL; + return ret; +} + +static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget) +{ + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *dai_data; + + if (!dai) + return; + + dai_data = dai->private; + if (dai_data) { + kfree(dai_data->comp_dai); + kfree(dai_data->dai_config); + kfree(dai_data); + } + kfree(dai); + break; + } + default: + break; + } +} + static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { struct sof_ipc_pipe_comp_connect connect; @@ -868,6 +1599,13 @@ static enum sof_tokens pga_token_list[] = { SOF_COMP_TOKENS, }; +static enum sof_tokens dai_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_DAI_TOKENS, + SOF_COMP_TOKENS, +}; + static enum sof_tokens process_token_list[] = { SOF_CORE_TOKENS, SOF_COMP_EXT_TOKENS, @@ -880,6 +1618,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY host_token_list, ARRAY_SIZE(host_token_list), NULL}, [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, host_token_list, ARRAY_SIZE(host_token_list), NULL}, + + [snd_soc_dapm_dai_in] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, + dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, + [snd_soc_dapm_dai_out] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, + dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp, buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 187f7c46a42b..c9c72e94f696 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -554,204 +554,20 @@ int get_token_dai_type(void *elem, void *object, u32 offset) return 0; } -/* DAI */ -static const struct sof_topology_token dai_tokens[] = { - {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_comp_dai, type)}, - {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dai_index)}, - {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, direction)}, -}; - -/* BE DAI link */ -static const struct sof_topology_token dai_link_tokens[] = { - {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_dai_config, type)}, - {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_config, dai_index)}, -}; - /* PCM */ static const struct sof_topology_token stream_tokens[] = { - {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)}, - {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)}, }; -/* Generic components */ -static const struct sof_topology_token comp_tokens[] = { - {SOF_TKN_COMP_PERIOD_SINK_COUNT, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_sink)}, - {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_source)}, - {SOF_TKN_COMP_FORMAT, - SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_comp_config, frame_fmt)}, -}; - -/* SSP */ -static const struct sof_topology_token ssp_tokens[] = { - {SOF_TKN_INTEL_SSP_CLKS_CONTROL, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, - {SOF_TKN_INTEL_SSP_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, - {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, - {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, - get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, - {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, quirks)}, - {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, - get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, - tdm_per_slot_padding_flag)}, - {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, - -}; - -/* ALH */ -static const struct sof_topology_token alh_tokens[] = { - {SOF_TKN_INTEL_ALH_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, rate)}, - {SOF_TKN_INTEL_ALH_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, channels)}, -}; - -/* DMIC */ -static const struct sof_topology_token dmic_tokens[] = { - {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, - {SOF_TKN_INTEL_DMIC_CLK_MIN, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, - {SOF_TKN_INTEL_DMIC_CLK_MAX, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, - {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, - {SOF_TKN_INTEL_DMIC_DUTY_MIN, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, - {SOF_TKN_INTEL_DMIC_DUTY_MAX, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, - {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, - num_pdm_active)}, - {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, - {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, - -}; - -/* ESAI */ -static const struct sof_topology_token esai_tokens[] = { - {SOF_TKN_IMX_ESAI_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, -}; - -/* SAI */ -static const struct sof_topology_token sai_tokens[] = { - {SOF_TKN_IMX_SAI_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, -}; - -/* Core tokens */ -static const struct sof_topology_token core_tokens[] = { - {SOF_TKN_COMP_CORE_ID, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp, core)}, -}; - -/* Component extended tokens */ -static const struct sof_topology_token comp_ext_tokens[] = { - {SOF_TKN_COMP_UUID, - SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, - offsetof(struct snd_sof_widget, uuid)}, -}; - -/* - * DMIC PDM Tokens - * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token - * as it increments the index while parsing the array of pdm tokens - * and determines the correct offset - */ -static const struct sof_topology_token dmic_pdm_tokens[] = { - {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id),}, - {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, - {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, - {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, - {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, - {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, - {SOF_TKN_INTEL_DMIC_PDM_SKEW, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, -}; - -/* HDA */ -static const struct sof_topology_token hda_tokens[] = { - {SOF_TKN_INTEL_HDA_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, rate)}, - {SOF_TKN_INTEL_HDA_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, channels)}, -}; - /* Leds */ static const struct sof_topology_token led_tokens[] = { {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_led_control, use_led)}, - {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct snd_sof_led_control, direction)}, -}; - -/* AFE */ -static const struct sof_topology_token afe_tokens[] = { - {SOF_TKN_MEDIATEK_AFE_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, - {SOF_TKN_MEDIATEK_AFE_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, - {SOF_TKN_MEDIATEK_AFE_FORMAT, - SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, + offsetof(struct snd_sof_led_control, use_led)}, + {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_led_control, direction)}, }; /** @@ -1116,14 +932,6 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, void *object, array_size, 1, 0); } -static void sof_dbg_comp_config(struct snd_soc_component *scomp, - struct sof_ipc_comp_config *config) -{ - dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", - config->periods_sink, config->periods_source, - config->frame_fmt); -} - /* * Standard Kcontrols. */ @@ -1510,110 +1318,6 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return 0; } -/** - * sof_comp_alloc - allocate and initialize buffer for a new component - * @swidget: pointer to struct snd_sof_widget containing extended data - * @ipc_size: IPC payload size that will be updated depending on valid - * extended data. - * @index: ID of the pipeline the component belongs to - * - * Return: The pointer to the new allocated component, NULL if failed. - */ -static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, - int index) -{ - struct sof_ipc_comp *comp; - size_t total_size = *ipc_size; - size_t ext_size = sizeof(swidget->uuid); - - /* only non-zero UUID is valid */ - if (!guid_is_null(&swidget->uuid)) - total_size += ext_size; - - comp = kzalloc(total_size, GFP_KERNEL); - if (!comp) - return NULL; - - /* configure comp new IPC message */ - comp->hdr.size = total_size; - comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - comp->id = swidget->comp_id; - comp->pipeline_id = index; - comp->core = swidget->core; - - /* handle the extended data if needed */ - if (total_size > *ipc_size) { - /* append extended data to the end of the component */ - memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); - comp->ext_data_length = ext_size; - } - - /* update ipc_size and return */ - *ipc_size = total_size; - return comp; -} - -static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct snd_sof_dai *dai) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_dai_private_data *dai_data; - struct sof_ipc_comp_dai *comp_dai; - size_t ipc_size = sizeof(*comp_dai); - int ret; - - dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); - if (!dai_data) - return -ENOMEM; - - comp_dai = (struct sof_ipc_comp_dai *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!comp_dai) { - ret = -ENOMEM; - goto free; - } - - /* configure dai IPC message */ - comp_dai->comp.type = SOF_COMP_DAI; - comp_dai->config.hdr.size = sizeof(comp_dai->config); - - ret = sof_parse_tokens(scomp, comp_dai, dai_tokens, - ARRAY_SIZE(dai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dai tokens failed %d\n", - le32_to_cpu(private->size)); - goto free; - } - - ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", - private->size); - goto free; - } - - dev_dbg(scomp->dev, "dai %s: type %d index %d\n", - swidget->widget->name, comp_dai->type, comp_dai->dai_index); - sof_dbg_comp_config(scomp, &comp_dai->config); - - if (dai) { - dai->scomp = scomp; - dai_data->comp_dai = comp_dai; - dai->private = dai_data; - } - - return 0; - -free: - kfree(dai_data); - return ret; -} - /* bind PCM ID to host component ID */ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) @@ -1734,6 +1438,21 @@ static int sof_widget_bind_event(struct snd_soc_component *scomp, return -EINVAL; } +static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples) +{ + int i; + + if (!tuples) + return -EINVAL; + + for (i = 0; i < num_tuples; i++) { + if (tuples[i].token == token_id) + return tuples[i].value.v; + } + + return -EINVAL; +} + /* external widget init - used for any driver specific init */ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, @@ -1746,9 +1465,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_sof_dai *dai; enum sof_tokens *token_list; int token_list_size; - struct sof_ipc_comp comp = { - .core = SOF_DSP_PRIMARY_CORE, - }; int ret = 0; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); @@ -1771,30 +1487,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, token_list = widget_ops[w->id].token_list; token_list_size = widget_ops[w->id].token_list_size; - ret = sof_parse_tokens(scomp, &comp, core_tokens, - ARRAY_SIZE(core_tokens), tw->priv.array, - le32_to_cpu(tw->priv.size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parsing core tokens failed %d\n", - ret); - kfree(swidget); - return ret; - } - - if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) - comp.core = SOF_DSP_PRIMARY_CORE; - - swidget->core = comp.core; - - ret = sof_parse_tokens(scomp, swidget, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), - tw->priv.array, le32_to_cpu(tw->priv.size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parsing comp_ext_tokens failed %d\n", - ret); - kfree(swidget); - return ret; - } - /* handle any special case widgets */ switch (w->id) { case snd_soc_dapm_dai_in: @@ -1803,9 +1495,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, if (!dai) { kfree(swidget); return -ENOMEM; + } - ret = sof_widget_load_dai(scomp, index, swidget, tw, dai); + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); if (!ret) ret = sof_connect_dai_widget(scomp, w, tw, dai); if (ret < 0) { @@ -1853,7 +1546,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, break; } - /* check IPC reply */ + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) { + swidget->core = SOF_DSP_PRIMARY_CORE; + } else { + int core = sof_get_token_value(SOF_TKN_COMP_CORE_ID, swidget->tuples, + swidget->num_tuples); + + if (core >= 0) + swidget->core = core; + } + + /* check token parsing reply */ if (ret < 0) { dev_err(scomp->dev, "error: failed to add widget id %d type %d name : %s stream %s\n", @@ -1927,14 +1630,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp, case snd_soc_dapm_dai_out: dai = swidget->private; - if (dai) { - struct sof_dai_private_data *dai_data = dai->private; - - kfree(dai_data->comp_dai); - kfree(dai_data->dai_config); - kfree(dai_data); + if (dai) list_del(&dai->list); - } break; default: break; @@ -1970,9 +1667,6 @@ out: if (widget_ops[swidget->id].ipc_free) widget_ops[swidget->id].ipc_free(swidget); - /* free private value */ - kfree(swidget->private); - kfree(swidget->tuples); /* remove and free swidget object */ @@ -2118,589 +1812,24 @@ static int sof_dai_unload(struct snd_soc_component *scomp, return 0; } -static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - /* clock directions wrt codec */ - if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) { - /* codec is bclk provider */ - if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) - config->format |= SOF_DAI_FMT_CBP_CFP; - else - config->format |= SOF_DAI_FMT_CBP_CFC; - } else { - /* codec is bclk consumer */ - if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) - config->format |= SOF_DAI_FMT_CBC_CFP; - else - config->format |= SOF_DAI_FMT_CBC_CFC; - } - - /* inverted clocks ? */ - if (hw_config->invert_bclk) { - if (hw_config->invert_fsync) - config->format |= SOF_DAI_FMT_IB_IF; - else - config->format |= SOF_DAI_FMT_IB_NF; - } else { - if (hw_config->invert_fsync) - config->format |= SOF_DAI_FMT_NB_IF; - else - config->format |= SOF_DAI_FMT_NB_NF; - } -} - -/* - * Send IPC and set the same config for all DAIs with name matching the link - * name. Note that the function can only be used for the case that all DAIs - * have a common DAI config for now. - */ -static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, - struct snd_soc_dai_link *link, - struct sof_ipc_dai_config *config, - int num_conf, int curr_conf) -{ - struct sof_dai_private_data *dai_data; - struct snd_sof_dai *dai; - int found = 0; - int i; - - list_for_each_entry(dai, &sdev->dai_list, list) { - dai_data = dai->private; - if (!dai->name) - continue; - - if (strcmp(link->name, dai->name) == 0) { - /* - * the same dai config will be applied to all DAIs in - * the same dai link. We have to ensure that the ipc - * dai config's dai_index match to the component's - * dai_index. - */ - for (i = 0; i < num_conf; i++) - config[i].dai_index = dai_data->comp_dai->dai_index; - - dev_dbg(sdev->dev, "set DAI config for %s index %d\n", - dai->name, config[curr_conf].dai_index); - - dai->number_configs = num_conf; - dai->current_config = curr_conf; - dai_data->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL); - if (!dai_data->dai_config) - return -ENOMEM; - - found = 1; - } - } - - /* - * machine driver may define a dai link with playback and capture - * dai enabled, but the dai link in topology would support both, one - * or none of them. Here print a warning message to notify user - */ - if (!found) { - dev_warn(sdev->dev, "warning: failed to find dai for dai link %s", - link->name); - } - - return 0; -} - -static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size, - struct snd_soc_dai_link *link, - struct sof_ipc_dai_config *config) -{ - return sof_set_dai_config_multi(sdev, size, link, config, 1, 0); -} - -static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config, int curr_conf) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - int num_conf = le32_to_cpu(cfg->num_hw_configs); - u32 size = sizeof(*config); - int ret; - int i; - - /* - * Parse common data, we should have 1 common data per hw_config. - */ - ret = sof_parse_token_sets(scomp, &config->ssp, ssp_tokens, - ARRAY_SIZE(ssp_tokens), private->array, - le32_to_cpu(private->size), - num_conf, size); - - if (ret != 0) { - dev_err(scomp->dev, "error: parse ssp tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* process all possible hw configs */ - for (i = 0; i < num_conf; i++) { - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(&hw_config[i], &config[i]); - - config[i].hdr.size = size; - - /* copy differentiating hw configs to ipc structs */ - config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate); - config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate); - config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate); - config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots); - config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width); - config[i].ssp.mclk_direction = hw_config[i].mclk_direction; - config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); - config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); - - dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n", - config[i].dai_index, config[i].format, - config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, - config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, - config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, - config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); - - /* validate SSP fsync rate and channel count */ - if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { - dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n", - config[i].dai_index); - return -EINVAL; - } - - if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for SSP%d\n", - config[i].dai_index); - return -EINVAL; - } - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config_multi(sdev, size, link, config, num_conf, curr_conf); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_sai_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->sai, 0, sizeof(struct sof_ipc_dai_sai_params)); - config->hdr.size = size; - - ret = sof_parse_tokens(scomp, &config->sai, sai_tokens, - ARRAY_SIZE(sai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse sai tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); - config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); - config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->sai.mclk_direction = hw_config->mclk_direction; - - config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); - config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); - config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); - - dev_info(scomp->dev, - "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", - config->dai_index, config->format, - config->sai.mclk_rate, config->sai.tdm_slot_width, - config->sai.tdm_slots, config->sai.mclk_id); - - if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for SAI%d\n", - config->dai_index); - return -EINVAL; - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for SAI%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_esai_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params)); - config->hdr.size = size; - - ret = sof_parse_tokens(scomp, &config->esai, esai_tokens, - ARRAY_SIZE(esai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse esai tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); - config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); - config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->esai.mclk_direction = hw_config->mclk_direction; - config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); - config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); - config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); - - dev_info(scomp->dev, - "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", - config->dai_index, config->format, - config->esai.mclk_rate, config->esai.tdm_slot_width, - config->esai.tdm_slots, config->esai.mclk_id); - - if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for ESAI%d\n", - config->dai_index); - return -EINVAL; - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for ESAI%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params)); - config->hdr.size = size; - - config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - - dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", - config->dai_index, config->acpdmic.tdm_slots, - config->acpdmic.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params)); - 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); - - dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", - config->dai_index, config->acpbt.tdm_slots, - config->acpbt.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params)); - config->hdr.size = size; - - config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - - dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", - config->dai_index, config->acpsp.tdm_slots, - config->acpsp.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_afe_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - config->hdr.size = size; - - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->afe, afe_tokens, - ARRAY_SIZE(afe_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "parse afe tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", - config->afe.rate, config->afe.channels, config->afe.format); - - config->afe.stream_id = DMA_CHAN_INVALID; - - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "failed to process afe dai link %s", link->name); - - return ret; -} - -static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - size_t size = sizeof(*config); - int ret, j; - - /* Ensure the entire DMIC config struct is zeros */ - memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); - - /* get DMIC tokens */ - ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, - ARRAY_SIZE(dmic_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dmic tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* get DMIC PDM tokens */ - ret = sof_parse_token_sets(scomp, &config->dmic.pdm[0], dmic_pdm_tokens, - ARRAY_SIZE(dmic_pdm_tokens), private->array, - le32_to_cpu(private->size), - config->dmic.num_pdm_active, - sizeof(struct sof_ipc_dai_dmic_pdm_ctrl)); - - if (ret != 0) { - dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* set IPC header size */ - config->hdr.size = size; - - /* debug messages */ - dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", - config->dai_index, config->dmic.driver_ipc_version); - dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", - config->dmic.pdmclk_min, config->dmic.pdmclk_max, - config->dmic.duty_min); - dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", - config->dmic.duty_max, config->dmic.fifo_fs, - config->dmic.num_pdm_active); - dev_dbg(scomp->dev, "fifo word length %hd\n", config->dmic.fifo_bits); - - for (j = 0; j < config->dmic.num_pdm_active; j++) { - dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].enable_mic_a, - config->dmic.pdm[j].enable_mic_b); - dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].polarity_mic_a, - config->dmic.pdm[j].polarity_mic_b); - dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].clk_edge, - config->dmic.pdm[j].skew); - } - - /* - * this takes care of backwards compatible handling of fifo_bits_b. - * It is deprecated since firmware ABI version 3.0.1. - */ - if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) - config->dmic.fifo_bits_b = config->dmic.fifo_bits; - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_hda_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* init IPC */ - memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params)); - config->hdr.size = size; - - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->hda, hda_tokens, - ARRAY_SIZE(hda_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse hda tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", - config->hda.rate, config->hda.channels); - - config->hda.link_dma_ch = DMA_CHAN_INVALID; - - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to process hda dai link %s", - link->name); - - return ret; -} - -static int sof_link_alh_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - ret = sof_parse_tokens(scomp, &config->alh, alh_tokens, - ARRAY_SIZE(alh_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse alh tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* init IPC */ - config->hdr.size = size; - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for ALH %d\n", - config->dai_index); - - return ret; -} +static const struct sof_topology_token common_dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct snd_sof_dai_link, type)}, +}; /* DAI link - used for any driver specific init */ -static int sof_link_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, +static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &cfg->priv; - struct snd_soc_tplg_hw_config *hw_config; - struct sof_ipc_dai_config common_config; - struct sof_ipc_dai_config *config; - int curr_conf; - int num_conf; - int ret; - int i; + struct snd_sof_dai_link *slink; + size_t size; + u32 token_id = 0; + int num_tuples = 0; + int ret, num_sets; if (!link->platforms) { dev_err(scomp->dev, "error: no platforms\n"); @@ -2737,104 +1866,159 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, return -EINVAL; } - memset(&common_config, 0, sizeof(common_config)); + slink = kzalloc(sizeof(*slink), GFP_KERNEL); + if (!slink) + return -ENOMEM; - /* get any common DAI tokens */ - ret = sof_parse_tokens(scomp, &common_config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens), - private->array, le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse link tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; + slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs); + slink->hw_configs = kmemdup(cfg->hw_config, + sizeof(*slink->hw_configs) * slink->num_hw_configs, + GFP_KERNEL); + if (!slink->hw_configs) { + kfree(slink); + return -ENOMEM; } - /* - * DAI links are expected to have at least 1 hw_config. - * But some older topologies might have no hw_config for HDA dai links. - */ - hw_config = cfg->hw_config; - num_conf = le32_to_cpu(cfg->num_hw_configs); - if (!num_conf) { - if (common_config.type != SOF_DAI_INTEL_HDA) { - dev_err(scomp->dev, "error: unexpected DAI config count %d!\n", - le32_to_cpu(cfg->num_hw_configs)); - return -EINVAL; - } - num_conf = 1; - curr_conf = 0; - } else { - dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n", - cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id)); + slink->default_hw_cfg_id = le32_to_cpu(cfg->default_hw_config_id); + slink->link = link; - for (curr_conf = 0; curr_conf < num_conf; curr_conf++) { - if (hw_config[curr_conf].id == cfg->default_hw_config_id) - break; - } + dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d for dai link %s!\n", + slink->num_hw_configs, slink->default_hw_cfg_id, link->name); - if (curr_conf == num_conf) { - dev_err(scomp->dev, "error: default hw_config id: %d not found!\n", - le32_to_cpu(cfg->default_hw_config_id)); - return -EINVAL; - } + ret = sof_parse_tokens(scomp, slink, common_dai_link_tokens, + ARRAY_SIZE(common_dai_link_tokens), + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); + kfree(slink->hw_configs); + kfree(slink); + return ret; } - /* Reserve memory for all hw configs, eventually freed by widget */ - config = kcalloc(num_conf, sizeof(*config), GFP_KERNEL); - if (!config) - return -ENOMEM; - - /* Copy common data to all config ipc structs */ - for (i = 0; i < num_conf; i++) { - config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config[i].format = le32_to_cpu(hw_config[i].fmt); - config[i].type = common_config.type; - config[i].dai_index = common_config.dai_index; - } + if (!token_list) + goto out; - /* now load DAI specific data and send IPC - type comes from token */ - switch (common_config.type) { + /* calculate size of tuples array */ + num_tuples += token_list[SOF_DAI_LINK_TOKENS].count; + num_sets = slink->num_hw_configs; + switch (slink->type) { case SOF_DAI_INTEL_SSP: - ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, config, curr_conf); + token_id = SOF_SSP_TOKENS; + num_tuples += token_list[SOF_SSP_TOKENS].count * slink->num_hw_configs; break; case SOF_DAI_INTEL_DMIC: - ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_DMIC_TOKENS; + num_tuples += token_list[SOF_DMIC_TOKENS].count; + + /* Allocate memory for max PDM controllers */ + num_tuples += token_list[SOF_DMIC_PDM_TOKENS].count * SOF_DAI_INTEL_DMIC_NUM_CTRL; break; case SOF_DAI_INTEL_HDA: - ret = sof_link_hda_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_HDA_TOKENS; + num_tuples += token_list[SOF_HDA_TOKENS].count; break; case SOF_DAI_INTEL_ALH: - ret = sof_link_alh_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_ALH_TOKENS; + num_tuples += token_list[SOF_ALH_TOKENS].count; break; case SOF_DAI_IMX_SAI: - ret = sof_link_sai_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_SAI_TOKENS; + num_tuples += token_list[SOF_SAI_TOKENS].count; break; case SOF_DAI_IMX_ESAI: - ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_BT: - ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_SP: - ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_DMIC: - ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, - config); + token_id = SOF_ESAI_TOKENS; + num_tuples += token_list[SOF_ESAI_TOKENS].count; break; case SOF_DAI_MEDIATEK_AFE: - ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_AFE_TOKENS; + num_tuples += token_list[SOF_AFE_TOKENS].count; break; default: - dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); - ret = -EINVAL; break; } - kfree(config); + /* allocate memory for tuples array */ + size = sizeof(struct snd_sof_tuple) * num_tuples; + slink->tuples = kzalloc(size, GFP_KERNEL); + if (!slink->tuples) { + kfree(slink->hw_configs); + kfree(slink); + return -ENOMEM; + } + + /* parse one set of DAI link tokens */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + SOF_DAI_LINK_TOKENS, 1, slink->tuples, + num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[SOF_DAI_LINK_TOKENS].name, link->name); + goto err; + } + + /* nothing more to do if there are no DAI type-specific tokens defined */ + if (!token_id || !token_list[token_id].tokens) + goto out; + + /* parse "num_sets" sets of DAI-specific tokens */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[token_id].name, link->name); + goto err; + } + + /* for DMIC, also parse all sets of DMIC PDM tokens based on active PDM count */ + if (token_id == SOF_DMIC_TOKENS) { + num_sets = sof_get_token_value(SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, + slink->tuples, slink->num_tuples); + + if (num_sets < 0) { + dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name); + ret = num_sets; + goto err; + } + + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples, + num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[SOF_DMIC_PDM_TOKENS].name, link->name); + goto err; + } + } +out: + link->dobj.private = slink; + list_add(&slink->list, &sdev->dai_link_list); + + return 0; + +err: + kfree(slink->tuples); + kfree(slink->hw_configs); + kfree(slink); return ret; } +static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) +{ + struct snd_sof_dai_link *slink = dobj->private; + + if (!slink) + return 0; + + kfree(slink->tuples); + list_del(&slink->list); + kfree(slink->hw_configs); + kfree(slink); + dobj->private = NULL; + + return 0; +} + /* DAI link - used for any driver specific init */ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) @@ -3121,6 +2305,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = { /* DAI link - used for any driver specific init */ .link_load = sof_link_load, + .link_unload = sof_link_unload, /* completion - called at completion of firmware loading */ .complete = sof_complete, -- cgit v1.2.3 From b5cee8feb1d482a9d07b677f4f2f9565bacda53e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:18 -0700 Subject: ASoC: SOF: topology: Make control parsing IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the control parser in topology IPC agnostic by introducing 2 new topology IPC ops, control_setup and control_free. These ops handle setting up/freeing the control data in the IPC format based on the IPC version. Along with this, modify the struct snd_sof_control to remove the IPC-specific field, control_data and replace it with the void pointer to ipc_control_data. Also, add a few new fields to store all the information parsed from topology. Finally, define and set the control setup/free ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-18-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 26 +++---- sound/soc/sof/ipc.c | 2 +- sound/soc/sof/ipc3-topology.c | 151 ++++++++++++++++++++++++++++++++++- sound/soc/sof/sof-audio.h | 20 ++++- sound/soc/sof/topology.c | 177 +++++++++++++----------------------------- 5 files changed, 235 insertions(+), 141 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index ef61936dad59..21ee0545945d 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -67,7 +67,7 @@ static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; int ret; @@ -97,7 +97,7 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -118,7 +118,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -166,7 +166,7 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -185,7 +185,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -214,7 +214,7 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -233,7 +233,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -260,7 +260,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct sof_abi_hdr *data = cdata->data; size_t size; @@ -296,7 +296,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct sof_abi_hdr *data = cdata->data; size_t size; @@ -335,7 +335,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; @@ -409,7 +409,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; size_t data_size; @@ -482,7 +482,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; @@ -534,7 +534,7 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *local_cdata; int i; - local_cdata = scontrol->control_data; + local_cdata = scontrol->ipc_control_data; if (cdata->cmd == SOF_CTRL_CMD_BINARY) { if (cdata->num_elems != local_cdata->data->size) { diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index cf892859355a..19a294cbbb8d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -812,7 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) { struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index e6aa78d492ea..96553d103c85 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -12,6 +12,9 @@ #include "sof-audio.h" #include "ops.h" +/* Full volume for default values */ +#define VOL_ZERO_DB BIT(VOLUME_FWL) + struct sof_widget_data { int ctrl_type; int ipc_cmd; @@ -743,6 +746,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp, struct sof_widget_data *wdata, size_t *size) { const struct snd_kcontrol_new *kc; + struct sof_ipc_ctrl_data *cdata; struct soc_mixer_control *sm; struct soc_bytes_ext *sbe; struct soc_enum *se; @@ -777,7 +781,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp, return -EINVAL; } - wdata[i].pdata = wdata[i].control->control_data->data; + cdata = wdata[i].control->ipc_control_data; + wdata[i].pdata = cdata->data; if (!wdata[i].pdata) return -EINVAL; @@ -789,7 +794,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp, *size += wdata[i].pdata->size; /* get data type */ - switch (wdata[i].control->control_data->cmd) { + switch (cdata->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: @@ -1553,6 +1558,146 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * return ret; } +static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + int ret; + + scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + if (scontrol->max_size < sizeof(*cdata) || + scontrol->max_size < sizeof(struct sof_abi_hdr)) { + ret = -EINVAL; + goto err; + } + + /* init the get/put bytes data */ + if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { + dev_err(sdev->dev, "err: bytes data size %zu exceeds max %zu.\n", + scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); + ret = -EINVAL; + goto err; + } + + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size; + + cdata = scontrol->ipc_control_data; + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->index = scontrol->index; + + if (scontrol->priv_size > 0) { + memcpy(cdata->data, scontrol->priv, scontrol->priv_size); + kfree(scontrol->priv); + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); + ret = -EINVAL; + goto err; + } + + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n", + cdata->data->abi); + ret = -EINVAL; + goto err; + } + + if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) { + dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n"); + ret = -EINVAL; + goto err; + } + } + + return 0; +err: + kfree(scontrol->ipc_control_data); + return ret; +} + +static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + int i; + + /* init the volume get/put data */ + scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); + + scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + cdata = scontrol->ipc_control_data; + cdata->index = scontrol->index; + + /* set cmd for mixer control */ + if (scontrol->max == 1) { + cdata->cmd = SOF_CTRL_CMD_SWITCH; + return 0; + } + + cdata->cmd = SOF_CTRL_CMD_VOLUME; + + /* set default volume values to 0dB in control */ + for (i = 0; i < scontrol->num_channels; i++) { + cdata->chanv[i].channel = i; + cdata->chanv[i].value = VOL_ZERO_DB; + } + + return 0; +} + +static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + + /* init the enum get/put data */ + scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); + + scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + cdata = scontrol->ipc_control_data; + cdata->index = scontrol->index; + cdata->cmd = SOF_CTRL_CMD_ENUM; + + return 0; +} + +static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + switch (scontrol->info_type) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + return sof_ipc3_control_load_volume(sdev, scontrol); + case SND_SOC_TPLG_CTL_BYTES: + return sof_ipc3_control_load_bytes(sdev, scontrol); + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + return sof_ipc3_control_load_enum(sdev, scontrol); + default: + break; + } + + return 0; +} + +static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_free fcomp; + + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; + fcomp.hdr.size = sizeof(fcomp); + fcomp.id = scontrol->comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0); +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -1651,6 +1796,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, .route_setup = sof_ipc3_route_setup, + .control_setup = sof_ipc3_control_setup, + .control_free = sof_ipc3_control_free, .token_list = ipc3_token_list, }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index bde86e078e08..a14b872ea261 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,8 +30,15 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +/* + * Volume fractional word length define to 16 sets + * the volume linear gain value to use Qx.16 format + */ +#define VOLUME_FWL 16 + struct snd_sof_widget; struct snd_sof_route; +struct snd_sof_control; /** * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets @@ -59,11 +66,15 @@ struct sof_ipc_tplg_widget_ops { * @token_list: List of all tokens supported by the IPC version. The size of the token_list * array should be SOF_TOKEN_COUNT. The unused elements in the array will be * initialized to 0. + * @control_setup: Function pointer for setting up kcontrol IPC-specific data + * @control_free: Function pointer for freeing kcontrol IPC-specific data */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; + int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); + int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); }; /** struct snd_sof_tuple - Tuple info @@ -165,13 +176,20 @@ struct snd_sof_led_control { /* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_soc_component *scomp; + const char *name; int comp_id; int min_volume_step; /* min volume step for volume_table */ int max_volume_step; /* max volume step for volume_table */ int num_channels; unsigned int access; u32 readback_offset; /* offset to mmapped data if used */ - struct sof_ipc_ctrl_data *control_data; + int info_type; + int index; /* pipeline ID */ + void *priv; /* private data copied from topology */ + size_t priv_size; /* size of private data */ + size_t max_size; + void *ipc_control_data; + int max; /* applicable to volume controls */ u32 size; /* cdata size */ u32 *volume_table; /* volume table computed from tlv data*/ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c9c72e94f696..f7adf058a768 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -28,15 +28,9 @@ #define VOL_TWENTIETH_ROOT_OF_TEN 73533 /* 40th root of 10 in Q1.16 fixed-point notation*/ #define VOL_FORTIETH_ROOT_OF_TEN 69419 -/* - * Volume fractional word length define to 16 sets - * the volume linear gain value to use Qx.16 format - */ -#define VOLUME_FWL 16 + /* 0.5 dB step value in topology TLV */ #define VOL_HALF_DB_STEP 50 -/* Full volume for default values */ -#define VOL_ZERO_DB BIT(VOLUME_FWL) /* TLV data items */ #define TLV_ITEMS 3 @@ -944,16 +938,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - struct sof_ipc_ctrl_data *cdata; int tlv[TLV_ITEMS]; - unsigned int i; int ret; /* validate topology data */ - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) { - ret = -EINVAL; - goto out; - } + if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; /* * If control has more than 2 channels we need to override the info. This is because even if @@ -964,48 +954,26 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, if (le32_to_cpu(mc->num_channels) > 2) kc->info = snd_sof_volume_info; - /* init the volume get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(mc->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) { - ret = -ENOMEM; - goto out; - } - scontrol->comp_id = sdev->next_comp_id; scontrol->min_volume_step = le32_to_cpu(mc->min); scontrol->max_volume_step = le32_to_cpu(mc->max); scontrol->num_channels = le32_to_cpu(mc->num_channels); - scontrol->control_data->index = kc->index; - /* set cmd for mixer control */ - if (le32_to_cpu(mc->max) == 1) { - scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->max = le32_to_cpu(mc->max); + if (le32_to_cpu(mc->max) == 1) goto skip; - } - - scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { dev_err(scomp->dev, "error: invalid TLV data\n"); - ret = -EINVAL; - goto out_free; + return -EINVAL; } /* set up volume table */ ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); if (ret < 0) { dev_err(scomp->dev, "error: setting up volume table\n"); - goto out_free; - } - - /* set default volume values to 0dB in control */ - cdata = scontrol->control_data; - for (i = 0; i < scontrol->num_channels; i++) { - cdata->chanv[i].channel = i; - cdata->chanv[i].value = VOL_ZERO_DB; + return ret; } skip: @@ -1016,7 +984,7 @@ skip: if (ret != 0) { dev_err(scomp->dev, "error: parse led tokens failed %d\n", le32_to_cpu(mc->priv.size)); - goto out_free_table; + goto err; } dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", @@ -1024,12 +992,10 @@ skip: return 0; -out_free_table: +err: if (le32_to_cpu(mc->max) > 1) kfree(scontrol->volume_table); -out_free: - kfree(scontrol->control_data); -out: + return ret; } @@ -1046,17 +1012,8 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) return -EINVAL; - /* init the enum get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(ec->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); - scontrol->control_data->index = kc->index; - scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1070,77 +1027,27 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct snd_soc_tplg_ctl_hdr *hdr) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_ctrl_data *cdata; struct snd_soc_tplg_bytes_control *control = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; - size_t max_size = sbe->max; size_t priv_size = le32_to_cpu(control->priv.size); - int ret; - - if (max_size < sizeof(struct sof_ipc_ctrl_data) || - max_size < sizeof(struct sof_abi_hdr)) { - ret = -EINVAL; - goto out; - } - - /* init the get/put bytes data */ - if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) { - dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n", - priv_size, max_size - sizeof(struct sof_ipc_ctrl_data)); - ret = -EINVAL; - goto out; - } - - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size; - - scontrol->control_data = kzalloc(max_size, GFP_KERNEL); - cdata = scontrol->control_data; - if (!scontrol->control_data) { - ret = -ENOMEM; - goto out; - } + scontrol->max_size = sbe->max; scontrol->comp_id = sdev->next_comp_id; - scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY; - scontrol->control_data->index = kc->index; - dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); + dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id); - if (le32_to_cpu(control->priv.size) > 0) { - memcpy(cdata->data, control->priv.data, - le32_to_cpu(control->priv.size)); + /* copy the private data */ + if (priv_size > 0) { + scontrol->priv = kzalloc(priv_size, GFP_KERNEL); + if (!scontrol->priv) + return -ENOMEM; - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - ret = -EINVAL; - goto out_free; - } - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, - cdata->data->abi)) { - dev_err(scomp->dev, - "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - ret = -EINVAL; - goto out_free; - } - if (cdata->data->size + sizeof(struct sof_abi_hdr) != - le32_to_cpu(control->priv.size)) { - dev_err(scomp->dev, - "error: Conflict in bytes vs. priv size.\n"); - ret = -EINVAL; - goto out_free; - } + memcpy(scontrol->priv, control->priv.data, priv_size); + scontrol->priv_size = priv_size; } return 0; - -out_free: - kfree(scontrol->control_data); -out: - return ret; } /* external kcontrol init - used for any driver specific init */ @@ -1163,8 +1070,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, if (!scontrol) return -ENOMEM; + scontrol->name = kstrdup(hdr->name, GFP_KERNEL); + if (!scontrol->name) + return -ENOMEM; + scontrol->scomp = scomp; scontrol->access = kc->access; + scontrol->info_type = le32_to_cpu(hdr->ops.info); + scontrol->index = kc->index; switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1215,22 +1128,26 @@ static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_free fcomp; + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_control *scontrol = dobj->private; + int ret = 0; - dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name); + dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name); - fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; - fcomp.hdr.size = sizeof(fcomp); - fcomp.id = scontrol->comp_id; + if (ipc_tplg_ops->control_free) { + ret = ipc_tplg_ops->control_free(sdev, scontrol); + if (ret < 0) + dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name); + } - kfree(scontrol->control_data); + /* free all data before returning in case of error too */ + kfree(scontrol->ipc_control_data); + kfree(scontrol->priv); + kfree(scontrol->name); list_del(&scontrol->list); kfree(scontrol); - /* send IPC to the DSP */ - return sof_ipc_tx_message(sdev->ipc, - fcomp.hdr.cmd, &fcomp, sizeof(fcomp), - NULL, 0); + + return ret; } /* @@ -1657,7 +1574,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, dev_warn(scomp->dev, "unsupported kcontrol_type\n"); goto out; } - kfree(scontrol->control_data); + kfree(scontrol->ipc_control_data); list_del(&scontrol->list); kfree(scontrol); } @@ -2167,10 +2084,22 @@ static int sof_complete(struct snd_soc_component *scomp) struct snd_sof_widget *swidget, *comp_swidget; const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_control *scontrol; int ret; + /* first update all control IPC structures based on the IPC version */ + if (ipc_tplg_ops->control_setup) + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + ret = ipc_tplg_ops->control_setup(sdev, scontrol); + if (ret < 0) { + dev_err(sdev->dev, "failed updating IPC struct for control %s\n", + scontrol->name); + return ret; + } + } + /* - * now update all widget IPC structures. If any of the ipc_setup callbacks fail, the + * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the * topology will be removed and all widgets will be unloaded resulting in freeing all * associated memories. */ -- cgit v1.2.3 From 8ef1439c51048b9359a8d1be2360dd4cd9848a77 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:19 -0700 Subject: ASoC: SOF: topology: Make widget binding IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make widget binding in the topology parser IPC agnostic by introducing a new op, bind_event, in struct ipc_tplg_widget_ops. Also set the op for all widget types in the IPC3 topology ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-19-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 192 ++++++++++++++++++++++++++++++++++++- sound/soc/sof/topology.c | 217 +++--------------------------------------- 2 files changed, 202 insertions(+), 207 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 96553d103c85..ea1311192877 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -8,6 +8,7 @@ // #include +#include #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" @@ -1698,6 +1699,194 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0); } +/* send pcm params ipc */ +static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct snd_pcm_hw_params *params; + struct sof_ipc_pcm_params pcm; + struct snd_sof_pcm *spcm; + int ret; + + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); + if (!spcm) { + dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name); + return -EINVAL; + } + + params = &spcm->params[dir]; + + /* set IPC PCM params */ + memset(&pcm, 0, sizeof(pcm)); + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = swidget->comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.direction = dir; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* set format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + default: + return -EINVAL; + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + if (ret < 0) + dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__, + swidget->widget->name); + + return ret; +} + + /* send stream trigger ipc */ +static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + /* set IPC stream params */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; + stream.comp_id = swidget->comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name); + + return ret; +} + +static int sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *scomp; + int stream = SNDRV_PCM_STREAM_CAPTURE; + struct snd_sof_pcm *spcm; + int ret = 0; + + if (!swidget) + return 0; + + scomp = swidget->scomp; + + dev_dbg(scomp->dev, "received event %d for widget %s\n", + event, w->name); + + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); + if (!spcm) { + dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__, + swidget->widget->name); + return -EINVAL; + } + + /* process events */ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); + return 0; + } + + /* set pcm params */ + ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream); + if (ret < 0) { + dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n", + __func__, swidget->widget->name); + break; + } + + /* start trigger */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, + swidget->widget->name); + break; + case SND_SOC_DAPM_POST_PMD: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(scomp->dev, + "POST_PMD event ignored, KWD pipeline will remain RUNNING\n"); + return 0; + } + + /* stop trigger */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, + swidget->widget->name); + + /* pcm free */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__, + swidget->widget->name); + break; + default: + break; + } + + return ret; +} + +/* event handlers for keyword detect component */ +static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { + {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_ipc3_keyword_dapm_event}, +}; + +static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget, u16 event_type) +{ + struct sof_ipc_comp *ipc_comp; + + /* validate widget event type */ + switch (event_type) { + case SOF_KEYWORD_DETECT_DAPM_EVENT: + /* only KEYWORD_DETECT comps should handle this */ + if (swidget->id != snd_soc_dapm_effect) + break; + + ipc_comp = swidget->private; + if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) + break; + + /* bind event to keyword detect comp */ + return snd_soc_tplg_widget_bind_event(swidget->widget, sof_kwd_events, + ARRAY_SIZE(sof_kwd_events), event_type); + default: + break; + } + + dev_err(scomp->dev, "Invalid event type %d for widget %s\n", event_type, + swidget->widget->name); + + return -EINVAL; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -1790,7 +1979,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, [snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp, - process_token_list, ARRAY_SIZE(process_token_list), NULL}, + process_token_list, ARRAY_SIZE(process_token_list), + sof_ipc3_widget_bind_event}, }; static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f7adf058a768..70677a36c304 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include "sof-priv.h" #include "sof-audio.h" @@ -135,171 +134,6 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so return 0; } -/* send pcm params ipc */ -static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) -{ - struct sof_ipc_pcm_params_reply ipc_params_reply; - struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_pcm_params pcm; - struct snd_pcm_hw_params *params; - struct snd_sof_pcm *spcm; - int ret; - - memset(&pcm, 0, sizeof(pcm)); - - /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); - if (!spcm) { - dev_err(scomp->dev, "error: cannot find PCM for %s\n", - swidget->widget->name); - return -EINVAL; - } - - params = &spcm->params[dir]; - - /* set IPC PCM params */ - pcm.hdr.size = sizeof(pcm); - pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; - pcm.comp_id = swidget->comp_id; - pcm.params.hdr.size = sizeof(pcm.params); - pcm.params.direction = dir; - pcm.params.sample_valid_bytes = params_width(params) >> 3; - pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; - pcm.params.rate = params_rate(params); - pcm.params.channels = params_channels(params); - pcm.params.host_period_bytes = params_period_bytes(params); - - /* set format */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16: - pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case SNDRV_PCM_FORMAT_S24: - pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; - break; - case SNDRV_PCM_FORMAT_S32: - pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - default: - return -EINVAL; - } - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), - &ipc_params_reply, sizeof(ipc_params_reply)); - if (ret < 0) - dev_err(scomp->dev, "error: pcm params failed for %s\n", - swidget->widget->name); - - return ret; -} - - /* send stream trigger ipc */ -static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) -{ - struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; - int ret; - - /* set IPC stream params */ - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; - stream.comp_id = swidget->comp_id; - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); - if (ret < 0) - dev_err(scomp->dev, "error: failed to trigger %s\n", - swidget->widget->name); - - return ret; -} - -static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *scomp; - int stream = SNDRV_PCM_STREAM_CAPTURE; - struct snd_sof_pcm *spcm; - int ret = 0; - - if (!swidget) - return 0; - - scomp = swidget->scomp; - - dev_dbg(scomp->dev, "received event %d for widget %s\n", - event, w->name); - - /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); - if (!spcm) { - dev_err(scomp->dev, "error: cannot find PCM for %s\n", - swidget->widget->name); - return -EINVAL; - } - - /* process events */ - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (spcm->stream[stream].suspend_ignored) { - dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); - return 0; - } - - /* set pcm params */ - ret = ipc_pcm_params(swidget, stream); - if (ret < 0) { - dev_err(scomp->dev, - "error: failed to set pcm params for widget %s\n", - swidget->widget->name); - break; - } - - /* start trigger */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - break; - case SND_SOC_DAPM_POST_PMD: - if (spcm->stream[stream].suspend_ignored) { - dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n"); - return 0; - } - - /* stop trigger */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - - /* pcm free */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - break; - default: - break; - } - - return ret; -} - -/* event handlers for keyword detect component */ -static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { - {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_keyword_dapm_event}, -}; - static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) { /* we only support dB scale TLV type at the moment */ @@ -1323,38 +1157,6 @@ err: return ret; } -static int sof_widget_bind_event(struct snd_soc_component *scomp, - struct snd_sof_widget *swidget, - u16 event_type) -{ - struct sof_ipc_comp *ipc_comp; - - /* validate widget event type */ - switch (event_type) { - case SOF_KEYWORD_DETECT_DAPM_EVENT: - /* only KEYWORD_DETECT comps should handle this */ - if (swidget->id != snd_soc_dapm_effect) - break; - - ipc_comp = swidget->private; - if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) - break; - - /* bind event to keyword detect comp */ - return snd_soc_tplg_widget_bind_event(swidget->widget, - sof_kwd_events, - ARRAY_SIZE(sof_kwd_events), - event_type); - default: - break; - } - - dev_err(scomp->dev, - "error: invalid event type %d for widget %s\n", - event_type, swidget->widget->name); - return -EINVAL; -} - static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples) { int i; @@ -1486,14 +1288,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, /* bind widget to external event */ if (tw->event_type) { - ret = sof_widget_bind_event(scomp, swidget, - le16_to_cpu(tw->event_type)); - if (ret) { - dev_err(scomp->dev, "error: widget event binding failed\n"); - kfree(swidget->private); - kfree(swidget->tuples); - kfree(swidget); - return ret; + if (widget_ops[w->id].bind_event) { + ret = widget_ops[w->id].bind_event(scomp, swidget, + le16_to_cpu(tw->event_type)); + if (ret) { + dev_err(scomp->dev, "widget event binding failed for %s\n", + swidget->widget->name); + kfree(swidget->private); + kfree(swidget->tuples); + kfree(swidget); + return ret; + } } } -- cgit v1.2.3 From 61ad28ff6cf349c3e25f6c4a56ce4d140c003d19 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 14 Mar 2022 13:05:20 -0700 Subject: ASoC: SOF: topology: remove snd_sof_complete_pipeline() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new topology IPC op, pipeline_complete in struct ipc_tplg_ops and set the op for IPC3. Replace the calls to snd_sof_complete_pipeline() with the calls to the topology IPC pipeline_complete op. Signed-off-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220314200520.1233427-20-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 23 +++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 18 ++++++++++++------ sound/soc/sof/sof-audio.h | 4 ++-- sound/soc/sof/topology.c | 23 ----------------------- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index ea1311192877..fe1d5a56080a 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1887,6 +1887,28 @@ static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, return -EINVAL; } +static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_pipe_ready ready; + struct sof_ipc_reply reply; + int ret; + + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + swidget->widget->name, swidget->comp_id); + + memset(&ready, 0, sizeof(ready)); + ready.hdr.size = sizeof(ready); + ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; + ready.comp_id = swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply, + sizeof(reply)); + if (ret < 0) + return ret; + + return 1; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -1988,6 +2010,7 @@ static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .route_setup = sof_ipc3_route_setup, .control_setup = sof_ipc3_control_setup, .control_free = sof_ipc3_control_free, + .pipeline_complete = sof_ipc3_complete_pipeline, .token_list = ipc3_token_list, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index c02dcad03b23..683c290bb69a 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -362,6 +362,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; int i, ret, num_widgets; @@ -432,10 +433,12 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in if (pipe_widget->complete) continue; - pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget); - if (pipe_widget->complete < 0) { - ret = pipe_widget->complete; - goto widget_free; + if (ipc_tplg_ops->pipeline_complete) { + pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget); + if (pipe_widget->complete < 0) { + ret = pipe_widget->complete; + goto widget_free; + } } } @@ -657,8 +660,11 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } - swidget->complete = - snd_sof_complete_pipeline(sdev, swidget); + if (ipc_tplg_ops->pipeline_complete) { + swidget->complete = ipc_tplg_ops->pipeline_complete(sdev, swidget); + if (swidget->complete < 0) + return swidget->complete; + } break; default: break; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a14b872ea261..622d43707b27 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -68,6 +68,7 @@ struct sof_ipc_tplg_widget_ops { * initialized to 0. * @control_setup: Function pointer for setting up kcontrol IPC-specific data * @control_free: Function pointer for freeing kcontrol IPC-specific data + * @pipeline_complete: Function pointer for pipeline complete IPC */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -75,6 +76,7 @@ struct sof_ipc_tplg_ops { const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); + int (*pipeline_complete)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); }; /** struct snd_sof_tuple - Tuple info @@ -318,8 +320,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, * be freed by snd_soc_unregister_component, */ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget); /* * Stream IPC diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 70677a36c304..367fbe2d5b31 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1825,29 +1825,6 @@ err: return ret; } -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget) -{ - struct sof_ipc_pipe_ready ready; - struct sof_ipc_reply reply; - int ret; - - dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", - swidget->widget->name, swidget->comp_id); - - memset(&ready, 0, sizeof(ready)); - ready.hdr.size = sizeof(ready); - ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; - ready.comp_id = swidget->comp_id; - - ret = sof_ipc_tx_message(sdev->ipc, - ready.hdr.cmd, &ready, sizeof(ready), &reply, - sizeof(reply)); - if (ret < 0) - return ret; - return 1; -} - /** * sof_set_pipe_widget - Set pipe_widget for a component * @sdev: pointer to struct snd_sof_dev -- cgit v1.2.3 From d39664cb758e9e329ac3ba05bd4f813f928f63c2 Mon Sep 17 00:00:00 2001 From: Xiaoke Wang Date: Mon, 14 Mar 2022 18:36:45 +0800 Subject: ASoC: samsung: i2s: check the return value of kstrdup() kstrdup() is a memory allocation function which can return NULL when some internal memory errors happen. It is better to check the return value of it to catch the error in time. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xiaoke Wang Link: https://lore.kernel.org/r/tencent_EC21778DC383823CBC4069EA9F0B84943905@qq.com Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 309badc97290..70c827162be4 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1349,6 +1349,10 @@ static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) return -ENOMEM; pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL); + if (!pdev_sec->driver_override) { + platform_device_put(pdev_sec); + return -ENOMEM; + } ret = platform_device_add(pdev_sec); if (ret < 0) { -- cgit v1.2.3 From cc5d8ac95663a5813c696008bc524b794d471215 Mon Sep 17 00:00:00 2001 From: zhangqilong Date: Tue, 15 Mar 2022 10:54:15 +0800 Subject: ASoC: rockchip: Fix PM usage reference of rockchip_i2s_tdm_resume pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to putting operation will result in reference leak here. We fix it by replacing it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes:081068fd64140 ("ASoC: rockchip: add support for i2s-tdm controller") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20220315025415.2593762-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s_tdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 5f9cb5c4c7f0..d3b710406941 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1738,7 +1738,7 @@ static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); int ret; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; ret = regcache_sync(i2s_tdm->regmap); -- cgit v1.2.3 From 740dc3e846537c3743da98bf106f376023fd085c Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 16 Mar 2022 11:15:30 +0000 Subject: ASoC: atmel: Fix error handling in sam9x5_wm8731_driver_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This function only calls of_node_put() in the regular path. And it will cause refcount leak in error path. Fixes: fdbcb3cba54b ("ASoC: atmel: machine driver for at91sam9x5-wm8731 boards") Signed-off-by: Miaoqian Lin Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220316111530.4551-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/atmel/sam9x5_wm8731.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c index 7c45dc4f8c1b..99310e40e7a6 100644 --- a/sound/soc/atmel/sam9x5_wm8731.c +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -142,7 +142,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) if (!cpu_np) { dev_err(&pdev->dev, "atmel,ssc-controller node missing\n"); ret = -EINVAL; - goto out; + goto out_put_codec_np; } dai->cpus->of_node = cpu_np; dai->platforms->of_node = cpu_np; @@ -153,12 +153,9 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n", ret, priv->ssc_id); - goto out; + goto out_put_cpu_np; } - of_node_put(codec_np); - of_node_put(cpu_np); - ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "Platform device allocation failed\n"); @@ -167,10 +164,14 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s ok\n", __func__); - return ret; + goto out_put_cpu_np; out_put_audio: atmel_ssc_put_audio(priv->ssc_id); +out_put_cpu_np: + of_node_put(cpu_np); +out_put_codec_np: + of_node_put(codec_np); out: return ret; } -- cgit v1.2.3 From 28a265a1ee11febeec5ea73a804f30dcec3181ca Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 16 Mar 2022 01:40:57 +0000 Subject: ASoC: mediatek: Fix error handling in mt8183_da7219_max98357_dev_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This function only calls of_node_put() in the regular path. And it will cause refcount leak in error paths. Fix this by calling of_node_put() in error handling too. Signed-off-by: Miaoqian Lin Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20220316014059.19292-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 718505c75418..f090dee0c7a4 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -695,8 +695,11 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) } card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); - if (!card) - return -EINVAL; + if (!card) { + ret = -EINVAL; + goto put_platform_node; + } + card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, @@ -761,12 +764,15 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) if (!mt8183_da7219_max98357_headset_dev.dlc.of_node) { dev_err(&pdev->dev, "Property 'mediatek,headset-codec' missing/invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto put_hdmi_codec; } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + if (!priv) { + ret = -ENOMEM; + goto put_hdmi_codec; + } snd_soc_card_set_drvdata(card, priv); @@ -775,13 +781,16 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) ret = PTR_ERR(pinctrl); dev_err(&pdev->dev, "%s failed to select default state %d\n", __func__, ret); - return ret; + goto put_hdmi_codec; } ret = devm_snd_soc_register_card(&pdev->dev, card); - of_node_put(platform_node); + +put_hdmi_codec: of_node_put(hdmi_codec); +put_platform_node: + of_node_put(platform_node); return ret; } -- cgit v1.2.3 From e8ca4cee43fa9d841d25c2b98c9c3a3390593094 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 15 Mar 2022 23:08:16 +0000 Subject: ASoC: ti: Fix spelling mistake "cant" -> "can't" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Acked-by: Jarkko Nikula Acked-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20220315230816.2964577-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-dmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index a26588e9c3bc..f3eed20611a3 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -474,7 +474,7 @@ static int asoc_dmic_probe(struct platform_device *pdev) dmic->fclk = devm_clk_get(dmic->dev, "fck"); if (IS_ERR(dmic->fclk)) { - dev_err(dmic->dev, "cant get fck\n"); + dev_err(dmic->dev, "can't get fck\n"); return -ENODEV; } -- cgit v1.2.3 From 9ebd62d60edcd4d9c75485e5ccd0b79581ad3c49 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 16 Mar 2022 04:19:24 +0000 Subject: ASoC: msm8916-wcd-analog: Fix error handling in pm8916_wcd_analog_spmi_probe In the error handling path, the clk_prepare_enable() function call should be balanced by a corresponding 'clk_disable_unprepare()' call , as already done in the remove function. Fixes: de66b3455023 ("ASoC: codecs: msm8916-wcd-analog: add MBHC support") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220316041924.17560-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/msm8916-wcd-analog.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 485cda46dbb9..e52a559c52d6 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -1222,8 +1222,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) } irq = platform_get_irq_byname(pdev, "mbhc_switch_int"); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_disable_clk; + } ret = devm_request_threaded_irq(dev, irq, NULL, pm8916_mbhc_switch_irq_handler, @@ -1235,8 +1237,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) if (priv->mbhc_btn_enabled) { irq = platform_get_irq_byname(pdev, "mbhc_but_press_det"); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_disable_clk; + } ret = devm_request_threaded_irq(dev, irq, NULL, mbhc_btn_press_irq_handler, @@ -1247,8 +1251,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) dev_err(dev, "cannot request mbhc button press irq\n"); irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det"); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_disable_clk; + } ret = devm_request_threaded_irq(dev, irq, NULL, mbhc_btn_release_irq_handler, @@ -1265,6 +1271,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) return devm_snd_soc_register_component(dev, &pm8916_wcd_analog, pm8916_wcd_analog_dai, ARRAY_SIZE(pm8916_wcd_analog_dai)); + +err_disable_clk: + clk_disable_unprepare(priv->mclk); + return ret; } static int pm8916_wcd_analog_spmi_remove(struct platform_device *pdev) -- cgit v1.2.3 From abed17fdf92e4b3bfe336f7872270e0924cc4463 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 15 Mar 2022 17:37:40 +0800 Subject: ASoC: rt1308-sdw: get calibration params after power on It will be safe when getting the calibration params after power-on. All powers are ready to read the calibration params from EFUSE. Signed-off-by: Shuming Fan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220315093740.12008-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1308-sdw.c | 73 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 149a76075c76..1ef836a68a56 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -50,6 +50,8 @@ static bool rt1308_volatile_register(struct device *dev, unsigned int reg) case 0x3008: case 0x300a: case 0xc000: + case 0xc860 ... 0xc863: + case 0xc870 ... 0xc873: return true; default: return false; @@ -159,12 +161,45 @@ static int rt1308_read_prop(struct sdw_slave *slave) return 0; } +static void rt1308_apply_calib_params(struct rt1308_sdw_priv *rt1308) +{ + unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; + unsigned int efuse_c_btl_l, efuse_c_btl_r; + + /* read efuse to apply calibration parameters */ + regmap_write(rt1308->regmap, 0xc7f0, 0x04); + regmap_write(rt1308->regmap, 0xc7f1, 0xfe); + msleep(100); + regmap_write(rt1308->regmap, 0xc7f0, 0x44); + msleep(20); + regmap_write(rt1308->regmap, 0xc240, 0x10); + + regmap_read(rt1308->regmap, 0xc861, &tmp); + efuse_m_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc860, &tmp); + efuse_m_btl_l = efuse_m_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc863, &tmp); + efuse_c_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc862, &tmp); + efuse_c_btl_l = efuse_c_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc871, &tmp); + efuse_m_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc870, &tmp); + efuse_m_btl_r = efuse_m_btl_r | (tmp << 8); + regmap_read(rt1308->regmap, 0xc873, &tmp); + efuse_c_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc872, &tmp); + efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); + dev_dbg(&rt1308->sdw_slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, + efuse_m_btl_l, efuse_m_btl_r); + dev_dbg(&rt1308->sdw_slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, + efuse_c_btl_l, efuse_c_btl_r); +} + static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); int ret = 0; - unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; - unsigned int efuse_c_btl_l, efuse_c_btl_r; if (rt1308->hw_init) return 0; @@ -196,37 +231,6 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) /* sw reset */ regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); - /* read efuse */ - regmap_write(rt1308->regmap, 0xc360, 0x01); - regmap_write(rt1308->regmap, 0xc361, 0x80); - regmap_write(rt1308->regmap, 0xc7f0, 0x04); - regmap_write(rt1308->regmap, 0xc7f1, 0xfe); - msleep(100); - regmap_write(rt1308->regmap, 0xc7f0, 0x44); - msleep(20); - regmap_write(rt1308->regmap, 0xc240, 0x10); - - regmap_read(rt1308->regmap, 0xc861, &tmp); - efuse_m_btl_l = tmp; - regmap_read(rt1308->regmap, 0xc860, &tmp); - efuse_m_btl_l = efuse_m_btl_l | (tmp << 8); - regmap_read(rt1308->regmap, 0xc863, &tmp); - efuse_c_btl_l = tmp; - regmap_read(rt1308->regmap, 0xc862, &tmp); - efuse_c_btl_l = efuse_c_btl_l | (tmp << 8); - regmap_read(rt1308->regmap, 0xc871, &tmp); - efuse_m_btl_r = tmp; - regmap_read(rt1308->regmap, 0xc870, &tmp); - efuse_m_btl_r = efuse_m_btl_r | (tmp << 8); - regmap_read(rt1308->regmap, 0xc873, &tmp); - efuse_c_btl_r = tmp; - regmap_read(rt1308->regmap, 0xc872, &tmp); - efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); - dev_dbg(&slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, - efuse_m_btl_l, efuse_m_btl_r); - dev_dbg(&slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, - efuse_c_btl_l, efuse_c_btl_r); - /* initial settings */ regmap_write(rt1308->regmap, 0xc103, 0xc0); regmap_write(rt1308->regmap, 0xc030, 0x17); @@ -323,6 +327,8 @@ static int rt1308_classd_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -331,6 +337,7 @@ static int rt1308_classd_event(struct snd_soc_dapm_widget *w, RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), 0x3, 0x3); msleep(40); + rt1308_apply_calib_params(rt1308); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_component_update_bits(component, -- cgit v1.2.3 From c4b7174fe5bb875a09a78674a14a1589d1a672f3 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 16 Mar 2022 08:46:15 +0000 Subject: ASoC: mediatek: mt8195: Fix error handling in mt8195_mt6359_rt1019_rt5682_dev_probe The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This function only calls of_node_put() in the regular path. And it will cause refcount leak in error path. Fixes: 082482a50227 ("ASoC: mediatek: mt8195: release device_node after snd_soc_register_card") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220316084623.24238-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index 29c2d3407cc7..e3146311722f 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1342,7 +1342,8 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) "mediatek,dai-link"); if (ret) { dev_dbg(&pdev->dev, "Parse dai-link fail\n"); - return -EINVAL; + ret = -EINVAL; + goto put_node; } } else { if (!sof_on) @@ -1398,6 +1399,7 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); +put_node: of_node_put(platform_node); of_node_put(adsp_node); of_node_put(dp_node); -- cgit v1.2.3 From 9531a631379169d57756b2411178c6238655df88 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 16 Mar 2022 08:36:31 +0000 Subject: ASoC: codecs: wcd934x: Add missing of_node_put() in wcd934x_codec_parse_data The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. This is similar to commit 64b92de9603f ("ASoC: wcd9335: fix a leaked reference by adding missing of_node_put") Fixes: a61f3b4f476e ("ASoC: wcd934x: add support to wcd9340/wcd9341 codec") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220316083631.14103-1-linmq006@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 6c468527fec6..acd344c4a37a 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -5883,6 +5883,7 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) } wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); + of_node_put(ifc_dev_np); if (!wcd->sidev) { dev_err(dev, "Unable to get SLIM Interface device\n"); return -EINVAL; -- cgit v1.2.3 From 54e1bf9f6177a3ffbd920474f4481a25361163aa Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Wed, 16 Mar 2022 17:13:03 +0800 Subject: ASoC: amd: Fix reference to PCM buffer address PCM buffers might be allocated dynamically when the buffer preallocation failed or a larger buffer is requested, and it's not guaranteed that substream->dma_buffer points to the actually used buffer. The driver needs to refer to substream->runtime->dma_addr instead for the buffer address. Fixes: cab396d8b22c1 ("ASoC: amd: add ACP5x pcm dma driver ops") Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220316091303.9745-1-tangmeng@uniontech.com Signed-off-by: Mark Brown --- sound/soc/amd/vangogh/acp5x-pcm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index 31fa166df98a..d36bb718370f 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -281,7 +281,7 @@ static int acp5x_dma_hw_params(struct snd_soc_component *component, return -EINVAL; } size = params_buffer_bytes(params); - rtd->dma_addr = substream->dma_buffer.addr; + rtd->dma_addr = substream->runtime->dma_addr; rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); config_acp5x_dma(rtd, substream->stream); return 0; -- cgit v1.2.3 From 455c5653f50e10b4f460ef24e99f0044fbe3401c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2022 17:41:58 +0100 Subject: ASoC: sti: Fix deadlock via snd_pcm_stop_xrun() call This is essentially a revert of the commit dc865fb9e7c2 ("ASoC: sti: Use snd_pcm_stop_xrun() helper"), which converted the manual snd_pcm_stop() calls with snd_pcm_stop_xrun(). The commit above introduced a deadlock as snd_pcm_stop_xrun() itself takes the PCM stream lock while the caller already holds it. Since the conversion was done only for consistency reason and the open-call with snd_pcm_stop() to the XRUN state is a correct usage, let's revert the commit back as the fix. Fixes: dc865fb9e7c2 ("ASoC: sti: Use snd_pcm_stop_xrun() helper") Reported-by: Daniel Palmer Cc: Arnaud POULIQUEN Cc: Link: https://lore.kernel.org/r/20220315091319.3351522-1-daniel@0x0f.com Signed-off-by: Takashi Iwai Reviewed-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20220315164158.19804-1-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/sti/uniperif_player.c | 6 +++--- sound/soc/sti/uniperif_reader.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 2ed92c990b97..dd9013c47664 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -91,7 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); } ret = IRQ_HANDLED; @@ -105,7 +105,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } @@ -138,7 +138,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index 136059331211..065c5f0d1f5f 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -65,7 +65,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { dev_err(reader->dev, "FIFO error detected\n"); - snd_pcm_stop_xrun(reader->substream); + snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } -- cgit v1.2.3 From 1c19601ddceda1517511e4bad3d24619e765c78c Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Tue, 15 Mar 2022 20:15:31 +0530 Subject: ASoC: codecs: Fix misplaced lpass_macro_pds_exit call Update power domains exit function calling from runtime resume to remove function which was wrongly placed and causing crash in device suspend and resume. Fixes: 9e3d83c52844 ("ASoC: codecs: Add power domains support in digital macro codecs") Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1647355531-4150-1-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 4 ++-- sound/soc/codecs/lpass-va-macro.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index b492d5984819..714a411d5337 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1920,6 +1920,8 @@ static int tx_macro_remove(struct platform_device *pdev) clk_disable_unprepare(tx->npl); clk_disable_unprepare(tx->fsgen); + lpass_macro_pds_exit(tx->pds); + return 0; } @@ -1964,8 +1966,6 @@ static int __maybe_unused tx_macro_runtime_resume(struct device *dev) regcache_sync(tx->regmap); tx->reset_swr = true; - lpass_macro_pds_exit(tx->pds); - return 0; err_fsgen: clk_disable_unprepare(tx->npl); diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 275f2cd78c9c..37674f68616b 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1503,6 +1503,8 @@ static int va_macro_remove(struct platform_device *pdev) clk_disable_unprepare(va->dcodec); clk_disable_unprepare(va->macro); + lpass_macro_pds_exit(va->pds); + return 0; } @@ -1532,8 +1534,6 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) regcache_cache_only(va->regmap, false); regcache_sync(va->regmap); - lpass_macro_pds_exit(va->pds); - return 0; } -- cgit v1.2.3 From 835ca59799f5c60b4b54bdc7aa785c99552f63e4 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Tue, 15 Mar 2022 02:32:26 +0000 Subject: ASoC: ak4642: Use of_device_get_match_data() Use of_device_get_match_data() to simplify the code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Link: https://lore.kernel.org/r/20220315023226.2118354-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 4d2e78101f28..034195c83bd7 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -653,15 +653,10 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, struct ak4613_priv *priv; regmap_cfg = NULL; - if (np) { - const struct of_device_id *of_id; - - of_id = of_match_device(ak4613_of_match, dev); - if (of_id) - regmap_cfg = of_id->data; - } else { + if (np) + regmap_cfg = of_device_get_match_data(dev); + else regmap_cfg = (const struct regmap_config *)id->driver_data; - } if (!regmap_cfg) return -EINVAL; -- cgit v1.2.3 From b26f965f7c83464f60d5af411ef9a03079e41800 Mon Sep 17 00:00:00 2001 From: Yihao Han Date: Thu, 17 Mar 2022 02:38:41 -0700 Subject: ASoC: SOF: topology: Use kmemdup() to replace kzalloc + memcpy fix memdup.cocci warning: sound/soc/sof/topology.c:876:19-26: WARNING opportunity for kmemdup Generated by: scripts/coccinelle/api/memdup.cocci Signed-off-by: Yihao Han Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317093841.3414-1-hanyihao@vivo.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 367fbe2d5b31..369693cc6d10 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -873,11 +873,10 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, /* copy the private data */ if (priv_size > 0) { - scontrol->priv = kzalloc(priv_size, GFP_KERNEL); + scontrol->priv = kmemdup(control->priv.data, priv_size, GFP_KERNEL); if (!scontrol->priv) return -ENOMEM; - memcpy(scontrol->priv, control->priv.data, priv_size); scontrol->priv_size = priv_size; } -- cgit v1.2.3 From 638cec39339db40df9fc2ece0411a64856669b93 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 16 Mar 2022 13:28:58 +0800 Subject: ASoC: fsl_spdif: Add new registers included on i.MX8ULP There are some new registers added on i.MX8ULP, they are the SPDIF transmit Professional C channel registers, 192bit SPDIF receive C channel registers, and 192bit SPDIF transmit C channel registers. There are two output lines, SPDIF_OUT1 and SPDIF_OUT2, the original REG_SPDIF_STCSCH and REG_SPDIF_STCSCL are used for SPDIF_OUT1, the new REG_SPDIF_STCSPH and REG_SPDIF_STCSPL are used for SPDIF_OUT2, the 192bit SPDIF C channel registers are used for both. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1647408538-2982-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/fsl/fsl_spdif.h | 14 +++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index b502e7c3c04d..42d11aca38a1 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -50,6 +50,7 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; * @shared_root_clock: flag of sharing a clock source with others; * so the driver shouldn't set root clock rate * @raw_capture_mode: if raw capture mode support + * @cchannel_192b: if there are registers for 192bits C channel data * @interrupts: interrupt number * @tx_burst: tx maxburst size * @rx_burst: rx maxburst size @@ -59,6 +60,7 @@ struct fsl_spdif_soc_data { bool imx; bool shared_root_clock; bool raw_capture_mode; + bool cchannel_192b; u32 interrupts; u32 tx_burst; u32 rx_burst; @@ -196,6 +198,7 @@ static struct fsl_spdif_soc_data fsl_spdif_imx8ulp = { .tx_burst = 2, /* Applied for EDMA */ .rx_burst = 2, /* Applied for EDMA */ .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */ + .cchannel_192b = true, }; /* Check if clk is a root clock that does not share clock source with others */ @@ -441,6 +444,23 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) regmap_write(regmap, REG_SPDIF_STCSCL, ch_status); dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status); + + if (spdif_priv->soc->cchannel_192b) { + ch_status = (bitrev8(ctrl->ch_status[0]) << 24) | + (bitrev8(ctrl->ch_status[1]) << 16) | + (bitrev8(ctrl->ch_status[2]) << 8) | + bitrev8(ctrl->ch_status[3]); + + regmap_update_bits(regmap, REG_SPDIF_SCR, 0x1000000, 0x1000000); + + /* + * The first 32bit should be in REG_SPDIF_STCCA_31_0 register, + * but here we need to set REG_SPDIF_STCCA_191_160 on 8ULP + * then can get correct result with HDMI analyzer capture. + * There is a hardware bug here. + */ + regmap_write(regmap, REG_SPDIF_STCCA_191_160, ch_status); + } } /* Set SPDIF PhaseConfig register for rx clock */ @@ -1229,6 +1249,8 @@ static const struct reg_default fsl_spdif_reg_defaults[] = { {REG_SPDIF_STR, 0x00000000}, {REG_SPDIF_STCSCH, 0x00000000}, {REG_SPDIF_STCSCL, 0x00000000}, + {REG_SPDIF_STCSPH, 0x00000000}, + {REG_SPDIF_STCSPL, 0x00000000}, {REG_SPDIF_STC, 0x00020f00}, }; @@ -1248,8 +1270,22 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) case REG_SPDIF_SRQ: case REG_SPDIF_STCSCH: case REG_SPDIF_STCSCL: + case REG_SPDIF_STCSPH: + case REG_SPDIF_STCSPL: case REG_SPDIF_SRFM: case REG_SPDIF_STC: + case REG_SPDIF_SRCCA_31_0: + case REG_SPDIF_SRCCA_63_32: + case REG_SPDIF_SRCCA_95_64: + case REG_SPDIF_SRCCA_127_96: + case REG_SPDIF_SRCCA_159_128: + case REG_SPDIF_SRCCA_191_160: + case REG_SPDIF_STCCA_31_0: + case REG_SPDIF_STCCA_63_32: + case REG_SPDIF_STCCA_95_64: + case REG_SPDIF_STCCA_127_96: + case REG_SPDIF_STCCA_159_128: + case REG_SPDIF_STCCA_191_160: return true; default: return false; @@ -1268,6 +1304,12 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) case REG_SPDIF_SRU: case REG_SPDIF_SRQ: case REG_SPDIF_SRFM: + case REG_SPDIF_SRCCA_31_0: + case REG_SPDIF_SRCCA_63_32: + case REG_SPDIF_SRCCA_95_64: + case REG_SPDIF_SRCCA_127_96: + case REG_SPDIF_SRCCA_159_128: + case REG_SPDIF_SRCCA_191_160: return true; default: return false; @@ -1286,7 +1328,15 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) case REG_SPDIF_STR: case REG_SPDIF_STCSCH: case REG_SPDIF_STCSCL: + case REG_SPDIF_STCSPH: + case REG_SPDIF_STCSPL: case REG_SPDIF_STC: + case REG_SPDIF_STCCA_31_0: + case REG_SPDIF_STCCA_63_32: + case REG_SPDIF_STCCA_95_64: + case REG_SPDIF_STCCA_127_96: + case REG_SPDIF_STCCA_159_128: + case REG_SPDIF_STCCA_191_160: return true; default: return false; @@ -1298,7 +1348,7 @@ static const struct regmap_config fsl_spdif_regmap_config = { .reg_stride = 4, .val_bits = 32, - .max_register = REG_SPDIF_STC, + .max_register = REG_SPDIF_STCCA_191_160, .reg_defaults = fsl_spdif_reg_defaults, .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults), .readable_reg = fsl_spdif_readable_reg, diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index bff8290e71f2..75b42a692c90 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -31,9 +31,23 @@ #define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */ #define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */ #define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */ +#define REG_SPDIF_STCSPH 0x3C /* SPDIFTxCChannel_Prof_h Register */ +#define REG_SPDIF_STCSPL 0x40 /* SPDIFTxCChannel_Prof_l Register */ #define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */ #define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */ +#define REG_SPDIF_SRCCA_31_0 0x60 /* SPDIF receive C channel register, bits 31-0 */ +#define REG_SPDIF_SRCCA_63_32 0x64 /* SPDIF receive C channel register, bits 63-32 */ +#define REG_SPDIF_SRCCA_95_64 0x68 /* SPDIF receive C channel register, bits 95-64 */ +#define REG_SPDIF_SRCCA_127_96 0x6C /* SPDIF receive C channel register, bits 127-96 */ +#define REG_SPDIF_SRCCA_159_128 0x70 /* SPDIF receive C channel register, bits 159-128 */ +#define REG_SPDIF_SRCCA_191_160 0x74 /* SPDIF receive C channel register, bits 191-160 */ +#define REG_SPDIF_STCCA_31_0 0x78 /* SPDIF transmit C channel register, bits 31-0 */ +#define REG_SPDIF_STCCA_63_32 0x7C /* SPDIF transmit C channel register, bits 63-32 */ +#define REG_SPDIF_STCCA_95_64 0x80 /* SPDIF transmit C channel register, bits 95-64 */ +#define REG_SPDIF_STCCA_127_96 0x84 /* SPDIF transmit C channel register, bits 127-96 */ +#define REG_SPDIF_STCCA_159_128 0x88 /* SPDIF transmit C channel register, bits 159-128 */ +#define REG_SPDIF_STCCA_191_160 0x8C /* SPDIF transmit C channel register, bits 191-160 */ /* SPDIF Configuration register */ #define SCR_RXFIFO_CTL_OFFSET 23 -- cgit v1.2.3 From 864cb14c0fa22344613ae93d68e155bf9bbbc9fb Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 18 Mar 2022 06:11:33 +0800 Subject: ALSA: hda/realtek: Fix LED on Zbook Studio G9 Commit 07bcab93946c ("ALSA: hda/realtek: Add support for HP Laptops") breaks mute and micmute LEDs because it changed the LED quirk from ALC285_FIXUP_HP_GPIO_LED to ALC245_FIXUP_HP_GPIO_LED, so change it back here. Also reorder the chain of quirks to ensure LED quirk is the last one being applied. Fixes: 07bcab93946c ("ALSA: hda/realtek: Add support for HP Laptops") Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20220317221134.566358-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d7bebe3bdea7..0a9ad35d34c1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8773,9 +8773,9 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_gpio_led, + .v.func = cs35l41_fixup_spi_four, .chained = true, - .chain_id = ALC245_FIXUP_CS35L41_SPI_4, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { .type = HDA_FIXUP_VERBS, -- cgit v1.2.3 From a893b7fc7b59cdf3fae01335c86537bee4407edc Mon Sep 17 00:00:00 2001 From: Elijah Harding Date: Thu, 17 Mar 2022 18:52:01 -0700 Subject: ALSA: core: Fix typo in 'PCM Timer Interface' help Signed-off-by: Elijah Harding Link: https://lore.kernel.org/r/20220318015201.30871-1-eharding830@gmail.com Signed-off-by: Takashi Iwai --- sound/core/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/Kconfig b/sound/core/Kconfig index db2e3c63ff41..dd7b40734723 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -84,7 +84,7 @@ config SND_PCM_TIMER help If you disable this option, pcm timer will be unavailable, so those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work - incorrectlly. + incorrectly. For some embedded devices, we may disable it to reduce memory footprint, about 20KB on x86_64 platform. -- cgit v1.2.3 From efb6402c3c4a7c26d97c92d70186424097b6e366 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Mar 2022 09:20:36 +0100 Subject: ALSA: oss: Fix PCM OSS buffer allocation overflow We've got syzbot reports hitting INT_MAX overflow at vmalloc() allocation that is called from snd_pcm_plug_alloc(). Although we apply the restrictions to input parameters, it's based only on the hw_params of the underlying PCM device. Since the PCM OSS layer allocates a temporary buffer for the data conversion, the size may become unexpectedly large when more channels or higher rates is given; in the reported case, it went over INT_MAX, hence it hits WARN_ON(). This patch is an attempt to avoid such an overflow and an allocation for too large buffers. First off, it adds the limit of 1MB as the upper bound for period bytes. This must be large enough for all use cases, and we really don't want to handle a larger temporary buffer than this size. The size check is performed at two places, where the original period bytes is calculated and where the plugin buffer size is calculated. In addition, the driver uses array_size() and array3_size() for multiplications to catch overflows for the converted period size and buffer bytes. Reported-by: syzbot+72732c532ac1454eeee9@syzkaller.appspotmail.com Suggested-by: Linus Torvalds Cc: Link: https://lore.kernel.org/r/00000000000085b1b305da5a66f3@google.com Link: https://lore.kernel.org/r/20220318082036.29699-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/oss/pcm_oss.c | 12 ++++++++---- sound/core/oss/pcm_plugin.c | 5 ++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 3ee9edf85815..f158f0abd25d 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -774,6 +774,11 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, if (oss_period_size < 16) return -EINVAL; + + /* don't allocate too large period; 1MB period must be enough */ + if (oss_period_size > 1024 * 1024) + return -ENOMEM; + runtime->oss.period_bytes = oss_period_size; runtime->oss.period_frames = 1; runtime->oss.periods = oss_periods; @@ -1043,10 +1048,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) goto failure; } #endif - oss_period_size *= oss_frame_size; - - oss_buffer_size = oss_period_size * runtime->oss.periods; - if (oss_buffer_size < 0) { + oss_period_size = array_size(oss_period_size, oss_frame_size); + oss_buffer_size = array_size(oss_period_size, runtime->oss.periods); + if (oss_buffer_size <= 0) { err = -EINVAL; goto failure; } diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 061ba06bc926..82e180c776ae 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -62,7 +62,10 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t width = snd_pcm_format_physical_width(format->format); if (width < 0) return width; - size = frames * format->channels * width; + size = array3_size(frames, format->channels, width); + /* check for too large period size once again */ + if (size > 1024 * 1024) + return -ENOMEM; if (snd_BUG_ON(size % 8)) return -ENXIO; size /= 8; -- cgit v1.2.3 From 8a580a26760cb14535c160613fe9cd0e4dc6f5c6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Mar 2022 09:21:57 +0100 Subject: ALSA: oss: Release temporary buffers upon errors When the parameter changes fails, we don't need to keep the old temporary buffers. Release those (and plugin instances) upon errors for reducing dead memory footprint. Since we always call it at the exit of snd_pcm_oss_changes_params_locked(), the explicit calls of snd_pcm_oss_plugin_clear() can be dropped, too. Along with it, unify the buffer-free calls to a single helper and call it from the needed places. Link: https://lore.kernel.org/r/20220318082157.29769-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/oss/pcm_oss.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 3ee9edf85815..b8afa9f02dd6 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -837,6 +837,17 @@ static void unlock_params(struct snd_pcm_runtime *runtime) mutex_unlock(&runtime->oss.params_lock); } +static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + kvfree(runtime->oss.buffer); + runtime->oss.buffer = NULL; +#ifdef CONFIG_SND_PCM_OSS_PLUGINS + snd_pcm_oss_plugin_clear(substream); +#endif +} + /* call with params_lock held */ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) { @@ -967,12 +978,10 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) snd_pcm_oss_plugin_clear(substream); if (!direct) { /* add necessary plugins */ - snd_pcm_oss_plugin_clear(substream); err = snd_pcm_plug_format_plugins(substream, params, sparams); if (err < 0) { pcm_dbg(substream->pcm, "snd_pcm_plug_format_plugins failed: %i\n", err); - snd_pcm_oss_plugin_clear(substream); goto failure; } if (runtime->oss.plugin_first) { @@ -981,7 +990,6 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) if (err < 0) { pcm_dbg(substream->pcm, "snd_pcm_plugin_build_io failed: %i\n", err); - snd_pcm_oss_plugin_clear(substream); goto failure; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -989,10 +997,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) } else { err = snd_pcm_plugin_insert(plugin); } - if (err < 0) { - snd_pcm_oss_plugin_clear(substream); + if (err < 0) goto failure; - } } } #endif @@ -1082,6 +1088,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) err = 0; failure: + if (err) + snd_pcm_oss_release_buffers(substream); kfree(sw_params); kfree(params); kfree(sparams); @@ -2351,13 +2359,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime; - runtime = substream->runtime; - kvfree(runtime->oss.buffer); - runtime->oss.buffer = NULL; -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - snd_pcm_oss_plugin_clear(substream); -#endif + snd_pcm_oss_release_buffers(substream); substream->oss.oss = 0; } -- cgit v1.2.3 From 40c2c63ac40d26bb0b8e17ada32e84541363f1b0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:26 -0700 Subject: ASoC: SOF: set up scheduler widget before all other widgets in the pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For dynamic pipelines, We set up the DAI widget during BE DAI hw_params and this results in it getting set up before the scheduler widget for the pipeline it belongs to is set up. Move the scheduler widget set up into sof_widget_setup() to ensure that the scheduler widget is always the first widget in a pipeline to be set up and the last one to get freed after all the other widgets have been freed. Fixes: 5fcdbb2d45df ('ASoC: SOF: Add support for dynamic pipelines') Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 82 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 683c290bb69a..cc0d647a2d0d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -104,7 +104,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) .id = swidget->comp_id, }; struct sof_ipc_reply reply; - int ret, ret1; + int ret, err; if (!swidget->private) return 0; @@ -136,31 +136,41 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } /* continue to disable core even if IPC fails */ - ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + err = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), &reply, sizeof(reply)); - if (ret < 0) + if (err < 0) dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); /* * disable widget core. continue to route setup status and complete flag * even if this fails and return the appropriate error */ - ret1 = snd_sof_dsp_core_put(sdev, swidget->core); - if (ret1 < 0) { + ret = snd_sof_dsp_core_put(sdev, swidget->core); + if (ret < 0) { dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", swidget->core, swidget->widget->name); - if (!ret) - ret = ret1; + if (!err) + err = ret; } /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); swidget->complete = 0; - if (!ret) + /* + * free the scheduler widget (same as pipe_widget) associated with the current swidget. + * skip for static pipelines + */ + if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { + ret = sof_widget_free(sdev, swidget->pipe_widget); + if (ret < 0 && !err) + err = ret; + } + + if (!err) dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); - return ret; + return err; } EXPORT_SYMBOL(sof_widget_free); @@ -181,12 +191,32 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (++swidget->use_count > 1) return 0; + /* + * The scheduler widget for a pipeline is not part of the connected DAPM + * widget list and it needs to be set up before the widgets in the pipeline + * are set up. The use_count for the scheduler widget is incremented for every + * widget in a given pipeline to ensure that it is freed only after the last + * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines. + */ + if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { + if (!swidget->pipe_widget) { + dev_err(sdev->dev, "No scheduler widget set for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto use_count_dec; + } + + ret = sof_widget_setup(sdev, swidget->pipe_widget); + if (ret < 0) + goto use_count_dec; + } + /* enable widget core */ ret = snd_sof_dsp_core_get(sdev, swidget->core); if (ret < 0) { dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", swidget->widget->name); - goto use_count_dec; + goto pipe_widget_free; } switch (swidget->id) { @@ -257,6 +287,9 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) core_put: snd_sof_dsp_core_put(sdev, swidget->core); +pipe_widget_free: + if (swidget->id != snd_soc_dapm_scheduler) + sof_widget_free(sdev, swidget->pipe_widget); use_count_dec: swidget->use_count--; return ret; @@ -374,36 +407,14 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in /* set up widgets in the list */ for_each_dapm_widgets(list, num_widgets, widget) { struct snd_sof_widget *swidget = widget->dobj.private; - struct snd_sof_widget *pipe_widget; if (!swidget) continue; - /* - * The scheduler widget for a pipeline is not part of the connected DAPM - * widget list and it needs to be set up before the widgets in the pipeline - * are set up. The use_count for the scheduler widget is incremented for every - * widget in a given pipeline to ensure that it is freed only after the last - * widget in the pipeline is freed. - */ - pipe_widget = swidget->pipe_widget; - if (!pipe_widget) { - dev_err(sdev->dev, "error: no pipeline widget found for %s\n", - swidget->widget->name); - ret = -EINVAL; - goto widget_free; - } - - ret = sof_widget_setup(sdev, pipe_widget); - if (ret < 0) - goto widget_free; - /* set up the widget */ ret = sof_widget_setup(sdev, swidget); - if (ret < 0) { - sof_widget_free(sdev, pipe_widget); + if (ret < 0) goto widget_free; - } } /* @@ -456,7 +467,6 @@ widget_free: break; sof_widget_free(sdev, swidget); - sof_widget_free(sdev, swidget->pipe_widget); } return ret; @@ -490,10 +500,6 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int ret = sof_widget_free(sdev, swidget); if (ret < 0) ret1 = ret; - - ret = sof_widget_free(sdev, swidget->pipe_widget); - if (ret < 0) - ret1 = ret; } snd_soc_dapm_dai_free_widgets(&list); -- cgit v1.2.3 From 051744b1bf0b13f63af5de3c296d04ab0cc6117c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:27 -0700 Subject: ASoC: SOF: Make sof_widget_setup/free IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 3 new topology IPC ops for widget_setup, widget_free and dai_config in order to make the pipeline management code IPC agnostic and implement the ops for IPC3. Use the newly introduced tplg dai_config op to configure the DAI during BE DAI hw_params and hw_free. Also, in preparation for IPC4, modify BE hw_params callback to skip setting up the DAI widget. All widgets will be set up during FW hw_params and the DAI_CONFIG IPC should be sent only if the widget use_count is > 0. With setting up/freeing removed from the BE hw_params, remove the configured flag as it is no longer needed. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220317175044.1752400-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 97 ++++-------------------- sound/soc/sof/intel/hda.c | 169 +++++++++++------------------------------- sound/soc/sof/intel/hda.h | 7 +- sound/soc/sof/ipc3-topology.c | 150 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 136 ++++++--------------------------- sound/soc/sof/sof-audio.h | 13 +++- 6 files changed, 246 insertions(+), 326 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 9b78eea8d76b..f9cb9f1f0237 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -162,58 +162,19 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream, return 0; } -/* Update config for the DAI widget */ -static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w, - int channel) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct sof_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - - if (!swidget) - return NULL; - - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(swidget->scomp->dev, "%s: No private data for DAI %s\n", __func__, - w->name); - return NULL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(swidget->scomp->dev, "%s: No config for DAI %s\n", __func__, w->name); - return NULL; - } - - config = &private->dai_config[sof_dai->current_config]; - - /* update config with stream tag */ - config->hda.link_dma_ch = channel; - - return config; -} - static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, struct snd_soc_dapm_widget *w, int channel, bool widget_setup) { - struct snd_sof_dev *sdev = hda_stream->sdev; - struct sof_ipc_dai_config *config; + struct snd_sof_dai_config_data data; - config = hda_dai_update_config(w, channel); - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); - return -ENOENT; - } + data.dai_data = channel; /* set up/free DAI widget and send DAI_CONFIG IPC */ if (widget_setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data); - return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); } static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -302,35 +263,16 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; - int ret; - - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int ret = 0; - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); - return -EINVAL; + if (tplg_ops->dai_config) { + ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL); + if (ret < 0) + dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__, + w->name); } - config = &private->dai_config[sof_dai->current_config]; - - /* set PAUSE command flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); - - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); - if (ret < 0) - dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); - return ret; } @@ -470,30 +412,17 @@ struct ssp_dai_dma_data { static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool setup) { - struct snd_soc_component *component; - struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - struct sof_ipc_fw_version *v; - struct snd_sof_dev *sdev; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; else w = dai->capture_widget; - swidget = w->dobj.private; - component = swidget->scomp; - sdev = snd_soc_component_get_drvdata(component); - v = &sdev->fw_ready.version; - - /* DAI_CONFIG IPC during hw_params is not supported in older firmware */ - if (v->abi_version < SOF_ABI_VER(3, 18, 0)) - return 0; - if (setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL); - return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL); } static int ssp_dai_startup(struct snd_pcm_substream *substream, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 7403b9848a56..019f8d6b91d4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,114 +41,68 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_ipc_dai_config *config; - struct sof_dai_private_data *private; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + struct snd_sof_dai *sof_dai = swidget->private; int ret; - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); + if (!sof_dai) { + dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name); return -EINVAL; } - /* DAI already configured, reset it before reconfiguring it */ - if (sof_dai->configured) { - ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); - if (ret < 0) - return ret; - } - - config = &private->dai_config[sof_dai->current_config]; - - /* - * For static pipelines, the DAI widget would already be set up and calling - * sof_widget_setup() simply returns without doing anything. - * For dynamic pipelines, the DAI widget will be set up now. - */ - ret = sof_widget_setup(sdev, swidget); - if (ret < 0) { - dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name); - return ret; - } + if (tplg_ops->dai_config) { + unsigned int flags; - /* set HW_PARAMS flag along with quirks */ - config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | - quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* set HW_PARAMS flag along with quirks */ + flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; - - /* send DAI_CONFIG IPC */ - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name); - return ret; + ret = tplg_ops->dai_config(sdev, swidget, flags, data); + if (ret < 0) { + dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__, + w->name); + return ret; + } } - sof_dai->configured = true; - return 0; } -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; - int ret; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + struct snd_sof_dai *sof_dai = swidget->private; - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); + if (!sof_dai) { + dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name); return -EINVAL; } - /* nothing to do if hw_free() is called without restarting the stream after resume. */ - if (!sof_dai->configured) - return 0; - - config = &private->dai_config[sof_dai->current_config]; + if (tplg_ops->dai_config) { + unsigned int flags; + int ret; - /* set HW_FREE flag along with any quirks */ - config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | - quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* set HW_FREE flag along with any quirks */ + flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); - if (ret < 0) - dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name); - - /* - * Reset the configured_flag and free the widget even if the IPC fails to keep - * the widget use_count balanced - */ - sof_dai->configured = false; + ret = tplg_ops->dai_config(sdev, swidget, flags, data); + if (ret < 0) + dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__, + w->name); + } - return sof_widget_free(sdev, swidget); + return 0; } #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) @@ -163,69 +117,34 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET; module_param(sdw_clock_stop_quirks, int, 0444); MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); -static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, - struct snd_soc_dapm_widget *w, - int link_id, int alh_stream_id, int dai_id, bool setup) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct sof_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - - if (!swidget) { - dev_err(sdev->dev, "error: No private data for widget %s\n", w->name); - return -EINVAL; - } - - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - config = &private->dai_config[sof_dai->current_config]; - - /* update config with link and stream ID */ - config->dai_index = (link_id << 8) | dai_id; - config->alh.stream_id = alh_stream_id; - - if (setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - - return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); -} - static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = params_data->dai; + struct snd_sof_dai_config_data data; struct snd_soc_dapm_widget *w; w = snd_soc_dai_get_widget(d, params_data->stream); + data.dai_index = (params_data->link_id << 8) | d->id; + data.dai_data = params_data->alh_stream_id; - return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, - d->id, true); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); } static int sdw_free_stream(struct device *dev, struct sdw_intel_stream_free_data *free_data) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = free_data->dai; + struct snd_sof_dai_config_data data; struct snd_soc_dapm_widget *w; w = snd_soc_dai_get_widget(d, free_data->stream); + data.dai_index = (free_data->link_id << 8) | d->id; /* send invalid stream_id */ - return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); + data.dai_data = 0xFFFF; + + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); } static const struct sdw_intel_ops sdw_callback = { diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 13b509c9f481..05e5e158614a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -17,6 +17,7 @@ #include #include #include "../sof-client-probes.h" +#include "../sof-audio.h" #include "shim.h" /* PCI registers */ @@ -730,8 +731,10 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) struct snd_sof_dai; struct sof_ipc_dai_config; -int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data); #define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ #define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index fe1d5a56080a..8d08ffb37008 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1909,6 +1909,153 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w return 1; } +static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_free ipc_free = { + .hdr = { + .size = sizeof(ipc_free), + .cmd = SOF_IPC_GLB_TPLG_MSG, + }, + .id = swidget->comp_id, + }; + struct sof_ipc_reply reply; + int ret; + + if (!swidget->private) + return 0; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + { + ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; + break; + } + case snd_soc_dapm_buffer: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; + break; + default: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; + break; + } + + ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name); + + return ret; +} + +static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + unsigned int flags, struct snd_sof_dai_config_data *data) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private; + struct sof_ipc_dai_config *config; + struct sof_ipc_reply reply; + int ret = 0; + + if (!dai || !dai->private) { + dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name); + return -EINVAL; + } + + private = dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", dai->name); + return -EINVAL; + } + + config = &private->dai_config[dai->current_config]; + if (!config) { + dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name); + return -EINVAL; + } + + switch (config->type) { + case SOF_DAI_INTEL_SSP: + /* + * DAI_CONFIG IPC during hw_params/hw_free for SSP DAI's is not supported in older + * firmware + */ + if (v->abi_version < SOF_ABI_VER(3, 18, 0) && + ((flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) || + (flags & SOF_DAI_CONFIG_FLAGS_HW_FREE))) + return 0; + break; + case SOF_DAI_INTEL_HDA: + if (data) + config->hda.link_dma_ch = data->dai_data; + break; + case SOF_DAI_INTEL_ALH: + if (data) { + config->dai_index = data->dai_index; + config->alh.stream_id = data->dai_data; + } + break; + default: + break; + } + + config->flags = flags; + + /* only send the IPC if the widget is set up in the DSP */ + if (swidget->use_count > 0) { + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); + } + + return ret; +} + +static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_comp_reply reply; + int ret; + + if (!swidget->private) + return 0; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *dai_data = dai->private; + struct sof_ipc_comp *comp = &dai_data->comp_dai->comp; + + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, + comp->hdr.size, &reply, sizeof(reply)); + break; + } + case snd_soc_dapm_scheduler: + { + struct sof_ipc_pipe_new *pipeline; + + pipeline = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), &reply, sizeof(reply)); + break; + } + default: + { + struct sof_ipc_cmd_hdr *hdr; + + hdr = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, + &reply, sizeof(reply)); + break; + } + } + if (ret < 0) + dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name); + + return ret; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2012,6 +2159,9 @@ static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .control_free = sof_ipc3_control_free, .pipeline_complete = sof_ipc3_complete_pipeline, .token_list = ipc3_token_list, + .widget_free = sof_ipc3_widget_free, + .widget_setup = sof_ipc3_widget_setup, + .dai_config = sof_ipc3_dai_config, }; const struct sof_ipc_ops ipc3_ops = { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index cc0d647a2d0d..7aa4ac313de3 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -27,31 +27,6 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * return ret; } -static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai) -{ - struct sof_dai_private_data *private = dai->private; - struct sof_ipc_dai_config *config; - struct sof_ipc_reply reply; - int ret; - - config = &private->dai_config[dai->current_config]; - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name); - return -EINVAL; - } - - /* set NONE flag to clear all previous settings */ - config->flags = SOF_DAI_CONFIG_FLAGS_NONE; - - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) - dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name); - - return ret; -} - static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { struct snd_sof_control *scontrol; @@ -96,15 +71,9 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - struct sof_ipc_free ipc_free = { - .hdr = { - .size = sizeof(ipc_free), - .cmd = SOF_IPC_GLB_TPLG_MSG, - }, - .id = swidget->comp_id, - }; - struct sof_ipc_reply reply; - int ret, err; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int err = 0; + int ret; if (!swidget->private) return 0; @@ -113,33 +82,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; - switch (swidget->id) { - case snd_soc_dapm_scheduler: - { - ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; - break; - } - case snd_soc_dapm_buffer: - ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; - break; - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - { - struct snd_sof_dai *dai = swidget->private; - - dai->configured = false; - fallthrough; - } - default: - ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; - break; - } - /* continue to disable core even if IPC fails */ - err = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), - &reply, sizeof(reply)); - if (err < 0) - dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); + if (tplg_ops->widget_free) + err = tplg_ops->widget_free(sdev, swidget); /* * disable widget core. continue to route setup status and complete flag @@ -176,11 +121,7 @@ EXPORT_SYMBOL(sof_widget_free); int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - struct sof_ipc_pipe_new *pipeline; - struct sof_ipc_comp_reply r; - struct sof_ipc_cmd_hdr *hdr; - struct sof_ipc_comp *comp; - struct snd_sof_dai *dai; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; int ret; /* skip if there is no private data */ @@ -219,53 +160,22 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) goto pipe_widget_free; } - switch (swidget->id) { - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - { - struct sof_dai_private_data *dai_data; - - dai = swidget->private; - dai_data = dai->private; - comp = &dai_data->comp_dai->comp; - dai->configured = false; - - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, - comp->hdr.size, &r, sizeof(r)); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to load widget %s\n", - swidget->widget->name); + /* setup widget in the DSP */ + if (tplg_ops->widget_setup) { + ret = tplg_ops->widget_setup(sdev, swidget); + if (ret < 0) goto core_put; - } + } - ret = sof_dai_config_setup(sdev, dai); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n", - swidget->widget->name); + /* send config for DAI components */ + if (WIDGET_IS_DAI(swidget->id)) { + unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE; - /* - * widget use_count and core ref_count will both be decremented by - * sof_widget_free() - */ - sof_widget_free(sdev, swidget); - return ret; + if (tplg_ops->dai_config) { + ret = tplg_ops->dai_config(sdev, swidget, flags, NULL); + if (ret < 0) + goto widget_free; } - break; - } - case snd_soc_dapm_scheduler: - pipeline = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), &r, sizeof(r)); - break; - default: - hdr = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, - &r, sizeof(r)); - break; - } - if (ret < 0) { - dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); - goto core_put; } /* restore kcontrols for widget */ @@ -273,18 +183,16 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", swidget->widget->name); - /* - * widget use_count and core ref_count will both be decremented by - * sof_widget_free() - */ - sof_widget_free(sdev, swidget); - return ret; + goto widget_free; } dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); return 0; +widget_free: + /* widget use_count and core ref_count will both be decremented by sof_widget_free() */ + sof_widget_free(sdev, swidget); core_put: snd_sof_dsp_core_put(sdev, swidget->core); pipe_widget_free: diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 622d43707b27..19386184d8f3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -40,6 +40,11 @@ struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control; +struct snd_sof_dai_config_data { + int dai_index; + int dai_data; /* contains DAI-specific information */ +}; + /** * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets * @ipc_setup: Function pointer for setting up widget IPC params @@ -69,6 +74,9 @@ struct sof_ipc_tplg_widget_ops { * @control_setup: Function pointer for setting up kcontrol IPC-specific data * @control_free: Function pointer for freeing kcontrol IPC-specific data * @pipeline_complete: Function pointer for pipeline complete IPC + * @widget_setup: Function pointer for setting up setup in the DSP + * @widget_free: Function pointer for freeing widget in the DSP + * @dai_config: Function pointer for sending DAI config IPC to the DSP */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -77,6 +85,10 @@ struct sof_ipc_tplg_ops { int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*pipeline_complete)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*widget_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + unsigned int flags, struct snd_sof_dai_config_data *data); }; /** struct snd_sof_tuple - Tuple info @@ -276,7 +288,6 @@ struct snd_sof_dai { int number_configs; int current_config; - bool configured; /* DAI configured during BE hw_params */ struct list_head list; /* list in sdev dai list */ void *private; }; -- cgit v1.2.3 From 657774acd00f3d63ebae06e5d15a74e013cee0ed Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:28 -0700 Subject: ASoC: SOF: Make sof_suspend/resume IPC agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new set of IPC ops for PM with the ctx_save and ctx_restore ops for suspend/resume and implement the ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/ipc.c | 1 + sound/soc/sof/ipc3-ops.h | 19 +++++++++++++ sound/soc/sof/ipc3-topology.c | 7 ++--- sound/soc/sof/ipc3.c | 44 ++++++++++++++++++++++++++++++ sound/soc/sof/pm.c | 63 +++++++++++++++++-------------------------- sound/soc/sof/sof-priv.h | 14 ++++++++-- 7 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 sound/soc/sof/ipc3-ops.h create mode 100644 sound/soc/sof/ipc3.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index e13dab59764c..59482903a243 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@ 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\ - ipc3-topology.o + ipc3-topology.o ipc3.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 19a294cbbb8d..46a989be9a82 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -17,6 +17,7 @@ #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" +#include "ipc3-ops.h" typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h new file mode 100644 index 000000000000..5d8cab92c1a4 --- /dev/null +++ b/sound/soc/sof/ipc3-ops.h @@ -0,0 +1,19 @@ +/* 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) 2021 Intel Corporation. All rights reserved. + * + * Author: Ranjani Sridharan + */ + +#ifndef __SOUND_SOC_SOF_IPC3_OPS_H +#define __SOUND_SOC_SOF_IPC3_OPS_H + +#include "sof-priv.h" + +extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; +extern const struct sof_ipc_ops ipc3_ops; + +#endif diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 8d08ffb37008..bf0cf38f4524 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -11,6 +11,7 @@ #include #include "sof-priv.h" #include "sof-audio.h" +#include "ipc3-ops.h" #include "ops.h" /* Full volume for default values */ @@ -2152,7 +2153,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY sof_ipc3_widget_bind_event}, }; -static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { +const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, .route_setup = sof_ipc3_route_setup, .control_setup = sof_ipc3_control_setup, @@ -2163,7 +2164,3 @@ static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, }; - -const struct sof_ipc_ops ipc3_ops = { - .tplg = &ipc3_tplg_ops, -}; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c new file mode 100644 index 000000000000..e71cf30908c6 --- /dev/null +++ b/sound/soc/sof/ipc3.c @@ -0,0 +1,44 @@ +// 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) 2021 Intel Corporation. All rights reserved. +// +// + +#include "sof-priv.h" +#include "ipc3-ops.h" + +static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) +{ + struct sof_ipc_pm_ctx pm_ctx = { + .hdr.size = sizeof(pm_ctx), + .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd, + }; + struct sof_ipc_reply reply; + + /* send ctx save ipc to dsp */ + return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, + sizeof(pm_ctx), &reply, sizeof(reply)); +} + +static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev) +{ + return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); +} + +static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev) +{ + return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); +} + +static const struct sof_ipc_pm_ops ipc3_pm_ops = { + .ctx_save = sof_ipc3_ctx_save, + .ctx_restore = sof_ipc3_ctx_restore, +}; + +const struct sof_ipc_ops ipc3_ops = { + .tplg = &ipc3_tplg_ops, + .pm = &ipc3_pm_ops, +}; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 7300ecadabd9..10adbbd0a9cd 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -48,22 +48,6 @@ static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) return target_dsp_state; } -static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) -{ - struct sof_ipc_pm_ctx pm_ctx; - struct sof_ipc_reply reply; - - memset(&pm_ctx, 0, sizeof(pm_ctx)); - - /* configure ctx save ipc message */ - pm_ctx.hdr.size = sizeof(pm_ctx); - pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd; - - /* send ctx save ipc to dsp */ - return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, - sizeof(pm_ctx), &reply, sizeof(reply)); -} - #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) static void sof_cache_debugfs(struct snd_sof_dev *sdev) { @@ -86,6 +70,7 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; u32 old_state = sdev->dsp_power_state.state; int ret; @@ -171,11 +156,11 @@ static int sof_resume(struct device *dev, bool runtime_resume) sof_resume_clients(sdev); /* notify DSP of system resume */ - ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); - if (ret < 0) - dev_err(sdev->dev, - "error: ctx_restore ipc error during resume %d\n", - ret); + if (pm_ops && pm_ops->ctx_restore) { + ret = pm_ops->ctx_restore(sdev); + if (ret < 0) + dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret); + } return ret; } @@ -183,6 +168,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; pm_message_t pm_state; u32 target_state = 0; int ret; @@ -232,21 +218,20 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) sof_cache_debugfs(sdev); #endif /* notify DSP of upcoming power down */ - ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); - if (ret == -EBUSY || ret == -EAGAIN) { - /* - * runtime PM has logic to handle -EBUSY/-EAGAIN so - * pass these errors up - */ - dev_err(sdev->dev, - "error: ctx_save ipc error during suspend %d\n", - ret); - return ret; - } else if (ret < 0) { - /* FW in unexpected state, continue to power down */ - dev_warn(sdev->dev, - "ctx_save ipc error %d, proceeding with suspend\n", - ret); + if (pm_ops && pm_ops->ctx_save) { + ret = pm_ops->ctx_save(sdev); + if (ret == -EBUSY || ret == -EAGAIN) { + /* + * runtime PM has logic to handle -EBUSY/-EAGAIN so + * pass these errors up + */ + dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret); + return ret; + } else if (ret < 0) { + /* FW in unexpected state, continue to power down */ + dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n", + ret); + } } suspend: @@ -278,9 +263,11 @@ suspend: int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) { + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + /* Notify DSP of upcoming power down */ - if (sof_ops(sdev)->remove) - return sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); + if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save) + return pm_ops->ctx_save(sdev); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0b89c3e6ef21..3e883044dd0f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -360,18 +360,28 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; +/** + * struct sof_ipc_pm_ops - IPC-specific PM ops + * @ctx_save: Function pointer for context save + * @ctx_restore: Function pointer for context restore + */ +struct sof_ipc_pm_ops { + int (*ctx_save)(struct snd_sof_dev *sdev); + int (*ctx_restore)(struct snd_sof_dev *sdev); +}; + struct sof_ipc_tplg_ops; /** * struct sof_ipc_ops - IPC-specific ops * @tplg: Pointer to IPC-specific topology ops + * @pm: Pointer to PM ops */ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; + const struct sof_ipc_pm_ops *pm; }; -extern const struct sof_ipc_ops ipc3_ops; - /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; -- cgit v1.2.3 From a0149a6bf0b4969a7f732528b2fb6ce32c309dfc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:29 -0700 Subject: ASoC: SOF: Introduce IPC ops for kcontrol IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce IPC-specific ops for kcontrol IO in struct ipc_tplg_ops. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 19386184d8f3..d241dd84e708 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -45,6 +45,28 @@ struct snd_sof_dai_config_data { int dai_data; /* contains DAI-specific information */ }; +/** + * struct ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO + */ +struct ipc_tplg_control_ops { + bool (*volume_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*volume_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + bool (*switch_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*switch_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + bool (*enum_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*enum_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_ext_get)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + int (*bytes_ext_volatile_get)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + int (*bytes_ext_put)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + /* update control data based on notification from the DSP */ + void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message); +}; + /** * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets * @ipc_setup: Function pointer for setting up widget IPC params @@ -67,6 +89,7 @@ struct sof_ipc_tplg_widget_ops { * @widget: Array of pointers to IPC-specific ops for widgets. This should always be of size * SND_SOF_DAPM_TYPE_COUNT i.e one per widget type. Unsupported widget types will be * initialized to 0. + * @control: Pointer to the IPC-specific ops for topology kcontrol IO * @route_setup: Function pointer for setting up pipeline connections * @token_list: List of all tokens supported by the IPC version. The size of the token_list * array should be SOF_TOKEN_COUNT. The unused elements in the array will be @@ -80,6 +103,7 @@ struct sof_ipc_tplg_widget_ops { */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; + const struct ipc_tplg_control_ops *control; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); -- cgit v1.2.3 From 10f461d79c2d1afb22344986cc1b4631169cf25e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:30 -0700 Subject: ASoC: SOF: Add IPC3 topology control ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the topology control IPC ops for IPC3, implement the control_notify op and use it. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/control.c | 144 -------------------------------------- sound/soc/sof/ipc.c | 6 +- sound/soc/sof/ipc3-control.c | 156 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3-ops.h | 1 + sound/soc/sof/ipc3-topology.c | 1 + sound/soc/sof/sof-audio.h | 6 +- 7 files changed, 166 insertions(+), 150 deletions(-) create mode 100644 sound/soc/sof/ipc3-control.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 59482903a243..f6d68a3096d9 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@ 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\ - ipc3-topology.o ipc3.o + ipc3-topology.o ipc3.o ipc3-control.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 21ee0545945d..fb2311d880d3 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -526,147 +526,3 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, return 0; } - -static void snd_sof_update_control(struct snd_sof_control *scontrol, - struct sof_ipc_ctrl_data *cdata) -{ - struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *local_cdata; - int i; - - local_cdata = scontrol->ipc_control_data; - - if (cdata->cmd == SOF_CTRL_CMD_BINARY) { - if (cdata->num_elems != local_cdata->data->size) { - dev_err(scomp->dev, - "error: cdata binary size mismatch %u - %u\n", - cdata->num_elems, local_cdata->data->size); - return; - } - - /* copy the new binary data */ - memcpy(local_cdata->data, cdata->data, cdata->num_elems); - } else if (cdata->num_elems != scontrol->num_channels) { - dev_err(scomp->dev, - "error: cdata channel count mismatch %u - %d\n", - cdata->num_elems, scontrol->num_channels); - } else { - /* copy the new values */ - for (i = 0; i < cdata->num_elems; i++) - local_cdata->chanv[i].value = cdata->chanv[i].value; - } -} - -void snd_sof_control_notify(struct snd_sof_dev *sdev, - struct sof_ipc_ctrl_data *cdata) -{ - struct snd_soc_dapm_widget *widget; - struct snd_sof_control *scontrol; - struct snd_sof_widget *swidget; - struct snd_kcontrol *kc = NULL; - struct soc_mixer_control *sm; - struct soc_bytes_ext *be; - size_t expected_size; - struct soc_enum *se; - bool found = false; - int i, type; - - if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || - cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { - dev_err(sdev->dev, - "Component data is not supported in control notification\n"); - return; - } - - /* Find the swidget first */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->comp_id == cdata->comp_id) { - found = true; - break; - } - } - - if (!found) - return; - - /* Translate SOF cmd to TPLG type */ - switch (cdata->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_SWITCH: - type = SND_SOC_TPLG_TYPE_MIXER; - break; - case SOF_CTRL_CMD_BINARY: - type = SND_SOC_TPLG_TYPE_BYTES; - break; - case SOF_CTRL_CMD_ENUM: - type = SND_SOC_TPLG_TYPE_ENUM; - break; - default: - dev_err(sdev->dev, "error: unknown cmd %u\n", cdata->cmd); - return; - } - - 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; - - switch (cdata->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_SWITCH: - sm = (struct soc_mixer_control *)kc->private_value; - scontrol = sm->dobj.private; - break; - case SOF_CTRL_CMD_BINARY: - be = (struct soc_bytes_ext *)kc->private_value; - scontrol = be->dobj.private; - break; - case SOF_CTRL_CMD_ENUM: - se = (struct soc_enum *)kc->private_value; - scontrol = se->dobj.private; - break; - default: - return; - } - - expected_size = sizeof(struct sof_ipc_ctrl_data); - switch (cdata->type) { - case SOF_CTRL_TYPE_VALUE_CHAN_GET: - case SOF_CTRL_TYPE_VALUE_CHAN_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_chan); - break; - case SOF_CTRL_TYPE_DATA_GET: - case SOF_CTRL_TYPE_DATA_SET: - expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); - break; - default: - return; - } - - if (cdata->rhdr.hdr.size != expected_size) { - dev_err(sdev->dev, "error: component notification size mismatch\n"); - return; - } - - if (cdata->num_elems) - /* - * The message includes the updated value/data, update the - * control's local cache using the received notification - */ - snd_sof_update_control(scontrol, cdata); - else - /* Mark the scontrol that the value/data is changed in SOF */ - scontrol->comp_data_dirty = true; - - snd_ctl_notify_one(swidget->scomp->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); -} diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 46a989be9a82..af0ae137842b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -470,6 +470,7 @@ EXPORT_SYMBOL(snd_sof_ipc_reply); static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) { + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; struct sof_ipc_cmd_hdr *hdr = msg_buf; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; @@ -482,7 +483,8 @@ static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) return; } - snd_sof_control_notify(sdev, msg_buf); + if (tplg_ops->control->update) + tplg_ops->control->update(sdev, msg_buf); } /* DSP firmware has sent host a message */ @@ -1031,7 +1033,7 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) ipc->ops = &ipc3_ops; /* check for mandatory ops */ - if (!ipc->ops->tplg || !ipc->ops->tplg->widget) { + if (!ipc->ops->tplg || !ipc->ops->tplg->widget || !ipc->ops->tplg->control) { dev_err(sdev->dev, "Invalid topology IPC ops\n"); return NULL; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c new file mode 100644 index 000000000000..d4086e805c18 --- /dev/null +++ b/sound/soc/sof/ipc3-control.c @@ -0,0 +1,156 @@ +// 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) 2021 Intel Corporation. All rights reserved. +// +// + +#include "sof-priv.h" +#include "sof-audio.h" +#include "ipc3-ops.h" + +static void snd_sof_update_control(struct snd_sof_control *scontrol, + struct sof_ipc_ctrl_data *cdata) +{ + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc_ctrl_data *local_cdata; + int i; + + local_cdata = scontrol->ipc_control_data; + + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + if (cdata->num_elems != local_cdata->data->size) { + dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n", + cdata->num_elems, local_cdata->data->size); + return; + } + + /* copy the new binary data */ + memcpy(local_cdata->data, cdata->data, cdata->num_elems); + } else if (cdata->num_elems != scontrol->num_channels) { + dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n", + cdata->num_elems, scontrol->num_channels); + } else { + /* copy the new values */ + for (i = 0; i < cdata->num_elems; i++) + local_cdata->chanv[i].value = cdata->chanv[i].value; + } +} + +static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) +{ + struct sof_ipc_ctrl_data *cdata = ipc_control_message; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; + struct snd_sof_widget *swidget; + struct snd_kcontrol *kc = NULL; + struct soc_mixer_control *sm; + struct soc_bytes_ext *be; + size_t expected_size; + struct soc_enum *se; + bool found = false; + int i, type; + + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || + cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { + dev_err(sdev->dev, "Component data is not supported in control notification\n"); + return; + } + + /* Find the swidget first */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == cdata->comp_id) { + found = true; + break; + } + } + + if (!found) + return; + + /* Translate SOF cmd to TPLG type */ + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + type = SND_SOC_TPLG_TYPE_MIXER; + break; + case SOF_CTRL_CMD_BINARY: + type = SND_SOC_TPLG_TYPE_BYTES; + break; + case SOF_CTRL_CMD_ENUM: + type = SND_SOC_TPLG_TYPE_ENUM; + break; + default: + dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); + return; + } + + 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; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SOF_CTRL_CMD_BINARY: + be = (struct soc_bytes_ext *)kc->private_value; + scontrol = be->dobj.private; + break; + case SOF_CTRL_CMD_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + default: + return; + } + + expected_size = sizeof(struct sof_ipc_ctrl_data); + switch (cdata->type) { + case SOF_CTRL_TYPE_VALUE_CHAN_GET: + case SOF_CTRL_TYPE_VALUE_CHAN_SET: + expected_size += cdata->num_elems * + sizeof(struct sof_ipc_ctrl_value_chan); + break; + case SOF_CTRL_TYPE_DATA_GET: + case SOF_CTRL_TYPE_DATA_SET: + expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + break; + default: + return; + } + + if (cdata->rhdr.hdr.size != expected_size) { + dev_err(sdev->dev, "Component notification size mismatch\n"); + return; + } + + if (cdata->num_elems) + /* + * The message includes the updated value/data, update the + * control's local cache using the received notification + */ + snd_sof_update_control(scontrol, cdata); + else + /* Mark the scontrol that the value/data is changed in SOF */ + scontrol->comp_data_dirty = true; + + snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); +} + +const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { + .update = sof_ipc3_control_update, +}; diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h index 5d8cab92c1a4..f3d6010d0b77 100644 --- a/sound/soc/sof/ipc3-ops.h +++ b/sound/soc/sof/ipc3-ops.h @@ -15,5 +15,6 @@ extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_ops ipc3_ops; +extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; #endif diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index bf0cf38f4524..55be97ee816b 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2155,6 +2155,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, + .control = &tplg_ipc3_control_ops, .route_setup = sof_ipc3_route_setup, .control_setup = sof_ipc3_control_setup, .control_free = sof_ipc3_control_free, diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index d241dd84e708..bcd38c882078 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -46,9 +46,9 @@ struct snd_sof_dai_config_data { }; /** - * struct ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO + * struct sof_ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO */ -struct ipc_tplg_control_ops { +struct sof_ipc_tplg_control_ops { bool (*volume_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); int (*volume_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); bool (*switch_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); @@ -103,7 +103,7 @@ struct sof_ipc_tplg_widget_ops { */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; - const struct ipc_tplg_control_ops *control; + const struct sof_ipc_tplg_control_ops *control; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); -- cgit v1.2.3 From 838d04f3e232c3fb8c421959e8ff09e3a918011e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:31 -0700 Subject: ASoC: SOF: Add volume_get/put IPC3 ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define and set the volume_get/put control IPC ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 64 ++++++--------------------- sound/soc/sof/ipc3-control.c | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 50 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index fb2311d880d3..9eb54b7024b3 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -45,26 +45,6 @@ static void update_mute_led(struct snd_sof_control *scontrol, #endif } -static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) -{ - if (value >= size) - return volume_map[size - 1]; - - return volume_map[value]; -} - -static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (volume_map[i] >= value) - return i; - } - - return i - 1; -} - static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; @@ -85,7 +65,8 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) scontrol->comp_data_dirty = false; ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) { - dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); + dev_err(scomp->dev, "Failed to get control data: %d\n", ret); + /* Set the flag to re-try next time to get the data */ scontrol->comp_data_dirty = true; } @@ -94,19 +75,14 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - snd_sof_refresh_control(scontrol); - - /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.integer.value[i] = - ipc_to_mixer(cdata->chanv[i].value, - scontrol->volume_table, sm->max + 1); + if (tplg_ops->control->volume_get) + return tplg_ops->control->volume_get(scontrol, ucontrol); return 0; } @@ -114,28 +90,16 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* update each channel */ - for (i = 0; i < channels; i++) { - value = mixer_to_ipc(ucontrol->value.integer.value[i], - scontrol->volume_table, sm->max + 1); - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + if (tplg_ops->control->volume_put) + return tplg_ops->control->volume_put(scontrol, ucontrol); - /* notify DSP of mixer updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); - return change; + return false; } int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index d4086e805c18..12aeb3ae4e52 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -11,6 +11,104 @@ #include "sof-audio.h" #include "ipc3-ops.h" +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +static void snd_sof_refresh_control(struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + int ret; + + if (!scontrol->comp_data_dirty) + return; + + if (!pm_runtime_active(scomp->dev)) + return; + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* refresh the component data from DSP */ + scontrol->comp_data_dirty = false; + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) { + dev_err(scomp->dev, "Failed to get control data: %d\n", ret); + + /* Set the flag to re-try next time to get the data */ + scontrol->comp_data_dirty = true; + } +} + +static int sof_ipc3_volume_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, + scontrol->max + 1); + + return 0; +} + +static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + + /* update each channel */ + for (i = 0; i < channels; i++) { + u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, scontrol->max + 1); + + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of mixer updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set mixer updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -152,5 +250,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ } const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { + .volume_put = sof_ipc3_volume_put, + .volume_get = sof_ipc3_volume_get, .update = sof_ipc3_control_update, }; -- cgit v1.2.3 From a6668746436824c46b54b3f7fd72523f05f089eb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:32 -0700 Subject: ASoC: SOF: Add switch get/put IPC3 ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the switch_get/put control IPC ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 39 +++++++++++----------------------- sound/soc/sof/ipc3-control.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 9eb54b7024b3..f9f8bd37b480 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -127,17 +127,14 @@ int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - - snd_sof_refresh_control(scontrol); + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.integer.value[i] = cdata->chanv[i].value; + if (tplg_ops->control->switch_get) + return tplg_ops->control->switch_get(scontrol, ucontrol); return 0; } @@ -145,31 +142,19 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; - - /* update each channel */ - for (i = 0; i < channels; i++) { - value = ucontrol->value.integer.value[i]; - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; if (scontrol->led_ctl.use_led) update_mute_led(scontrol, kcontrol, ucontrol); - /* notify DSP of mixer updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->switch_put) + return tplg_ops->control->switch_put(scontrol, ucontrol); - return change; + return false; } int snd_sof_enum_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 12aeb3ae4e52..ba9cde011b5a 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -109,6 +109,54 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, return change; } +static int sof_ipc3_switch_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + return 0; +} + +static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + u32 value; + + /* update each channel */ + for (i = 0; i < channels; i++) { + value = ucontrol->value.integer.value[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of mixer updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set mixer updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -252,5 +300,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .volume_put = sof_ipc3_volume_put, .volume_get = sof_ipc3_volume_get, + .switch_put = sof_ipc3_switch_put, + .switch_get = sof_ipc3_switch_get, .update = sof_ipc3_control_update, }; -- cgit v1.2.3 From 049307aad2a355f7b44736eeb5795d6d4499fd12 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:33 -0700 Subject: ASoC: SOF: Add enum_get/put control ops for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define and set the enum_get/put control IPC ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 39 +++++++++++----------------------- sound/soc/sof/ipc3-control.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index f9f8bd37b480..499d426c5d38 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -160,17 +160,14 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_enum *se = - (struct soc_enum *)kcontrol->private_value; + struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - - snd_sof_refresh_control(scontrol); + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + if (tplg_ops->control->enum_get) + return tplg_ops->control->enum_get(scontrol, ucontrol); return 0; } @@ -178,28 +175,16 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_enum *se = - (struct soc_enum *)kcontrol->private_value; + struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; - - /* update each channel */ - for (i = 0; i < channels; i++) { - value = ucontrol->value.enumerated.item[i]; - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* notify DSP of enum updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->enum_put) + return tplg_ops->control->enum_put(scontrol, ucontrol); - return change; + return false; } int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index ba9cde011b5a..03948f8f7eb0 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -157,6 +157,54 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, return change; } +static int sof_ipc3_enum_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + + return 0; +} + +static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + u32 value; + + /* update each channel */ + for (i = 0; i < channels; i++) { + value = ucontrol->value.enumerated.item[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of enum updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set enum updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -302,5 +350,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .volume_get = sof_ipc3_volume_get, .switch_put = sof_ipc3_switch_put, .switch_get = sof_ipc3_switch_get, + .enum_put = sof_ipc3_enum_put, + .enum_get = sof_ipc3_enum_get, .update = sof_ipc3_control_update, }; -- cgit v1.2.3 From 544ac8858f249950b4d99c68e538cdc07300528f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:34 -0700 Subject: ASoC: SOF: Add bytes_get/put control IPC ops for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define and set the bytes_get/put IPC control ops for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-10-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 64 +++++++----------------------------------- sound/soc/sof/ipc3-control.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 54 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 499d426c5d38..2a4997e1cd1e 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -190,35 +190,14 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct sof_abi_hdr *data = cdata->data; - size_t size; - - snd_sof_refresh_control(scontrol); - - if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(scomp->dev, - "error: data max %d exceeds ucontrol data array size\n", - be->max); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > be->max - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, - "error: %u bytes of control data is invalid, max is %zu\n", - data->size, be->max - sizeof(*data)); - return -EINVAL; - } - - size = data->size + sizeof(*data); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* copy back to kcontrol */ - memcpy(ucontrol->value.bytes.data, data, size); + if (tplg_ops->control->bytes_get) + return tplg_ops->control->bytes_get(scontrol, ucontrol); return 0; } @@ -226,37 +205,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct sof_abi_hdr *data = cdata->data; - size_t size; - - if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(scomp->dev, - "error: data max %d exceeds ucontrol data array size\n", - be->max); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > be->max - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, - "error: data size too big %u bytes max is %zu\n", - data->size, be->max - sizeof(*data)); - return -EINVAL; - } - - size = data->size + sizeof(*data); - - /* copy from kcontrol */ - memcpy(data, ucontrol->value.bytes.data, size); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->bytes_put) + return tplg_ops->control->bytes_put(scontrol, ucontrol); return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 03948f8f7eb0..df8e4df9663d 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -205,6 +205,71 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, return change; } +static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + size_t size; + + snd_sof_refresh_control(scontrol); + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, + "%u bytes of control data is invalid, max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy back to kcontrol */ + memcpy(ucontrol->value.bytes.data, data, size); + + return 0; +} + +static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + size_t size; + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy from kcontrol */ + memcpy(data, ucontrol->value.bytes.data, size); + + /* notify DSP of byte control updates */ + if (pm_runtime_active(scomp->dev)) + return snd_sof_ipc_set_get_comp_data(scontrol, true); + + return 0; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -352,5 +417,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .switch_get = sof_ipc3_switch_get, .enum_put = sof_ipc3_enum_put, .enum_get = sof_ipc3_enum_get, + .bytes_put = sof_ipc3_bytes_put, + .bytes_get = sof_ipc3_bytes_get, .update = sof_ipc3_control_update, }; -- cgit v1.2.3 From 67ec2a091630c28ea8d05db2bd7178a05b04b7e6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:35 -0700 Subject: ASoC: SOF: Add bytes_ext control IPC ops for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define and set the get/put/volatile_get control IPC ops for byte controls for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-11-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 197 ++++--------------------------------------- sound/soc/sof/ipc3-control.c | 171 +++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 180 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 2a4997e1cd1e..de1778c4002b 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -45,33 +45,6 @@ static void update_mute_led(struct snd_sof_control *scontrol, #endif } -static void snd_sof_refresh_control(struct snd_sof_control *scontrol) -{ - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_soc_component *scomp = scontrol->scomp; - int ret; - - if (!scontrol->comp_data_dirty) - return; - - if (!pm_runtime_active(scomp->dev)) - return; - - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - - /* refresh the component data from DSP */ - scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); - if (ret < 0) { - dev_err(scomp->dev, "Failed to get control data: %d\n", ret); - - /* Set the flag to re-try next time to get the data */ - scontrol->comp_data_dirty = true; - } -} - int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -221,74 +194,18 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, const unsigned int __user *binary_data, unsigned int size) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - const struct snd_ctl_tlv __user *tlvd = - (const struct snd_ctl_tlv __user *)binary_data; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; /* make sure we have at least a header */ if (size < sizeof(struct snd_ctl_tlv)) return -EINVAL; - /* - * The beginning of bytes data contains a header from where - * the length (as bytes) is needed to know the correct copy - * length of data from tlvd->tlv. - */ - if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) - return -EFAULT; - - /* make sure TLV info is consistent */ - if (header.length + sizeof(struct snd_ctl_tlv) > size) { - dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", - header.length, sizeof(struct snd_ctl_tlv), size); - return -EINVAL; - } - - /* be->max is coming from topology */ - if (header.length > be->max) { - dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", - header.length, be->max); - return -EINVAL; - } - - /* Check that header id matches the command */ - if (header.numid != cdata->cmd) { - dev_err_ratelimited(scomp->dev, - "error: incorrect numid %d\n", - header.numid); - return -EINVAL; - } - - if (copy_from_user(cdata->data, tlvd->tlv, header.length)) - return -EFAULT; - - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err_ratelimited(scomp->dev, - "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - return -EINVAL; - } - - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); - return -EINVAL; - } - - /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->bytes_ext_put) + return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size); return 0; } @@ -299,67 +216,24 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; - size_t data_size; - int ret; - int err; - - /* - * Decrement the limit by ext bytes header size to - * ensure the user space buffer is not exceeded. - */ - if (size < sizeof(struct snd_ctl_tlv)) - return -ENOSPC; - size -= sizeof(struct snd_ctl_tlv); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int ret, err; ret = pm_runtime_get_sync(scomp->dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); + dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret); pm_runtime_put_noidle(scomp->dev); return ret; } - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); - if (ret < 0) - goto out; - - /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", - cdata->data->size, - be->max - sizeof(struct sof_abi_hdr)); - ret = -EINVAL; - goto out; - } - - data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + if (tplg_ops->control->bytes_ext_volatile_get) + ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); - /* make sure we don't exceed size provided by user space for data */ - if (data_size > size) { - ret = -ENOSPC; - goto out; - } - - header.numid = cdata->cmd; - header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { - ret = -EFAULT; - goto out; - } - - if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - ret = -EFAULT; -out: pm_runtime_mark_last_busy(scomp->dev); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) - dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); + dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err); return ret; } @@ -368,51 +242,14 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, unsigned int size) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - struct snd_ctl_tlv __user *tlvd = - (struct snd_ctl_tlv __user *)binary_data; - size_t data_size; - - snd_sof_refresh_control(scontrol); - - /* - * Decrement the limit by ext bytes header size to - * ensure the user space buffer is not exceeded. - */ - if (size < sizeof(struct snd_ctl_tlv)) - return -ENOSPC; - size -= sizeof(struct snd_ctl_tlv); - - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - - /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", - cdata->data->size, - be->max - sizeof(struct sof_abi_hdr)); - return -EINVAL; - } - - data_size = cdata->data->size + sizeof(struct sof_abi_hdr); - - /* make sure we don't exceed size provided by user space for data */ - if (data_size > size) - return -ENOSPC; - - header.numid = cdata->cmd; - header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) - return -EFAULT; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - return -EFAULT; + if (tplg_ops->control->bytes_ext_get) + return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size); return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index df8e4df9663d..cdd5ad860a94 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -270,6 +270,174 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return 0; } +static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + size_t data_size; + + snd_sof_refresh_control(scontrol); + + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + + size -= sizeof(struct snd_ctl_tlv); + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + cdata->data->size, + scontrol->max_size - sizeof(struct sof_abi_hdr)); + return -EINVAL; + } + + data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = cdata->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + return -EFAULT; + + return 0; +} + +static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + + /* + * The beginning of bytes data contains a header from where + * the length (as bytes) is needed to know the correct copy + * length of data from tlvd->tlv. + */ + if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + /* make sure TLV info is consistent */ + if (header.length + sizeof(struct snd_ctl_tlv) > size) { + dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n", + header.length, sizeof(struct snd_ctl_tlv), size); + return -EINVAL; + } + + /* be->max is coming from topology */ + if (header.length > scontrol->max_size) { + dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", + header.length, scontrol->max_size); + return -EINVAL; + } + + /* Check that header id matches the command */ + if (header.numid != cdata->cmd) { + dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n", + header.numid); + return -EINVAL; + } + + if (copy_from_user(cdata->data, tlvd->tlv, header.length)) + return -EFAULT; + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); + return -EINVAL; + } + + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", + cdata->data->abi); + return -EINVAL; + } + + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); + return -EINVAL; + } + + /* notify DSP of byte control updates */ + if (pm_runtime_active(scomp->dev)) + return snd_sof_ipc_set_get_comp_data(scontrol, true); + + return 0; +} + +static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + size_t data_size; + int ret; + + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + + size -= sizeof(struct snd_ctl_tlv); + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* get all the component data from DSP */ + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) + return ret; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + cdata->data->size, + scontrol->max_size - sizeof(struct sof_abi_hdr)); + return -EINVAL; + } + + data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = cdata->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + return -EFAULT; + + return ret; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -419,5 +587,8 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .enum_get = sof_ipc3_enum_get, .bytes_put = sof_ipc3_bytes_put, .bytes_get = sof_ipc3_bytes_get, + .bytes_ext_put = sof_ipc3_bytes_ext_put, + .bytes_ext_get = sof_ipc3_bytes_ext_get, + .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, .update = sof_ipc3_control_update, }; -- cgit v1.2.3 From 967885ee45e48b669e0904a38f6aeb1a09b0d9a1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:36 -0700 Subject: ASoC: SOF: Introduce IPC-specific PCM ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the IPC-specific PCM ops that will be used to abstract the PCM related IPC's. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-12-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 17 +++++++++++++++++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index bcd38c882078..4d25e781e5ae 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -45,6 +45,23 @@ struct snd_sof_dai_config_data { int dai_data; /* contains DAI-specific information */ }; +/** + * struct sof_ipc_pcm_ops - IPC-specific PCM ops + * @hw_params: Function pointer for hw_params + * @hw_free: Function pointer for hw_free + * @trigger: Function pointer for trigger + * @dai_link_fixup: Function pointer for DAI link fixup + */ +struct sof_ipc_pcm_ops { + int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream); + int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + int cmd); + int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); +}; + /** * struct sof_ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3e883044dd0f..0d9b640ae24c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -371,15 +371,18 @@ struct sof_ipc_pm_ops { }; struct sof_ipc_tplg_ops; +struct sof_ipc_pcm_ops; /** * struct sof_ipc_ops - IPC-specific ops * @tplg: Pointer to IPC-specific topology ops * @pm: Pointer to PM ops + * @pcm: Pointer to PCM ops */ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; const struct sof_ipc_pm_ops *pm; + const struct sof_ipc_pcm_ops *pcm; }; /* SOF generic IPC data */ -- cgit v1.2.3 From 442c7128219b1769af5685c5453b13711f6b84e2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:37 -0700 Subject: ASoC: SOF: pcm: expose the sof_pcm_setup_connected_widgets() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used in IPC-specific code. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-13-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 5 ++--- sound/soc/sof/sof-audio.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1661b0bc6f12..47599b57ff19 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -105,9 +105,8 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev return ret; } -static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, - struct snd_soc_pcm_runtime *rtd, - struct snd_sof_pcm *spcm, int dir) +int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, int dir) { struct snd_soc_dai *dai; int ret, j; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 4d25e781e5ae..3ec3f746663a 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -457,4 +457,6 @@ int get_token_uuid(void *elem, void *object, u32 offset); int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num); +int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, int dir); #endif -- cgit v1.2.3 From 4123c24bd13caa8ff633d9e17fa2089d53b1f766 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:38 -0700 Subject: ASoC: SOF: Introduce IPC3 PCM hw_free op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the IPC3 PCM ops, define the hw_free op and modify all users to use the op. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220317175044.1752400-14-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/ipc.c | 5 +++-- sound/soc/sof/ipc3-ops.h | 1 + sound/soc/sof/ipc3-pcm.c | 43 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3.c | 1 + sound/soc/sof/pcm.c | 44 +++++++++++++++----------------------------- sound/soc/sof/sof-audio.c | 11 ++++++++--- 7 files changed, 72 insertions(+), 35 deletions(-) create mode 100644 sound/soc/sof/ipc3-pcm.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index f6d68a3096d9..18acbc001b9a 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@ 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\ - ipc3-topology.o ipc3.o ipc3-control.o + ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index af0ae137842b..5f5753608c79 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -1033,8 +1033,9 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) ipc->ops = &ipc3_ops; /* check for mandatory ops */ - if (!ipc->ops->tplg || !ipc->ops->tplg->widget || !ipc->ops->tplg->control) { - dev_err(sdev->dev, "Invalid topology IPC ops\n"); + if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget || + !ipc->ops->tplg->control) { + dev_err(sdev->dev, "Invalid IPC ops\n"); return NULL; } diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h index f3d6010d0b77..a4784626a3d7 100644 --- a/sound/soc/sof/ipc3-ops.h +++ b/sound/soc/sof/ipc3-ops.h @@ -16,5 +16,6 @@ extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_ops ipc3_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; +extern const struct sof_ipc_pcm_ops ipc3_pcm_ops; #endif diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c new file mode 100644 index 000000000000..96f498b4b2d6 --- /dev/null +++ b/sound/soc/sof/ipc3-pcm.c @@ -0,0 +1,43 @@ +// 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) 2021 Intel Corporation. All rights reserved. +// +// + +#include +#include "ipc3-ops.h" +#include "ops.h" +#include "sof-priv.h" +#include "sof-audio.h" + +static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + if (!spcm->prepared[substream->stream]) + return 0; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + +const struct sof_ipc_pcm_ops ipc3_pcm_ops = { + .hw_free = sof_ipc3_pcm_hw_free, +}; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index e71cf30908c6..03e914b62728 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -41,4 +41,5 @@ static const struct sof_ipc_pm_ops ipc3_pm_ops = { const struct sof_ipc_ops ipc3_ops = { .tplg = &ipc3_tplg_ops, .pm = &ipc3_pm_ops, + .pcm = &ipc3_pcm_ops, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 47599b57ff19..8ef477aff938 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -82,29 +82,6 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) -{ - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; - int ret; - - if (!spcm->prepared[substream->stream]) - return 0; - - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; - stream.comp_id = spcm->stream[substream->stream].comp_id; - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); - if (!ret) - spcm->prepared[substream->stream] = false; - - return ret; -} - int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, struct snd_sof_pcm *spcm, int dir) { @@ -145,6 +122,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_platform_stream_params platform_params = { 0 }; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_pcm *spcm; @@ -164,9 +142,13 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(component, substream); + if (ret < 0) + return ret; + + spcm->prepared[substream->stream] = false; + } dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -289,6 +271,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_pcm *spcm; int ret, err = 0; @@ -304,10 +287,13 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm->pcm.pcm_id, substream->stream); /* free PCM in the DSP */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(component, substream); + if (ret < 0) + err = ret; + spcm->prepared[substream->stream] = false; + } /* stop DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7aa4ac313de3..5088ec7019ed 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -591,12 +591,17 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; int ret; /* Send PCM_FREE IPC to reset pipeline */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(sdev->component, substream); + if (ret < 0) + return ret; + } + + spcm->prepared[substream->stream] = false; /* stop the DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); -- cgit v1.2.3 From 621fd48c8cc8d77101a3ac69f7f058d3f8afdbcc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:39 -0700 Subject: ASoC: SOF: Define hw_params PCM op for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the hw_params op for IPC3 and use it. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-15-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-pcm.c | 100 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 110 +++++++---------------------------------------- 2 files changed, 115 insertions(+), 95 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 96f498b4b2d6..b715199a2aaa 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -38,6 +38,106 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, sizeof(stream), &reply, sizeof(reply)); } +static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct sof_ipc_pcm_params pcm; + struct snd_sof_pcm *spcm; + int ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + memset(&pcm, 0, sizeof(pcm)); + + /* number of pages should be rounded up */ + pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes); + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = spcm->stream[substream->stream].comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.buffer.phy_addr = spcm->stream[substream->stream].page_table.addr; + pcm.params.buffer.size = runtime->dma_bytes; + pcm.params.direction = substream->stream; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* container size */ + ret = snd_pcm_format_physical_width(params_format(params)); + if (ret < 0) + return ret; + pcm.params.sample_container_bytes = ret >> 3; + + /* format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + case SNDRV_PCM_FORMAT_FLOAT: + pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; + break; + default: + return -EINVAL; + } + + /* Update the IPC message with information from the platform */ + pcm.params.stream_tag = platform_params->stream_tag; + + if (platform_params->use_phy_address) + pcm.params.buffer.phy_addr = platform_params->phy_addr; + + if (platform_params->no_ipc_position) { + /* For older ABIs set host_period_bytes to zero to inform + * FW we don't want position updates. Newer versions use + * no_stream_position for this purpose. + */ + if (v->abi_version < SOF_ABI_VER(3, 10, 0)) + pcm.params.host_period_bytes = 0; + else + pcm.params.no_stream_position = 1; + } + + dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); + + /* send hw_params IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + if (ret < 0) { + dev_err(component->dev, "HW params ipc failed for stream %d\n", + pcm.params.stream_tag); + return ret; + } + + ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset); + if (ret < 0) { + dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", + __func__, spcm->pcm.pcm_id); + return ret; + } + + return ret; +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { + .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8ef477aff938..8a194c130981 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -119,15 +119,12 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_platform_stream_params platform_params = { 0 }; - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_pcm *spcm; - struct sof_ipc_pcm_params pcm; - struct sof_ipc_pcm_params_reply ipc_params_reply; int ret; /* nothing to do for BE */ @@ -153,117 +150,40 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - memset(&pcm, 0, sizeof(pcm)); + /* if this is a repeated hw_params without hw_free, skip setting up widgets */ + if (!spcm->stream[substream->stream].list) { + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream); + if (ret < 0) + return ret; + } /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { ret = create_page_table(component, substream, runtime->dma_area, runtime->dma_bytes); + if (ret < 0) return ret; } - /* number of pages should be rounded up */ - pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes); - - /* set IPC PCM parameters */ - pcm.hdr.size = sizeof(pcm); - pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; - pcm.comp_id = spcm->stream[substream->stream].comp_id; - pcm.params.hdr.size = sizeof(pcm.params); - pcm.params.buffer.phy_addr = - spcm->stream[substream->stream].page_table.addr; - pcm.params.buffer.size = runtime->dma_bytes; - pcm.params.direction = substream->stream; - pcm.params.sample_valid_bytes = params_width(params) >> 3; - pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; - pcm.params.rate = params_rate(params); - pcm.params.channels = params_channels(params); - pcm.params.host_period_bytes = params_period_bytes(params); - - /* container size */ - ret = snd_pcm_format_physical_width(params_format(params)); - if (ret < 0) - return ret; - pcm.params.sample_container_bytes = ret >> 3; - - /* format */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16: - pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case SNDRV_PCM_FORMAT_S24: - pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; - break; - case SNDRV_PCM_FORMAT_S32: - pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - case SNDRV_PCM_FORMAT_FLOAT: - pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; - break; - default: - return -EINVAL; - } - - /* firmware already configured host stream */ - ret = snd_sof_pcm_platform_hw_params(sdev, - substream, - params, - &platform_params); + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); if (ret < 0) { - dev_err(component->dev, "error: platform hw params failed\n"); + dev_err(component->dev, "platform hw params failed\n"); return ret; } - /* Update the IPC message with information from the platform */ - pcm.params.stream_tag = platform_params.stream_tag; - - if (platform_params.use_phy_address) - pcm.params.buffer.phy_addr = platform_params.phy_addr; - - if (platform_params.no_ipc_position) { - /* For older ABIs set host_period_bytes to zero to inform - * FW we don't want position updates. Newer versions use - * no_stream_position for this purpose. - */ - if (v->abi_version < SOF_ABI_VER(3, 10, 0)) - pcm.params.host_period_bytes = 0; - else - pcm.params.no_stream_position = 1; - } - - dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); - - /* if this is a repeated hw_params without hw_free, skip setting up widgets */ - if (!spcm->stream[substream->stream].list) { - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream); + if (pcm_ops->hw_params) { + ret = pcm_ops->hw_params(component, substream, params, &platform_params); if (ret < 0) return ret; } - /* send hw_params IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), - &ipc_params_reply, sizeof(ipc_params_reply)); - if (ret < 0) { - dev_err(component->dev, "error: hw params ipc failed for stream %d\n", - pcm.params.stream_tag); - return ret; - } - - ret = snd_sof_set_stream_data_offset(sdev, substream, - ipc_params_reply.posn_offset); - if (ret < 0) { - dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", - __func__, spcm->pcm.pcm_id); - return ret; - } - spcm->prepared[substream->stream] = true; /* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params)); - return ret; + return 0; } static int sof_pcm_hw_free(struct snd_soc_component *component, -- cgit v1.2.3 From beac3f4cb66fa05e902768ae75ea691c4a2c0911 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:40 -0700 Subject: ASoC: SOF: Add trigger PCM op for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the trigger PCM op for IPC3 and use it. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-16-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-pcm.c | 43 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 23 ++++++----------------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index b715199a2aaa..d83d54540bbe 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -137,7 +137,50 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, return ret; } +static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + fallthrough; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + default: + dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); + return -EINVAL; + } + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, + .trigger = sof_ipc3_pcm_trigger, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8a194c130981..dc55a04a1588 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -274,13 +274,12 @@ static int sof_pcm_trigger(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_pcm *spcm; - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; bool reset_hw_params = false; bool free_widget_list = false; bool ipc_first = false; - int ret; + int ret = 0; /* nothing to do for BE */ if (rtd->dai_link->no_pcm) @@ -293,17 +292,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", spcm->pcm.pcm_id, substream->stream, cmd); - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; - stream.comp_id = spcm->stream[substream->stream].comp_id; - switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; ipc_first = true; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { @@ -315,7 +308,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = false; return 0; } - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; break; case SNDRV_PCM_TRIGGER_SUSPEND: if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && @@ -332,13 +324,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, free_widget_list = true; fallthrough; case SNDRV_PCM_TRIGGER_STOP: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; ipc_first = true; reset_hw_params = true; break; default: - dev_err(component->dev, "error: unhandled trigger cmd %d\n", - cmd); + dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); return -EINVAL; } @@ -349,11 +339,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (!ipc_first) snd_sof_pcm_platform_trigger(sdev, substream, cmd); - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); + if (pcm_ops->trigger) + ret = pcm_ops->trigger(component, substream, cmd); - /* need to STOP DMA even if STOP IPC failed */ + /* need to STOP DMA even if trigger IPC failed */ if (ipc_first) snd_sof_pcm_platform_trigger(sdev, substream, cmd); -- cgit v1.2.3 From b243b437f4c46b09ec26fc02bea610ace4b45aa2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:41 -0700 Subject: ASoC: SOF: Add dai_link_fixup PCM op for IPC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the dai_link_fixup PCM op for IPC3 and use it Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220317175044.1752400-17-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-pcm.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 174 +------------------------------------------- 2 files changed, 189 insertions(+), 171 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index d83d54540bbe..58b75943cf6d 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -179,8 +179,194 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, sizeof(stream), &reply, sizeof(reply)); } +static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, + struct snd_pcm_hw_params *params) +{ + struct sof_ipc_dai_config *config; + struct snd_sof_dai *dai; + int i; + + /* + * Search for all matching DAIs as we can have both playback and capture DAI + * associated with the same link. + */ + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name || strcmp(link_name, dai->name)) + continue; + for (i = 0; i < dai->number_configs; i++) { + struct sof_dai_private_data *private = dai->private; + + config = &private->dai_config[i]; + if (config->ssp.fsync_rate == params_rate(params)) { + dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); + dai->current_config = i; + break; + } + } + } +} + +static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private; + struct snd_soc_dpcm *dpcm; + + if (!dai) { + dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + private = dai->private; + if (!private) { + dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (private->comp_dai->config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(component->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (private->dai_config->type) { + case SOF_DAI_INTEL_SSP: + /* search for config to pcm params match, if not found use default */ + ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); + + rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; + rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; + channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; + channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* DMIC only supports 16 or 32 bit formats */ + if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(component->dev, "Invalid fmt %d for DAI type %d\n", + private->comp_dai->config.frame_fmt, + private->dai_config->type); + } + break; + case SOF_DAI_INTEL_HDA: + /* + * HDAudio does not follow the default trigger + * sequence due to firmware implementation + */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + SND_SOC_DPCM_TRIGGER_POST; + } + break; + case SOF_DAI_INTEL_ALH: + /* + * Dai could run with different channel count compared with + * front end, so get dai channel count from topology + */ + channels->min = private->dai_config->alh.channels; + channels->max = private->dai_config->alh.channels; + break; + case SOF_DAI_IMX_ESAI: + rate->min = private->dai_config->esai.fsync_rate; + rate->max = private->dai_config->esai.fsync_rate; + channels->min = private->dai_config->esai.tdm_slots; + channels->max = private->dai_config->esai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = private->dai_config->afe.rate; + rate->max = private->dai_config->afe.rate; + channels->min = private->dai_config->afe.channels; + channels->max = private->dai_config->afe.channels; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_IMX_SAI: + rate->min = private->dai_config->sai.fsync_rate; + rate->max = private->dai_config->sai.fsync_rate; + channels->min = private->dai_config->sai.tdm_slots; + channels->max = private->dai_config->sai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_BT: + rate->min = private->dai_config->acpbt.fsync_rate; + rate->max = private->dai_config->acpbt.fsync_rate; + channels->min = private->dai_config->acpbt.tdm_slots; + channels->max = private->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = private->dai_config->acpsp.fsync_rate; + rate->max = private->dai_config->acpsp.fsync_rate; + channels->min = private->dai_config->acpsp.tdm_slots; + channels->max = private->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = private->dai_config->acpdmic.fsync_rate; + rate->max = private->dai_config->acpdmic.fsync_rate; + channels->min = private->dai_config->acpdmic.tdm_slots; + channels->max = private->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_DMIC 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; + } + + return 0; +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, .trigger = sof_ipc3_pcm_trigger, + .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index dc55a04a1588..658cd8966c9a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -556,33 +556,6 @@ capture: return 0; } -static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, - struct snd_pcm_hw_params *params) -{ - struct sof_ipc_dai_config *config; - struct snd_sof_dai *dai; - int i; - - /* - * Search for all matching DAIs as we can have both playback and capture DAI - * associated with the same link. - */ - list_for_each_entry(dai, &sdev->dai_list, list) { - if (!dai->name || strcmp(link_name, dai->name)) - continue; - for (i = 0; i < dai->number_configs; i++) { - struct sof_dai_private_data *private = dai->private; - - config = &private->dai_config[i]; - if (config->ssp.fsync_rate == params_rate(params)) { - dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); - dai->current_config = i; - break; - } - } - } -} - /* fixup the BE DAI link to match any values from topology */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -596,8 +569,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_dai_private_data *private = dai->private; - struct snd_soc_dpcm *dpcm; + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; /* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -618,148 +590,8 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa return 0; } - /* read format from topology */ - snd_mask_none(fmt); - - switch (private->comp_dai->config.frame_fmt) { - case SOF_IPC_FRAME_S16_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); - break; - case SOF_IPC_FRAME_S24_4LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); - break; - case SOF_IPC_FRAME_S32_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - break; - default: - dev_err(component->dev, "error: No available DAI format!\n"); - return -EINVAL; - } - - /* read rate and channels from topology */ - switch (private->dai_config->type) { - case SOF_DAI_INTEL_SSP: - /* search for config to pcm params match, if not found use default */ - ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - - rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; - rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; - channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; - channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - - break; - case SOF_DAI_INTEL_DMIC: - /* DMIC only supports 16 or 32 bit formats */ - if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { - dev_err(component->dev, - "error: invalid fmt %d for DAI type %d\n", - private->comp_dai->config.frame_fmt, - private->dai_config->type); - } - break; - case SOF_DAI_INTEL_HDA: - /* - * HDAudio does not follow the default trigger - * sequence due to firmware implementation - */ - for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { - struct snd_soc_pcm_runtime *fe = dpcm->fe; - - fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = - SND_SOC_DPCM_TRIGGER_POST; - } - break; - case SOF_DAI_INTEL_ALH: - /* - * Dai could run with different channel count compared with - * front end, so get dai channel count from topology - */ - channels->min = private->dai_config->alh.channels; - channels->max = private->dai_config->alh.channels; - break; - case SOF_DAI_IMX_ESAI: - rate->min = private->dai_config->esai.fsync_rate; - rate->max = private->dai_config->esai.fsync_rate; - channels->min = private->dai_config->esai.tdm_slots; - channels->max = private->dai_config->esai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_MEDIATEK_AFE: - rate->min = private->dai_config->afe.rate; - rate->max = private->dai_config->afe.rate; - channels->min = private->dai_config->afe.channels; - channels->max = private->dai_config->afe.channels; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_IMX_SAI: - rate->min = private->dai_config->sai.fsync_rate; - rate->max = private->dai_config->sai.fsync_rate; - channels->min = private->dai_config->sai.tdm_slots; - channels->max = private->dai_config->sai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_BT: - rate->min = private->dai_config->acpbt.fsync_rate; - rate->max = private->dai_config->acpbt.fsync_rate; - channels->min = private->dai_config->acpbt.tdm_slots; - channels->max = private->dai_config->acpbt.tdm_slots; - - dev_dbg(component->dev, - "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_BT channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_SP: - rate->min = private->dai_config->acpsp.fsync_rate; - rate->max = private->dai_config->acpsp.fsync_rate; - channels->min = private->dai_config->acpsp.tdm_slots; - channels->max = private->dai_config->acpsp.tdm_slots; - - dev_dbg(component->dev, - "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_SP channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_DMIC: - rate->min = private->dai_config->acpdmic.fsync_rate; - rate->max = private->dai_config->acpdmic.fsync_rate; - channels->min = private->dai_config->acpdmic.tdm_slots; - channels->max = private->dai_config->acpdmic.tdm_slots; - - dev_dbg(component->dev, - "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_DMIC channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - default: - dev_err(component->dev, "error: invalid DAI type %d\n", - private->dai_config->type); - break; - } + if (pcm_ops->dai_link_fixup) + return pcm_ops->dai_link_fixup(rtd, params); return 0; } -- cgit v1.2.3 From 3816bbea644202fd0a8410e54dbc30bd93f3292c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:42 -0700 Subject: ASoC: SOF: expose sof_route_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used in IPC3-specific code. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220317175044.1752400-18-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 4 ++-- sound/soc/sof/sof-audio.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 5088ec7019ed..8885709f40df 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -204,8 +204,8 @@ use_count_dec: } EXPORT_SYMBOL(sof_widget_setup); -static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, - struct snd_soc_dapm_widget *wsink) +int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) { const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_widget *src_widget = wsource->dobj.private; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 3ec3f746663a..777da321e2aa 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -441,6 +441,8 @@ void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); +int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink); /* PCM */ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); -- cgit v1.2.3 From 31cd6e469364c42c9c929750991c51e83a95e80b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:43 -0700 Subject: ASoC: SOF: topology: Add ops for setting up and tearing down pipelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce two new ops, set_up_all_pipelines and tear_down_all_pipelines in struct ipc_tplg_ops and define these for IPC3. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220317175044.1752400-19-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 197 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pm.c | 17 ++-- sound/soc/sof/sof-audio.c | 186 --------------------------------------- sound/soc/sof/sof-audio.h | 6 +- sound/soc/sof/topology.c | 55 +++++------- 5 files changed, 235 insertions(+), 226 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 55be97ee816b..3ac65dacc7b9 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2057,6 +2057,201 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget return ret; } +static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + int ret; + + /* restore pipeline components */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + /* only set up the widgets belonging to static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) + continue; + + /* + * For older firmware, skip scheduler widgets in this loop, + * sof_widget_setup() will be called in the 'complete pipeline' loop + */ + if (v->abi_version < SOF_ABI_VER(3, 19, 0) && + swidget->id == snd_soc_dapm_scheduler) + continue; + + /* update DAI config. The IPC will be sent in sof_widget_setup() */ + if (WIDGET_IS_DAI(swidget->id)) { + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private; + struct sof_ipc_dai_config *config; + + if (!dai || !dai->private) + continue; + private = dai->private; + if (!private->dai_config) + continue; + + config = private->dai_config; + /* + * The link DMA channel would be invalidated for running + * streams but not for streams that were in the PAUSED + * state during suspend. So invalidate it here before setting + * the dai config in the DSP. + */ + if (config->type == SOF_DAI_INTEL_HDA) + config->hda.link_dma_ch = DMA_CHAN_INVALID; + } + + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + + /* restore pipeline connections */ + list_for_each_entry(sroute, &sdev->route_list, list) { + /* only set up routes belonging to static pipelines */ + if (!verify && (sroute->src_widget->dynamic_pipeline_widget || + sroute->sink_widget->dynamic_pipeline_widget)) + continue; + + /* + * For virtual routes, both sink and source are not buffer. IPC3 only supports + * connections between a buffer and a component. Ignore the rest. + */ + if (sroute->src_widget->id != snd_soc_dapm_buffer && + sroute->sink_widget->id != snd_soc_dapm_buffer) + continue; + + ret = sof_route_setup(sdev, sroute->src_widget->widget, + sroute->sink_widget->widget); + if (ret < 0) { + dev_err(sdev->dev, "%s: route set up failed\n", __func__); + return ret; + } + } + + /* complete pipeline */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + switch (swidget->id) { + case snd_soc_dapm_scheduler: + /* only complete static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) + continue; + + if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + + swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget); + if (swidget->complete < 0) + return swidget->complete; + break; + default: + break; + } + } + + return 0; +} + +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); + if (ret < 0) + return ret; + } + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * For older firmware, this function doesn't free widgets for static pipelines during suspend. + * It only resets use_count for all widgets. + */ +static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + int ret; + + /* + * This function is called during suspend and for one-time topology verification during + * first boot. In both cases, there is no need to protect swidget->use_count and + * sroute->setup because during suspend all running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->dynamic_pipeline_widget) + continue; + + /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ + if (!verify && !swidget->dynamic_pipeline_widget && + v->abi_version < SOF_ABI_VER(3, 19, 0)) { + swidget->use_count = 0; + swidget->complete = 0; + continue; + } + + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + * Skip this step for older firmware. + */ + if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + + list_for_each_entry(sroute, &sdev->route_list, list) + sroute->setup = false; + + return 0; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2164,4 +2359,6 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, + .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, + .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, }; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 10adbbd0a9cd..1c319582ca6f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -71,6 +71,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; u32 old_state = sdev->dsp_power_state.state; int ret; @@ -144,12 +145,12 @@ static int sof_resume(struct device *dev, bool runtime_resume) } /* restore pipelines */ - ret = sof_set_up_pipelines(sdev, false); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to restore pipeline after resume %d\n", - ret); - return ret; + if (tplg_ops->set_up_all_pipelines) { + ret = tplg_ops->set_up_all_pipelines(sdev, false); + if (ret < 0) { + dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); + return ret; + } } /* Notify clients not managed by pm framework about core resume */ @@ -169,6 +170,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; pm_message_t pm_state; u32 target_state = 0; int ret; @@ -204,7 +206,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) goto suspend; } - sof_tear_down_pipelines(sdev, false); + if (tplg_ops->tear_down_all_pipelines) + tplg_ops->tear_down_all_pipelines(sdev, false); /* release trace */ snd_sof_release_trace(sdev); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 8885709f40df..b4ee65cf9841 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -498,96 +498,6 @@ int sof_set_hw_params_upon_resume(struct device *dev) return snd_sof_dsp_hw_params_upon_resume(sdev); } -int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) -{ - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; - struct snd_sof_widget *swidget; - struct snd_sof_route *sroute; - int ret; - - /* restore pipeline components */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - /* only set up the widgets belonging to static pipelines */ - if (!verify && swidget->dynamic_pipeline_widget) - continue; - - /* - * For older firmware, skip scheduler widgets in this loop, - * sof_widget_setup() will be called in the 'complete pipeline' loop - */ - if (v->abi_version < SOF_ABI_VER(3, 19, 0) && - swidget->id == snd_soc_dapm_scheduler) - continue; - - /* update DAI config. The IPC will be sent in sof_widget_setup() */ - if (WIDGET_IS_DAI(swidget->id)) { - struct snd_sof_dai *dai = swidget->private; - struct sof_dai_private_data *private = dai->private; - struct sof_ipc_dai_config *config; - - if (!dai || !private || !private->dai_config) - continue; - - config = private->dai_config; - /* - * The link DMA channel would be invalidated for running - * streams but not for streams that were in the PAUSED - * state during suspend. So invalidate it here before setting - * the dai config in the DSP. - */ - if (config->type == SOF_DAI_INTEL_HDA) - config->hda.link_dma_ch = DMA_CHAN_INVALID; - } - - ret = sof_widget_setup(sdev, swidget); - if (ret < 0) - return ret; - } - - /* restore pipeline connections */ - list_for_each_entry(sroute, &sdev->route_list, list) { - - /* only set up routes belonging to static pipelines */ - if (!verify && (sroute->src_widget->dynamic_pipeline_widget || - sroute->sink_widget->dynamic_pipeline_widget)) - continue; - - ret = ipc_tplg_ops->route_setup(sdev, sroute); - if (ret < 0) { - dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); - return ret; - } - } - - /* complete pipeline */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - switch (swidget->id) { - case snd_soc_dapm_scheduler: - /* only complete static pipelines */ - if (!verify && swidget->dynamic_pipeline_widget) - continue; - - if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { - ret = sof_widget_setup(sdev, swidget); - if (ret < 0) - return ret; - } - - if (ipc_tplg_ops->pipeline_complete) { - swidget->complete = ipc_tplg_ops->pipeline_complete(sdev, swidget); - if (swidget->complete < 0) - return swidget->complete; - } - break; - default: - break; - } - } - - return 0; -} - int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { @@ -618,102 +528,6 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs return ret; } -/* - * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that - * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. - */ -static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) -{ - struct snd_sof_widget *swidget; - struct snd_sof_pcm *spcm; - int dir, ret; - - /* - * free all PCMs and their associated DAPM widgets if their connected DAPM widget - * list is not NULL. This should only be true for paused streams at this point. - * This is equivalent to the handling of FE DAI suspend trigger for running streams. - */ - list_for_each_entry(spcm, &sdev->pcm_list, list) - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream = spcm->stream[dir].substream; - - if (!substream || !substream->runtime) - continue; - - if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); - if (ret < 0) - return ret; - } - } - - /* - * free any left over DAI widgets. This is equivalent to the handling of suspend trigger - * for the BE DAI for running streams. - */ - list_for_each_entry(swidget, &sdev->widget_list, list) - if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { - ret = sof_widget_free(sdev, swidget); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * For older firmware, this function doesn't free widgets for static pipelines during suspend. - * It only resets use_count for all widgets. - */ -int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) -{ - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; - struct snd_sof_widget *swidget; - struct snd_sof_route *sroute; - int ret; - - /* - * This function is called during suspend and for one-time topology verification during - * first boot. In both cases, there is no need to protect swidget->use_count and - * sroute->setup because during suspend all running streams are suspended and during - * topology loading the sound card unavailable to open PCMs. - */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->dynamic_pipeline_widget) - continue; - - /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ - if (!verify && !swidget->dynamic_pipeline_widget && - v->abi_version < SOF_ABI_VER(3, 19, 0)) { - swidget->use_count = 0; - swidget->complete = 0; - continue; - } - - ret = sof_widget_free(sdev, swidget); - if (ret < 0) - return ret; - } - - /* - * Tear down all pipelines associated with PCMs that did not get suspended - * and unset the prepare flag so that they can be set up again during resume. - * Skip this step for older firmware. - */ - if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { - ret = sof_tear_down_left_over_pipelines(sdev); - if (ret < 0) { - dev_err(sdev->dev, "failed to tear down paused pipelines\n"); - return ret; - } - } - - list_for_each_entry(sroute, &sdev->route_list, list) - sroute->setup = false; - - return 0; -} - /* * Generic object lookup APIs. */ diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 777da321e2aa..b9a9956b5baa 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -117,6 +117,8 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @set_up_all_pipelines: Function pointer for setting up all topology pipelines + * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -130,6 +132,8 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); + int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); }; /** struct snd_sof_tuple - Tuple info @@ -429,8 +433,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); /* PM */ -int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify); -int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify); int sof_set_hw_params_upon_resume(struct device *dev); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 369693cc6d10..9b11e9795a7a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1796,29 +1796,15 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sink_swidget->id == snd_soc_dapm_output) goto err; - /* - * For virtual routes, both sink and source are not - * buffer. Since only buffer linked to component is supported by - * FW, others are reported as error, add check in route function, - * do not send it to FW when both source and sink are not buffer - */ - if (source_swidget->id != snd_soc_dapm_buffer && - sink_swidget->id != snd_soc_dapm_buffer) { - dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", - route->source, route->sink); - goto err; - } else { - sroute->route = route; - dobj->private = sroute; - sroute->src_widget = source_swidget; - sroute->sink_widget = sink_swidget; - - /* add route to route list */ - list_add(&sroute->list, &sdev->route_list); + sroute->route = route; + dobj->private = sroute; + sroute->src_widget = source_swidget; + sroute->sink_widget = sink_swidget; - return 0; - } + /* add route to route list */ + list_add(&sroute->list, &sdev->route_list); + return 0; err: kfree(sroute); return ret; @@ -1917,21 +1903,28 @@ static int sof_complete(struct snd_soc_component *scomp) /* verify topology components loading including dynamic pipelines */ if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { - ret = sof_set_up_pipelines(sdev, true); - if (ret < 0) { - dev_err(sdev->dev, "error: topology verification failed %d\n", ret); - return ret; - } + if (ipc_tplg_ops->set_up_all_pipelines && ipc_tplg_ops->tear_down_all_pipelines) { + ret = ipc_tplg_ops->set_up_all_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", + ret); + return ret; + } - ret = sof_tear_down_pipelines(sdev, true); - if (ret < 0) { - dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret); - return ret; + ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", + ret); + return ret; + } } } /* set up static pipelines */ - return sof_set_up_pipelines(sdev, false); + if (ipc_tplg_ops->set_up_all_pipelines) + return ipc_tplg_ops->set_up_all_pipelines(sdev, false); + + return 0; } /* manifest - optional to inform component of manifest */ -- cgit v1.2.3 From 85f7a8b6e1bea0ad494fb786a5dd7d9715a976d2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 17 Mar 2022 10:50:44 -0700 Subject: ASoC: SOF: Add a new dai_get_clk topology IPC op This will help make the code for getting the mclk and bclk IPC specific. Add the implementation for IPC3 as well. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20220317175044.1752400-20-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 29 +++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 31 +++++++------------------------ sound/soc/sof/sof-audio.h | 6 ++++++ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 3ac65dacc7b9..2f8450a8c0a1 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2252,6 +2252,34 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif return 0; } +static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +{ + struct sof_dai_private_data *private = dai->private; + + if (!private || !private->dai_config) + return 0; + + switch (private->dai_config->type) { + case SOF_DAI_INTEL_SSP: + switch (clk_type) { + case SOF_DAI_CLK_INTEL_SSP_MCLK: + return private->dai_config->ssp.mclk_rate; + case SOF_DAI_CLK_INTEL_SSP_BCLK: + return private->dai_config->ssp.bclk_rate; + default: + break; + } + dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type); + break; + default: + /* not yet implemented for platforms other than the above */ + dev_err(sdev->dev, "DAI type %d not supported yet!\n", private->dai_config->type); + break; + } + + return -EINVAL; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2359,6 +2387,7 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, + .dai_get_clk = sof_ipc3_dai_get_clk, .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b4ee65cf9841..b2f009a0c5b7 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -628,40 +628,23 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, return NULL; } -#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 -#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 - static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct sof_dai_private_data *private = dai->private; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; /* use the tplg configured mclk if existed */ - if (!dai || !private || !private->dai_config) + if (!dai) return 0; - switch (private->dai_config->type) { - case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: - return private->dai_config->ssp.mclk_rate; - case SOF_DAI_CLK_INTEL_SSP_BCLK: - return private->dai_config->ssp.bclk_rate; - default: - dev_err(rtd->dev, "fail to get SSP clk %d rate\n", - clk_type); - return -EINVAL; - } - break; - default: - /* not yet implemented for platforms other than the above */ - dev_err(rtd->dev, "DAI type %d not supported yet!\n", - private->dai_config->type); - return -EINVAL; - } + if (tplg_ops->dai_get_clk) + return tplg_ops->dai_get_clk(sdev, dai, clk_type); + + return 0; } /* diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b9a9956b5baa..7f15b3bc8196 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,6 +30,9 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 +#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 + /* * Volume fractional word length define to 16 sets * the volume linear gain value to use Qx.16 format @@ -39,6 +42,7 @@ struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control; +struct snd_sof_dai; struct snd_sof_dai_config_data { int dai_index; @@ -117,6 +121,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @dai_get_clk: Function pointer for getting the DAI clock setting * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines */ @@ -132,6 +137,7 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); }; -- cgit v1.2.3 From d1129bbe141bf08c19d44a701869ac0780754e86 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 16 Mar 2022 21:18:06 -0700 Subject: MAINTAINERS: Add Shengjiu to maintainer list of sound/soc/fsl Shengjiu has been actively working on latest FSL platforms and keeping upstream effort as well, while I have been working on other subsystem lately and cannot guarantee audio patch review in the near term. So replacing with him in the maintainer list. Signed-off-by: Nicolin Chen Reviewed-by: Fabio Estevam Acked-by: Shengjiu Wang Link: https://lore.kernel.org/r/20220317041806.28230-1-nicoleotsuka@gmail.com Signed-off-by: Mark Brown --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 321e08ee8c5d..19625fe547cc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7778,10 +7778,10 @@ F: drivers/net/ethernet/freescale/fs_enet/ F: include/linux/fs_enet_pd.h FREESCALE SOC SOUND DRIVERS -M: Nicolin Chen +M: Shengjiu Wang M: Xiubo Li R: Fabio Estevam -R: Shengjiu Wang +R: Nicolin Chen L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Maintained -- cgit v1.2.3 From c639e85e93aa10ea0512ee416eead60da466e161 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 18 Mar 2022 11:26:09 +0200 Subject: ASoC: atmel: mchp-pdmc: print the correct property name The correct property is 'microchip,mic-pos', not 'mchp,mic-pos', so replace all occurences of 'mchp,mic-pos' with 'microchip,mic-pos'. Fix a multi-line comment format while we are at it. Fixes: 50291652af52 ("ASoC: atmel: mchp-pdmc: add PDMC driver") Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20220318092609.130901-1-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-pdmc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index e0aec5fe8a26..1a7802fbf23c 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -879,13 +879,13 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos"); if (dd->mic_no < 0) { - dev_err(dd->dev, "failed to get mchp,mic-pos: %d", + dev_err(dd->dev, "failed to get microchip,mic-pos: %d", dd->mic_no); return dd->mic_no; } if (!dd->mic_no || dd->mic_no % 2 || dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) { - dev_err(dd->dev, "invalid array length for mchp,mic-pos: %d", + dev_err(dd->dev, "invalid array length for microchip,mic-pos: %d", dd->mic_no); return -EINVAL; } @@ -894,9 +894,10 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no); - /* by default, we consider the order of microphones in mchp,mic-pos to - * be the same with the channel mapping; 1st microphone channel 0, 2nd - * microphone channel 1, etc. + /* + * by default, we consider the order of microphones in + * microchip,mic-pos to be the same with the channel mapping; + * 1st microphone channel 0, 2nd microphone channel 1, etc. */ for (i = 0; i < dd->mic_no; i++) { int ds; -- cgit v1.2.3 From 92ee3c60ec9fe64404dc035e7c41277d74aa26cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Mar 2022 18:07:17 +0100 Subject: ALSA: pcm: Fix races among concurrent hw_params and hw_free calls Currently we have neither proper check nor protection against the concurrent calls of PCM hw_params and hw_free ioctls, which may result in a UAF. Since the existing PCM stream lock can't be used for protecting the whole ioctl operations, we need a new mutex to protect those racy calls. This patch introduced a new mutex, runtime->buffer_mutex, and applies it to both hw_params and hw_free ioctl code paths. Along with it, the both functions are slightly modified (the mmap_count check is moved into the state-check block) for code simplicity. Reported-by: Hu Jiahui Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220322170720.3529-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 1 + sound/core/pcm.c | 2 ++ sound/core/pcm_native.c | 61 +++++++++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 36da42cd0774..314f2779cab5 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -401,6 +401,7 @@ struct snd_pcm_runtime { wait_queue_head_t tsleep; /* transfer sleep */ struct fasync_struct *fasync; bool stop_operating; /* sync_stop will be called */ + struct mutex buffer_mutex; /* protect for buffer changes */ /* -- private section -- */ void *private_data; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index ba4a987ed1c6..edd9849210f2 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -969,6 +969,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, init_waitqueue_head(&runtime->tsleep); runtime->status->state = SNDRV_PCM_STATE_OPEN; + mutex_init(&runtime->buffer_mutex); substream->runtime = runtime; substream->private_data = pcm->private_data; @@ -1002,6 +1003,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) } else { substream->runtime = NULL; } + mutex_destroy(&runtime->buffer_mutex); kfree(runtime); put_pid(substream->pid); substream->pid = NULL; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a056b3ef3c84..266895374b83 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -685,33 +685,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, return 0; } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +#define is_oss_stream(substream) ((substream)->oss.oss) +#else +#define is_oss_stream(substream) false +#endif + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime; - int err, usecs; + int err = 0, usecs; unsigned int bits; snd_pcm_uframes_t frames; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: + if (!is_oss_stream(substream) && + atomic_read(&substream->mmap_count)) + err = -EBADFD; break; default: - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + err = -EBADFD; + break; } snd_pcm_stream_unlock_irq(substream); -#if IS_ENABLED(CONFIG_SND_PCM_OSS) - if (!substream->oss.oss) -#endif - if (atomic_read(&substream->mmap_count)) - return -EBADFD; + if (err) + goto unlock; snd_pcm_sync_stop(substream, true); @@ -799,16 +806,21 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, if (usecs >= 0) cpu_latency_qos_add_request(&substream->latency_pm_qos_req, usecs); - return 0; + err = 0; _error: - /* hardware might be unusable from this time, - so we force application to retry to set - the correct hardware parameter settings */ - snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); - if (substream->ops->hw_free != NULL) - substream->ops->hw_free(substream); - if (substream->managed_buffer_alloc) - snd_pcm_lib_free_pages(substream); + if (err) { + /* hardware might be unusable from this time, + * so we force application to retry to set + * the correct hardware parameter settings + */ + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + if (substream->managed_buffer_alloc) + snd_pcm_lib_free_pages(substream); + } + unlock: + mutex_unlock(&runtime->buffer_mutex); return err; } @@ -848,26 +860,31 @@ static int do_hw_free(struct snd_pcm_substream *substream) static int snd_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - int result; + int result = 0; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: + if (atomic_read(&substream->mmap_count)) + result = -EBADFD; break; default: - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + result = -EBADFD; + break; } snd_pcm_stream_unlock_irq(substream); - if (atomic_read(&substream->mmap_count)) - return -EBADFD; + if (result) + goto unlock; result = do_hw_free(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); + unlock: + mutex_unlock(&runtime->buffer_mutex); return result; } -- cgit v1.2.3 From dca947d4d26dbf925a64a6cfb2ddbc035e831a3d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Mar 2022 18:07:18 +0100 Subject: ALSA: pcm: Fix races among concurrent read/write and buffer changes In the current PCM design, the read/write syscalls (as well as the equivalent ioctls) are allowed before the PCM stream is running, that is, at PCM PREPARED state. Meanwhile, we also allow to re-issue hw_params and hw_free ioctl calls at the PREPARED state that may change or free the buffers, too. The problem is that there is no protection against those mix-ups. This patch applies the previously introduced runtime->buffer_mutex to the read/write operations so that the concurrent hw_params or hw_free call can no longer interfere during the operation. The mutex is unlocked before scheduling, so we don't take it too long. Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220322170720.3529-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f2090025236b..a40a35e51fad 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1906,9 +1906,11 @@ static int wait_for_avail(struct snd_pcm_substream *substream, if (avail >= runtime->twake) break; snd_pcm_stream_unlock_irq(substream); + mutex_unlock(&runtime->buffer_mutex); tout = schedule_timeout(wait_time); + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); set_current_state(TASK_INTERRUPTIBLE); switch (runtime->status->state) { @@ -2219,6 +2221,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, nonblock = !!(substream->f_flags & O_NONBLOCK); + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); err = pcm_accessible_state(runtime); if (err < 0) @@ -2310,6 +2313,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, if (xfer > 0 && err >= 0) snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); + mutex_unlock(&runtime->buffer_mutex); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } EXPORT_SYMBOL(__snd_pcm_lib_xfer); -- cgit v1.2.3 From 3c3201f8c7bb77eb53b08a3ca8d9a4ddc500b4c0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Mar 2022 18:07:19 +0100 Subject: ALSA: pcm: Fix races among concurrent prepare and hw_params/hw_free calls Like the previous fixes to hw_params and hw_free ioctl races, we need to paper over the concurrent prepare ioctl calls against hw_params and hw_free, too. This patch implements the locking with the existing runtime->buffer_mutex for prepare ioctls. Unlike the previous case for snd_pcm_hw_hw_params() and snd_pcm_hw_free(), snd_pcm_prepare() is performed to the linked streams, hence the lock can't be applied simply on the top. For tracking the lock in each linked substream, we modify snd_pcm_action_group() slightly and apply the buffer_mutex for the case stream_lock=false (formerly there was no lock applied) there. Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220322170720.3529-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 266895374b83..0e4fbf5fd87b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1190,15 +1190,17 @@ struct action_ops { static int snd_pcm_action_group(const struct action_ops *ops, struct snd_pcm_substream *substream, snd_pcm_state_t state, - bool do_lock) + bool stream_lock) { struct snd_pcm_substream *s = NULL; struct snd_pcm_substream *s1; int res = 0, depth = 1; snd_pcm_group_for_each_entry(s, substream) { - if (do_lock && s != substream) { - if (s->pcm->nonatomic) + if (s != substream) { + if (!stream_lock) + mutex_lock_nested(&s->runtime->buffer_mutex, depth); + else if (s->pcm->nonatomic) mutex_lock_nested(&s->self_group.mutex, depth); else spin_lock_nested(&s->self_group.lock, depth); @@ -1226,18 +1228,18 @@ static int snd_pcm_action_group(const struct action_ops *ops, ops->post_action(s, state); } _unlock: - if (do_lock) { - /* unlock streams */ - snd_pcm_group_for_each_entry(s1, substream) { - if (s1 != substream) { - if (s1->pcm->nonatomic) - mutex_unlock(&s1->self_group.mutex); - else - spin_unlock(&s1->self_group.lock); - } - if (s1 == s) /* end */ - break; + /* unlock streams */ + snd_pcm_group_for_each_entry(s1, substream) { + if (s1 != substream) { + if (!stream_lock) + mutex_unlock(&s1->runtime->buffer_mutex); + else if (s1->pcm->nonatomic) + mutex_unlock(&s1->self_group.mutex); + else + spin_unlock(&s1->self_group.lock); } + if (s1 == s) /* end */ + break; } return res; } @@ -1367,10 +1369,12 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops, /* Guarantee the group members won't change during non-atomic action */ down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->runtime->buffer_mutex); if (snd_pcm_stream_linked(substream)) res = snd_pcm_action_group(ops, substream, state, false); else res = snd_pcm_action_single(ops, substream, state); + mutex_unlock(&substream->runtime->buffer_mutex); up_read(&snd_pcm_link_rwsem); return res; } -- cgit v1.2.3 From 69534c48ba8ce552ce383b3dfdb271ffe51820c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Mar 2022 18:07:20 +0100 Subject: ALSA: pcm: Fix races among concurrent prealloc proc writes We have no protection against concurrent PCM buffer preallocation changes via proc files, and it may potentially lead to UAF or some weird problem. This patch applies the PCM open_mutex to the proc write operation for avoiding the racy proc writes and the PCM stream open (and further operations). Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220322170720.3529-5-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_memory.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index b70ce3b69ab4..8848d2f3160d 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -163,19 +163,20 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, size_t size; struct snd_dma_buffer new_dmab; + mutex_lock(&substream->pcm->open_mutex); if (substream->runtime) { buffer->error = -EBUSY; - return; + goto unlock; } if (!snd_info_get_line(buffer, line, sizeof(line))) { snd_info_get_str(str, line, sizeof(str)); size = simple_strtoul(str, NULL, 10) * 1024; if ((size != 0 && size < 8192) || size > substream->dma_max) { buffer->error = -EINVAL; - return; + goto unlock; } if (substream->dma_buffer.bytes == size) - return; + goto unlock; memset(&new_dmab, 0, sizeof(new_dmab)); new_dmab.dev = substream->dma_buffer.dev; if (size > 0) { @@ -189,7 +190,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, substream->pcm->card->number, substream->pcm->device, substream->stream ? 'c' : 'p', substream->number, substream->pcm->name, size); - return; + goto unlock; } substream->buffer_bytes_max = size; } else { @@ -201,6 +202,8 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, } else { buffer->error = -EINVAL; } + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static inline void preallocate_info_init(struct snd_pcm_substream *substream) -- cgit v1.2.3 From 1f68915b2efd0d6bfd6e124aa63c94b3c69f127c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Mar 2022 18:13:25 +0100 Subject: ALSA: pcm: Add stream lock during PCM reset ioctl operations snd_pcm_reset() is a non-atomic operation, and it's allowed to run during the PCM stream running. It implies that the manipulation of hw_ptr and other parameters might be racy. This patch adds the PCM stream lock at appropriate places in snd_pcm_*_reset() actions for covering that. Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220322171325.4355-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0e4fbf5fd87b..704fdc9ebf91 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1864,11 +1864,13 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; + snd_pcm_stream_lock_irq(substream); runtime->hw_ptr_base = 0; runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; + snd_pcm_stream_unlock_irq(substream); return 0; } @@ -1876,10 +1878,12 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream, snd_pcm_state_t state) { struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_stream_lock_irq(substream); runtime->control->appl_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); + snd_pcm_stream_unlock_irq(substream); } static const struct action_ops snd_pcm_action_reset = { -- cgit v1.2.3 From 17aaf0193392cb3451bf0ac75ba396ec4cbded6e Mon Sep 17 00:00:00 2001 From: Giacomo Guiduzzi Date: Tue, 22 Mar 2022 21:06:54 +0100 Subject: ALSA: pci: fix reading of swapped values from pcmreg in AC97 codec Tests 72 and 78 for ALSA in kselftest fail due to reading inconsistent values from some devices on a VirtualBox Virtual Machine using the snd_intel8x0 driver for the AC'97 Audio Controller device. Taking for example test number 72, this is what the test reports: "Surround Playback Volume.0 expected 1 but read 0, is_volatile 0" "Surround Playback Volume.1 expected 0 but read 1, is_volatile 0" These errors repeat for each value from 0 to 31. Taking a look at these error messages it is possible to notice that the written values are read back swapped. When the write is performed, these values are initially stored in an array used to sanity-check them and write them in the pcmreg array. To write them, the two one-byte values are packed together in a two-byte variable through bitwise operations: the first value is shifted left by one byte and the second value is stored in the right byte through a bitwise OR. When reading the values back, right shifts are performed to retrieve the previously stored bytes. These shifts are executed in the wrong order, thus reporting the values swapped as shown above. This patch fixes this mistake by reversing the read operations' order. Signed-off-by: Giacomo Guiduzzi Signed-off-by: Paolo Valente Cc: Link: https://lore.kernel.org/r/20220322200653.15862-1-guiduzzi.giacomo@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 01f296d524ce..cb60a07d39a8 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -938,8 +938,8 @@ static int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct int codec = kcontrol->private_value & 3; mutex_lock(&ac97->page_mutex); - ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); - ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); + ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); + ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); mutex_unlock(&ac97->page_mutex); return 0; } -- cgit v1.2.3 From ef248d9bd616b04df8be25539a4dc5db4b6c56f4 Mon Sep 17 00:00:00 2001 From: Matt Kramer Date: Tue, 22 Mar 2022 13:48:17 -0700 Subject: ALSA: hda/realtek: Add alc256-samsung-headphone fixup This fixes the near-silence of the headphone jack on the ALC256-based Samsung Galaxy Book Flex Alpha (NP730QCJ). The magic verbs were found through trial and error, using known ALC298 hacks as inspiration. The fixup is auto-enabled only when the NP730QCJ is detected. It can be manually enabled using model=alc256-samsung-headphone. Signed-off-by: Matt Kramer Link: https://lore.kernel.org/r/3168355.aeNJFYEL58@linus Signed-off-by: Takashi Iwai --- Documentation/sound/hd-audio/models.rst | 4 ++++ sound/pci/hda/patch_realtek.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst index d25335993e55..9b52f50a6854 100644 --- a/Documentation/sound/hd-audio/models.rst +++ b/Documentation/sound/hd-audio/models.rst @@ -261,6 +261,10 @@ alc-sense-combo huawei-mbx-stereo Enable initialization verbs for Huawei MBX stereo speakers; might be risky, try this at your own risk +alc298-samsung-headphone + Samsung laptops with ALC298 +alc256-samsung-headphone + Samsung laptops with ALC256 ALC66x/67x/892 ============== diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1d14be4ee31d..f6ee67f41c45 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6958,6 +6958,7 @@ enum { ALC236_FIXUP_HP_MUTE_LED, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, + ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS, ALC269VC_FIXUP_ACER_HEADSET_MIC, @@ -8286,6 +8287,14 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf}, + { } + }, + }, [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -9099,6 +9108,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), @@ -9445,6 +9455,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"}, {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, {.id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc298-samsung-headphone"}, + {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"}, -- cgit v1.2.3