aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt42
-rw-r--r--Documentation/devicetree/bindings/sound/tlv320aic31xx.txt9
-rw-r--r--include/uapi/sound/asoc.h35
-rw-r--r--sound/soc/codecs/tas5086.c2
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c216
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h2
-rw-r--r--sound/soc/codecs/tlv320dac33.c17
-rw-r--r--sound/soc/soc-topology.c115
-rw-r--r--sound/soc/tegra/Kconfig11
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c2
-rw-r--r--sound/soc/tegra/tegra_sgtl5000.c212
12 files changed, 585 insertions, 80 deletions
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt
new file mode 100644
index 000000000000..5da7da4ea07a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-sgtl5000.txt
@@ -0,0 +1,42 @@
+NVIDIA Tegra audio complex, with SGTL5000 CODEC
+
+Required properties:
+- compatible : "nvidia,tegra-audio-sgtl5000"
+- clocks : Must contain an entry for each entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names : Must include the following entries:
+ - pll_a
+ - pll_a_out0
+ - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
+- nvidia,model : The user-visible name of this sound complex.
+- nvidia,audio-routing : A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the connection's sink,
+ the second being the connection's source. Valid names for sources and
+ sinks are the SGTL5000's pins (as documented in its binding), and the jacks
+ on the board:
+
+ * Headphone Jack
+ * Line In Jack
+ * Mic Jack
+
+- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
+ connected to the CODEC.
+- nvidia,audio-codec : The phandle of the SGTL5000 audio codec.
+
+Example:
+
+sound {
+ compatible = "toradex,tegra-audio-sgtl5000-apalis_t30",
+ "nvidia,tegra-audio-sgtl5000";
+ nvidia,model = "Toradex Apalis T30";
+ nvidia,audio-routing =
+ "Headphone Jack", "HP_OUT",
+ "LINE_IN", "Line In Jack",
+ "MIC_IN", "Mic Jack";
+ nvidia,i2s-controller = <&tegra_i2s2>;
+ nvidia,audio-codec = <&sgtl5000>;
+ clocks = <&tegra_car TEGRA30_CLK_PLL_A>,
+ <&tegra_car TEGRA30_CLK_PLL_A_OUT0>,
+ <&tegra_car TEGRA30_CLK_EXTERN1>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
+};
diff --git a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
index eff12be5e789..9340d2ddcc54 100644
--- a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
+++ b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
@@ -11,6 +11,7 @@ Required properties:
"ti,tlv320aic3110" - TLV320AIC3110 (stereo speaker amp, no MiniDSP)
"ti,tlv320aic3120" - TLV320AIC3120 (mono speaker amp, MiniDSP)
"ti,tlv320aic3111" - TLV320AIC3111 (stereo speaker amp, MiniDSP)
+ "ti,tlv320dac3100" - TLV320DAC3100 (no ADC, mono speaker amp, no MiniDSP)
- reg - <int> - I2C slave address
- HPVDD-supply, SPRVDD-supply, SPLVDD-supply, AVDD-supply, IOVDD-supply,
@@ -37,9 +38,11 @@ CODEC output pins:
* MICBIAS
CODEC input pins:
- * MIC1LP
- * MIC1RP
- * MIC1LM
+ * MIC1LP, devices with ADC
+ * MIC1RP, devices with ADC
+ * MIC1LM, devices with ADC
+ * AIN1, devices without ADC
+ * AIN2, devices without ADC
The pins can be used in referring sound node's audio-routing property.
diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h
index e4701a3c6331..33d00a4ce656 100644
--- a/include/uapi/sound/asoc.h
+++ b/include/uapi/sound/asoc.h
@@ -83,7 +83,7 @@
#define SND_SOC_TPLG_NUM_TEXTS 16
/* ABI version */
-#define SND_SOC_TPLG_ABI_VERSION 0x4
+#define SND_SOC_TPLG_ABI_VERSION 0x5
/* Max size of TLV data */
#define SND_SOC_TPLG_TLV_SIZE 32
@@ -105,7 +105,8 @@
#define SND_SOC_TPLG_TYPE_CODEC_LINK 9
#define SND_SOC_TPLG_TYPE_BACKEND_LINK 10
#define SND_SOC_TPLG_TYPE_PDATA 11
-#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_PDATA
+#define SND_SOC_TPLG_TYPE_BE_DAI 12
+#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_BE_DAI
/* vendor block IDs - please add new vendor types to end */
#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000
@@ -124,6 +125,11 @@
#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4
#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5
+/* BE DAI flags */
+#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES (1 << 0)
+#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1)
+#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2)
+
/*
* Block Header.
* This header precedes all object and object arrays below.
@@ -251,6 +257,7 @@ struct snd_soc_tplg_stream_caps {
__le32 period_size_max; /* max period size bytes */
__le32 buffer_size_min; /* min buffer size bytes */
__le32 buffer_size_max; /* max buffer size bytes */
+ __le32 sig_bits; /* number of bits of content */
} __attribute__((packed));
/*
@@ -285,6 +292,8 @@ struct snd_soc_tplg_manifest {
__le32 graph_elems; /* number of graph elements */
__le32 pcm_elems; /* number of PCM elements */
__le32 dai_link_elems; /* number of DAI link elements */
+ __le32 be_dai_elems; /* number of BE DAI elements */
+ __le32 reserved[20]; /* reserved for new ABI element types */
struct snd_soc_tplg_private priv;
} __attribute__((packed));
@@ -450,4 +459,26 @@ struct snd_soc_tplg_link_config {
struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
__le32 num_streams; /* number of streams */
} __attribute__((packed));
+
+/*
+ * Describes SW/FW specific features of BE DAI.
+ *
+ * File block representation for BE DAI :-
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_hdr | 1 |
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_be_dai | N |
+ * +-----------------------------------+-----+
+ */
+struct snd_soc_tplg_be_dai {
+ __le32 size; /* in bytes of this structure */
+ char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */
+ __le32 dai_id; /* unique ID - used to match */
+ __le32 playback; /* supports playback mode */
+ __le32 capture; /* supports capture mode */
+ struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */
+ __le32 flag_mask; /* bitmask of flags to configure */
+ __le32 flags; /* SND_SOC_TPLG_DAI_FLGBIT_* */
+ struct snd_soc_tplg_private priv;
+} __attribute__((packed));
#endif
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index c297b9fc8bf6..b7de857abb16 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -387,7 +387,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
priv->mclk / priv->rate);
if (val < 0) {
- dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
+ dev_err(codec->dev, "Invalid MCLK / Fs ratio\n");
return -EINVAL;
}
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index e46fb472e48d..be1a64bfd320 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -273,10 +273,20 @@ static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
/*
* controls to be exported to the user space
*/
-static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
+static const struct snd_kcontrol_new common31xx_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
+ SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 2, 1, 0),
+ SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
+ AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
+
+ SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
+ AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
adc_fgain_tlv),
@@ -286,14 +296,6 @@ static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
119, 0, mic_pga_tlv),
-
- SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
- AIC31XX_HPRGAIN, 2, 1, 0),
- SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
- AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
-
- SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
- AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
};
static const struct snd_kcontrol_new aic311x_snd_controls[] = {
@@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static const struct snd_kcontrol_new left_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
};
-static const struct snd_kcontrol_new right_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
};
+static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
+ SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
+ SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
+ SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
+ SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
+ SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
+};
+
static const struct snd_kcontrol_new p_term_mic1lp =
SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
@@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",
@@ -473,14 +486,7 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- /* Output Mixers */
- SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
- left_output_switches,
- ARRAY_SIZE(left_output_switches)),
- SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
- right_output_switches,
- ARRAY_SIZE(right_output_switches)),
-
+ /* HP */
SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
&aic31xx_dapm_hpl_switch),
SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
@@ -494,10 +500,34 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
NULL, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
- /* ADC */
- SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
- aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_POST_PMD),
+ /* Mic Bias */
+ SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+ dac31xx_left_output_switches,
+ ARRAY_SIZE(dac31xx_left_output_switches)),
+ SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+ dac31xx_right_output_switches,
+ ARRAY_SIZE(dac31xx_right_output_switches)),
+};
+
+static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1LP"),
+ SND_SOC_DAPM_INPUT("MIC1RP"),
+ SND_SOC_DAPM_INPUT("MIC1LM"),
/* Input Selection to MIC_PGA */
SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
@@ -507,24 +537,25 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
&p_term_mic1lm),
+ /* ADC */
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
+ aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
&m_term_mic1lm),
+
/* Enabling & Disabling MIC Gain Ctl */
SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
7, 1, NULL, 0),
- /* Mic Bias */
- SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-
- /* Outputs */
- SND_SOC_DAPM_OUTPUT("HPL"),
- SND_SOC_DAPM_OUTPUT("HPR"),
-
- /* Inputs */
- SND_SOC_DAPM_INPUT("MIC1LP"),
- SND_SOC_DAPM_INPUT("MIC1RP"),
- SND_SOC_DAPM_INPUT("MIC1LM"),
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+ aic31xx_left_output_switches,
+ ARRAY_SIZE(aic31xx_left_output_switches)),
+ SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+ aic31xx_right_output_switches,
+ ARRAY_SIZE(aic31xx_right_output_switches)),
};
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
@@ -554,7 +585,7 @@ static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route
-aic31xx_audio_map[] = {
+common31xx_audio_map[] = {
/* DAC Input Routing */
{"DAC Left Input", "Left Data", "DAC IN"},
{"DAC Left Input", "Right Data", "DAC IN"},
@@ -565,6 +596,31 @@ aic31xx_audio_map[] = {
{"DAC Left", NULL, "DAC Left Input"},
{"DAC Right", NULL, "DAC Right Input"},
+ /* HPL path */
+ {"HP Left", "Switch", "Output Left"},
+ {"HPL Driver", NULL, "HP Left"},
+ {"HPL", NULL, "HPL Driver"},
+
+ /* HPR path */
+ {"HP Right", "Switch", "Output Right"},
+ {"HPR Driver", NULL, "HP Right"},
+ {"HPR", NULL, "HPR Driver"},
+};
+
+static const struct snd_soc_dapm_route
+dac31xx_audio_map[] = {
+ /* Left Output */
+ {"Output Left", "From Left DAC", "DAC Left"},
+ {"Output Left", "From AIN1", "AIN1"},
+ {"Output Left", "From AIN2", "AIN2"},
+
+ /* Right Output */
+ {"Output Right", "From Right DAC", "DAC Right"},
+ {"Output Right", "From AIN2", "AIN2"},
+};
+
+static const struct snd_soc_dapm_route
+aic31xx_audio_map[] = {
/* Mic input */
{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
@@ -595,16 +651,6 @@ aic31xx_audio_map[] = {
/* Right Output */
{"Output Right", "From Right DAC", "DAC Right"},
{"Output Right", "From MIC1RP", "MIC1RP"},
-
- /* HPL path */
- {"HP Left", "Switch", "Output Left"},
- {"HPL Driver", NULL, "HP Left"},
- {"HPL", NULL, "HPL Driver"},
-
- /* HPR path */
- {"HP Right", "Switch", "Output Right"},
- {"HPR Driver", NULL, "HP Right"},
- {"HPR", NULL, "HPR Driver"},
};
static const struct snd_soc_dapm_route
@@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec)
int ret = 0;
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))
+ ret = snd_soc_add_codec_controls(
+ codec, aic31xx_snd_controls,
+ ARRAY_SIZE(aic31xx_snd_controls));
+ if (ret)
+ return ret;
+
if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
ret = snd_soc_add_codec_controls(
codec, aic311x_snd_controls,
@@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec)
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
int ret = 0;
+ if (aic31xx->pdata.codec_type & DAC31XX_BIT) {
+ ret = snd_soc_dapm_new_controls(
+ dapm, dac31xx_dapm_widgets,
+ ARRAY_SIZE(dac31xx_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
+ ARRAY_SIZE(dac31xx_audio_map));
+ if (ret)
+ return ret;
+ } else {
+ ret = snd_soc_dapm_new_controls(
+ dapm, aic31xx_dapm_widgets,
+ ARRAY_SIZE(aic31xx_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
+ ARRAY_SIZE(aic31xx_audio_map));
+ if (ret)
+ return ret;
+ }
+
if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
ret = snd_soc_dapm_new_controls(
dapm, aic311x_dapm_widgets,
@@ -1115,12 +1192,12 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
.suspend_bias_off = true,
.component_driver = {
- .controls = aic31xx_snd_controls,
- .num_controls = ARRAY_SIZE(aic31xx_snd_controls),
- .dapm_widgets = aic31xx_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(aic31xx_dapm_widgets),
- .dapm_routes = aic31xx_audio_map,
- .num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map),
+ .controls = common31xx_snd_controls,
+ .num_controls = ARRAY_SIZE(common31xx_snd_controls),
+ .dapm_widgets = common31xx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets),
+ .dapm_routes = common31xx_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(common31xx_audio_map),
},
};
@@ -1131,19 +1208,34 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.digital_mute = aic31xx_dac_mute,
};
+static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
+ {
+ .name = "tlv32dac31xx-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC31XX_RATES,
+ .formats = AIC31XX_FORMATS,
+ },
+ .ops = &aic31xx_dai_ops,
+ .symmetric_rates = 1,
+ }
+};
+
static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
{
.name = "tlv320aic31xx-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
@@ -1261,9 +1353,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
- aic31xx_dai_driver,
- ARRAY_SIZE(aic31xx_dai_driver));
+ if (aic31xx->pdata.codec_type & DAC31XX_BIT)
+ return snd_soc_register_codec(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ dac31xx_dai_driver,
+ ARRAY_SIZE(dac31xx_dai_driver));
+ else
+ return snd_soc_register_codec(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ aic31xx_dai_driver,
+ ARRAY_SIZE(aic31xx_dai_driver));
}
static int aic31xx_i2c_remove(struct i2c_client *i2c)
@@ -1279,6 +1378,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
{ "tlv320aic3110", AIC3110 },
{ "tlv320aic3120", AIC3120 },
{ "tlv320aic3111", AIC3111 },
+ { "tlv320dac3100", DAC3100 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index ac9b146526eb..5acd5b69fb83 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -24,12 +24,14 @@
#define AIC31XX_STEREO_CLASS_D_BIT 0x1
#define AIC31XX_MINIDSP_BIT 0x2
+#define DAC31XX_BIT 0x4
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
AIC3120 = AIC31XX_MINIDSP_BIT,
AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
+ DAC3100 = DAC31XX_BIT,
};
struct aic31xx_pdata {
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index d64eac74d1cc..7bcf01efdf9a 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -90,7 +90,6 @@ static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
struct tlv320dac33_priv {
struct mutex mutex;
- struct workqueue_struct *dac33_wq;
struct work_struct work;
struct snd_soc_codec *codec;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
@@ -771,7 +770,7 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
/* Do not schedule the workqueue in Mode7 */
if (dac33->fifo_mode != DAC33_FIFO_MODE7)
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
return IRQ_HANDLED;
}
@@ -1127,7 +1126,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (dac33->fifo_mode) {
dac33->state = DAC33_PREFILL;
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -1135,7 +1134,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (dac33->fifo_mode) {
dac33->state = DAC33_FLUSH;
- queue_work(dac33->dac33_wq, &dac33->work);
+ schedule_work(&dac33->work);
}
break;
default:
@@ -1410,14 +1409,6 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
dac33->irq = -1;
}
if (dac33->irq != -1) {
- /* Setup work queue */
- dac33->dac33_wq =
- create_singlethread_workqueue("tlv320dac33");
- if (dac33->dac33_wq == NULL) {
- free_irq(dac33->irq, codec);
- return -ENOMEM;
- }
-
INIT_WORK(&dac33->work, dac33_work);
}
}
@@ -1437,7 +1428,7 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
if (dac33->irq >= 0) {
free_irq(dac33->irq, dac33->codec);
- destroy_workqueue(dac33->dac33_wq);
+ flush_work(&dac33->work);
}
return 0;
}
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index ee7f15aa46fc..6b05047a4134 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -48,9 +48,10 @@
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
#define SOC_TPLG_PASS_PINS 6
+#define SOC_TPLG_PASS_BE_DAI 7
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
-#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS
+#define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI
struct soc_tplg {
const struct firmware *fw;
@@ -1475,6 +1476,7 @@ widget:
if (widget == NULL) {
dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
w->name);
+ ret = -ENOMEM;
goto hdr_err;
}
@@ -1554,6 +1556,25 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream,
stream->rate_min = caps->rate_min;
stream->rate_max = caps->rate_max;
stream->formats = caps->formats;
+ stream->sig_bits = caps->sig_bits;
+}
+
+static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
+ unsigned int flag_mask, unsigned int flags)
+{
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES)
+ dai_drv->symmetric_rates =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0;
+
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS)
+ dai_drv->symmetric_channels =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ?
+ 1 : 0;
+
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS)
+ dai_drv->symmetric_samplebits =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ?
+ 1 : 0;
}
static int soc_tplg_dai_create(struct soc_tplg *tplg,
@@ -1690,8 +1711,96 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
return 0;
}
+/* *
+ * soc_tplg_be_dai_config - Find and configure an existing BE DAI.
+ * @tplg: topology context
+ * @be: topology BE DAI configs.
+ *
+ * The BE dai should already be registered by the platform driver. The
+ * platform driver should specify the BE DAI name and ID for matching.
+ */
+static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
+ struct snd_soc_tplg_be_dai *be)
+{
+ struct snd_soc_dai_link_component dai_component = {0};
+ struct snd_soc_dai *dai;
+ struct snd_soc_dai_driver *dai_drv;
+ struct snd_soc_pcm_stream *stream;
+ struct snd_soc_tplg_stream_caps *caps;
+ int ret;
+
+ dai_component.dai_name = be->dai_name;
+ dai = snd_soc_find_dai(&dai_component);
+ if (!dai) {
+ dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n",
+ be->dai_name);
+ return -EINVAL;
+ }
+
+ if (be->dai_id != dai->id) {
+ dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n",
+ be->dai_name);
+ return -EINVAL;
+ }
+
+ dai_drv = dai->driver;
+ if (!dai_drv)
+ return -EINVAL;
+
+ if (be->playback) {
+ stream = &dai_drv->playback;
+ caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
+ set_stream_info(stream, caps);
+ }
+
+ if (be->capture) {
+ stream = &dai_drv->capture;
+ caps = &be->caps[SND_SOC_TPLG_STREAM_CAPTURE];
+ set_stream_info(stream, caps);
+ }
+
+ if (be->flag_mask)
+ set_dai_flags(dai_drv, be->flag_mask, be->flags);
+
+ /* pass control to component driver for optional further init */
+ ret = soc_tplg_dai_load(tplg, dai_drv);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_be_dai *be;
+ int count = hdr->count;
+ int i;
+
+ if (tplg->pass != SOC_TPLG_PASS_BE_DAI)
+ return 0;
+
+ /* config the existing BE DAIs */
+ for (i = 0; i < count; i++) {
+ be = (struct snd_soc_tplg_be_dai *)tplg->pos;
+ if (be->size != sizeof(*be)) {
+ dev_err(tplg->dev, "ASoC: invalid BE DAI size\n");
+ return -EINVAL;
+ }
+
+ soc_tplg_be_dai_config(tplg, be);
+ tplg->pos += (sizeof(*be) + be->priv.size);
+ }
+
+ dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count);
+ return 0;
+}
+
+
static int soc_tplg_manifest_load(struct soc_tplg *tplg,
- struct snd_soc_tplg_hdr *hdr)
+ struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_manifest *manifest;
@@ -1793,6 +1902,8 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
return soc_tplg_dapm_widget_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_PCM:
return soc_tplg_pcm_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_BE_DAI:
+ return soc_tplg_be_dai_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_MANIFEST:
return soc_tplg_manifest_load(tplg, hdr);
default:
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index a6768f832c6f..efbe8d4c019e 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -138,3 +138,14 @@ config SND_SOC_TEGRA_RT5677
help
Say Y or M here if you want to add support for SoC audio on Tegra
boards using the RT5677 codec, such as Ryu.
+
+config SND_SOC_TEGRA_SGTL5000
+ tristate "SoC Audio support for Tegra boards using a SGTL5000 codec"
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+ select SND_SOC_SGTL5000
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the SGTL5000 codec, such as Apalis T30, Apalis TK1 or
+ Colibri T30.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 9171655ad843..f214a3fd0024 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -26,6 +26,7 @@ snd-soc-tegra-wm9712-objs := tegra_wm9712.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
snd-soc-tegra-max98090-objs := tegra_max98090.o
+snd-soc-tegra-sgtl5000-objs := tegra_sgtl5000.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o
+obj-$(CONFIG_SND_SOC_TEGRA_SGTL5000) += snd-soc-tegra-sgtl5000.o \ No newline at end of file
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 773daecaa5e8..e5ef4e9c4ac5 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -1,5 +1,5 @@
/*
-* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
+* tegra_rt5640.c - Tegra machine ASoC driver for boards using RT5640 codec.
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c
new file mode 100644
index 000000000000..1e76869dd488
--- /dev/null
+++ b/sound/soc/tegra/tegra_sgtl5000.c
@@ -0,0 +1,212 @@
+/*
+ * tegra_sgtl5000.c - Tegra machine ASoC driver for boards using SGTL5000 codec
+ *
+ * Author: Marcel Ziswiler <marcel@ziswiler.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/sgtl5000.h"
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-sgtl5000"
+
+struct tegra_sgtl5000 {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int tegra_sgtl5000_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12288000;
+ break;
+ }
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_sgtl5000_ops = {
+ .hw_params = tegra_sgtl5000_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tegra_sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static struct snd_soc_dai_link tegra_sgtl5000_dai = {
+ .name = "sgtl5000",
+ .stream_name = "HiFi",
+ .codec_dai_name = "sgtl5000",
+ .ops = &tegra_sgtl5000_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_sgtl5000 = {
+ .name = "tegra-sgtl5000",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_sgtl5000_dai,
+ .num_links = 1,
+ .dapm_widgets = tegra_sgtl5000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_sgtl5000_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &snd_soc_tegra_sgtl5000;
+ struct tegra_sgtl5000 *machine;
+ int ret;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000),
+ GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n");
+ return -ENOMEM;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_sgtl5000_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!tegra_sgtl5000_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_sgtl5000_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!tegra_sgtl5000_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing/invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_sgtl5000_dai.platform_of_node = tegra_sgtl5000_dai.cpu_of_node;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err:
+ return ret;
+}
+
+static int tegra_sgtl5000_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ return ret;
+}
+
+static const struct of_device_id tegra_sgtl5000_of_match[] = {
+ { .compatible = "nvidia,tegra-audio-sgtl5000", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver tegra_sgtl5000_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_sgtl5000_of_match,
+ },
+ .probe = tegra_sgtl5000_driver_probe,
+ .remove = tegra_sgtl5000_driver_remove,
+};
+module_platform_driver(tegra_sgtl5000_driver);
+
+MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>");
+MODULE_DESCRIPTION("Tegra SGTL5000 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_sgtl5000_of_match);