From 7a1625c1711b526a77cb9c3acc15dbba71896a40 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 8 Apr 2024 10:18:40 +0200 Subject: ASoC: Intel: avs: Fix debug window description Recent changes addressed PAGE_SIZE ambiguity in 2/3 locations for struct avs_icl_memwnd2. The unaddressed one causes build errors when PAGE_SIZE != SZ_4K. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202404070100.i3t3Jf7d-lkp@intel.com/ Fixes: 275b583d047a ("ASoC: Intel: avs: ICL-based platforms support") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240408081840.1319431-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/icl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c index 9d9921e1cd4d..d2554c857732 100644 --- a/sound/soc/intel/avs/icl.c +++ b/sound/soc/intel/avs/icl.c @@ -64,7 +64,7 @@ struct avs_icl_memwnd2_desc { struct avs_icl_memwnd2 { union { struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; - u8 rsvd[PAGE_SIZE]; + u8 rsvd[SZ_4K]; }; u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; } __packed; -- cgit From 2e93a29b48a017c777d4fcbfcc51aba4e6a90d38 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 5 Apr 2024 10:43:06 +0000 Subject: ASoC: tegra: Fix DSPK 16-bit playback DSPK configuration is wrong for 16-bit playback and this happens because the client config is always fixed at 24-bit in hw_params(). Fix this by updating the client config to 16-bit for the respective playback. Fixes: 327ef6470266 ("ASoC: tegra: Add Tegra186 based DSPK driver") Cc: stable@vger.kernel.org Signed-off-by: Sameer Pujar Acked-by: Thierry Reding Link: https://msgid.link/r/20240405104306.551036-1-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra186_dspk.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c index aa37c4ab0adb..21cd41fec7a9 100644 --- a/sound/soc/tegra/tegra186_dspk.c +++ b/sound/soc/tegra/tegra186_dspk.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // tegra186_dspk.c - Tegra186 DSPK driver -// -// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. #include #include @@ -241,14 +240,14 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - cif_conf.client_bits = TEGRA_ACIF_BITS_24; - switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + cif_conf.client_bits = TEGRA_ACIF_BITS_16; break; case SNDRV_PCM_FORMAT_S32_LE: cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_24; break; default: dev_err(dev, "unsupported format!\n"); -- cgit From e50729d742ec364895f1c389c32315984a987aa5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Apr 2024 21:15:59 +0200 Subject: ASoC: Intel: bytcr_rt5640: Apply Asus T100TA quirk to Asus T100TAM too The Asus T100TA quirk has been using an exact match on a product-name of "T100TA" but there are also T100TAM variants with a slightly higher clocked CPU and a metal backside which need the same quirk. Sort the existing T100TA (stereo speakers) below the more specific T100TAF (mono speaker) quirk and switch from exact matching to substring matching so that the T100TA quirk will also match on the T100TAM models. Signed-off-by: Hans de Goede Link: https://msgid.link/r/20240407191559.21596-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 05f38d1f7d82..b41a1147f1c3 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -636,28 +636,30 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_USE_AMCR0F28), }, { + /* Asus T100TAF, unlike other T100TA* models this one has a mono speaker */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_JD_SRC_JD2_IN4N | BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, { + /* Asus T100TA and T100TAM, must come after T100TAF (mono spk) match */ .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_JD_SRC_JD2_IN4N | BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | - BYT_RT5640_MONO_SPEAKER | - BYT_RT5640_DIFF_MIC | - BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, { -- cgit From 70ee853eec5693fefd8348a2b049d9cb83362e58 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:00 +0100 Subject: regmap: Add regmap_read_bypassed() Add a regmap_read_bypassed() to allow reads from the hardware registers while the regmap is in cache-only mode. A typical use for this is to keep the cache in cache-only mode until the hardware has reached a valid state, but one or more status registers must be polled to determine when this state is reached. For example, firmware download on the cs35l56 can take several seconds if there are multiple amps sharing limited bus bandwidth. This is too long to block in probe() so it is done as a background task. The device must be soft-reset to reboot the firmware and during this time the registers are not accessible, so the cache should be in cache-only. But the driver must poll a register to detect when reboot has completed. Signed-off-by: Richard Fitzgerald Fixes: 8a731fd37f8b ("ASoC: cs35l56: Move utility functions to shared file") Link: https://msgid.link/r/20240408101803.43183-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 8 ++++++++ 2 files changed, 45 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 5cb425f6f02d..0a34dd3c4f38 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2838,6 +2838,43 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) } EXPORT_SYMBOL_GPL(regmap_read); +/** + * regmap_read_bypassed() - Read a value from a single register direct + * from the device, bypassing the cache + * + * @map: Register map to read from + * @reg: Register to be read from + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val) +{ + int ret; + bool bypass, cache_only; + + if (!IS_ALIGNED(reg, map->reg_stride)) + return -EINVAL; + + map->lock(map->lock_arg); + + bypass = map->cache_bypass; + cache_only = map->cache_only; + map->cache_bypass = true; + map->cache_only = false; + + ret = _regmap_read(map, reg, val); + + map->cache_bypass = bypass; + map->cache_only = cache_only; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_read_bypassed); + /** * regmap_raw_read() - Read raw data from the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b743241cfb7c..d470303b1bbb 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1230,6 +1230,7 @@ int regmap_multi_reg_write_bypassed(struct regmap *map, int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); +int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); int regmap_noinc_read(struct regmap *map, unsigned int reg, @@ -1739,6 +1740,13 @@ static inline int regmap_read(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_read_bypassed(struct regmap *map, unsigned int reg, + unsigned int *val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len) { -- cgit From 73580ec607dfe125b140ed30c7c0a074db78c558 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:01 +0100 Subject: ALSA: hda: cs35l56: Exit cache-only after cs35l56_wait_for_firmware_boot() Adds calls to disable regmap cache-only after a successful return from cs35l56_wait_for_firmware_boot(). This is to prepare for a change in the shared ASoC module that will leave regmap in cache-only mode after cs35l56_system_reset(). This is to prevent register accesses going to the hardware while it is rebooting. Signed-off-by: Richard Fitzgerald Link: https://msgid.link/r/20240408101803.43183-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/pci/hda/cs35l56_hda.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 1a3f84599cb5..558c1f38fe97 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -644,6 +644,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) goto err_powered_up; + + regcache_cache_only(cs35l56->base.regmap, false); } /* Disable auto-hibernate so that runtime_pm has control */ @@ -1002,6 +1004,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) if (ret) goto err; + regcache_cache_only(cs35l56->base.regmap, false); + ret = cs35l56_set_patch(&cs35l56->base); if (ret) goto err; -- cgit From d4884fd48a44f3d7f0d4d7399b663b69c000233d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:02 +0100 Subject: ASoC: cs35l56: Fix unintended bus access while resetting amp Use the new regmap_read_bypassed() so that the regmap can be left in cache-only mode while it is booting, but the driver can still read boot-status and chip-id information during this time. This fixes race conditions where some writes could be issued to the silicon while it is still rebooting, before the driver has determined that the boot is complete. This is typically prevented by putting regmap into cache-only until the hardware is ready. But this assumes that the driver does not need to access device registers to determine when it is "ready". For cs35l56 this involves polling a register and the original implementation relied on having special handlers to block racing callbacks until dsp_work() is complete. However, some cases were missed, most notably the ASP DAI functions. The regmap_read_bypassed() function allows the fix for this to be simplified to putting regmap into cache-only during the reset. The initial boot stages (poll HALO_STATE and read the chip ID) are all done bypassed. Only when the amp is seen to be booted is the cache-only revoked. Changes are: - cs35l56_system_reset() now leaves the regmap in cache-only status. - cs35l56_wait_for_firmware_boot() polls using regmap_read_bypassed(). - cs35l56_init() revokes cache-only either via cs35l56_hw_init() or when firmware has rebooted after a soft reset. - cs35l56_hw_init() exits cache-only after it has determined that the amp has booted. - cs35l56_sdw_init() doesn't disable cache-only, since this must be deferred to cs35l56_init(). - cs35l56_runtime_resume_common() waits for firmware boot before exiting cache-only. These changes cover three situations where the registers are not accessible: 1) SoundWire first-time enumeration. The regmap is kept in cache-only until the chip is fully booted. The original code had to exit cache-only to read chip status in cs35l56_init() and cs35l56_hw_init() but this is now deferred to after the firmware has rebooted. In this case cs35l56_sdw_probe() leaves regmap in cache-only (unchanged behaviour) and cs35l56_hw_init() exits cache-only after the firmware is booted and the chip identified. 2) Soft reset during first-time initialization. cs35l56_init() calls cs35l56_system_reset(), which puts regmap into cache-only. On I2C/SPI cs35l56_init() then flows through to call cs35l56_wait_for_firmware_boot() and exit cache-only. On SoundWire the re-enumeration will enter cs35l56_init() again, which then drops down to call cs35l56_wait_for_firmware_boot() and exit cache-only. 3) Soft reset after firmware download. dsp_work() calls cs35l56_system_reset(), which puts regmap into cache-only. After this the flow is the same as (2). Signed-off-by: Richard Fitzgerald Fixes: 8a731fd37f8b ("ASoC: cs35l56: Move utility functions to shared file") Link: https://msgid.link/r/20240408101803.43183-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 2 -- sound/soc/codecs/cs35l56-shared.c | 20 ++++++++++++-------- sound/soc/codecs/cs35l56.c | 2 ++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 14a5f86019aa..70ff55c1517f 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -188,8 +188,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) goto out; } - regcache_cache_only(cs35l56->base.regmap, false); - ret = cs35l56_init(cs35l56); if (ret < 0) { regcache_cache_only(cs35l56->base.regmap, true); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 08cac58e3ab2..a83317db75ed 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -307,10 +307,10 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) reg = CS35L56_DSP1_HALO_STATE; /* - * This can't be a regmap_read_poll_timeout() because cs35l56 will NAK - * I2C until it has booted which would terminate the poll + * The regmap must remain in cache-only until the chip has + * booted, so use a bypassed read of the status register. */ - poll_ret = read_poll_timeout(regmap_read, read_ret, + poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US, @@ -362,7 +362,8 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) return; cs35l56_wait_control_port_ready(); - regcache_cache_only(cs35l56_base->regmap, false); + + /* Leave in cache-only. This will be revoked when the chip has rebooted. */ } EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED); @@ -577,14 +578,14 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou cs35l56_issue_wake_event(cs35l56_base); out_sync: - regcache_cache_only(cs35l56_base->regmap, false); - ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) { dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); goto err; } + regcache_cache_only(cs35l56_base->regmap, false); + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) goto err; @@ -757,7 +758,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) * devices so the REVID needs to be determined before waiting for the * firmware to boot. */ - ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid); + ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); return ret; @@ -768,7 +769,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) if (ret) return ret; - ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid); + ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Device ID failed\n"); return ret; @@ -787,6 +788,9 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) cs35l56_base->type = devid & 0xFF; + /* Silicon is now identified and booted so exit cache-only */ + regcache_cache_only(cs35l56_base->regmap, false); + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) { dev_err(cs35l56_base->dev, "Get Secure status failed\n"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 8d2f021fb362..5a4e0e479414 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1531,6 +1531,8 @@ post_soft_reset: return ret; dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n"); + + regcache_cache_only(cs35l56->base.regmap, false); } /* Disable auto-hibernate so that runtime_pm has control */ -- cgit From dfd2ffb373999630a14d7ff614440f1c2fcc704c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:03 +0100 Subject: ASoC: cs35l56: Prevent overwriting firmware ASP config Only populate the ASP1 config registers in the regmap cache if the ASP DAI is used. This prevents regcache_sync() from overwriting these registers with their defaults when the firmware owns control of these registers. On a SoundWire system the ASP could be owned by the firmware to share reference audio with the firmware on other cs35l56. Or it can be used as a normal codec-codec interface owned by the driver. The driver must not overwrite the registers if the firmware has control of them. The original implementation for this in commit 07f7d6e7a124 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") was to still provide defaults for these registers, assuming that if they were never reconfigured from defaults then regcache_sync() would not write them out because they are not dirty. Unfortunately regcache_sync() is not that smart. If the chip has not reset (so the driver has not called regcache_mark_dirty()) a regcache_sync() could write out registers that are not dirty. To avoid accidental overwriting of the ASP registers, they are removed from the table of defaults and instead are populated with defaults only if one of the ASP DAI configuration functions is called. So if the DAI has never been configured, the firmware is assumed to have ownership of these registers, and the regmap cache will not contain any entries for them. Signed-off-by: Richard Fitzgerald Fixes: 07f7d6e7a124 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") Link: https://msgid.link/r/20240408101803.43183-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 2 ++ sound/soc/codecs/cs35l56-shared.c | 63 ++++++++++++++++++++++++++------------- sound/soc/codecs/cs35l56.c | 24 ++++++++++++++- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index e0629699b563..1a3c6f66f620 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -267,6 +267,7 @@ struct cs35l56_base { bool fw_patched; bool secured; bool can_hibernate; + bool fw_owns_asp1; bool cal_data_valid; s8 cal_index; struct cirrus_amp_cal_data cal_data; @@ -283,6 +284,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; int cs35l56_set_patch(struct cs35l56_base *cs35l56_base); +int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base); int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base); int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index a83317db75ed..ec1d95e57061 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -40,16 +40,11 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { /* no defaults for OTP_MEM - first read populates cache */ - { CS35L56_ASP1_ENABLES1, 0x00000000 }, - { CS35L56_ASP1_CONTROL1, 0x00000028 }, - { CS35L56_ASP1_CONTROL2, 0x18180200 }, - { CS35L56_ASP1_CONTROL3, 0x00000002 }, - { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, - { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, - { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, - { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, - - /* no defaults for ASP1TX mixer */ + /* + * No defaults for ASP1 control or ASP1TX mixer. See + * cs35l56_populate_asp1_register_defaults() and + * cs35l56_sync_asp1_mixer_widgets_with_firmware(). + */ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, @@ -210,6 +205,36 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } +static const struct reg_sequence cs35l56_asp1_defaults[] = { + REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000), + REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028), + REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200), + REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002), + REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100), + REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100), + REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018), + REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018), +}; + +/* + * The firmware can have control of the ASP so we don't provide regmap + * with defaults for these registers, to prevent a regcache_sync() from + * overwriting the firmware settings. But if the machine driver hooks up + * the ASP it means the driver is taking control of the ASP, so then the + * registers are populated with the defaults. + */ +int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base) +{ + if (!cs35l56_base->fw_owns_asp1) + return 0; + + cs35l56_base->fw_owns_asp1 = false; + + return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults, + ARRAY_SIZE(cs35l56_asp1_defaults)); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED); + /* * The firmware boot sequence can overwrite the ASP1 config registers so that * they don't match regmap's view of their values. Rewrite the values from the @@ -217,19 +242,15 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) */ int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) { - struct reg_sequence asp1_regs[] = { - { .reg = CS35L56_ASP1_ENABLES1 }, - { .reg = CS35L56_ASP1_CONTROL1 }, - { .reg = CS35L56_ASP1_CONTROL2 }, - { .reg = CS35L56_ASP1_CONTROL3 }, - { .reg = CS35L56_ASP1_FRAME_CONTROL1 }, - { .reg = CS35L56_ASP1_FRAME_CONTROL5 }, - { .reg = CS35L56_ASP1_DATA_CONTROL1 }, - { .reg = CS35L56_ASP1_DATA_CONTROL5 }, - }; + struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)]; int i, ret; - /* Read values from regmap cache into a write sequence */ + if (cs35l56_base->fw_owns_asp1) + return 0; + + memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs)); + + /* Read current values from regmap cache into the write sequence */ for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); if (ret) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 5a4e0e479414..6331b8c6136e 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -454,9 +454,14 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); unsigned int val; + int ret; dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; @@ -530,6 +535,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx unsigned int rx_mask, int slots, int slot_width) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + int ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; if ((slots == 0) || (slot_width == 0)) { dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); @@ -578,6 +588,11 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); unsigned int rate = params_rate(params); u8 asp_width, asp_wl; + int ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; asp_wl = params_width(params); if (cs35l56->asp_slot_width) @@ -634,7 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int freq_id; + int freq_id, ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; if (freq == 0) { cs35l56->sysclk_set = false; @@ -1403,6 +1422,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; + /* Assume that the firmware owns ASP1 until we know different */ + cs35l56->base.fw_owns_asp1 = true; + dev_set_drvdata(cs35l56->base.dev, cs35l56); cs35l56_fill_supply_names(cs35l56->supplies); -- cgit From 965e49cdf8c19f21b8308adeded3a8139cff5c84 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:33 +0300 Subject: ASoC: SOF: ipc4-pcm: Use consistent name for snd_sof_pcm_stream pointer Throughout the file the pointer for snd_sof_pcm_stream is named either 'stream' or (wrongly) 'spcm' which confuses the reader. Use 'sps' for the pointer name as it is the most common name used in SOF codebase. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index e915f9f87a6c..f989cc2992bf 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -764,7 +764,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return 0; } -static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm) +static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps) { struct sof_ipc4_copier *host_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL; @@ -775,7 +775,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc int i; /* find host & dai to locate info in memory window */ - for_each_dapm_widgets(spcm->list, i, widget) { + for_each_dapm_widgets(sps->list, i, widget) { struct snd_sof_widget *swidget = widget->dobj.private; if (!swidget) @@ -795,7 +795,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - info = spcm->private; + info = sps->private; info->host_copier = host_copier; info->dai_copier = dai_copier; info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + @@ -864,7 +864,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_sof_pcm_stream *stream, + struct snd_sof_pcm_stream *sps, struct sof_ipc4_timestamp_info *time_info) { struct sof_ipc4_copier *host_copier = time_info->host_copier; @@ -918,7 +918,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; snd_pcm_uframes_t head_cnt, tail_cnt; - struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm_stream *sps; u64 dai_cnt, host_cnt, host_ptr; struct snd_sof_pcm *spcm; int ret; @@ -927,8 +927,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, if (!spcm) return -EOPNOTSUPP; - stream = &spcm->stream[substream->stream]; - time_info = stream->private; + sps = &spcm->stream[substream->stream]; + time_info = sps->private; if (!time_info) return -EOPNOTSUPP; @@ -938,7 +938,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, * the statistics is complete. And it will not change after the first initiailization. */ if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { - ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info); + ret = sof_ipc4_get_stream_start_offset(sdev, substream, sps, time_info); if (ret < 0) return -EOPNOTSUPP; } @@ -1030,15 +1030,15 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; - struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm_stream *sps; struct snd_sof_pcm *spcm; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return 0; - stream = &spcm->stream[substream->stream]; - time_info = stream->private; + sps = &spcm->stream[substream->stream]; + time_info = sps->private; /* * Report the stored delay value calculated in the pointer callback. * In the unlikely event that the calculation was skipped/aborted, the -- cgit From 36e980050b0733829e4e0f97b97f7907ba9f00bb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:34 +0300 Subject: ASoC: SOF: ipc4-pcm: Use consistent name for sof_ipc4_timestamp_info pointer The pointer to sof_ipc4_timestamp_info named most of the time as 'time_info' only to be named as 'stream_info' or 'info' in two function. Use the consistent name of 'time_info' throughout the file. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index f989cc2992bf..74b0d0d00270 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -721,7 +721,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct sof_ipc4_timestamp_info *stream_info; + struct sof_ipc4_timestamp_info *time_info; bool support_info = true; u32 abi_version; u32 abi_offset; @@ -752,13 +752,13 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm if (!support_info) continue; - stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL); - if (!stream_info) { + time_info = kzalloc(sizeof(*time_info), GFP_KERNEL); + if (!time_info) { sof_ipc4_pcm_free(sdev, spcm); return -ENOMEM; } - spcm->stream[stream].private = stream_info; + spcm->stream[stream].private = time_info; } return 0; @@ -769,7 +769,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc struct sof_ipc4_copier *host_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL; struct sof_ipc4_llp_reading_slot llp_slot; - struct sof_ipc4_timestamp_info *info; + struct sof_ipc4_timestamp_info *time_info; struct snd_soc_dapm_widget *widget; struct snd_sof_dai *dai; int i; @@ -795,44 +795,44 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - info = sps->private; - info->host_copier = host_copier; - info->dai_copier = dai_copier; - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + - sdev->fw_info_box.offset; + time_info = sps->private; + time_info->host_copier = host_copier; + time_info->dai_copier = dai_copier; + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_gpdma_reading_slots) + sdev->fw_info_box.offset; /* find llp slot used by current dai */ for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) { - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) break; - info->llp_offset += sizeof(llp_slot); + time_info->llp_offset += sizeof(llp_slot); } if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS) return; /* if no llp gpdma slot is used, check aggregated sdw slot */ - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) + - sdev->fw_info_box.offset; + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_sndw_reading_slots) + sdev->fw_info_box.offset; for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) { - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) break; - info->llp_offset += sizeof(llp_slot); + time_info->llp_offset += sizeof(llp_slot); } if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS) return; /* check EVAD slot */ - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) + - sdev->fw_info_box.offset; - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_evad_reading_slot) + sdev->fw_info_box.offset; + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) - info->llp_offset = 0; + time_info->llp_offset = 0; } static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, -- cgit From 551af3280c16166244425bbb1d73028f3a907e1f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:35 +0300 Subject: ASoC: SOF: ipc4-pcm: Introduce generic sof_ipc4_pcm_stream_priv Using the sof_ipc4_timestamp_info struct directly as sps->private data is too restrictive, add a new generic sof_ipc4_pcm_stream_priv struct containing the time_info to allow new information to be stored in a generic way. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 74b0d0d00270..34ce6bb7f37d 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -37,6 +37,22 @@ struct sof_ipc4_timestamp_info { snd_pcm_sframes_t delay; }; +/** + * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data + * @time_info: pointer to time info struct if it is supported, otherwise NULL + */ +struct sof_ipc4_pcm_stream_priv { + struct sof_ipc4_timestamp_info *time_info; +}; + +static inline struct sof_ipc4_timestamp_info * +sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps) +{ + struct sof_ipc4_pcm_stream_priv *stream_priv = sps->private; + + return stream_priv->time_info; +} + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { @@ -452,7 +468,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * Invalidate the stream_start_offset to make sure that it is * going to be updated if the stream resumes */ - time_info = spcm->stream[substream->stream].private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); if (time_info) time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; @@ -706,12 +722,16 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + struct sof_ipc4_pcm_stream_priv *stream_priv; int stream; for_each_pcm_streams(stream) { pipeline_list = &spcm->stream[stream].pipeline_list; kfree(pipeline_list->pipelines); pipeline_list->pipelines = NULL; + + stream_priv = spcm->stream[stream].private; + kfree(stream_priv->time_info); kfree(spcm->stream[stream].private); spcm->stream[stream].private = NULL; } @@ -721,6 +741,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_pcm_stream_priv *stream_priv; struct sof_ipc4_timestamp_info *time_info; bool support_info = true; u32 abi_version; @@ -749,6 +770,14 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return -ENOMEM; } + stream_priv = kzalloc(sizeof(*stream_priv), GFP_KERNEL); + if (!stream_priv) { + sof_ipc4_pcm_free(sdev, spcm); + return -ENOMEM; + } + + spcm->stream[stream].private = stream_priv; + if (!support_info) continue; @@ -758,7 +787,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return -ENOMEM; } - spcm->stream[stream].private = time_info; + stream_priv->time_info = time_info; } return 0; @@ -795,7 +824,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(sps); time_info->host_copier = host_copier; time_info->dai_copier = dai_copier; time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, @@ -849,7 +878,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, if (!spcm) return -EINVAL; - time_info = spcm->stream[substream->stream].private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); /* delay calculation is not supported by current fw_reg ABI */ if (!time_info) return 0; @@ -928,7 +957,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, return -EOPNOTSUPP; sps = &spcm->stream[substream->stream]; - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(sps); if (!time_info) return -EOPNOTSUPP; @@ -1030,15 +1059,13 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; - struct snd_sof_pcm_stream *sps; struct snd_sof_pcm *spcm; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return 0; - sps = &spcm->stream[substream->stream]; - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); /* * Report the stored delay value calculated in the pointer callback. * In the unlikely event that the calculation was skipped/aborted, the -- cgit From 7211814f2adcf376b8db6321447a9725c33b6ae7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:36 +0300 Subject: ASoC: SOF: ipc4-pcm: Do not reset the ChainDMA if it has not been allocated The ChainDMA operation differs from normal pipelines that it is only created when the stream started, in fact a PCM using ChainDMA has no pipelines, modules. To reset a ChainDMA, it needs to be first allocated in firmware. When PulseAudio/PipeWire starts, they will probe the PCMs by opening them, check hw_params and then close the PCM without starting audio. Unconditionally resetting the ChainDMA can result the following error: ipc tx : 0xe040000|0x0: GLB_CHAIN_DMA ipc tx reply: 0x2e000007|0x0: GLB_CHAIN_DMA FW reported error: 7 - Unsupported operation requested ipc error for msg 0xe040000|0x0 sof_pcm_stream_free: pcm_ops hw_free failed -22 Add a new chain_dma_allocated flag to sof_ipc4_pcm_stream_priv to store the ChainDMA allocation state and use this flag to skip sending the reset if the ChainDMA is not allocated. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 34ce6bb7f37d..4594470ed08b 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -40,9 +40,12 @@ struct sof_ipc4_timestamp_info { /** * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data * @time_info: pointer to time info struct if it is supported, otherwise NULL + * @chain_dma_allocated: indicates the ChainDMA allocation state */ struct sof_ipc4_pcm_stream_priv { struct sof_ipc4_timestamp_info *time_info; + + bool chain_dma_allocated; }; static inline struct sof_ipc4_timestamp_info * @@ -269,14 +272,17 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, */ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, - int direction, + struct snd_sof_pcm *spcm, int direction, struct snd_sof_pcm_stream_pipeline_list *pipeline_list, int state, int cmd) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_pcm_stream_priv *stream_priv; bool allocate, enable, set_fifo_size; struct sof_ipc4_msg msg = {{ 0 }}; - int i; + int ret, i; + + stream_priv = spcm->stream[direction].private; switch (state) { case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ @@ -297,6 +303,11 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, set_fifo_size = false; break; case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ + + /* ChainDMA can only be reset if it has been allocated */ + if (!stream_priv->chain_dma_allocated) + return 0; + allocate = false; enable = false; set_fifo_size = false; @@ -354,7 +365,12 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, if (enable) msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + /* Update the ChainDMA allocation state */ + if (!ret) + stream_priv->chain_dma_allocated = allocate; + + return ret; } static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, @@ -394,7 +410,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * trigger function that handles the rest for the substream. */ if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, substream->stream, + return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, pipeline_list, state, cmd); /* allocate memory for the pipeline data */ -- cgit From 305539a25a1c9929b058381aac6104bd939c0fee Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 8 Apr 2024 14:41:45 -0500 Subject: ASoC: SOF: Intel: add default firmware library path for LNL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit cd6f2a2e6346 ("ASoC: SOF: Intel: Set the default firmware library path for IPC4") added the default_lib_path field for all platforms, but this was missed when LunarLake was later introduced. Fixes: 64a63d9914a5 ("ASoC: SOF: Intel: LNL: Add support for Lunarlake platform") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://msgid.link/r/20240408194147.28919-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-lnl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index b26ffe767fab..b14e508f1f31 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -35,6 +35,9 @@ static const struct sof_dev_desc lnl_desc = { .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl", }, + .default_lib_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/lnl", + }, .default_tplg_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, -- cgit From 90a2353080eedec855d63f6aadfda14104ee9b06 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 8 Apr 2024 14:41:46 -0500 Subject: ASoC: SOF: pcm: Restrict DSP D0i3 during S0ix to IPC3 Introduce a new field in struct sof_ipc_pcm_ops that can be used to restrict DSP D0i3 during S0ix suspend to IPC3. With IPC4, all streams must be stopped before S0ix suspend. Reviewed-by: Uday M Bhat Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240408194147.28919-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-pcm.c | 1 + sound/soc/sof/pcm.c | 13 ++++++------- sound/soc/sof/sof-audio.h | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 35769dd7905e..af0bf354cb20 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -434,4 +434,5 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .trigger = sof_ipc3_pcm_trigger, .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, .reset_hw_params_during_stop = true, + .d0i3_supported_in_s0ix = true, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index f03cee94bce6..8804e00e7251 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -325,14 +325,13 @@ static int sof_pcm_trigger(struct snd_soc_component *component, ipc_first = true; break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && + /* + * If DSP D0I3 is allowed during S0iX, set the suspend_ignored flag for + * D0I3-compatible streams to keep the firmware pipeline running + */ + if (pcm_ops && pcm_ops->d0i3_supported_in_s0ix && + sdev->system_suspend_target == SOF_SUSPEND_S0IX && spcm->stream[substream->stream].d0i3_compatible) { - /* - * trap the event, not sending trigger stop to - * prevent the FW pipelines from being stopped, - * and mark the flag to ignore the upcoming DAPM - * PM events. - */ spcm->stream[substream->stream].suspend_ignored = true; return 0; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 86bbb531e142..499b6084b526 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -116,6 +116,7 @@ struct snd_sof_dai_config_data { * triggers. The FW keeps the host DMA running in this case and * therefore the host must do the same and should stop the DMA during * hw_free. + * @d0i3_supported_in_s0ix: Allow DSP D0I3 during S0iX */ struct sof_ipc_pcm_ops { int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, @@ -135,6 +136,7 @@ struct sof_ipc_pcm_ops { bool reset_hw_params_during_stop; bool ipc_first_on_start; bool platform_stop_during_hw_free; + bool d0i3_supported_in_s0ix; }; /** -- cgit From 17f4041244e66a417c646c8a90bc6747d5f1de1e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 8 Apr 2024 14:41:47 -0500 Subject: ASoC: SOF: debug: show firmware/topology prefix/names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SOF driver has multiple profiles to select firmware/topology prefix/names depending on the platform and ipc_type, and each of those fields can be overridden with kernel parameters. This results in some cases in confusion on what configuration is actually used in a given test. We currently log the firmware and topology names in the kernel logs, but there's been an ask to add the information in debugfs to simplify test scripts used by developers and CI. This isn't meant to be a stable ABI used by apps, changes will be allowed as needed. Closes: https://github.com/thesofproject/linux/issues/3867 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://msgid.link/r/20240408194147.28919-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 7c8aafca8fde..7275437ea8d8 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -330,14 +330,32 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); int snd_sof_dbg_init(struct snd_sof_dev *sdev) { + struct snd_sof_pdata *plat_data = sdev->pdata; struct snd_sof_dsp_ops *ops = sof_ops(sdev); const struct snd_sof_debugfs_map *map; + struct dentry *fw_profile; int i; int err; /* use "sof" as top level debugFS dir */ sdev->debugfs_root = debugfs_create_dir("sof", NULL); + /* expose firmware/topology prefix/names for test purposes */ + fw_profile = debugfs_create_dir("fw_profile", sdev->debugfs_root); + + debugfs_create_str("fw_path", 0444, fw_profile, + (char **)&plat_data->fw_filename_prefix); + debugfs_create_str("fw_lib_path", 0444, fw_profile, + (char **)&plat_data->fw_lib_prefix); + debugfs_create_str("tplg_path", 0444, fw_profile, + (char **)&plat_data->tplg_filename_prefix); + debugfs_create_str("fw_name", 0444, fw_profile, + (char **)&plat_data->fw_filename); + debugfs_create_str("tplg_name", 0444, fw_profile, + (char **)&plat_data->tplg_filename); + debugfs_create_u32("ipc_type", 0444, fw_profile, + (u32 *)&plat_data->ipc_type); + /* init dfsentry list */ INIT_LIST_HEAD(&sdev->dfsentry_list); -- cgit From 4b9a474c7c820391c0913d64431ae9e1f52a5143 Mon Sep 17 00:00:00 2001 From: "end.to.start" Date: Mon, 8 Apr 2024 18:24:54 +0300 Subject: ASoC: acp: Support microphone from device Acer 315-24p This patch adds microphone detection for the Acer 315-24p, after which a microphone appears on the device and starts working Signed-off-by: end.to.start Link: https://msgid.link/r/20240408152454.45532-1-end.to.start@mail.ru Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 69c68d8e7a6b..1760b5d42460 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -430,6 +430,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "MRID6"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MDC"), + DMI_MATCH(DMI_BOARD_NAME, "Herbag_MDU"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit From 103abab975087e1f01b76fcb54c91dbb65dbc249 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Mon, 8 Apr 2024 17:10:56 +0800 Subject: ASoC: rt5645: Fix the electric noise due to the CBJ contacts floating The codec leaves tie combo jack's sleeve/ring2 to floating status default. It would cause electric noise while connecting the active speaker jack during boot or shutdown. This patch requests a gpio to control the additional jack circuit to tie the contacts to the ground or floating. Signed-off-by: Derek Fang Link: https://msgid.link/r/20240408091057.14165-1-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e3ba04484813..d0d24a53df74 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -444,6 +444,7 @@ struct rt5645_priv { struct regmap *regmap; struct i2c_client *i2c; struct gpio_desc *gpiod_hp_det; + struct gpio_desc *gpiod_cbj_sleeve; struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; @@ -3186,6 +3187,9 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, 0); + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 1); + msleep(600); regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val); val &= 0x7; @@ -3202,6 +3206,8 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, @@ -3229,6 +3235,9 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } return rt5645->jack_type; @@ -4012,6 +4021,16 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) return ret; } + rt5645->gpiod_cbj_sleeve = devm_gpiod_get_optional(&i2c->dev, "cbj-sleeve", + GPIOD_OUT_LOW); + + if (IS_ERR(rt5645->gpiod_cbj_sleeve)) { + ret = PTR_ERR(rt5645->gpiod_cbj_sleeve); + dev_info(&i2c->dev, "failed to initialize gpiod, ret=%d\n", ret); + if (ret != -ENOENT) + return ret; + } + for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) rt5645->supplies[i].supply = rt5645_supply_names[i]; @@ -4259,6 +4278,9 @@ static void rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); } @@ -4274,6 +4296,9 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) 0); msleep(20); regmap_write(rt5645->regmap, RT5645_RESET, 0); + + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } static int __maybe_unused rt5645_sys_suspend(struct device *dev) -- cgit From 306b38e3fa727d22454a148a364123709e356600 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Mon, 8 Apr 2024 17:10:57 +0800 Subject: ASoC: dt-bindings: rt5645: add cbj sleeve gpio property Add an optional gpio property to control external CBJ circuits to avoid some electric noise caused by sleeve/ring2 contacts floating. Signed-off-by: Derek Fang Link: https://msgid.link/r/20240408091057.14165-2-derek.fang@realtek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5645.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/rt5645.txt b/Documentation/devicetree/bindings/sound/rt5645.txt index 41a62fd2ae1f..c1fa379f5f3e 100644 --- a/Documentation/devicetree/bindings/sound/rt5645.txt +++ b/Documentation/devicetree/bindings/sound/rt5645.txt @@ -20,6 +20,11 @@ Optional properties: a GPIO spec for the external headphone detect pin. If jd-mode = 0, we will get the JD status by getting the value of hp-detect-gpios. +- cbj-sleeve-gpios: + a GPIO spec to control the external combo jack circuit to tie the sleeve/ring2 + contacts to the ground or floating. It could avoid some electric noise from the + active speaker jacks. + - realtek,in2-differential Boolean. Indicate MIC2 input are differential, rather than single-ended. @@ -68,6 +73,7 @@ codec: rt5650@1a { compatible = "realtek,rt5650"; reg = <0x1a>; hp-detect-gpios = <&gpio 19 0>; + cbj-sleeve-gpios = <&gpio 20 0>; interrupt-parent = <&gpio>; interrupts = <7 IRQ_TYPE_EDGE_FALLING>; realtek,dmic-en = "true"; -- cgit From cb9946971d7cb717b726710e1a9fa4ded00b9135 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 9 Apr 2024 06:47:43 +0000 Subject: ASoC: rt722-sdca: modify channel number to support 4 channels Channel numbers of dmic supports 4 channels, modify channels_max regarding to this issue. Signed-off-by: Jack Yu Link: https://msgid.link/r/6a9b1d1fb2ea4f04b2157799f04053b1@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index e0ea3a23f7cc..43bec8dd2ff7 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1330,7 +1330,7 @@ static struct snd_soc_dai_driver rt722_sdca_dai[] = { .capture = { .stream_name = "DP6 DMic Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = RT722_STEREO_RATES, .formats = RT722_FORMATS, }, -- cgit From 140e0762ca055d1aa84b17847cde5d9e47f56f76 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 9 Apr 2024 06:47:34 +0000 Subject: ASoC: rt722-sdca: add headset microphone vrefo setting Add vrefo settings to fix jd and headset mic recording issue. Signed-off-by: Jack Yu Link: https://msgid.link/r/727219ed45d3485ba8f4646700aaa8a8@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 25 +++++++++++++++++++------ sound/soc/codecs/rt722-sdca.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 43bec8dd2ff7..e5bd9ef812de 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1439,9 +1439,12 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) int loop_check, chk_cnt = 100, ret; unsigned int calib_status = 0; - /* Read eFuse */ - rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, - 0x4808); + /* Config analog bias */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3, + 0xa081); + /* GE related settings */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, + 0xa009); /* Button A, B, C, D bypass mode */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, 0xcf00); @@ -1475,9 +1478,6 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) if ((calib_status & 0x0040) == 0x0) break; } - /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, - 0x0010); /* Set ADC09 power entity floating control */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a12); @@ -1490,8 +1490,21 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) /* Set DAC03 and HP power entity floating control */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, 0x4040); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1, + 0x4141); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1, + 0x0101); /* Fine tune PDE40 latency */ regmap_write(rt722->regmap, 0x2f58, 0x07); + regmap_write(rt722->regmap, 0x2f03, 0x06); + /* MIC VRefo */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_VREFO_GAT, 0x4000, 0x4000); + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); } int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index 44af8901352e..2464361a7958 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -69,6 +69,7 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_COMBO_JACK_AUTO_CTL2 0x46 #define RT722_COMBO_JACK_AUTO_CTL3 0x47 #define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_VREFO_GAT 0x63 #define RT722_FSM_CTL 0x67 #define RT722_SDCA_INTR_REC 0x82 #define RT722_SW_CONFIG1 0x8a @@ -127,6 +128,8 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_UMP_HID_CTL6 0x66 #define RT722_UMP_HID_CTL7 0x67 #define RT722_UMP_HID_CTL8 0x68 +#define RT722_FLOAT_CTRL_1 0x70 +#define RT722_ENT_FLOAT_CTRL_1 0x76 /* Parameter & Verb control 01 (0x1a)(NID:20h) */ #define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) -- cgit From eefb831d2e4dd58d58002a2ef75ff989e073230d Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Thu, 11 Apr 2024 15:26:48 +0100 Subject: ASoC: cs35l41: Update DSP1RX5/6 Sources for DSP config Currently, all ASoC systems are set to use VPMON for DSP1RX5_SRC, however, this is required only for internal boost systems. External boost systems require VBSTMON instead of VPMON to be the input to DSP1RX5_SRC. Shared Boost Active acts like Internal boost (requires VPMON). Shared Boost Passive acts like External boost (requires VBSTMON) All systems require DSP1RX6_SRC to be set to VBSTMON. Signed-off-by: Stefan Binding Reviewed-by: Richard Fitzgerald Link: https://msgid.link/r/20240411142648.650921-1-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index dfb4ce53491b..f8e57a2fc3e3 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1094,6 +1094,7 @@ static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cf static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) { struct wm_adsp *dsp; + uint32_t dsp1rx5_src; int ret; dsp = &cs35l41->dsp; @@ -1113,16 +1114,29 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) return ret; } - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, - CS35L41_INPUT_SRC_VPMON); + switch (cs35l41->hw_cfg.bst_type) { + case CS35L41_INT_BOOST: + case CS35L41_SHD_BOOST_ACTV: + dsp1rx5_src = CS35L41_INPUT_SRC_VPMON; + break; + case CS35L41_EXT_BOOST: + case CS35L41_SHD_BOOST_PASS: + dsp1rx5_src = CS35L41_INPUT_SRC_VBSTMON; + break; + default: + dev_err(cs35l41->dev, "wm_halo_init failed - Invalid Boost Type: %d\n", + cs35l41->hw_cfg.bst_type); + goto err_dsp; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, dsp1rx5_src); if (ret < 0) { - dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + dev_err(cs35l41->dev, "Write DSP1RX5_SRC: %d failed: %d\n", dsp1rx5_src, ret); goto err_dsp; } - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, - CS35L41_INPUT_SRC_CLASSH); + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_VBSTMON); if (ret < 0) { - dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + dev_err(cs35l41->dev, "Write CS35L41_INPUT_SRC_VBSTMON failed: %d\n", ret); goto err_dsp; } ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, -- cgit From cebfbc89ae2552dbb58cd9b8206a5c8e0e6301e9 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 15 Apr 2024 06:27:23 +0000 Subject: ASoC: rt715: add vendor clear control register Add vendor clear control register in readable register's callback function. This prevents an access failure reported in Intel CI tests. Signed-off-by: Jack Yu Closes: https://github.com/thesofproject/linux/issues/4860 Tested-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/6a103ce9134d49d8b3941172c87a7bd4@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 7e13868ff99f..f012fe0ded6d 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -111,6 +111,7 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) case 0x839d: case 0x83a7: case 0x83a9: + case 0x752001: case 0x752039: return true; default: -- cgit From 9a039db9273b44427b3daca88173e57596545ec0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Apr 2024 10:58:04 +0300 Subject: ASoC: SOF: Core: Handle error returned by sof_select_ipc_and_paths The patch which fixed the missing remove_late() calls missed a case when sof_select_ipc_and_paths() could return with error and in this case sof_init_environment() would just return with 0. Do not ignore the error code returned by sof_select_ipc_and_paths(). Fixes: 90f8917e7a15 ("ASoC: SOF: Core: Add remove_late() to sof_init_environment failure path") Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240417075804.10829-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index cc84d4c81be9..238bda5f6b76 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -350,7 +350,9 @@ static int sof_init_environment(struct snd_sof_dev *sdev) } ret = sof_select_ipc_and_paths(sdev); - if (!ret && plat_data->ipc_type != base_profile->ipc_type) { + if (ret) { + goto err_machine_check; + } else if (plat_data->ipc_type != base_profile->ipc_type) { /* IPC type changed, re-initialize the ops */ sof_ops_free(sdev); -- cgit From 4cbb5050bffc49c716381ea2ecb07306dd46f83a Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 18 Apr 2024 16:26:21 +0200 Subject: ASoC: Intel: avs: Set name of control as in topology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating controls attached to widgets, there are a lot of rules if they get their name prefixed with widget name or not. Due to that controls ended up with weirdly looking names like "ssp0_fe DSP Volume", while topology set it to "DSP Volume". Fix this by setting no_wname_in_kcontrol_name to true in avs topology widgets which disables unwanted behaviour. Fixes: be2b81b519d7 ("ASoC: Intel: avs: Parse control tuples") Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20240418142621.2487478-1-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 13061bd1488b..42b42903ae9d 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1582,6 +1582,8 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, if (!le32_to_cpu(dw->priv.size)) return 0; + w->no_wname_in_kcontrol_name = true; + if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) { dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n"); w->ignore_suspend = false; -- cgit From d18ca8635db2f88c17acbdf6412f26d4f6aff414 Mon Sep 17 00:00:00 2001 From: Joao Paulo Goncalves Date: Wed, 17 Apr 2024 15:41:38 -0300 Subject: ASoC: ti: davinci-mcasp: Fix race condition during probe When using davinci-mcasp as CPU DAI with simple-card, there are some conditions that cause simple-card to finish registering a sound card before davinci-mcasp finishes registering all sound components. This creates a non-working sound card from userspace with no problem indication apart from not being able to play/record audio on a PCM stream. The issue arises during simultaneous probe execution of both drivers. Specifically, the simple-card driver, awaiting a CPU DAI, proceeds as soon as davinci-mcasp registers its DAI. However, this process can lead to the client mutex lock (client_mutex in soc-core.c) being held or davinci-mcasp being preempted before PCM DMA registration on davinci-mcasp finishes. This situation occurs when the probes of both drivers run concurrently. Below is the code path for this condition. To solve the issue, defer davinci-mcasp CPU DAI registration to the last step in the audio part of it. This way, simple-card CPU DAI parsing will be deferred until all audio components are registered. Fail Code Path: simple-card.c: probe starts simple-card.c: simple_dai_link_of: simple_parse_node(..,cpu,..) returns EPROBE_DEFER, no CPU DAI yet davinci-mcasp.c: probe starts davinci-mcasp.c: devm_snd_soc_register_component() register CPU DAI simple-card.c: probes again, finish CPU DAI parsing and call devm_snd_soc_register_card() simple-card.c: finish probe davinci-mcasp.c: *dma_pcm_platform_register() register PCM DMA davinci-mcasp.c: probe finish Cc: stable@vger.kernel.org Fixes: 9fbd58cf4ab0 ("ASoC: davinci-mcasp: Choose PCM driver based on configured DMA controller") Signed-off-by: Joao Paulo Goncalves Acked-by: Peter Ujfalusi Reviewed-by: Jai Luthra Link: https://lore.kernel.org/r/20240417184138.1104774-1-jpaulo.silvagoncalves@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index b892d66f7847..1e760c315521 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2417,12 +2417,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp_reparent_fck(pdev); - ret = devm_snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, - &davinci_mcasp_dai[mcasp->op_mode], 1); - - if (ret != 0) - goto err; - ret = davinci_mcasp_get_dma_type(mcasp); switch (ret) { case PCM_EDMA: @@ -2449,6 +2443,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) goto err; } + ret = devm_snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, + &davinci_mcasp_dai[mcasp->op_mode], 1); + + if (ret != 0) + goto err; + no_audio: ret = davinci_mcasp_init_gpiochip(mcasp); if (ret) { -- cgit From 32ac501957e5f68fe0e4bf88fb4db75cfb8f6566 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 19 Apr 2024 15:00:12 +0100 Subject: ASoC: codecs: wsa881x: set clk_stop_mode1 flag WSA881x codecs do not retain the state while clock is stopped, so mark this with clk_stop_mode1 flag. Fixes: a0aab9e1404a ("ASoC: codecs: add wsa881x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20240419140012.91384-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 3c025dabaf7a..1253695bebd8 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1155,6 +1155,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + pdev->prop.clk_stop_mode1 = true; gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); -- cgit From f2602fba4723e408380eb9a56e921d36a1ae21f8 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 22 Apr 2024 11:32:11 +0100 Subject: ASoC: cs35l56: Avoid static analysis warning of uninitialised variable Static checkers complain that the silicon_uid variable passed by pointer to cs35l56_read_silicon_uid() could later be used uninitialised when calling cs_amp_get_efi_calibration_data(). cs35l56_read_silicon_uid() must have succeeded to call cs_amp_get_efi_calibration_data() and that would have populated the variable. However, initialise the value so we are not haunted by it forevermore. Signed-off-by: Simon Trimmer Fixes: e1830f66f6c6 ("ASoC: cs35l56: Add helper functions for amp calibration") Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240422103211.236063-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index ec1d95e57061..fd02b621da52 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -706,7 +706,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED); int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) { - u64 silicon_uid; + u64 silicon_uid = 0; int ret; /* Driver can't apply calibration to a secured part, so skip */ -- cgit From bda16500dd0b05e2e047093b36cbe0873c95aeae Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 23 Apr 2024 06:59:35 +0000 Subject: ASoC: rt715-sdca: volume step modification Volume step (dB/step) modification to fix format error which shown in amixer control. Signed-off-by: Jack Yu Link: https://lore.kernel.org/r/b1f546ad16dc4c7abb7daa7396e8345c@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdca.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 3fb7b9adb61d..bc3579203c7a 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -316,7 +316,7 @@ static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol, return 0; } -static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); static int rt715_sdca_get_volsw(struct snd_kcontrol *kcontrol, @@ -477,7 +477,7 @@ static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = { RT715_SDCA_FU_VOL_CTRL, CH_01), SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, + 0x2f, 0x3f, 0, rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put, in_vol_tlv), RT715_SDCA_EXT_TLV("FU02 Capture Volume", @@ -485,13 +485,13 @@ static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = { RT715_SDCA_FU_VOL_CTRL, CH_01), rt715_sdca_set_amp_gain_4ch_get, rt715_sdca_set_amp_gain_4ch_put, - in_vol_tlv, 4, 0x7f), + in_vol_tlv, 4, 0x3f), RT715_SDCA_EXT_TLV("FU06 Capture Volume", SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, RT715_SDCA_FU_VOL_CTRL, CH_01), rt715_sdca_set_amp_gain_4ch_get, rt715_sdca_set_amp_gain_4ch_put, - in_vol_tlv, 4, 0x7f), + in_vol_tlv, 4, 0x3f), /* MIC Boost Control */ RT715_SDCA_BOOST_EXT_TLV("FU0E Boost", SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, -- cgit From 398321d7531963b95841865eb371fe65c44c6921 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:11 +0200 Subject: ALSA: emu10k1: fix E-MU card dock presence monitoring While there are two separate IRQ status bits for dock attach and detach, the hardware appears to mix them up more or less randomly, making them useless for tracking what actually happened. It is much safer to check the dock status separately and proceed based on that, as the old polling code did. Note that the code assumes that only the dock can be hot-plugged - if other option card bits changed, the logic would break. Fixes: fbb64eedf5a3 ("ALSA: emu10k1: make E-MU dock monitoring interrupt-driven") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218584 Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-2-oswald.buddenhagen@gmx.de> --- sound/pci/emu10k1/emu10k1_main.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index de5c41e578e1..85f70368a27d 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -778,6 +778,11 @@ static void emu1010_firmware_work(struct work_struct *work) msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) { + /* Audio Dock removed */ + dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); + /* The hardware auto-mutes all, so we unmute again */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } } @@ -810,14 +815,12 @@ static void emu1010_interrupt(struct snd_emu10k1 *emu) u32 sts; snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts); - if (sts & EMU_HANA_IRQ_DOCK_LOST) { - /* Audio Dock removed */ - dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); - /* The hardware auto-mutes all, so we unmute again */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - } else if (sts & EMU_HANA_IRQ_DOCK) { + + // The distinction of the IRQ status bits is unreliable, + // so we dispatch later based on option card status. + if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST)) schedule_work(&emu->emu1010.firmware_work); - } + if (sts & EMU_HANA_IRQ_WCLK_CHANGED) schedule_work(&emu->emu1010.clock_work); } -- cgit From 28deafd0fbdc45cc9c63bd7dd4efc35137958862 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:12 +0200 Subject: ALSA: emu10k1: factor out snd_emu1010_load_dock_firmware() Pulled out of the next patch to improve its legibility. As the function is now available, call it directly from snd_emu10k1_emu1010_init(), thus making the MicroDock firmware loading synchronous - there isn't really a reason not to. Note that this does not affect the AudioDocks of rev1 cards, as these have no independent power supplies, and thus come up only a while after the main card is initialized. As a drive-by, adjust the priorities of two messages to better reflect their impact. Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-3-oswald.buddenhagen@gmx.de> --- sound/pci/emu10k1/emu10k1_main.c | 66 ++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 85f70368a27d..6265fc9ae260 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -732,11 +732,43 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock, return snd_emu1010_load_firmware_entry(emu, *fw); } +static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) +{ + u32 tmp, tmp2; + int err; + + dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n"); + /* Return to Audio Dock programming mode */ + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, + EMU_HANA_FPGA_CONFIG_AUDIODOCK); + err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); + if (err < 0) + return; + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); + + snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); + dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); + if ((tmp & 0x1f) != 0x15) { + /* FPGA failed to be programmed */ + dev_err(emu->card->dev, + "emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n", + tmp); + return; + } + dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n"); + + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); + dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2); + + /* Allow DLL to settle, to sync clocking between 1010 and Dock */ + msleep(10); +} + static void emu1010_firmware_work(struct work_struct *work) { struct snd_emu10k1 *emu; - u32 tmp, tmp2, reg; - int err; + u32 reg; emu = container_of(work, struct snd_emu10k1, emu1010.firmware_work); @@ -749,33 +781,7 @@ static void emu1010_firmware_work(struct work_struct *work) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { /* Audio Dock attached */ - /* Return to Audio Dock programming mode */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware\n"); - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, - EMU_HANA_FPGA_CONFIG_AUDIODOCK); - err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); - if (err < 0) - return; - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); - snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); - dev_info(emu->card->dev, - "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); - if ((tmp & 0x1f) != 0x15) { - /* FPGA failed to be programmed */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", - tmp); - return; - } - dev_info(emu->card->dev, - "emu1010: Audio Dock Firmware loaded\n"); - snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); - snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); - dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2); - /* Sync clocking between 1010 and Dock */ - /* Allow DLL to settle */ - msleep(10); + snd_emu1010_load_dock_firmware(emu); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) { @@ -892,7 +898,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg); if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) - schedule_work(&emu->emu1010.firmware_work); + snd_emu1010_load_dock_firmware(emu); if (emu->card_capabilities->no_adat) { emu->emu1010.optical_in = 0; /* IN_SPDIF */ emu->emu1010.optical_out = 0; /* OUT_SPDIF */ -- cgit From f848337cd801c7106a4ec0d61765771dab2a5909 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:13 +0200 Subject: ALSA: emu10k1: move the whole GPIO event handling to the workqueue The actual event processing was already done by workqueue items. We can move the event dispatching there as well, rather than doing it already in the interrupt handler callback. This change has a rather profound "side effect" on the reliability of the FPGA programming: once we enter programming mode, we must not issue any snd_emu1010_fpga_{read,write}() calls until we're done, as these would badly mess up the programming protocol. But exactly that would happen when trying to program the dock, as that triggers GPIO interrupts as a side effect. This is mitigated by deferring the actual interrupt handling, as workqueue items are not re-entrant. To avoid scheduling the dispatcher on non-events, we now explicitly ignore GPIO IRQs triggered by "uninteresting" pins, which happens a lot as a side effect of calling snd_emu1010_fpga_{read,write}(). Fixes: fbb64eedf5a3 ("ALSA: emu10k1: make E-MU dock monitoring interrupt-driven") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218584 Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-4-oswald.buddenhagen@gmx.de> --- include/sound/emu10k1.h | 3 +-- sound/pci/emu10k1/emu10k1.c | 3 +-- sound/pci/emu10k1/emu10k1_main.c | 56 ++++++++++++++++++++-------------------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 1af9e6819392..9cc10fab01a8 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1684,8 +1684,7 @@ struct snd_emu1010 { unsigned int clock_fallback; unsigned int optical_in; /* 0:SPDIF, 1:ADAT */ unsigned int optical_out; /* 0:SPDIF, 1:ADAT */ - struct work_struct firmware_work; - struct work_struct clock_work; + struct work_struct work; }; struct snd_emu10k1 { diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index fe72e7d77241..dadeda7758ce 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -189,8 +189,7 @@ static int snd_emu10k1_suspend(struct device *dev) emu->suspend = 1; - cancel_work_sync(&emu->emu1010.firmware_work); - cancel_work_sync(&emu->emu1010.clock_work); + cancel_work_sync(&emu->emu1010.work); snd_ac97_suspend(emu->ac97); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 6265fc9ae260..86eaf5963502 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -765,19 +765,10 @@ static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) msleep(10); } -static void emu1010_firmware_work(struct work_struct *work) +static void emu1010_dock_event(struct snd_emu10k1 *emu) { - struct snd_emu10k1 *emu; u32 reg; - emu = container_of(work, struct snd_emu10k1, - emu1010.firmware_work); - if (emu->card->shutdown) - return; -#ifdef CONFIG_PM_SLEEP - if (emu->suspend) - return; -#endif snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { /* Audio Dock attached */ @@ -792,20 +783,10 @@ static void emu1010_firmware_work(struct work_struct *work) } } -static void emu1010_clock_work(struct work_struct *work) +static void emu1010_clock_event(struct snd_emu10k1 *emu) { - struct snd_emu10k1 *emu; struct snd_ctl_elem_id id; - emu = container_of(work, struct snd_emu10k1, - emu1010.clock_work); - if (emu->card->shutdown) - return; -#ifdef CONFIG_PM_SLEEP - if (emu->suspend) - return; -#endif - spin_lock_irq(&emu->reg_lock); // This is the only thing that can actually happen. emu->emu1010.clock_source = emu->emu1010.clock_fallback; @@ -816,19 +797,40 @@ static void emu1010_clock_work(struct work_struct *work) snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id); } -static void emu1010_interrupt(struct snd_emu10k1 *emu) +static void emu1010_work(struct work_struct *work) { + struct snd_emu10k1 *emu; u32 sts; + emu = container_of(work, struct snd_emu10k1, emu1010.work); + if (emu->card->shutdown) + return; +#ifdef CONFIG_PM_SLEEP + if (emu->suspend) + return; +#endif + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts); // The distinction of the IRQ status bits is unreliable, // so we dispatch later based on option card status. if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST)) - schedule_work(&emu->emu1010.firmware_work); + emu1010_dock_event(emu); if (sts & EMU_HANA_IRQ_WCLK_CHANGED) - schedule_work(&emu->emu1010.clock_work); + emu1010_clock_event(emu); +} + +static void emu1010_interrupt(struct snd_emu10k1 *emu) +{ + // We get an interrupt on each GPIO input pin change, but we + // care only about the ones triggered by the dedicated pin. + u16 sts = inw(emu->port + A_GPIO); + u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000; + if (!(sts & bit)) + return; + + schedule_work(&emu->emu1010.work); } /* @@ -969,8 +971,7 @@ static void snd_emu10k1_free(struct snd_card *card) /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); } - cancel_work_sync(&emu->emu1010.firmware_work); - cancel_work_sync(&emu->emu1010.clock_work); + cancel_work_sync(&emu->emu1010.work); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1549,8 +1550,7 @@ int snd_emu10k1_create(struct snd_card *card, emu->irq = -1; emu->synth = NULL; emu->get_synth_voice = NULL; - INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); - INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work); + INIT_WORK(&emu->emu1010.work, emu1010_work); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); -- cgit From 2d3f4810886eb7c319cec41b6d725d2953bfa88a Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:14 +0200 Subject: ALSA: emu10k1: use mutex for E-MU FPGA access locking The FPGA access through the GPIO port does not interfere with other sound processor register access, so there is no need to subject it to emu_lock. And after moving all FPGA access out of the interrupt handler, it does not need to be IRQ-safe, either. What's more, attaching the dock causes a firmware upload, which takes several seconds. We really don't want to disable IRQs for this long, and even less also have someone else spin with IRQs disabled waiting for us. Therefore, use a mutex for FPGA access locking. This makes the code somewhat more noisy, as we need to wrap bigger sections into the mutex, as it needs to enclose the spinlocks. The latter has the "side effect" of fixing dock FPGA programming in a corner case: a really badly timed mixer access right between entering FPGA programming mode and uploading the netlist would mess up the protocol. Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-5-oswald.buddenhagen@gmx.de> --- include/sound/emu10k1.h | 4 ++++ sound/pci/emu10k1/emu10k1_main.c | 19 ++++++++++++---- sound/pci/emu10k1/emumixer.c | 18 ++++++++++----- sound/pci/emu10k1/emuproc.c | 9 ++++++++ sound/pci/emu10k1/io.c | 48 +++++++++++++++++----------------------- 5 files changed, 61 insertions(+), 37 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 9cc10fab01a8..234b5baea69c 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1685,6 +1685,7 @@ struct snd_emu1010 { unsigned int optical_in; /* 0:SPDIF, 1:ADAT */ unsigned int optical_out; /* 0:SPDIF, 1:ADAT */ struct work_struct work; + struct mutex lock; }; struct snd_emu10k1 { @@ -1833,6 +1834,9 @@ unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data); int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data); int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value); +static inline void snd_emu1010_fpga_lock(struct snd_emu10k1 *emu) { mutex_lock(&emu->emu1010.lock); }; +static inline void snd_emu1010_fpga_unlock(struct snd_emu10k1 *emu) { mutex_unlock(&emu->emu1010.lock); }; +void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value); void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value); void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value); void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 86eaf5963502..1a2905e8672b 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -810,6 +810,8 @@ static void emu1010_work(struct work_struct *work) return; #endif + snd_emu1010_fpga_lock(emu); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts); // The distinction of the IRQ status bits is unreliable, @@ -819,6 +821,8 @@ static void emu1010_work(struct work_struct *work) if (sts & EMU_HANA_IRQ_WCLK_CHANGED) emu1010_clock_event(emu); + + snd_emu1010_fpga_unlock(emu); } static void emu1010_interrupt(struct snd_emu10k1 *emu) @@ -852,6 +856,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) * Proper init follows in snd_emu10k1_init(). */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); + snd_emu1010_fpga_lock(emu); + /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); @@ -877,7 +883,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) err = snd_emu1010_load_firmware(emu, 0, &emu->firmware); if (err < 0) { dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n"); - return err; + goto fail; } /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ @@ -887,7 +893,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) dev_info(emu->card->dev, "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg); - return -ENODEV; + err = -ENODEV; + goto fail; } dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n"); @@ -947,7 +954,9 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) // so it is safe to simply enable the outputs. snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - return 0; +fail: + snd_emu1010_fpga_unlock(emu); + return err; } /* * Create the EMU10K1 instance @@ -969,9 +978,10 @@ static void snd_emu10k1_free(struct snd_card *card) } if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) { /* Disable 48Volt power to Audio Dock */ - snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0); } cancel_work_sync(&emu->emu1010.work); + mutex_destroy(&emu->emu1010.lock); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1551,6 +1561,7 @@ int snd_emu10k1_create(struct snd_card *card, emu->synth = NULL; emu->get_synth_voice = NULL; INIT_WORK(&emu->emu1010.work, emu1010_work); + mutex_init(&emu->emu1010.lock); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 0a32ea53d8c6..05b98d9b547b 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -661,7 +661,9 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.output_source[channel] != val); if (change) { emu->emu1010.output_source[channel] = val; + snd_emu1010_fpga_lock(emu); snd_emu1010_output_source_apply(emu, channel, val); + snd_emu1010_fpga_unlock(emu); } return change; } @@ -705,7 +707,9 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.input_source[channel] != val); if (change) { emu->emu1010.input_source[channel] = val; + snd_emu1010_fpga_lock(emu); snd_emu1010_input_source_apply(emu, channel, val); + snd_emu1010_fpga_unlock(emu); } return change; } @@ -774,7 +778,7 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct cache = cache & ~mask; change = (cache != emu->emu1010.adc_pads); if (change) { - snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache ); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache ); emu->emu1010.adc_pads = cache; } @@ -832,7 +836,7 @@ static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct cache = cache & ~mask; change = (cache != emu->emu1010.dac_pads); if (change) { - snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache ); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache ); emu->emu1010.dac_pads = cache; } @@ -980,6 +984,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, val = ucontrol->value.enumerated.item[0] ; if (val >= emu_ci->num) return -EINVAL; + snd_emu1010_fpga_lock(emu); spin_lock_irq(&emu->reg_lock); change = (emu->emu1010.clock_source != val); if (change) { @@ -996,6 +1001,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, } else { spin_unlock_irq(&emu->reg_lock); } + snd_emu1010_fpga_unlock(emu); return change; } @@ -1041,7 +1047,7 @@ static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.clock_fallback != val); if (change) { emu->emu1010.clock_fallback = val; - snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 1 - val); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val); } return change; } @@ -1093,7 +1099,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol, emu->emu1010.optical_out = val; tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) | (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF); - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp); } return change; } @@ -1144,7 +1150,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol, emu->emu1010.optical_in = val; tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) | (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF); - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp); } return change; } @@ -2323,7 +2329,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, for (i = 0; i < emu_ri->n_outs; i++) emu->emu1010.output_source[i] = emu1010_map_source(emu_ri, emu_ri->out_dflts[i]); + snd_emu1010_fpga_lock(emu); snd_emu1010_apply_sources(emu); + snd_emu1010_fpga_unlock(emu); kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu); err = snd_ctl_add(card, kctl); diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 2f80fd91017c..737c28d31b41 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -165,6 +165,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, u32 value2; if (emu->card_capabilities->emu_model) { + snd_emu1010_fpga_lock(emu); + // This represents the S/PDIF lock status on 0404b, which is // kinda weird and unhelpful, because monitoring it via IRQ is // impractical (one gets an IRQ flood as long as it is desynced). @@ -197,6 +199,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n", value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer", value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : ""); + + snd_emu1010_fpga_unlock(emu); } else { snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS); snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS); @@ -458,6 +462,9 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, struct snd_emu10k1 *emu = entry->private_data; u32 value; int i; + + snd_emu1010_fpga_lock(emu); + snd_iprintf(buffer, "EMU1010 Registers:\n\n"); for(i = 0; i < 0x40; i+=1) { @@ -496,6 +503,8 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, snd_emu_proc_emu1010_link_read(emu, buffer, 0x701); } } + + snd_emu1010_fpga_unlock(emu); } static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry, diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 74df2330015f..f3260a81e47b 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -289,20 +289,28 @@ static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) { - unsigned long flags; + if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock))) + return; + snd_emu1010_fpga_write_locked(emu, reg, value); +} - spin_lock_irqsave(&emu->emu_lock, flags); +void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value) +{ + snd_emu1010_fpga_lock(emu); snd_emu1010_fpga_write_locked(emu, reg, value); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_unlock(emu); } -static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value) +void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) { // The higest input pin is used as the designated interrupt trigger, // so it needs to be masked out. // But note that any other input pin change will also cause an IRQ, // so using this function often causes an IRQ as a side effect. u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f; + + if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock))) + return; if (snd_BUG_ON(reg > 0x3f)) return; reg += 0x40; /* 0x40 upwards are registers. */ @@ -313,47 +321,31 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 * *value = ((inw(emu->port + A_GPIO) >> 8) & mask); } -void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) -{ - unsigned long flags; - - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_read_locked(emu, reg, value); - spin_unlock_irqrestore(&emu->emu_lock, flags); -} - /* Each Destination has one and only one Source, * but one Source can feed any number of Destinations simultaneously. */ void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src) { - unsigned long flags; - if (snd_BUG_ON(dst & ~0x71f)) return; if (snd_BUG_ON(src & ~0x71f)) return; - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f); + snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f); } u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst) { - unsigned long flags; u32 hi, lo; if (snd_BUG_ON(dst & ~0x71f)) return 0; - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f); - snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi); - snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f); + snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi); + snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo); return (hi << 8) | lo; } -- cgit From e8289fd3fa65d60cf04dab6f7845eda352c04ea6 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:15 +0200 Subject: ALSA: emu10k1: fix E-MU dock initialization A side effect of making the dock monitoring interrupt-driven was that we'd be very quick to program a freshly connected dock. However, for unclear reasons, the dock does not work when we do that - despite the FPGA netlist upload going just fine. We work around this by adding a delay before programming the dock; for safety, the value is several times as much as was determined empirically. Note that a badly timed dock hot-plug would have triggered the problem even before the referenced commit - but now it would happen 100% instead of about 3% of the time, thus making it impossible to work around by re-plugging. Fixes: fbb64eedf5a3 ("ALSA: emu10k1: make E-MU dock monitoring interrupt-driven") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218584 Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-6-oswald.buddenhagen@gmx.de> --- sound/pci/emu10k1/emu10k1_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 1a2905e8672b..8ccc0178360c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -737,6 +737,12 @@ static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) u32 tmp, tmp2; int err; + // The docking events clearly arrive prematurely - while the + // Dock's FPGA seems to be successfully programmed, the Dock + // fails to initialize subsequently if we don't give it some + // time to "warm up" here. + msleep(200); + dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n"); /* Return to Audio Dock programming mode */ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, -- cgit From 15c7e87aa88f0ab2d51c2e2123b127a6d693ca21 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 28 Apr 2024 11:37:16 +0200 Subject: ALSA: emu10k1: make E-MU FPGA writes potentially more reliable We did not delay after the second strobe signal, so another immediately following access could potentially corrupt the written value. This is a purely speculative fix with no supporting evidence, but after taking out the spinlocks around the writes, it seems plausible that a modern processor could be actually too fast. Also, it's just cleaner to be consistent. Signed-off-by: Oswald Buddenhagen Signed-off-by: Takashi Iwai Message-ID: <20240428093716.3198666-7-oswald.buddenhagen@gmx.de> --- sound/pci/emu10k1/io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index f3260a81e47b..f4a1c2d4b078 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -285,6 +285,7 @@ static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 outw(value, emu->port + A_GPIO); udelay(10); outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ + udelay(10); } void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) -- cgit From 2d5af3ab9e6f1cf1468b2a5221b5c1f7f46c3333 Mon Sep 17 00:00:00 2001 From: Aman Dhoot Date: Mon, 22 Apr 2024 18:08:23 +0530 Subject: ALSA: hda/realtek: Fix mute led of HP Laptop 15-da3001TU This patch simply add SND_PCI_QUIRK for HP Laptop 15-da3001TU to fixed mute led of laptop. Signed-off-by: Aman Dhoot Cc: Link: https://lore.kernel.org/r/CAMTp=B+3NG65Z684xMwHqdXDJhY+DJK-kuSw4adn6xwnG+b5JA@mail.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 70d80b6af3fe..e77013309795 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9937,6 +9937,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), -- cgit From b11d26660dff8d7430892008616452dc8e5fb0f3 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:38 +0200 Subject: ASoC: meson: axg-fifo: use threaded irq to check periods With the AXG audio subsystem, there is a possible random channel shift on TDM capture, when the slot number per lane is more than 2, and there is more than one lane used. The problem has been there since the introduction of the axg audio support but such scenario is pretty uncommon. This is why there is no loud complains about the problem. Solving the problem require to make the links non-atomic and use the trigger() callback to start FEs and BEs in the appropriate order. This was tried in the past and reverted because it caused the block irq to sleep while atomic. However, instead of reverting, the solution is to call snd_pcm_period_elapsed() in a non atomic context. Use the bottom half of a threaded IRQ to do so. Fixes: 6dc4fa179fb8 ("ASoC: meson: add axg fifo base driver") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-2-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index bebee0ca8e38..ecb3eb7a9723 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -204,18 +204,26 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) unsigned int status; regmap_read(fifo->map, FIFO_STATUS1, &status); - status = FIELD_GET(STATUS1_INT_STS, status); + axg_fifo_ack_irq(fifo, status); + + /* Use the thread to call period elapsed on nonatomic links */ if (status & FIFO_INT_COUNT_REPEAT) - snd_pcm_period_elapsed(ss); - else - dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", - status); + return IRQ_WAKE_THREAD; - /* Ack irqs */ - axg_fifo_ack_irq(fifo, status); + dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", + status); + + return IRQ_NONE; +} + +static irqreturn_t axg_fifo_pcm_irq_block_thread(int irq, void *dev_id) +{ + struct snd_pcm_substream *ss = dev_id; + + snd_pcm_period_elapsed(ss); - return IRQ_RETVAL(status); + return IRQ_HANDLED; } int axg_fifo_pcm_open(struct snd_soc_component *component, @@ -243,8 +251,9 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, if (ret) return ret; - ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0, - dev_name(dev), ss); + ret = request_threaded_irq(fifo->irq, axg_fifo_pcm_irq_block, + axg_fifo_pcm_irq_block_thread, + IRQF_ONESHOT, dev_name(dev), ss); if (ret) return ret; -- cgit From dcba52ace7d4c12e2c8c273eff55ea03a84c8baf Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:39 +0200 Subject: ASoC: meson: axg-card: make links nonatomic Non atomic operations need to be performed in the trigger callback of the TDM interfaces. Those are BEs but what matters is the nonatomic flag of the FE in the DPCM context. Just set nonatomic for everything so, at least, what is done is clear. Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-3-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-card.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index 3180aa4d3a15..8c5605c1e34e 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -318,6 +318,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; + dai_link->nonatomic = true; ret = meson_card_parse_dai(card, np, dai_link->cpus); if (ret) -- cgit From f949ed458ad15a00d41b37c745ebadaef171aaae Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:40 +0200 Subject: ASoC: meson: axg-tdm-interface: manage formatters in trigger So far, the formatters have been reset/enabled using the .prepare() callback. This was done in this callback because walking the formatters use a mutex. A mutex is used because formatter handling require dealing possibly slow clock operation. With the support of non-atomic, .trigger() callback may be used which also allows to properly enable and disable formatters on start but also pause/resume. This solve a random shift on TDMIN as well repeated samples on for TDMOUT. Fixes: d60e4f1e4be5 ("ASoC: meson: add tdm interface driver") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-4-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index bf708717635b..8bf3735dedaa 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -349,26 +349,31 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, return 0; } -static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, +static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { - struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct axg_tdm_stream *ts = + snd_soc_dai_get_dma_data(dai, substream); - /* Stop all attached formatters */ - axg_tdm_stream_stop(ts); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + axg_tdm_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + axg_tdm_stream_stop(ts); + break; + default: + return -EINVAL; + } return 0; } -static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - - /* Force all attached formatters to update */ - return axg_tdm_stream_reset(ts); -} - static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) { int stream; @@ -412,8 +417,7 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = { .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, .hw_params = axg_tdm_iface_hw_params, - .prepare = axg_tdm_iface_prepare, - .hw_free = axg_tdm_iface_hw_free, + .trigger = axg_tdm_iface_trigger, }; /* TDM Backend DAIs */ -- cgit From a5a89037d080e0870d7517c61f8b2123d58ab33b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:41 +0200 Subject: ASoC: meson: axg-tdm: add continuous clock support Some devices may need the clocks running, even while paused. Add support for this use case. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-5-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-formatter.c | 40 +++++++++++++++++++++++++++++++++++++ sound/soc/meson/axg-tdm-interface.c | 16 ++++++++++++++- sound/soc/meson/axg-tdm.h | 5 +++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index 63333a2b0a9c..a6579efd3775 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -392,6 +392,46 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts) } EXPORT_SYMBOL_GPL(axg_tdm_stream_free); +int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts, + unsigned int fmt) +{ + int ret = 0; + + if (fmt & SND_SOC_DAIFMT_CONT) { + /* Clock are already enabled - skipping */ + if (ts->clk_enabled) + return 0; + + ret = clk_prepare_enable(ts->iface->mclk); + if (ret) + return ret; + + ret = clk_prepare_enable(ts->iface->sclk); + if (ret) + goto err_sclk; + + ret = clk_prepare_enable(ts->iface->lrclk); + if (ret) + goto err_lrclk; + + ts->clk_enabled = true; + return 0; + } + + /* Clocks are already disabled - skipping */ + if (!ts->clk_enabled) + return 0; + + clk_disable_unprepare(ts->iface->lrclk); +err_lrclk: + clk_disable_unprepare(ts->iface->sclk); +err_sclk: + clk_disable_unprepare(ts->iface->mclk); + ts->clk_enabled = false; + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks); + MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); MODULE_AUTHOR("Jerome Brunet "); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 8bf3735dedaa..62057c71f742 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -309,6 +309,7 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); int ret; switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -346,7 +347,19 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, return ret; } - return 0; + ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt); + if (ret) + dev_err(dai->dev, "failed to apply continuous clock setting\n"); + + return ret; +} + +static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + return axg_tdm_stream_set_cont_clocks(ts, 0); } static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream, @@ -417,6 +430,7 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = { .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, .hw_params = axg_tdm_iface_hw_params, + .hw_free = axg_tdm_iface_hw_free, .trigger = axg_tdm_iface_trigger, }; diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h index 42f7470b9a7f..daaca10fec9e 100644 --- a/sound/soc/meson/axg-tdm.h +++ b/sound/soc/meson/axg-tdm.h @@ -58,12 +58,17 @@ struct axg_tdm_stream { unsigned int physical_width; u32 *mask; bool ready; + + /* For continuous clock tracking */ + bool clk_enabled; }; struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface); void axg_tdm_stream_free(struct axg_tdm_stream *ts); int axg_tdm_stream_start(struct axg_tdm_stream *ts); void axg_tdm_stream_stop(struct axg_tdm_stream *ts); +int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts, + unsigned int fmt); static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts) { -- cgit From 6db26f9ea4edd8a17d39ab3c20111e3ccd704aef Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 15:41:47 +0200 Subject: ASoC: meson: cards: select SND_DYNAMIC_MINORS Amlogic sound cards do create a lot of pcm interfaces, possibly more than 8. Some pcm interfaces are internal (like DPCM backends and c2c) and not exposed to userspace. Those interfaces still increase the number passed to snd_find_free_minor(), which eventually exceeds 8 causing -EBUSY error on card registration if CONFIG_SND_DYNAMIC_MINORS=n and the interface is exposed to userspace. select CONFIG_SND_DYNAMIC_MINORS for Amlogic cards to avoid the problem. Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426134150.3053741-1-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index b93ea33739f2..6458d5dc4902 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -99,6 +99,7 @@ config SND_MESON_AXG_PDM config SND_MESON_CARD_UTILS tristate + select SND_DYNAMIC_MINORS config SND_MESON_CODEC_GLUE tristate -- cgit From e8a6a5ad73acbafd98e8fd3f0cbf6e379771bb76 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:30:33 -0500 Subject: ASoC: da7219-aad: fix usage of device_get_named_child_node() The documentation for device_get_named_child_node() mentions this important point: " The caller is responsible for calling fwnode_handle_put() on the returned fwnode pointer. " Add fwnode_handle_put() to avoid a leaked reference. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240426153033.38500-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219-aad.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 6bc068cdcbe2..15e5e3eb592b 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -671,8 +671,10 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) return NULL; aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL); - if (!aad_pdata) + if (!aad_pdata) { + fwnode_handle_put(aad_np); return NULL; + } aad_pdata->irq = i2c->irq; @@ -753,6 +755,8 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) else aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + fwnode_handle_put(aad_np); + return aad_pdata; } -- cgit From fbd741f0993203d07b2b6562d68d1e5e4745b59b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:29:39 -0500 Subject: ASoC: cs35l56: fix usages of device_get_named_child_node() The documentation for device_get_named_child_node() mentions this important point: " The caller is responsible for calling fwnode_handle_put() on the returned fwnode pointer. " Add fwnode_handle_put() to avoid leaked references. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240426152939.38471-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 6331b8c6136e..4986e78105da 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1360,6 +1360,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj); if (ret) { dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret); + fwnode_handle_put(af01_fwnode); return -ENOENT; } @@ -1367,6 +1368,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 if (obj->package.count != 4) { dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n", obj->package.count); + fwnode_handle_put(af01_fwnode); return -ENOENT; } @@ -1381,6 +1383,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 */ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping); if (ret) { + fwnode_handle_put(af01_fwnode); return dev_err_probe(cs35l56->base.dev, ret, "Failed to add gpio mapping to AF01\n"); } @@ -1388,14 +1391,17 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 ret = devm_add_action_or_reset(cs35l56->base.dev, cs35l56_acpi_dev_release_driver_gpios, adev); - if (ret) + if (ret) { + fwnode_handle_put(af01_fwnode); return ret; + } dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n"); } desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL); if (IS_ERR(desc)) { + fwnode_handle_put(af01_fwnode); ret = PTR_ERR(desc); return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n"); } @@ -1404,9 +1410,12 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 gpiod_put(desc); if (ret < 0) { + fwnode_handle_put(af01_fwnode); dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n"); return ret; - } + } + + fwnode_handle_put(af01_fwnode); dev_info(cs35l56->base.dev, "Got spk-id from AF01\n"); -- cgit From 79ac4c1443eaec0d09355307043a9149287f23c1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:28:18 -0500 Subject: ALSA: hda: intel-dsp-config: harden I2C/I2S codec detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SOF driver is selected whenever specific I2C/I2S HIDs are reported as 'present' in the ACPI DSDT. In some cases, an HID is reported but the hardware does not actually rely on I2C/I2S. This false positive leads to an invalid selection of the SOF driver and as a result an invalid topology is loaded. This patch hardens the detection with a check that the NHLT table is consistent with the report of an I2S-based codec in DSDT. This table should expose at least one SSP endpoint configured for an I2S-codec connection. Tested on Huawei Matebook D14 (NBLB-WAX9N) using an HDaudio codec with an invalid ES8336 ACPI HID reported: [ 7.858249] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040380 [ 7.858312] snd_hda_intel 0000:00:1f.3: snd_intel_dsp_find_config: no valid SSP found for HID ESSX8336, skipped Reported-by: Mauro Carvalho Chehab Tested-by: Mauro Carvalho Chehab Closes: https://github.com/thesofproject/linux/issues/4934 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Message-ID: <20240426152818.38443-1-pierre-louis.bossart@linux.intel.com> Signed-off-by: Takashi Iwai --- sound/hda/intel-dsp-config.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 6a384b922e4f..d1f6cdcf1866 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -557,9 +557,32 @@ static const struct config_entry *snd_intel_dsp_find_config 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)) + for (i = 0; i < table->codec_hid->num_codecs; i++) { + struct nhlt_acpi_table *nhlt; + bool ssp_found = false; + + if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1)) + continue; + + nhlt = intel_nhlt_init(&pci->dev); + if (!nhlt) { + dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n", + __func__, table->codec_hid->codecs[i]); + continue; + } + + if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) && + intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S)) + ssp_found = true; + + intel_nhlt_free(nhlt); + + if (ssp_found) break; + + dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n", + __func__, table->codec_hid->codecs[i]); + } if (i == table->codec_hid->num_codecs) continue; } -- cgit From c158cf914713efc3bcdc25680c7156c48c12ef6a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:27:31 -0500 Subject: ALSA: hda: intel-sdw-acpi: fix usage of device_get_named_child_node() The documentation for device_get_named_child_node() mentions this important point: " The caller is responsible for calling fwnode_handle_put() on the returned fwnode pointer. " Add fwnode_handle_put() to avoid a leaked reference. Signed-off-by: Pierre-Louis Bossart Fixes: 08c2a4bc9f2a ("ALSA: hda: move Intel SoundWire ACPI scan to dedicated module") Message-ID: <20240426152731.38420-1-pierre-louis.bossart@linux.intel.com> Signed-off-by: Takashi Iwai --- sound/hda/intel-sdw-acpi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index 5f60658c6051..d7417a40392b 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -45,6 +45,8 @@ static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx) "intel-quirk-mask", &quirk_mask); + fwnode_handle_put(link); + if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) return false; -- cgit From 1e707769df072757bdcafab158bb159ead73daa4 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 30 Apr 2024 17:15:53 +0800 Subject: ALSA: hda/realtek - Set GPIO3 to default at S4 state for Thinkpad with ALC1318 There is a chance of damaging the IC when S4 resume. Add safe mode for no stream to disable GPIO3. Thinkpad with ALC1318 platform need to add this workaround. Signed-off-by: Kailang Yang Link: https://lore.kernel.org/r/a853dc4f0a4e412381d5f60565181247@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e77013309795..b96c6863f272 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -920,6 +920,8 @@ static void alc_pre_init(struct hda_codec *codec) ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) #define is_s4_resume(codec) \ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) +#define is_s4_suspend(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) static int alc_init(struct hda_codec *codec) { @@ -7183,6 +7185,44 @@ static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec, alc245_fixup_hp_gpio_led(codec, fix, action); } +/* + * ALC287 PCM hooks + */ +static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */ + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ + break; + case HDA_GEN_PCM_ACT_CLOSE: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + break; + } +} + +static void alc287_s4_power_gpio3_default(struct hda_codec *codec) +{ + if (is_s4_suspend(codec)) { + alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */ + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + } +} + +static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->power_hook = alc287_s4_power_gpio3_default; + spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; +} + enum { ALC269_FIXUP_GPIO2, @@ -7470,7 +7510,8 @@ enum { ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC, ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, - ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1 + ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1, + ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9726,6 +9767,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_ASUS_GA403U, }, + [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -10394,6 +10441,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), + SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), -- cgit From 39815cdfc8d46ce2c72cbf2aa3d991c4bfb0024f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Apr 2024 18:32:04 +0200 Subject: ALSA: hda/realtek: Fix conflicting PCI SSID 17aa:386f for Lenovo Legion models Unfortunately both Lenovo Legion Pro 7 16ARX8H and Legion 7i 16IAX7 got the very same PCI SSID while the hardware implementations are completely different (the former is with TI TAS2781 codec while the latter is with Cirrus CS35L41 codec). The former model got broken by the recent fix for the latter model. For addressing the regression, check the codec SSID and apply the proper quirk for each model now. Fixes: 24b6332c2d4f ("ALSA: hda: Add Lenovo Legion 7i gen7 sound quirk") Cc: Link: https://bugzilla.suse.com/show_bug.cgi?id=1223462 Message-ID: <20240430163206.5200-1-tiwai@suse.de> Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b96c6863f272..e704425788eb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7466,6 +7466,7 @@ enum { ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_14IRP8_DUETITL, + ALC287_FIXUP_LENOVO_LEGION_7, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -7551,6 +7552,23 @@ static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec, __snd_hda_apply_fixup(codec, id, action, 0); } +/* Another hilarious PCI SSID conflict with Lenovo Legion Pro 7 16ARX8H (with + * TAS2781 codec) and Legion 7i 16IAX7 (with CS35L41 codec); + * we apply a corresponding fixup depending on the codec SSID instead + */ +static void alc287_fixup_lenovo_legion_7(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + int id; + + if (codec->core.subsystem_id == 0x17aa38a8) + id = ALC287_FIXUP_TAS2781_I2C; /* Legion Pro 7 16ARX8H */ + else + id = ALC287_FIXUP_CS35L41_I2C_2; /* Legion 7i 16IAX7 */ + __snd_hda_apply_fixup(codec, id, action, 0); +} + static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_GPIO2] = { .type = HDA_FIXUP_FUNC, @@ -9445,6 +9463,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_lenovo_14irp8_duetitl, }, + [ALC287_FIXUP_LENOVO_LEGION_7] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_lenovo_legion_7, + }, [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -10472,7 +10494,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7/7i", ALC287_FIXUP_LENOVO_LEGION_7), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), -- cgit From fdb3f29dfe0d51bdb8e7b3a6d876ea8339d44df8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 2 May 2024 08:24:42 +0200 Subject: ALSA: hda/realtek: Fix build error without CONFIG_PM The alc_spec.power_hook is defined only with CONFIG_PM, and the recent fix overlooked it, resulting in a build error without CONFIG_PM. Fix it with the simple ifdef and set __maybe_unused for the function. We may drop the whole CONFIG_PM dependency there, but it should be done in a separate cleanup patch later. Fixes: 1e707769df07 ("ALSA: hda/realtek - Set GPIO3 to default at S4 state for Thinkpad with ALC1318") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202405012104.Dr7h318W-lkp@intel.com/ Message-ID: <20240502062442.30545-1-tiwai@suse.de> Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e704425788eb..b29739bd330b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7204,7 +7204,7 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, } } -static void alc287_s4_power_gpio3_default(struct hda_codec *codec) +static void __maybe_unused alc287_s4_power_gpio3_default(struct hda_codec *codec) { if (is_s4_suspend(codec)) { alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */ @@ -7219,7 +7219,9 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; +#ifdef CONFIG_PM spec->power_hook = alc287_s4_power_gpio3_default; +#endif spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } -- cgit