summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/cs530x.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/cs530x.c')
-rw-r--r--sound/soc/codecs/cs530x.c528
1 files changed, 450 insertions, 78 deletions
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
index b9eff240b929..18b5ff75feec 100644
--- a/sound/soc/codecs/cs530x.c
+++ b/sound/soc/codecs/cs530x.c
@@ -2,28 +2,26 @@
//
// CS530x CODEC driver
//
-// Copyright (C) 2024 Cirrus Logic, Inc. and
-// Cirrus Logic International Semiconductor Ltd.
+// Copyright (C) 2024-2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
-#include <sound/core.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
-#include <sound/initval.h>
#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "cs530x.h"
-#define CS530X_MAX_ADC_CH 8
-#define CS530X_MIN_ADC_CH 2
-
static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = {
"vdd-a",
"vdd-io",
@@ -48,6 +46,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 },
};
@@ -73,6 +83,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;
@@ -97,6 +120,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);
@@ -104,10 +128,10 @@ static bool cs530x_writeable_register(struct device *dev, unsigned int reg)
}
static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
struct regmap *regmap = cs530x->regmap;
int ret;
@@ -118,8 +142,8 @@ static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol,
if (ret)
goto volsw_err;
- /* Write IN_VU bit for the volume change to take effect */
- regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU);
+ /* Write INOUT_VU bit for the volume change to take effect */
+ regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_INOUT_VU);
volsw_err:
snd_soc_dapm_mutex_unlock(dapm);
@@ -129,7 +153,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",
@@ -137,24 +161,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",
@@ -162,13 +198,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",
@@ -193,8 +236,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[] = {
@@ -203,8 +246,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[] = {
@@ -217,14 +260,14 @@ 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,
- struct snd_kcontrol *kcontrol, int event)
+ 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);
@@ -236,24 +279,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;
}
@@ -263,18 +392,24 @@ static int cs530x_adc_event(struct snd_soc_dapm_widget *w,
static const struct snd_kcontrol_new adc12_ctrl =
SOC_DAPM_SINGLE_VIRT("Switch", 1);
-
static const struct snd_kcontrol_new adc34_ctrl =
SOC_DAPM_SINGLE_VIRT("Switch", 1);
-
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[] = {
@@ -291,7 +426,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),
};
@@ -387,7 +522,7 @@ static const struct snd_soc_dapm_route adc_ch5_8_routes[] = {
static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
snd_soc_add_component_controls(component,
cs530x_in_1_to_2_controls,
@@ -402,7 +537,7 @@ static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)
static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
snd_soc_add_component_controls(component,
cs530x_in_3_to_4_controls,
@@ -415,6 +550,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_to_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_to_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);
@@ -450,7 +732,7 @@ static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)
}
static int cs530x_set_pll_refclk(struct snd_soc_component *component,
- const unsigned int freq)
+ const unsigned int freq)
{
struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
struct regmap *regmap = priv->regmap;
@@ -492,37 +774,35 @@ static int cs530x_hw_params(struct snd_pcm_substream *substream,
int ret = 0, fs = params_rate(params), bclk;
unsigned int fs_val;
-
switch (fs) {
case 32000:
fs_val = CS530X_FS_32K;
break;
case 44100:
case 48000:
- fs_val = CS530X_FS_48K_44P1K;
+ fs_val = CS530X_FS_44P1K_48K;
break;
case 88200:
case 96000:
- fs_val = CS530X_FS_96K_88P2K;
+ fs_val = CS530X_FS_88P2K_96K;
break;
case 176400:
case 192000:
- fs_val = CS530X_FS_192K_176P4K;
+ fs_val = CS530X_FS_176P4K_192K;
break;
case 356800:
case 384000:
- fs_val = CS530X_FS_384K_356P8K;
+ fs_val = CS530X_FS_356P8K_384K;
break;
case 705600:
case 768000:
- fs_val = CS530X_FS_768K_705P6K;
+ fs_val = CS530X_FS_705P6K_768K;
break;
default:
dev_err(component->dev, "Invalid sample rate %d\n", fs);
return -EINVAL;
}
- cs530x->fs = fs;
regmap_update_bits(regmap, CS530X_CLK_CFG_1,
CS530X_SAMPLE_RATE_MASK, fs_val);
@@ -540,7 +820,7 @@ static int cs530x_hw_params(struct snd_pcm_substream *substream,
}
if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0,
- CS530X_PLL_REFCLK_SRC_MASK)) {
+ CS530X_PLL_REFCLK_SRC_MASK)) {
ret = cs530x_set_pll_refclk(component, bclk);
if (ret)
return ret;
@@ -614,7 +894,7 @@ static bool cs530x_check_mclk_freq(struct snd_soc_component *component,
}
static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots, int slot_width)
+ unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_component *component = dai->component;
struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
@@ -675,8 +955,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,
},
@@ -686,8 +969,8 @@ static const struct snd_soc_dai_driver cs530x_dai = {
};
static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,
- int source, unsigned int freq_in,
- unsigned int freq_out)
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
struct regmap *regmap = cs530x->regmap;
@@ -724,13 +1007,50 @@ static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,
static int cs530x_component_probe(struct snd_soc_component *component)
{
struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
int num_widgets;
snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets,
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;
@@ -743,7 +1063,6 @@ static int cs530x_component_probe(struct snd_soc_component *component)
cs530x_in_sum_4ch_controls,
num_widgets);
break;
-
case CS5308:
cs530x_add_12_adc_widgets(component);
cs530x_add_34_adc_widgets(component);
@@ -775,25 +1094,26 @@ static int cs530x_component_probe(struct snd_soc_component *component)
}
static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
- int source, unsigned int freq, int dir)
+ int source, unsigned int freq, int dir)
{
struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
struct regmap *regmap = cs530x->regmap;
switch (source) {
case CS530X_SYSCLK_SRC_MCLK:
- if (freq != 24560000 && freq != 22572000) {
- dev_err(component->dev, "Invalid MCLK source rate %d\n",
- freq);
+ switch (freq) {
+ case CS530X_SYSCLK_REF_45_1MHZ:
+ case CS530X_SYSCLK_REF_49_1MHZ:
+ break;
+ default:
+ dev_err(component->dev, "Invalid MCLK source rate %d\n", freq);
return -EINVAL;
}
-
- cs530x->mclk_rate = freq;
break;
case CS530X_SYSCLK_SRC_PLL:
break;
default:
- dev_err(component->dev, "Invalid clock id %d\n", clk_id);
+ dev_err(component->dev, "Invalid sysclk source: %d\n", source);
return -EINVAL;
}
@@ -809,7 +1129,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs530x = {
.endianness = 1,
};
-const struct regmap_config cs530x_regmap = {
+const struct regmap_config cs530x_regmap_i2c = {
.reg_bits = 16,
.val_bits = 16,
@@ -821,7 +1141,27 @@ const struct regmap_config cs530x_regmap = {
.reg_defaults = cs530x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
};
-EXPORT_SYMBOL_NS_GPL(cs530x_regmap, "SND_SOC_CS530X");
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap_i2c, "SND_SOC_CS530X");
+
+const struct regmap_config cs530x_regmap_spi = {
+ .reg_bits = 16,
+ .pad_bits = 16,
+ .val_bits = 16,
+
+ .reg_stride = 2,
+
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS530X_MAX_REGISTER,
+ .writeable_reg = cs530x_writeable_register,
+ .readable_reg = cs530x_readable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs530x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
+};
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap_spi, "SND_SOC_CS530X");
static int cs530x_check_device_id(struct cs530x_priv *cs530x)
{
@@ -837,9 +1177,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;
@@ -854,6 +1205,15 @@ static int cs530x_check_device_id(struct cs530x_priv *cs530x)
dev_id);
}
+ if (cs530x->devtype != dev_id) {
+ dev_err(dev, "Read device ID 0x%x is not the expected devtype 0x%x\n",
+ dev_id, cs530x->devtype);
+ return -EINVAL;
+ }
+
+ 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;
}
@@ -882,6 +1242,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",
@@ -895,8 +1258,8 @@ int cs530x_probe(struct cs530x_priv *cs530x)
int ret, i;
cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai,
- sizeof(*(cs530x->dev_dai)),
- GFP_KERNEL);
+ sizeof(*(cs530x->dev_dai)),
+ GFP_KERNEL);
if (!cs530x->dev_dai)
return -ENOMEM;
@@ -914,10 +1277,10 @@ int cs530x_probe(struct cs530x_priv *cs530x)
return dev_err_probe(dev, ret, "Failed to enable supplies");
cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_HIGH);
+ GPIOD_OUT_HIGH);
if (IS_ERR(cs530x->reset_gpio)) {
ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio),
- "Reset gpio not available\n");
+ "Reset gpio not available\n");
goto err_regulator;
}
@@ -944,10 +1307,19 @@ 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, cs530x->dev_dai, 1);
+ &soc_component_dev_cs530x,
+ cs530x->dev_dai, 1);
if (ret) {
dev_err_probe(dev, ret, "Can't register cs530x component\n");
goto err_reset;