From dfce24f0032439113848939816ef78b6e83f4086 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Thu, 12 Jun 2025 17:00:22 +0100 Subject: [PATCH] ALSA: hda: cs35l41: Add support for center channel in CS35L41 HDA Currently only left and right channels are supported for each amp. Support is needed for a center channel, using both left and right channel audio. Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20250612160029.848104-2-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- include/sound/cs35l41.h | 12 +++++ sound/pci/hda/cs35l41_hda.c | 103 +++++++++++++++++++++++------------- sound/pci/hda/cs35l41_hda.h | 1 + 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 43c6a9ef8d9f1..7542cabfa7266 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -609,6 +609,18 @@ #define CS35L41_DSP_NG_DELAY_MASK 0x0F00 #define CS35L41_DSP_NG_DELAY_SHIFT 8 +#define CS35L41_ASP_RX1_EN_MASK 0x00010000 +#define CS35L41_ASP_RX1_EN_SHIFT 16 +#define CS35L41_ASP_RX2_EN_MASK 0x00020000 +#define CS35L41_ASP_RX2_EN_SHIFT 17 +#define CS35L41_ASP_TX1_EN_MASK 0x00000001 +#define CS35L41_ASP_TX1_EN_SHIFT 0 +#define CS35L41_ASP_TX2_EN_MASK 0x00000002 +#define CS35L41_ASP_TX2_EN_SHIFT 1 +#define CS35L41_ASP_TX3_EN_MASK 0x00000004 +#define CS35L41_ASP_TX3_EN_SHIFT 2 +#define CS35L41_ASP_TX4_EN_MASK 0x00000008 +#define CS35L41_ASP_TX4_EN_SHIFT 3 #define CS35L41_ASP_FMT_MASK 0x0700 #define CS35L41_ASP_FMT_SHIFT 8 #define CS35L41_ASP_DOUT_HIZ_MASK 0x03 diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index d5bc81099d0d6..c2cf3813872a0 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -93,47 +93,36 @@ module_param(firmware_autostart, bool, 0444); MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot" "(0=Disable, 1=Enable) (default=1); "); +static const char channel_name[3] = { 'L', 'R', 'C' }; + static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1 { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer - { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON - { CS35L41_ASP_TX3_SRC, 0x00000032 }, // ASPTX3 SRC = ERRVOL - { CS35L41_ASP_TX4_SRC, 0x00000033 }, // ASPTX4 SRC = CLASSH_TGT - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000009 }, // DSP1RX2 SRC = ASPRX2 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON +}; + +static const struct reg_sequence cs35l41_hda_config_no_dsp[] = { + { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 + { CS35L41_ASP_TX3_SRC, 0x00000000 }, // ASPTX3 SRC = ZERO FILL + { CS35L41_ASP_TX4_SRC, 0x00000000 }, // ASPTX4 SRC = ZERO FILL { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL + { CS35L41_DSP1_RX6_SRC, 0x00000021 }, // DSP1RX6 SRC = CLASSH_TGT }; static const struct reg_sequence cs35l41_hda_config_dsp[] = { - { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 - { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN - { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1 - { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz - { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled - { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 - { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON - { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; @@ -657,6 +646,41 @@ static void cs35l41_irq_release(struct cs35l41_hda *cs35l41) cs35l41->irq_errors = 0; } +static void cs35l41_update_mixer(struct cs35l41_hda *cs35l41) +{ + struct regmap *reg = cs35l41->regmap; + unsigned int asp_en = 0; + unsigned int dsp1rx2_src = 0; + + regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); + + if (cs35l41->cs_dsp.running) { + asp_en |= CS35L41_ASP_TX1_EN_MASK; // ASP_TX1_EN = 1 + regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, + ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_config_no_dsp, + ARRAY_SIZE(cs35l41_hda_config_no_dsp)); + } + + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) { + asp_en |= CS35L41_ASP_RX2_EN_MASK; // ASP_RX2_EN = 1 + dsp1rx2_src = 0x00000009; // DSP1RX2 SRC = ASPRX2 + } else { + dsp1rx2_src = 0x00000008; // DSP1RX2 SRC = ASPRX1 + } + + asp_en |= CS35L41_ASP_RX1_EN_MASK; // ASP_RX1_EN = 1 + + regmap_write(reg, CS35L41_SP_ENABLES, asp_en); + regmap_write(reg, CS35L41_DSP1_RX1_SRC, 0x00000008); // DSP1RX1 SRC = ASPRX1 + regmap_write(reg, CS35L41_DSP1_RX2_SRC, dsp1rx2_src); +} + static void cs35l41_hda_play_start(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); @@ -671,19 +695,13 @@ static void cs35l41_hda_play_start(struct device *dev) cs35l41->playback_started = true; + cs35l41_update_mixer(cs35l41); + if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, - ARRAY_SIZE(cs35l41_hda_config_dsp)); - if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); - else - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); } regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) @@ -841,22 +859,31 @@ static void cs35l41_hda_post_playback_hook(struct device *dev, int action) } } -static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) +static int cs35l41_hda_channel_map(struct cs35l41_hda *cs35l41) { - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - static const char * const channel_name[] = { "L", "R" }; + unsigned int tx_num = 0; + unsigned int *tx_slot = NULL; + unsigned int rx_num; + unsigned int *rx_slot; + unsigned int mono = 0; if (!cs35l41->amp_name) { - if (*rx_slot >= ARRAY_SIZE(channel_name)) + if (cs35l41->hw_cfg.spk_pos >= ARRAY_SIZE(channel_name)) return -EINVAL; - cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d", - channel_name[*rx_slot], cs35l41->channel_index); + cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%c%d", + channel_name[cs35l41->hw_cfg.spk_pos], + cs35l41->channel_index); if (!cs35l41->amp_name) return -ENOMEM; } + rx_num = 1; + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) + rx_slot = &mono; + else + rx_slot = &cs35l41->hw_cfg.spk_pos; + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, rx_slot); } @@ -1495,7 +1522,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH, - cs35l41->hw_cfg.spk_pos ? 'R' : 'L', + channel_name[cs35l41->hw_cfg.spk_pos], cs35l41->cs_dsp.running, cs35l41->speaker_id); return ret; @@ -1709,7 +1736,7 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) if (using_irq) cs35l41_configure_interrupt(cs35l41, irq_pol); - return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); + return cs35l41_hda_channel_map(cs35l41); } int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id) diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index c730b33515894..7d003c598e93e 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -42,6 +42,7 @@ struct cs35l41_amp_efi_data { enum cs35l41_hda_spk_pos { CS35L41_LEFT, CS35L41_RIGHT, + CS35L41_CENTER, }; enum cs35l41_hda_gpio_function { -- 2.47.2