]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: cs530x: Add CODEC and DAC support
authorSimon Trimmer <simont@opensource.cirrus.com>
Thu, 23 Oct 2025 09:03:14 +0000 (10:03 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 27 Oct 2025 11:10:27 +0000 (11:10 +0000)
- Added DAC register address constants
- Add the new registers to the regmap config
- Renamed constants that are shared between the DACs and ADCs
- Add the device IDs and device names of the CODEC and DACs along with
  their different capabilities
- Add DAPM widgets, ALSA controls and event handling for the DAC
  functionality
- Add Playback DAI support

Signed-off-by: Simon Trimmer <simont@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
Link: https://patch.msgid.link/20251023090327.58275-7-vitalyr@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/cs530x-i2c.c
sound/soc/codecs/cs530x.c
sound/soc/codecs/cs530x.h

index d6b7883ba7b9a6819d80fb2f2e4ece93745f403b..ab410826f7778f31ed0589d2927865d8dc01b42c 100644 (file)
 
 static const struct of_device_id cs530x_of_match[] = {
        {
+               .compatible = "cirrus,cs4282",
+               .data = (void *)CS4282,
+       }, {
+               .compatible = "cirrus,cs4302",
+               .data = (void *)CS4302,
+       }, {
+               .compatible = "cirrus,cs4304",
+               .data = (void *)CS4304,
+       }, {
+               .compatible = "cirrus,cs4308",
+               .data = (void *)CS4308,
+       }, {
                .compatible = "cirrus,cs5302",
                .data = (void *)CS5302,
        }, {
@@ -28,6 +40,10 @@ static const struct of_device_id cs530x_of_match[] = {
 MODULE_DEVICE_TABLE(of, cs530x_of_match);
 
 static const struct i2c_device_id cs530x_i2c_id[] = {
+       { "cs4282", CS4282 },
+       { "cs4302", CS4302 },
+       { "cs4304", CS4304 },
+       { "cs4308", CS4308 },
        { "cs5302", CS5302 },
        { "cs5304", CS5304 },
        { "cs5308", CS5308 },
index 6fa62fb6c681ed66931d3f671bff277d8f338bf4..380405d3e3a81117a4489573f95aafef4514f2ec 100644 (file)
@@ -45,6 +45,18 @@ static const struct reg_default cs530x_reg_defaults[] = {
        { CS530X_IN_VOL_CTRL3_1, 0x8000 },
        { CS530X_IN_VOL_CTRL4_0, 0x8000 },
        { CS530X_IN_VOL_CTRL4_1, 0x8000 },
+       { CS530X_OUT_ENABLES, 0 },
+       { CS530X_OUT_RAMP_SUM, 0x0022 },
+       { CS530X_OUT_FILTER, 0 },
+       { CS530X_OUT_INV, 0 },
+       { CS530X_OUT_VOL_CTRL1_0, 0x8000 },
+       { CS530X_OUT_VOL_CTRL1_1, 0x8000 },
+       { CS530X_OUT_VOL_CTRL2_0, 0x8000 },
+       { CS530X_OUT_VOL_CTRL2_1, 0x8000 },
+       { CS530X_OUT_VOL_CTRL3_0, 0x8000 },
+       { CS530X_OUT_VOL_CTRL3_1, 0x8000 },
+       { CS530X_OUT_VOL_CTRL4_0, 0x8000 },
+       { CS530X_OUT_VOL_CTRL4_1, 0x8000 },
        { CS530X_PAD_FN, 0 },
        { CS530X_PAD_LVL, 0 },
 };
@@ -70,6 +82,19 @@ static bool cs530x_read_and_write_regs(unsigned int reg)
        case CS530X_IN_VOL_CTRL3_1:
        case CS530X_IN_VOL_CTRL4_0:
        case CS530X_IN_VOL_CTRL4_1:
+       case CS530X_OUT_ENABLES:
+       case CS530X_OUT_RAMP_SUM:
+       case CS530X_OUT_DEEMPH:
+       case CS530X_OUT_FILTER:
+       case CS530X_OUT_INV:
+       case CS530X_OUT_VOL_CTRL1_0:
+       case CS530X_OUT_VOL_CTRL1_1:
+       case CS530X_OUT_VOL_CTRL2_0:
+       case CS530X_OUT_VOL_CTRL2_1:
+       case CS530X_OUT_VOL_CTRL3_0:
+       case CS530X_OUT_VOL_CTRL3_1:
+       case CS530X_OUT_VOL_CTRL4_0:
+       case CS530X_OUT_VOL_CTRL4_1:
        case CS530X_PAD_FN:
        case CS530X_PAD_LVL:
                return true;
@@ -94,6 +119,7 @@ static bool cs530x_writeable_register(struct device *dev, unsigned int reg)
        switch (reg) {
        case CS530X_SW_RESET:
        case CS530X_IN_VOL_CTRL5:
+       case CS530X_OUT_VOL_CTRL5:
                return true;
        default:
                return cs530x_read_and_write_regs(reg);
@@ -126,7 +152,7 @@ volsw_err:
 
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0);
 
-static const char * const cs530x_in_filter_text[] = {
+static const char * const cs530x_inout_filter_text[] = {
        "Min Phase Slow Roll-off",
        "Min Phase Fast Roll-off",
        "Linear Phase Slow Roll-off",
@@ -134,24 +160,36 @@ static const char * const cs530x_in_filter_text[] = {
 };
 
 static SOC_ENUM_SINGLE_DECL(cs530x_in_filter_enum, CS530X_IN_FILTER,
-                           CS530X_IN_FILTER_SHIFT,
-                           cs530x_in_filter_text);
+                           CS530X_INOUT_FILTER_SHIFT,
+                           cs530x_inout_filter_text);
 
-static const char * const cs530x_in_4ch_sum_text[] = {
+static SOC_ENUM_SINGLE_DECL(cs530x_out_filter_enum, CS530X_OUT_FILTER,
+                           CS530X_INOUT_FILTER_SHIFT,
+                           cs530x_inout_filter_text);
+
+static const char * const cs530x_4ch_sum_text[] = {
        "None",
        "Groups of 2",
        "Groups of 4",
 };
 
 static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM,
-                           CS530X_IN_SUM_MODE_SHIFT,
-                           cs530x_in_4ch_sum_text);
+                           CS530X_INOUT_SUM_MODE_SHIFT,
+                           cs530x_4ch_sum_text);
 
 static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = {
 SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum),
 };
 
-static const char * const cs530x_in_8ch_sum_text[] = {
+static SOC_ENUM_SINGLE_DECL(cs530x_out_sum_ch4_enum, CS530X_OUT_RAMP_SUM,
+                           CS530X_INOUT_SUM_MODE_SHIFT,
+                           cs530x_4ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_out_sum_4ch_controls[] = {
+SOC_ENUM("OUT Sum Select", cs530x_out_sum_ch4_enum),
+};
+
+static const char * const cs530x_8ch_sum_text[] = {
        "None",
        "Groups of 2",
        "Groups of 4",
@@ -159,13 +197,20 @@ static const char * const cs530x_in_8ch_sum_text[] = {
 };
 
 static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM,
-                           CS530X_IN_SUM_MODE_SHIFT,
-                           cs530x_in_8ch_sum_text);
+                           CS530X_INOUT_SUM_MODE_SHIFT,
+                           cs530x_8ch_sum_text);
 
 static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = {
 SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum),
 };
 
+static SOC_ENUM_SINGLE_DECL(cs530x_out_sum_ch8_enum, CS530X_OUT_RAMP_SUM,
+                           CS530X_INOUT_SUM_MODE_SHIFT,
+                           cs530x_8ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_out_sum_8ch_controls[] = {
+SOC_ENUM("OUT Sum Select", cs530x_out_sum_ch8_enum),
+};
 
 static const char * const cs530x_vol_ramp_text[] = {
        "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
@@ -190,8 +235,8 @@ SOC_ENUM("IN DEC Filter Select", cs530x_in_filter_enum),
 SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum),
 SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum),
 
-SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0),
-SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_INOUT1_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_INOUT2_INV_SHIFT, 1, 0),
 };
 
 static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = {
@@ -200,8 +245,8 @@ SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1,
 SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1,
                    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
 
-SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0),
-SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_INOUT3_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_INOUT4_INV_SHIFT, 1, 0),
 };
 
 static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = {
@@ -214,10 +259,10 @@ SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1,
 SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1,
                    snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
 
-SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0),
-SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0),
-SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0),
-SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_INOUT5_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_INOUT6_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_INOUT7_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_INOUT8_INV_SHIFT, 1, 0),
 };
 
 static int cs530x_adc_event(struct snd_soc_dapm_widget *w,
@@ -233,24 +278,110 @@ static int cs530x_adc_event(struct snd_soc_dapm_widget *w,
                break;
        case SND_SOC_DAPM_POST_PMU:
                regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
-                                 (w->shift * 2), CS530X_IN_MUTE);
+                                 (w->shift * 2), CS530X_INOUT_MUTE);
                regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
-                                 ((w->shift + 1) * 2), CS530X_IN_MUTE);
+                                 ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
 
                cs530x->adc_pairs_count--;
                if (!cs530x->adc_pairs_count) {
                        usleep_range(1000, 1100);
                        return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
-                                           CS530X_IN_VU);
+                                           CS530X_INOUT_VU);
                }
                break;
        case SND_SOC_DAPM_PRE_PMD:
                regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
-                               (w->shift * 2), CS530X_IN_MUTE);
+                               (w->shift * 2), CS530X_INOUT_MUTE);
                regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
-                               ((w->shift + 1) * 2), CS530X_IN_MUTE);
+                               ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
                return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
-                                   CS530X_IN_VU);
+                                   CS530X_INOUT_VU);
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_out_inc_enum, CS530X_OUT_RAMP_SUM,
+                           CS530X_RAMP_RATE_INC_SHIFT,
+                           cs530x_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_out_dec_enum, CS530X_OUT_RAMP_SUM,
+                           CS530X_RAMP_RATE_DEC_SHIFT,
+                           cs530x_vol_ramp_text);
+
+static const struct snd_kcontrol_new cs530x_out_1_to_2_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT1 Volume", CS530X_OUT_VOL_CTRL1_0, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT2 Volume", CS530X_OUT_VOL_CTRL1_1, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_ENUM("OUT DEC Filter Select", cs530x_out_filter_enum),
+SOC_ENUM("Output Ramp Up", cs530x_ramp_out_inc_enum),
+SOC_ENUM("Output Ramp Down", cs530x_ramp_out_dec_enum),
+
+SOC_SINGLE("DAC1 Invert Switch", CS530X_OUT_INV, CS530X_INOUT1_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC2 Invert Switch", CS530X_OUT_INV, CS530X_INOUT2_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_out_3_to_4_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT3 Volume", CS530X_OUT_VOL_CTRL2_0, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT4 Volume", CS530X_OUT_VOL_CTRL2_1, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("DAC3 Invert Switch", CS530X_OUT_INV, CS530X_INOUT3_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC4 Invert Switch", CS530X_OUT_INV, CS530X_INOUT4_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_out_5_to_8_controls[] = {
+SOC_SINGLE_EXT_TLV("OUT5 Volume", CS530X_OUT_VOL_CTRL3_0, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT6 Volume", CS530X_OUT_VOL_CTRL3_1, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT7 Volume", CS530X_OUT_VOL_CTRL4_0, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("OUT8 Volume", CS530X_OUT_VOL_CTRL4_1, 0, 255, 1,
+                   snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("DAC5 Invert Switch", CS530X_OUT_INV, CS530X_INOUT5_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC6 Invert Switch", CS530X_OUT_INV, CS530X_INOUT6_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC7 Invert Switch", CS530X_OUT_INV, CS530X_INOUT7_INV_SHIFT, 1, 0),
+SOC_SINGLE("DAC8 Invert Switch", CS530X_OUT_INV, CS530X_INOUT8_INV_SHIFT, 1, 0),
+};
+
+static int cs530x_dac_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+       struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+       struct regmap *regmap = cs530x->regmap;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               cs530x->dac_pairs_count++;
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_clear_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+                                (w->shift * 2), CS530X_INOUT_MUTE);
+               regmap_clear_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+                                ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+
+               cs530x->dac_pairs_count--;
+               if (!cs530x->dac_pairs_count) {
+                       usleep_range(1000, 1100);
+                       return regmap_write(regmap, CS530X_OUT_VOL_CTRL5,
+                                           CS530X_INOUT_VU);
+               }
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_set_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+                              (w->shift * 2), CS530X_INOUT_MUTE);
+               regmap_set_bits(regmap, CS530X_OUT_VOL_CTRL1_0 +
+                              ((w->shift + 1) * 2), CS530X_INOUT_MUTE);
+               return regmap_write(regmap, CS530X_OUT_VOL_CTRL5,
+                                   CS530X_INOUT_VU);
        default:
                return -EINVAL;
        }
@@ -266,8 +397,18 @@ static const struct snd_kcontrol_new adc56_ctrl =
        SOC_DAPM_SINGLE_VIRT("Switch", 1);
 static const struct snd_kcontrol_new adc78_ctrl =
        SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac12_ctrl =
+       SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac34_ctrl =
+       SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac56_ctrl =
+       SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new dac78_ctrl =
+       SOC_DAPM_SINGLE_VIRT("Switch", 1);
 static const struct snd_kcontrol_new in_hpf_ctrl =
        SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new out_hpf_ctrl =
+       SOC_DAPM_SINGLE_VIRT("Switch", 1);
 
 /* General DAPM widgets for all devices */
 static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = {
@@ -284,7 +425,7 @@ SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0,
                   SND_SOC_DAPM_PRE_PMU),
 SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0),
 SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl),
-SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT,
+SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_INOUT_HPF_EN_SHIFT,
                    0, &in_hpf_ctrl),
 };
 
@@ -408,6 +549,153 @@ static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)
                                ARRAY_SIZE(adc_ch3_4_routes));
 }
 
+/* DAC's Channels 1 and 2 plus generic DAC DAPM events */
+static const struct snd_soc_dapm_widget cs530x_dac_ch12_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT1"),
+SND_SOC_DAPM_OUTPUT("OUT2"),
+SND_SOC_DAPM_DAC_E("DAC1", NULL, CS530X_OUT_ENABLES, 0, 0,
+                  cs530x_dac_event,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC2", NULL, CS530X_OUT_ENABLES, 1, 0),
+SND_SOC_DAPM_SWITCH("DAC12 Enable", SND_SOC_NOPM, 0, 0, &dac12_ctrl),
+SND_SOC_DAPM_SWITCH("OUT HPF", CS530X_OUT_FILTER, CS530X_INOUT_HPF_EN_SHIFT,
+                   0, &out_hpf_ctrl),
+};
+
+/* DAC's Channels 3 and 4 */
+static const struct snd_soc_dapm_widget cs530x_dac_ch34_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_DAC_E("DAC3", NULL, CS530X_OUT_ENABLES, 2, 0,
+                  cs530x_dac_event,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC4", NULL, CS530X_OUT_ENABLES, 3, 0),
+SND_SOC_DAPM_SWITCH("DAC34 Enable", SND_SOC_NOPM, 0, 0, &dac34_ctrl),
+};
+
+/* DAC's Channels 5 to 8 */
+static const struct snd_soc_dapm_widget cs530x_dac_ch58_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("OUT5"),
+SND_SOC_DAPM_OUTPUT("OUT6"),
+SND_SOC_DAPM_OUTPUT("OUT7"),
+SND_SOC_DAPM_OUTPUT("OUT8"),
+SND_SOC_DAPM_DAC_E("DAC5", NULL, CS530X_OUT_ENABLES, 4, 0,
+                  cs530x_dac_event,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC6", NULL, CS530X_OUT_ENABLES, 5, 0),
+SND_SOC_DAPM_SWITCH("DAC56 Enable", SND_SOC_NOPM, 0, 0, &dac56_ctrl),
+SND_SOC_DAPM_DAC_E("DAC7", NULL, CS530X_OUT_ENABLES, 6, 0,
+                  cs530x_dac_event,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_DAC("DAC8", NULL, CS530X_OUT_ENABLES, 7, 0),
+SND_SOC_DAPM_SWITCH("DAC78 Enable", SND_SOC_NOPM, 0, 0, &dac78_ctrl),
+};
+
+static const struct snd_soc_dapm_route dac_ch1_2_routes[] = {
+       { "DAC1", NULL, "Global Enable" },
+       { "DAC2", NULL, "Global Enable" },
+
+       { "DAC12 Enable", "Switch", "OUT1" },
+       { "DAC12 Enable", "Switch", "OUT2" },
+       { "DAC1", NULL, "DAC12 Enable" },
+       { "DAC2", NULL, "DAC12 Enable" },
+       { "OUT HPF", "Switch", "DAC1" },
+       { "OUT HPF", "Switch", "DAC2" },
+
+       { "OUT HPF", NULL, "AIF Playback" },
+       { "DAC1", NULL, "AIF Playback" },
+       { "DAC2", NULL, "AIF Playback" },
+
+       { "OUT1", NULL, "DAC1" },
+       { "OUT2", NULL, "DAC2" },
+};
+
+static const struct snd_soc_dapm_route dac_ch3_4_routes[] = {
+       { "DAC3", NULL, "Global Enable" },
+       { "DAC4", NULL, "Global Enable" },
+
+       { "DAC34 Enable", "Switch", "OUT3" },
+       { "DAC34 Enable", "Switch", "OUT4" },
+       { "DAC3", NULL, "DAC34 Enable" },
+       { "DAC4", NULL, "DAC34 Enable" },
+       { "OUT HPF", "Switch", "DAC3" },
+       { "OUT HPF", "Switch", "DAC4" },
+
+       { "DAC3", NULL, "AIF Playback" },
+       { "DAC4", NULL, "AIF Playback" },
+
+       { "OUT3", NULL, "DAC3" },
+       { "OUT4", NULL, "DAC4" },
+};
+
+static const struct snd_soc_dapm_route dac_ch5_8_routes[] = {
+       { "DAC5", NULL, "Global Enable" },
+       { "DAC6", NULL, "Global Enable" },
+
+       { "DAC56 Enable", "Switch", "OUT5" },
+       { "DAC56 Enable", "Switch", "OUT6" },
+       { "DAC5", NULL, "DAC56 Enable" },
+       { "DAC6", NULL, "DAC56 Enable" },
+       { "OUT HPF", "Switch", "DAC5" },
+       { "OUT HPF", "Switch", "DAC6" },
+
+       { "DAC5", NULL, "AIF Playback" },
+       { "DAC6", NULL, "AIF Playback" },
+
+       { "OUT5", NULL, "DAC5" },
+       { "OUT6", NULL, "DAC6" },
+
+       { "DAC7", NULL, "Global Enable" },
+       { "DAC8", NULL, "Global Enable" },
+
+       { "DAC78 Enable", "Switch", "OUT7" },
+       { "DAC78 Enable", "Switch", "OUT8" },
+       { "DAC7", NULL, "DAC78 Enable" },
+       { "DAC8", NULL, "DAC78 Enable" },
+       { "OUT HPF", "Switch", "DAC7" },
+       { "OUT HPF", "Switch", "DAC8" },
+
+       { "DAC7", NULL, "AIF Playback" },
+       { "DAC8", NULL, "AIF Playback" },
+
+       { "OUT7", NULL, "DAC7" },
+       { "OUT8", NULL, "DAC8" },
+};
+
+static void cs530x_add_12_dac_widgets(struct snd_soc_component *component)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+       snd_soc_add_component_controls(component,
+                                      cs530x_out_1_to_2_controls,
+                                      ARRAY_SIZE(cs530x_out_1_to_2_controls));
+
+       snd_soc_dapm_new_controls(dapm, cs530x_dac_ch12_dapm_widgets,
+                                 ARRAY_SIZE(cs530x_dac_ch12_dapm_widgets));
+
+       snd_soc_dapm_add_routes(dapm, dac_ch1_2_routes,
+                               ARRAY_SIZE(dac_ch1_2_routes));
+}
+
+static void cs530x_add_34_dac_widgets(struct snd_soc_component *component)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+       snd_soc_add_component_controls(component,
+                                      cs530x_out_3_to_4_controls,
+                                      ARRAY_SIZE(cs530x_out_3_to_4_controls));
+
+       snd_soc_dapm_new_controls(dapm, cs530x_dac_ch34_dapm_widgets,
+                                 ARRAY_SIZE(cs530x_dac_ch34_dapm_widgets));
+
+       snd_soc_dapm_add_routes(dapm, dac_ch3_4_routes,
+                               ARRAY_SIZE(dac_ch3_4_routes));
+}
+
 static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)
 {
        struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
@@ -666,8 +954,11 @@ static const struct snd_soc_dai_driver cs530x_dai = {
        .name = "cs530x-dai",
        .capture = {
                .stream_name = "AIF Capture",
-               .channels_min = 2,
-               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_KNOT,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .playback = {
+               .stream_name = "AIF Playback",
                .rates = SNDRV_PCM_RATE_KNOT,
                .formats = SNDRV_PCM_FMTBIT_S32_LE,
        },
@@ -722,6 +1013,43 @@ static int cs530x_component_probe(struct snd_soc_component *component)
                                  ARRAY_SIZE(cs530x_gen_dapm_widgets));
 
        switch (cs530x->devtype) {
+       case CS4282:
+               cs530x_add_12_adc_widgets(component);
+               cs530x_add_12_dac_widgets(component);
+               break;
+       case CS4302:
+               cs530x_add_12_dac_widgets(component);
+               break;
+       case CS4304:
+               cs530x_add_12_dac_widgets(component);
+               cs530x_add_34_dac_widgets(component);
+
+               num_widgets = ARRAY_SIZE(cs530x_out_sum_4ch_controls);
+               snd_soc_add_component_controls(component,
+                                              cs530x_out_sum_4ch_controls,
+                                              num_widgets);
+               break;
+       case CS4308:
+               cs530x_add_12_dac_widgets(component);
+               cs530x_add_34_dac_widgets(component);
+
+               num_widgets = ARRAY_SIZE(cs530x_out_5_to_8_controls);
+               snd_soc_add_component_controls(component,
+                                              cs530x_out_5_to_8_controls,
+                                              num_widgets);
+
+               num_widgets = ARRAY_SIZE(cs530x_out_sum_8ch_controls);
+               snd_soc_add_component_controls(component,
+                                              cs530x_out_sum_8ch_controls,
+                                              num_widgets);
+
+               num_widgets = ARRAY_SIZE(cs530x_dac_ch58_dapm_widgets);
+               snd_soc_dapm_new_controls(dapm, cs530x_dac_ch58_dapm_widgets,
+                                         num_widgets);
+
+               snd_soc_dapm_add_routes(dapm, dac_ch5_8_routes,
+                                       ARRAY_SIZE(dac_ch5_8_routes));
+               break;
        case CS5302:
                cs530x_add_12_adc_widgets(component);
                break;
@@ -825,9 +1153,20 @@ static int cs530x_check_device_id(struct cs530x_priv *cs530x)
        if (ret)
                return dev_err_probe(dev, ret, "Can't read REV ID\n");
 
-       dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev);
-
        switch (dev_id) {
+       case CS530X_2CH_CODEC_DEV_ID:
+               cs530x->num_dacs = 2;
+               cs530x->num_adcs = 2;
+               break;
+       case CS530X_2CH_DAC_DEV_ID:
+               cs530x->num_dacs = 2;
+               break;
+       case CS530X_4CH_DAC_DEV_ID:
+               cs530x->num_dacs = 4;
+               break;
+       case CS530X_8CH_DAC_DEV_ID:
+               cs530x->num_dacs = 8;
+               break;
        case CS530X_2CH_ADC_DEV_ID:
                cs530x->num_adcs = 2;
                break;
@@ -842,6 +1181,9 @@ static int cs530x_check_device_id(struct cs530x_priv *cs530x)
                                     dev_id);
        }
 
+       dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x (%d in %d out)\n", dev_id, rev,
+               cs530x->num_adcs, cs530x->num_dacs);
+
        return 0;
 }
 
@@ -870,6 +1212,9 @@ static int cs530x_parse_device_properties(struct cs530x_priv *cs530x)
                        val |= CS530X_IN12_HIZ;
 
                return regmap_set_bits(regmap, CS530X_IN_HIZ, val);
+       case 0:
+               /* No ADCs */
+               return 0;
        default:
                return dev_err_probe(dev, -EINVAL,
                                     "Invalid number of adcs %d\n",
@@ -932,7 +1277,15 @@ int cs530x_probe(struct cs530x_priv *cs530x)
        if (ret)
                goto err_reset;
 
-       cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;
+       if (cs530x->num_adcs) {
+               cs530x->dev_dai->capture.channels_min = 2;
+               cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;
+       }
+
+       if (cs530x->num_dacs) {
+               cs530x->dev_dai->playback.channels_min = 2;
+               cs530x->dev_dai->playback.channels_max = cs530x->num_dacs;
+       }
 
        ret = devm_snd_soc_register_component(dev,
                                              &soc_component_dev_cs530x,
index f7640161c77f1457466167a340d67afde53c38c0..52fb148df36ae3c77fa11a22bca2fa7dc48264d4 100644 (file)
 #include <linux/regulator/consumer.h>
 
 /* Devices */
+#define CS530X_2CH_CODEC_DEV_ID                 0x4282
+#define CS530X_2CH_DAC_DEV_ID           0x4302
+#define CS530X_4CH_DAC_DEV_ID           0x4304
+#define CS530X_8CH_DAC_DEV_ID           0x4308
 #define CS530X_2CH_ADC_DEV_ID           0x5302
 #define CS530X_4CH_ADC_DEV_ID           0x5304
 #define CS530X_8CH_ADC_DEV_ID           0x5308
 #define CS530X_IN_VOL_CTRL4_1          0x000009E
 #define CS530X_IN_VOL_CTRL5            0x00000A0
 
+#define CS530X_OUT_ENABLES             0x00000C0
+#define CS530X_OUT_RAMP_SUM            0x00000C2
+#define CS530X_OUT_DEEMPH              0x00000C4
+#define CS530X_OUT_FILTER              0x00000C6
+#define CS530X_OUT_INV                 0x00000CA
+#define CS530X_OUT_VOL_CTRL1_0         0x00000D0
+#define CS530X_OUT_VOL_CTRL1_1         0x00000D2
+#define CS530X_OUT_VOL_CTRL2_0         0x00000D4
+#define CS530X_OUT_VOL_CTRL2_1         0x00000D6
+#define CS530X_OUT_VOL_CTRL3_0         0x00000D8
+#define CS530X_OUT_VOL_CTRL3_1         0x00000DA
+#define CS530X_OUT_VOL_CTRL4_0         0x00000DC
+#define CS530X_OUT_VOL_CTRL4_1         0x00000DE
+#define CS530X_OUT_VOL_CTRL5           0x00000E0
+
 #define CS530X_PAD_FN                  0x0003D24
 #define CS530X_PAD_LVL                 0x0003D28
 
 #define CS530X_14_15_TDM_SLOT_MASK     GENMASK(15, 14)
 #define CS530X_14_15_TDM_SLOT_VAL      7
 
-/* IN_RAMP_SUM */
+/* IN_RAMP_SUM and OUT_RAMP_SUM */
 #define CS530X_RAMP_RATE_INC_SHIFT     0
 #define CS530X_RAMP_RATE_DEC_SHIFT     4
-#define CS530X_IN_SUM_MODE_SHIFT       13
+#define CS530X_INOUT_SUM_MODE_SHIFT    13
 
-/* IN_FILTER */
-#define CS530X_IN_FILTER_SHIFT         8
-#define CS530X_IN_HPF_EN_SHIFT         12
+/* IN_FILTER and OUT_FILTER */
+#define CS530X_INOUT_FILTER_SHIFT              8
+#define CS530X_INOUT_HPF_EN_SHIFT              12
 
 /* IN_HIZ */
 #define CS530X_IN12_HIZ                        BIT(0)
 #define CS530X_IN56_HIZ                        BIT(2)
 #define CS530X_IN78_HIZ                        BIT(3)
 
-/* IN_INV */
-#define CS530X_IN1_INV_SHIFT           0
-#define CS530X_IN2_INV_SHIFT           1
-#define CS530X_IN3_INV_SHIFT           2
-#define CS530X_IN4_INV_SHIFT           3
-#define CS530X_IN5_INV_SHIFT           4
-#define CS530X_IN6_INV_SHIFT           5
-#define CS530X_IN7_INV_SHIFT           6
-#define CS530X_IN8_INV_SHIFT           7
+/* IN_INV and OUT_INV */
+#define CS530X_INOUT1_INV_SHIFT                0
+#define CS530X_INOUT2_INV_SHIFT                1
+#define CS530X_INOUT3_INV_SHIFT                2
+#define CS530X_INOUT4_INV_SHIFT                3
+#define CS530X_INOUT5_INV_SHIFT                4
+#define CS530X_INOUT6_INV_SHIFT                5
+#define CS530X_INOUT7_INV_SHIFT                6
+#define CS530X_INOUT8_INV_SHIFT                7
 
-/* IN_VOL_CTLy_z */
-#define CS530X_IN_MUTE                 BIT(15)
+/* IN_VOL_CTLy_z and OUT_VOL_CTLy_z */
+#define CS530X_INOUT_MUTE                      BIT(15)
 
 /* IN_VOL_CTL5 */
 #define CS530X_IN_VU                   BIT(0)
 #define CS530X_CONFIG3_LVL             BIT(7)
 #define CS530X_CONFIG4_LVL             BIT(8)
 #define CS530X_CONFIG5_LVL             BIT(9)
+/* IN_VOL_CTL5 and OUT_VOL_CTL5 */
+#define CS530X_INOUT_VU                        BIT(0)
 
 /* System Clock Source */
 #define CS530X_SYSCLK_SRC_MCLK         0
 #define CS530X_NUM_SUPPLIES            2
 
 enum cs530x_type {
+       CS4282,
+       CS4302,
+       CS4304,
+       CS4308,
        CS5302,
        CS5304,
        CS5308,
@@ -210,6 +235,7 @@ struct cs530x_priv {
        int tdm_width;
        int tdm_slots;
        int adc_pairs_count;
+       int dac_pairs_count;
 
        struct gpio_desc *reset_gpio;
 };