|
@@ -647,6 +647,10 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
|
|
|
eq_tlv),
|
|
|
};
|
|
|
|
|
|
+static const struct snd_kcontrol_new wm8958_snd_controls[] = {
|
|
|
+SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
|
|
|
+};
|
|
|
+
|
|
|
static int clk_sys_event(struct snd_soc_dapm_widget *w,
|
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
|
{
|
|
@@ -953,14 +957,47 @@ static const struct snd_kcontrol_new aif2adc_mux =
|
|
|
SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum);
|
|
|
|
|
|
static const char *aif3adc_text[] = {
|
|
|
- "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT",
|
|
|
+ "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM",
|
|
|
};
|
|
|
|
|
|
-static const struct soc_enum aif3adc_enum =
|
|
|
+static const struct soc_enum wm8994_aif3adc_enum =
|
|
|
SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text);
|
|
|
|
|
|
-static const struct snd_kcontrol_new aif3adc_mux =
|
|
|
- SOC_DAPM_ENUM("AIF3ADC Mux", aif3adc_enum);
|
|
|
+static const struct snd_kcontrol_new wm8994_aif3adc_mux =
|
|
|
+ SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum);
|
|
|
+
|
|
|
+static const struct soc_enum wm8958_aif3adc_enum =
|
|
|
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 4, aif3adc_text);
|
|
|
+
|
|
|
+static const struct snd_kcontrol_new wm8958_aif3adc_mux =
|
|
|
+ SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum);
|
|
|
+
|
|
|
+static const char *mono_pcm_out_text[] = {
|
|
|
+ "None", "AIF2ADCL", "AIF2ADCR",
|
|
|
+};
|
|
|
+
|
|
|
+static const struct soc_enum mono_pcm_out_enum =
|
|
|
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 9, 3, mono_pcm_out_text);
|
|
|
+
|
|
|
+static const struct snd_kcontrol_new mono_pcm_out_mux =
|
|
|
+ SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum);
|
|
|
+
|
|
|
+static const char *aif2dac_src_text[] = {
|
|
|
+ "AIF2", "AIF3",
|
|
|
+};
|
|
|
+
|
|
|
+/* Note that these two control shouldn't be simultaneously switched to AIF3 */
|
|
|
+static const struct soc_enum aif2dacl_src_enum =
|
|
|
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 7, 2, aif2dac_src_text);
|
|
|
+
|
|
|
+static const struct snd_kcontrol_new aif2dacl_src_mux =
|
|
|
+ SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum);
|
|
|
+
|
|
|
+static const struct soc_enum aif2dacr_src_enum =
|
|
|
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 8, 2, aif2dac_src_text);
|
|
|
+
|
|
|
+static const struct snd_kcontrol_new aif2dacr_src_mux =
|
|
|
+ SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum);
|
|
|
|
|
|
static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
|
|
|
SND_SOC_DAPM_INPUT("DMIC1DAT"),
|
|
@@ -1034,7 +1071,6 @@ SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
|
SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux),
|
|
|
SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux),
|
|
|
SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux),
|
|
|
-SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &aif3adc_mux),
|
|
|
|
|
|
SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
|
|
|
SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
@@ -1072,8 +1108,18 @@ SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
|
|
|
SND_SOC_DAPM_POST("Debug log", post_ev),
|
|
|
};
|
|
|
|
|
|
-static const struct snd_soc_dapm_route intercon[] = {
|
|
|
+static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = {
|
|
|
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux),
|
|
|
+};
|
|
|
|
|
|
+static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = {
|
|
|
+SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux),
|
|
|
+SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux),
|
|
|
+SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux),
|
|
|
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8958_aif3adc_mux),
|
|
|
+};
|
|
|
+
|
|
|
+static const struct snd_soc_dapm_route intercon[] = {
|
|
|
{ "CLK_SYS", NULL, "AIF1CLK", check_clk_sys },
|
|
|
{ "CLK_SYS", NULL, "AIF2CLK", check_clk_sys },
|
|
|
|
|
@@ -1181,9 +1227,6 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|
|
{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
|
|
|
{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
|
|
|
|
|
|
- { "AIF2DACL", NULL, "AIF2DAC Mux" },
|
|
|
- { "AIF2DACR", NULL, "AIF2DAC Mux" },
|
|
|
-
|
|
|
{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
|
|
|
{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
|
|
|
{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
|
|
@@ -1256,6 +1299,26 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|
|
{ "Right Headphone Mux", "DAC", "DAC1R" },
|
|
|
};
|
|
|
|
|
|
+static const struct snd_soc_dapm_route wm8994_intercon[] = {
|
|
|
+ { "AIF2DACL", NULL, "AIF2DAC Mux" },
|
|
|
+ { "AIF2DACR", NULL, "AIF2DAC Mux" },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct snd_soc_dapm_route wm8958_intercon[] = {
|
|
|
+ { "AIF2DACL", NULL, "AIF2DACL Mux" },
|
|
|
+ { "AIF2DACR", NULL, "AIF2DACR Mux" },
|
|
|
+
|
|
|
+ { "AIF2DACL Mux", "AIF2", "AIF2DAC Mux" },
|
|
|
+ { "AIF2DACL Mux", "AIF3", "AIF3DACDAT" },
|
|
|
+ { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" },
|
|
|
+ { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" },
|
|
|
+
|
|
|
+ { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" },
|
|
|
+ { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" },
|
|
|
+
|
|
|
+ { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" },
|
|
|
+};
|
|
|
+
|
|
|
/* The size in bits of the FLL divide multiplied by 10
|
|
|
* to allow rounding later */
|
|
|
#define FIXED_FLL_SIZE ((1 << 16) * 10)
|
|
@@ -1635,6 +1698,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
|
|
|
static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = dai->codec;
|
|
|
+ struct wm8994 *control = codec->control_data;
|
|
|
int ms_reg;
|
|
|
int aif1_reg;
|
|
|
int ms = 0;
|
|
@@ -1719,6 +1783,13 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ /* The AIF2 format configuration needs to be mirrored to AIF3
|
|
|
+ * on WM8958 if it's in use so just do it all the time. */
|
|
|
+ if (control->type == WM8958 && dai->id == 2)
|
|
|
+ snd_soc_update_bits(codec, WM8958_AIF3_CONTROL_1,
|
|
|
+ WM8994_AIF1_LRCLK_INV |
|
|
|
+ WM8958_AIF3_FMT_MASK, aif1);
|
|
|
+
|
|
|
snd_soc_update_bits(codec, aif1_reg,
|
|
|
WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV |
|
|
|
WM8994_AIF1_FMT_MASK,
|
|
@@ -1759,6 +1830,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = dai->codec;
|
|
|
+ struct wm8994 *control = codec->control_data;
|
|
|
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
|
|
int aif1_reg;
|
|
|
int bclk_reg;
|
|
@@ -1797,6 +1869,14 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
|
|
|
dev_dbg(codec->dev, "AIF2 using split LRCLK\n");
|
|
|
}
|
|
|
break;
|
|
|
+ case 3:
|
|
|
+ switch (control->type) {
|
|
|
+ case WM8958:
|
|
|
+ aif1_reg = WM8958_AIF3_CONTROL_1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
default:
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -1900,6 +1980,47 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_hw_params *params,
|
|
|
+ struct snd_soc_dai *dai)
|
|
|
+{
|
|
|
+ struct snd_soc_codec *codec = dai->codec;
|
|
|
+ struct wm8994 *control = codec->control_data;
|
|
|
+ int aif1_reg;
|
|
|
+ int aif1 = 0;
|
|
|
+
|
|
|
+ switch (dai->id) {
|
|
|
+ case 3:
|
|
|
+ switch (control->type) {
|
|
|
+ case WM8958:
|
|
|
+ aif1_reg = WM8958_AIF3_CONTROL_1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (params_format(params)) {
|
|
|
+ case SNDRV_PCM_FORMAT_S16_LE:
|
|
|
+ break;
|
|
|
+ case SNDRV_PCM_FORMAT_S20_3LE:
|
|
|
+ aif1 |= 0x20;
|
|
|
+ break;
|
|
|
+ case SNDRV_PCM_FORMAT_S24_LE:
|
|
|
+ aif1 |= 0x40;
|
|
|
+ break;
|
|
|
+ case SNDRV_PCM_FORMAT_S32_LE:
|
|
|
+ aif1 |= 0x60;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
|
|
|
+}
|
|
|
+
|
|
|
static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = codec_dai->codec;
|
|
@@ -1981,6 +2102,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
|
|
|
};
|
|
|
|
|
|
static struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
|
|
|
+ .hw_params = wm8994_aif3_hw_params,
|
|
|
.set_tristate = wm8994_set_tristate,
|
|
|
};
|
|
|
|
|
@@ -2511,9 +2633,35 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
|
|
ARRAY_SIZE(wm8994_snd_controls));
|
|
|
snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
|
|
|
ARRAY_SIZE(wm8994_dapm_widgets));
|
|
|
+
|
|
|
+ switch (control->type) {
|
|
|
+ case WM8994:
|
|
|
+ snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
|
|
|
+ ARRAY_SIZE(wm8994_specific_dapm_widgets));
|
|
|
+ break;
|
|
|
+ case WM8958:
|
|
|
+ snd_soc_add_controls(codec, wm8958_snd_controls,
|
|
|
+ ARRAY_SIZE(wm8958_snd_controls));
|
|
|
+ snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
|
|
|
+ ARRAY_SIZE(wm8958_dapm_widgets));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
wm_hubs_add_analogue_routes(codec, 0, 0);
|
|
|
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
|
|
|
|
|
|
+ switch (control->type) {
|
|
|
+ case WM8994:
|
|
|
+ snd_soc_dapm_add_routes(dapm, wm8994_intercon,
|
|
|
+ ARRAY_SIZE(wm8994_intercon));
|
|
|
+ break;
|
|
|
+ case WM8958:
|
|
|
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
|
|
|
+ ARRAY_SIZE(wm8958_intercon));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
err_irq:
|