]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: hda: cs35l41: Add support for center channel in CS35L41 HDA
authorStefan Binding <sbinding@opensource.cirrus.com>
Thu, 12 Jun 2025 16:00:22 +0000 (17:00 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 13 Jun 2025 08:19:34 +0000 (10:19 +0200)
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 <sbinding@opensource.cirrus.com>
Link: https://patch.msgid.link/20250612160029.848104-2-sbinding@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/cs35l41.h
sound/pci/hda/cs35l41_hda.c
sound/pci/hda/cs35l41_hda.h

index 43c6a9ef8d9f1991c263316942cccbb4b5e88d43..7542cabfa7266e6e1d3693f4bff00e76af751ae3 100644 (file)
 #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
index d5bc81099d0d68b2f7fd2ae0b841b46f12a581be..c2cf3813872a05a4efb6882e059df42ff7c7ee56 100644 (file)
@@ -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)
index c730b335158944f4aef86cfc3da60b90752d08af..7d003c598e93e539c50f34fead675cb182339f58 100644 (file)
@@ -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 {