Merge remote-tracking branches 'asoc/fix/rt5659', 'asoc/fix/sigmadsp', 'asoc/fix...
[cascardo/linux.git] / sound / soc / codecs / wm5110.c
index c04c0bc..97c0f1e 100644 (file)
@@ -360,15 +360,13 @@ static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
 
 static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
 {
-       struct reg_sequence clear_pga = {
-               ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80
-       };
+       unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
        int ret;
 
-       ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1);
+       ret = regmap_write(arizona->regmap, reg, 0x80);
        if (ret)
                dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
-                       clear_pga.reg, ret);
+                       reg, ret);
 
        return ret;
 }
@@ -439,18 +437,17 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
-       struct snd_soc_card *card = dapm->card;
        int ret;
 
        /*
         * PGA Volume is also used as part of the enable sequence, so
         * usage of it should be avoided whilst that is running.
         */
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -460,18 +457,17 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
-       struct snd_soc_card *card = dapm->card;
        int ret;
 
        /*
         * PGA Volume is also used as part of the enable sequence, so
         * usage of it should be avoided whilst that is running.
         */
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       snd_soc_dapm_mutex_lock(dapm);
 
        ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
 
-       mutex_unlock(&card->dapm_mutex);
+       snd_soc_dapm_mutex_unlock(dapm);
 
        return ret;
 }
@@ -575,6 +571,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
        SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
        SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
 
+#define WM5110_RXANC_INPUT_ROUTES(widget, name) \
+       { widget, NULL, name " NG Mux" }, \
+       { name " NG Internal", NULL, "RXANC NG Clock" }, \
+       { name " NG Internal", NULL, name " Channel" }, \
+       { name " NG External", NULL, "RXANC NG External Clock" }, \
+       { name " NG External", NULL, name " Channel" }, \
+       { name " NG Mux", "None", name " Channel" }, \
+       { name " NG Mux", "Internal", name " NG Internal" }, \
+       { name " NG Mux", "External", name " NG External" }, \
+       { name " Channel", "Left", name " Left Input" }, \
+       { name " Channel", "Combine", name " Left Input" }, \
+       { name " Channel", "Right", name " Right Input" }, \
+       { name " Channel", "Combine", name " Right Input" }, \
+       { name " Left Input", "IN1", "IN1L PGA" }, \
+       { name " Right Input", "IN1", "IN1R PGA" }, \
+       { name " Left Input", "IN2", "IN2L PGA" }, \
+       { name " Right Input", "IN2", "IN2R PGA" }, \
+       { name " Left Input", "IN3", "IN3L PGA" }, \
+       { name " Right Input", "IN3", "IN3R PGA" }, \
+       { name " Left Input", "IN4", "IN4L PGA" }, \
+       { name " Right Input", "IN4", "IN4R PGA" }
+
+#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \
+       { widget, NULL, name " ANC Source" }, \
+       { name " ANC Source", "RXANCL", "RXANCL" }, \
+       { name " ANC Source", "RXANCR", "RXANCR" }
+
 static const struct snd_kcontrol_new wm5110_snd_controls[] = {
 SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
 SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
@@ -639,6 +662,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
 SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
 SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
 
+SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START,
+             ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START,
+             ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START,
+             ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1),
+
 ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
@@ -995,6 +1027,31 @@ static const struct soc_enum wm5110_aec_loopback =
 static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
        SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
 
+static const struct snd_kcontrol_new wm5110_anc_input_mux[] = {
+       SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]),
+       SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]),
+       SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]),
+       SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new wm5110_anc_ng_mux =
+       SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum);
+
+static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
+       SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]),
+       SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]),
+       SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]),
+       SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]),
+       SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]),
+       SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]),
+       SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]),
+       SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]),
+       SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]),
+       SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]),
+       SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]),
+       SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]),
+};
+
 static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
                    0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
@@ -1185,6 +1242,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
                       &wm5110_aec_loopback_mux),
 
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+                  ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+                  ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+                &wm5110_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT,
+                  0, NULL, 0, arizona_anc_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT,
+                  0, NULL, 0, arizona_anc_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[7]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[8]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[9]),
+SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[10]),
+SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
+                &wm5110_output_anc_src[11]),
+
 SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
                     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
 SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
@@ -1690,6 +1806,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        { "Slim2 Capture", NULL, "SYSCLK" },
        { "Slim3 Capture", NULL, "SYSCLK" },
 
+       { "Voice Control DSP", NULL, "DSP3" },
+       { "Voice Control DSP", NULL, "SYSCLK" },
+
        { "IN1L PGA", NULL, "IN1L" },
        { "IN1R PGA", NULL, "IN1R" },
 
@@ -1838,6 +1957,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        { "SPKDAT2L", NULL, "OUT6L" },
        { "SPKDAT2R", NULL, "OUT6R" },
 
+       WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+       WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+       WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
+       WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
+
        { "MICSUPP", NULL, "SYSCLK" },
 
        { "DRC1 Signal Activity", NULL, "DRC1L" },
@@ -1996,12 +2131,65 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
                 },
                .ops = &arizona_simple_dai_ops,
        },
+       {
+               .name = "wm5110-cpu-voicectrl",
+               .capture = {
+                       .stream_name = "Voice Control CPU",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = WM5110_RATES,
+                       .formats = WM5110_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "wm5110-dsp-voicectrl",
+               .capture = {
+                       .stream_name = "Voice Control DSP",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = WM5110_RATES,
+                       .formats = WM5110_FORMATS,
+               },
+       },
 };
 
+static int wm5110_open(struct snd_compr_stream *stream)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+       struct arizona *arizona = priv->core.arizona;
+       int n_adsp;
+
+       if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
+               n_adsp = 2;
+       } else {
+               dev_err(arizona->dev,
+                       "No suitable compressed stream for DAI '%s'\n",
+                       rtd->codec_dai->name);
+               return -EINVAL;
+       }
+
+       return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
+static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
+{
+       struct wm5110_priv *florida = data;
+       int ret;
+
+       ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
+       if (ret == -ENODEV)
+               return IRQ_NONE;
+
+       return IRQ_HANDLED;
+}
+
 static int wm5110_codec_probe(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->core.arizona;
        int i, ret;
 
        priv->core.arizona->dapm = dapm;
@@ -2010,6 +2198,14 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
 
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+                                 "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+                                 priv);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+               return ret;
+       }
+
        for (i = 0; i < WM5110_NUM_ADSP; ++i) {
                ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
                if (ret)
@@ -2030,12 +2226,15 @@ err_adsp2_codec_probe:
        for (--i; i >= 0; --i)
                wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
 
+       arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
        return ret;
 }
 
 static int wm5110_codec_remove(struct snd_soc_codec *codec)
 {
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->core.arizona;
        int i;
 
        for (i = 0; i < WM5110_NUM_ADSP; ++i)
@@ -2043,6 +2242,8 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
 
        priv->core.arizona->dapm = NULL;
 
+       arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
        return 0;
 }
 
@@ -2088,6 +2289,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
        .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
 };
 
+static struct snd_compr_ops wm5110_compr_ops = {
+       .open = wm5110_open,
+       .free = wm_adsp_compr_free,
+       .set_params = wm_adsp_compr_set_params,
+       .get_caps = wm_adsp_compr_get_caps,
+       .trigger = wm_adsp_compr_trigger,
+       .pointer = wm_adsp_compr_pointer,
+       .copy = wm_adsp_compr_copy,
+};
+
+static struct snd_soc_platform_driver wm5110_compr_platform = {
+       .compr_ops = &wm5110_compr_ops,
+};
+
 static int wm5110_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -2148,12 +2363,26 @@ static int wm5110_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_idle(&pdev->dev);
 
-       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
+       ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+               goto error;
+       }
+
+       ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
                                      wm5110_dai, ARRAY_SIZE(wm5110_dai));
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+               snd_soc_unregister_platform(&pdev->dev);
+       }
+
+error:
+       return ret;
 }
 
 static int wm5110_remove(struct platform_device *pdev)
 {
+       snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);