ASoC: Fixes for v6.14

More driver specific fixes, the firmware change is part of fixing the
 race conditions in the Cirrus driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAme/DMgACgkQJNaLcl1U
 h9ATfgf9HaoS6L5AM7/ipu+u0uvFzfKFLmF1Rv7WrUmjCXNiJsNVSymMqv2gMmRJ
 XMXDz0gmzsAl5m9hqftoenNHTQnh4QNfQ9tfvSTnVbMLWDK+8Tn/gtkPLVpnTqlm
 96uGDgTtOqQveT2fCFJNfrGIwa0N2/CTE2tKJ4HJRKk4DE1wx+3YsAQXN6HBBWVe
 tKsli49mtsYs0G1s2uBS6DFQ9fYQms6wPyo/N1dibS/M6V9LeiOdJj/ZqwD7MpIa
 2z293gVLnhcs1WfWrRc4Kq62pcXyh6gq70mehnMAvaIsJM5zpMdUYguw0xA5AvhB
 butZr+RvbFDyzAIVDH6/0m31rvmJ2g==
 =QktC
 -----END PGP SIGNATURE-----

Merge tag 'asoc-fix-v6.14-rc4' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Fixes for v6.14

More driver specific fixes, the firmware change is part of fixing the
race conditions in the Cirrus driver.
This commit is contained in:
Takashi Iwai 2025-02-26 15:00:25 +01:00
commit fe1544deda
11 changed files with 142 additions and 51 deletions

View File

@ -1609,8 +1609,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
goto out_fw;
}
ret = regmap_raw_write_async(regmap, reg, buf->buf,
le32_to_cpu(region->len));
ret = regmap_raw_write(regmap, reg, buf->buf,
le32_to_cpu(region->len));
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
@ -1625,12 +1625,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
regions++;
}
ret = regmap_async_complete(regmap);
if (ret != 0) {
cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);
goto out_fw;
}
if (pos > firmware->size)
cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
@ -1638,7 +1632,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
cs_dsp_debugfs_save_wmfwname(dsp, file);
out_fw:
regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list);
if (ret == -EOVERFLOW)
@ -2326,8 +2319,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
file, blocks, le32_to_cpu(blk->len),
reg);
ret = regmap_raw_write_async(regmap, reg, buf->buf,
le32_to_cpu(blk->len));
ret = regmap_raw_write(regmap, reg, buf->buf,
le32_to_cpu(blk->len));
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write to %x in %s: %d\n",
@ -2339,10 +2332,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
blocks++;
}
ret = regmap_async_complete(regmap);
if (ret != 0)
cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);
if (pos > firmware->size)
cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, blocks, pos - firmware->size);
@ -2350,7 +2339,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
cs_dsp_debugfs_save_binname(dsp, file);
out_fw:
regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list);
if (ret == -EOVERFLOW)
@ -2561,8 +2549,8 @@ static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp)
{
int ret;
ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA, ADSP2_SYS_ENA);
ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA, ADSP2_SYS_ENA);
if (ret != 0)
return ret;

View File

@ -12,6 +12,7 @@
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/cs-amp-lib.h>
#define CS35L56_DEVID 0x0000000
@ -61,6 +62,7 @@
#define CS35L56_IRQ1_MASK_8 0x000E0AC
#define CS35L56_IRQ1_MASK_18 0x000E0D4
#define CS35L56_IRQ1_MASK_20 0x000E0DC
#define CS35L56_DSP_MBOX_1_RAW 0x0011000
#define CS35L56_DSP_VIRTUAL1_MBOX_1 0x0011020
#define CS35L56_DSP_VIRTUAL1_MBOX_2 0x0011024
#define CS35L56_DSP_VIRTUAL1_MBOX_3 0x0011028
@ -224,6 +226,7 @@
#define CS35L56_HALO_STATE_SHUTDOWN 1
#define CS35L56_HALO_STATE_BOOT_DONE 2
#define CS35L56_MBOX_CMD_PING 0x0A000000
#define CS35L56_MBOX_CMD_AUDIO_PLAY 0x0B000001
#define CS35L56_MBOX_CMD_AUDIO_PAUSE 0x0B000002
#define CS35L56_MBOX_CMD_AUDIO_REINIT 0x0B000003
@ -254,6 +257,16 @@
#define CS35L56_NUM_BULK_SUPPLIES 3
#define CS35L56_NUM_DSP_REGIONS 5
/* Additional margin for SYSTEM_RESET to control port ready on SPI */
#define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)
struct cs35l56_spi_payload {
__be32 addr;
__be16 pad;
__be32 value;
} __packed;
static_assert(sizeof(struct cs35l56_spi_payload) == 10);
struct cs35l56_base {
struct device *dev;
struct regmap *regmap;
@ -269,6 +282,7 @@ struct cs35l56_base {
s8 cal_index;
struct cirrus_amp_cal_data cal_data;
struct gpio_desc *reset_gpio;
struct cs35l56_spi_payload *spi_payload_buf;
};
static inline bool cs35l56_is_otp_register(unsigned int reg)
@ -276,6 +290,23 @@ static inline bool cs35l56_is_otp_register(unsigned int reg)
return (reg >> 16) == 3;
}
static inline int cs35l56_init_config_for_spi(struct cs35l56_base *cs35l56,
struct spi_device *spi)
{
cs35l56->spi_payload_buf = devm_kzalloc(&spi->dev,
sizeof(*cs35l56->spi_payload_buf),
GFP_KERNEL | GFP_DMA);
if (!cs35l56->spi_payload_buf)
return -ENOMEM;
return 0;
}
static inline bool cs35l56_is_spi(struct cs35l56_base *cs35l56)
{
return IS_ENABLED(CONFIG_SPI_MASTER) && !!cs35l56->spi_payload_buf;
}
extern const struct regmap_config cs35l56_regmap_i2c;
extern const struct regmap_config cs35l56_regmap_spi;
extern const struct regmap_config cs35l56_regmap_sdw;

View File

@ -22,6 +22,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
return -ENOMEM;
cs35l56->base.dev = &spi->dev;
ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
if (ret)
return ret;
#ifdef CS35L56_WAKE_HOLD_TIME_US
cs35l56->base.can_hibernate = true;

View File

@ -10,6 +10,7 @@
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <sound/cs-amp-lib.h>
@ -303,6 +304,79 @@ void cs35l56_wait_min_reset_pulse(void)
}
EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, "SND_SOC_CS35L56_SHARED");
static const struct {
u32 addr;
u32 value;
} cs35l56_spi_system_reset_stages[] = {
{ .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET },
/* The next write is necessary to delimit the soft reset */
{ .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING },
};
static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base,
struct spi_device *spi)
{
struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf;
struct spi_transfer t = {
.tx_buf = buf,
.len = sizeof(*buf),
};
struct spi_message m;
int i, ret;
for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) {
buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr);
buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value);
spi_message_init_with_transfers(&m, &t, 1);
ret = spi_sync_locked(spi, &m);
if (ret)
dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret);
usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US,
2 * CS35L56_SPI_RESET_TO_PORT_READY_US);
}
}
static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
{
struct spi_device *spi = to_spi_device(cs35l56_base->dev);
unsigned int val;
int read_ret, ret;
/*
* There must not be any other SPI bus activity while the amp is
* soft-resetting.
*/
ret = spi_bus_lock(spi->controller);
if (ret) {
dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret);
return;
}
cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi);
spi_bus_unlock(spi->controller);
/*
* Check firmware boot by testing for a response in MBOX_2.
* HALO_STATE cannot be trusted yet because the reset sequence
* can leave it with stale state. But MBOX is reset.
* The regmap must remain in cache-only until the chip has
* booted, so use a bypassed read.
*/
ret = read_poll_timeout(regmap_read_bypassed, read_ret,
(val > 0) && (val < 0xffffffff),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US,
false,
cs35l56_base->regmap,
CS35L56_DSP_VIRTUAL1_MBOX_2,
&val);
if (ret) {
dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n",
read_ret, val);
}
}
static const struct reg_sequence cs35l56_system_reset_seq[] = {
REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0),
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
@ -315,6 +389,12 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
* accesses other than the controlled system reset sequence below.
*/
regcache_cache_only(cs35l56_base->regmap, true);
if (cs35l56_is_spi(cs35l56_base)) {
cs35l56_spi_system_reset(cs35l56_base);
return;
}
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_system_reset_seq,
ARRAY_SIZE(cs35l56_system_reset_seq));

View File

@ -33,6 +33,9 @@ static int cs35l56_spi_probe(struct spi_device *spi)
cs35l56->base.dev = &spi->dev;
cs35l56->base.can_hibernate = true;
ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
if (ret)
return ret;
ret = cs35l56_common_probe(cs35l56);
if (ret != 0)

View File

@ -233,7 +233,6 @@ static const struct snd_kcontrol_new es8328_right_line_controls =
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
@ -243,7 +242,6 @@ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
@ -336,10 +334,10 @@ static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
ES8328_DACPOWER_LDAC_OFF, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_MIXER("Left Mixer", ES8328_DACCONTROL17, 7, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_MIXER("Right Mixer", ES8328_DACCONTROL20, 7, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
@ -418,19 +416,14 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
{ "Left Out 1", NULL, "Left DAC" },
{ "Right Out 1", NULL, "Right DAC" },
{ "Left Out 2", NULL, "Left DAC" },
{ "Right Out 2", NULL, "Right DAC" },
{ "Left Mixer", "Playback Switch", "Left DAC" },
{ "Left Mixer", NULL, "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Right Mixer", "Playback Switch", "Right DAC" },
{ "Right Mixer", NULL, "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },

View File

@ -994,10 +994,10 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
{
.name = "sai-tx",
.playback = {
.stream_name = "CPU-Playback",
.stream_name = "SAI-Playback",
.channels_min = 1,
.channels_max = 32,
.rate_min = 8000,
.rate_min = 8000,
.rate_max = 2822400,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_SAI_FORMATS,
@ -1007,7 +1007,7 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
{
.name = "sai-rx",
.capture = {
.stream_name = "CPU-Capture",
.stream_name = "SAI-Capture",
.channels_min = 1,
.channels_max = 32,
.rate_min = 8000,

View File

@ -119,8 +119,8 @@ static const struct snd_soc_ops imx_audmix_be_ops = {
static const char *name[][3] = {
{"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"},
{"sai-tx", "sai-tx", "sai-rx"},
{"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"},
{"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"},
{"AUDMIX-Playback-0", "AUDMIX-Playback-1", "SAI-Capture"},
{"SAI-Playback", "SAI-Playback", "AUDMIX-Capture-0"},
};
static int imx_audmix_probe(struct platform_device *pdev)

View File

@ -803,7 +803,9 @@ static int create_sdw_dailink(struct snd_soc_card *card,
int *be_id, struct snd_soc_codec_conf **codec_conf)
{
struct device *dev = card->dev;
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
struct asoc_sdw_endpoint *sof_end;
int stream;
@ -900,6 +902,11 @@ static int create_sdw_dailink(struct snd_soc_card *card,
codecs[j].name = sof_end->codec_name;
codecs[j].dai_name = sof_end->dai_info->dai_name;
if (sof_end->dai_info->dai_type == SOC_SDW_DAI_TYPE_MIC &&
mach_params->dmic_num > 0) {
dev_warn(dev,
"Both SDW DMIC and PCH DMIC are present, if incorrect, please set kernel params snd_sof_intel_hda_generic dmic_num=0 to disable PCH DMIC\n");
}
j++;
}

View File

@ -1312,22 +1312,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
/* report to machine driver if any DMICs are found */
mach->mach_params.dmic_num = check_dmic_num(sdev);
if (sdw_mach_found) {
/*
* DMICs use up to 4 pins and are typically pin-muxed with SoundWire
* link 2 and 3, or link 1 and 2, thus we only try to enable dmics
* if all conditions are true:
* a) 2 or fewer links are used by SoundWire
* b) the NHLT table reports the presence of microphones
*/
if (hweight_long(mach->link_mask) <= 2)
dmic_fixup = true;
else
mach->mach_params.dmic_num = 0;
} else {
if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER)
dmic_fixup = true;
}
if (sdw_mach_found || mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER)
dmic_fixup = true;
if (tplg_fixup &&
dmic_fixup &&

View File

@ -10,7 +10,7 @@ set -eu
STYLE_COMPONENT_ON="color=dodgerblue;style=bold"
STYLE_COMPONENT_OFF="color=gray40;style=filled;fillcolor=gray90"
STYLE_NODE_ON="shape=box,style=bold,color=green4"
STYLE_NODE_ON="shape=box,style=bold,color=green4,fillcolor=white"
STYLE_NODE_OFF="shape=box,style=filled,color=gray30,fillcolor=gray95"
# Print usage and exit