Merge tag 'asoc-v4.16-5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Feb 2018 20:11:09 +0000 (12:11 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Feb 2018 20:11:09 +0000 (12:11 -0800)
Pull more ASoC updates from Mark Brown:
 "With the merge window having been delayed for another week here's
  another batch of updates that came in during that week.

  There's a few important fixes in here, mainly a fix for I/O on a
  number of devices caused by some of the component rework and a fix for
  a potential issue if more than one component in a link provides
  compressed operations. The I/O fixes are particularly important as the
  problem causes a power regression on a number of OMAP platforms"

* tag 'asoc-v4.16-5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound: (22 commits)
  ASoC: stm32: add of dependency for stm32 drivers
  ASoC: mt8173-rt5650: fix child-node lookup
  ASoC: dapm: fix debugfs read using path->connected
  ASoC: compress: Fixup error messages
  ASoC: compress: Remove some extraneous blank lines
  ASoC: compress: Correct handling of copy callback
  ASoC: Intel: kbl: Enable mclk and ssp sclk early
  ASoC: Intel: Skylake: Add extended I2S config blob support in Clock driver
  ASoC: Intel: Skylake: Add ssp clock driver
  ASoC: Fix twl4030 and 6040 regression by adding back read and write
  ASoC: sun8i-codec: Add ADC support for a33
  ASoC: rockchip: Use dummy_dai for rt5514 dsp dailink
  ASoC: soc-pcm: rename .pmdown_time to .use_pmdown_time for Component
  ASoC: ak4613: call dummy write for PW_MGMT1/3 when Playback
  ASoC: soc-pcm: don't call flush_delayed_work() many times in soc_pcm_private_free()
  ASoC: soc-core: snd_soc_rtdcom_lookup() cares component driver name
  ASoC: sam9x5_wm8731: Drop 'ASoC' prefix from error messages
  ASoC: sam9g20_wm8731: use dev_*() logging functions
  ASoC: max98373 Changed SPDX header in C++ comments style
  ASoC: dmic: Fix check of return value from read of 'num-channels'
  ...

29 files changed:
include/sound/soc.h
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/atmel/sam9x5_wm8731.c
sound/soc/codecs/ak4613.c
sound/soc/codecs/dmic.c
sound/soc/codecs/max98373.c
sound/soc/codecs/max98373.h
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/intel/Kconfig
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/kbl_rt5663_max98927.c
sound/soc/intel/skylake/Makefile
sound/soc/intel/skylake/skl-i2s.h
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-ssp-clk.c [new file with mode: 0644]
sound/soc/intel/skylake/skl-ssp-clk.h
sound/soc/intel/skylake/skl.h
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
sound/soc/mediatek/mt8173/mt8173-rt5650.c
sound/soc/rockchip/rk3399_gru_sound.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/stm/Kconfig
sound/soc/sunxi/sun8i-codec.c

index b655d987fbe767bb57787cf32af21dc2f68a96c3..747fd583b9dcf2afeb75e81de56a8ad6cc604507 100644 (file)
@@ -841,7 +841,7 @@ struct snd_soc_component_driver {
        /* bits */
        unsigned int idle_bias_on:1;
        unsigned int suspend_bias_off:1;
-       unsigned int pmdown_time:1; /* care pmdown_time at stop */
+       unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
        unsigned int endianness:1;
        unsigned int non_legacy_dai_naming:1;
 };
index d7469cdd90dc854a9f0db0dd5c666948ecf9bdf3..98f93e79c65472e33a161dca7b75968a7c4784d8 100644 (file)
@@ -110,16 +110,15 @@ static const struct snd_soc_dapm_route intercon[] = {
 static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct device *dev = rtd->dev;
        int ret;
 
-       printk(KERN_DEBUG
-                       "at91sam9g20ek_wm8731 "
-                       ": at91sam9g20ek_wm8731_init() called\n");
+       dev_dbg(dev, "%s called\n", __func__);
 
        ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
-               MCLK_RATE, SND_SOC_CLOCK_IN);
+                                    MCLK_RATE, SND_SOC_CLOCK_IN);
        if (ret < 0) {
-               printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+               dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
                return ret;
        }
 
@@ -179,21 +178,21 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
         */
        mclk = clk_get(NULL, "pck0");
        if (IS_ERR(mclk)) {
-               printk(KERN_ERR "ASoC: Failed to get MCLK\n");
+               dev_err(&pdev->dev, "Failed to get MCLK\n");
                ret = PTR_ERR(mclk);
                goto err;
        }
 
        pllb = clk_get(NULL, "pllb");
        if (IS_ERR(pllb)) {
-               printk(KERN_ERR "ASoC: Failed to get PLLB\n");
+               dev_err(&pdev->dev, "Failed to get PLLB\n");
                ret = PTR_ERR(pllb);
                goto err_mclk;
        }
        ret = clk_set_parent(mclk, pllb);
        clk_put(pllb);
        if (ret != 0) {
-               printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
+               dev_err(&pdev->dev, "Failed to set MCLK parent\n");
                goto err_mclk;
        }
 
@@ -236,7 +235,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_card(card);
        if (ret) {
-               printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n");
+               dev_err(&pdev->dev, "snd_soc_register_card() failed\n");
        }
 
        return ret;
index ccdf547f4d8c24583bdb6c1d6f3f57b5416e0a4a..e6c303ab869d49eea4670c1620bf70c1069657ef 100644 (file)
@@ -49,13 +49,13 @@ static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
        struct device *dev = rtd->dev;
        int ret;
 
-       dev_dbg(dev, "ASoC: %s called\n", __func__);
+       dev_dbg(dev, "%s called\n", __func__);
 
        /* set the codec system clock for DAC and ADC */
        ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
                                     MCLK_RATE, SND_SOC_CLOCK_IN);
        if (ret < 0) {
-               dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
+               dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
                return ret;
        }
 
@@ -146,8 +146,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
 
        ret = atmel_ssc_set_audio(priv->ssc_id);
        if (ret != 0) {
-               dev_err(&pdev->dev,
-                       "ASoC: Failed to set SSC %d for audio: %d\n",
+               dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
                        ret, priv->ssc_id);
                goto out;
        }
@@ -157,12 +156,11 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_card(card);
        if (ret) {
-               dev_err(&pdev->dev,
-                       "ASoC: Platform device allocation failed\n");
+               dev_err(&pdev->dev, "Platform device allocation failed\n");
                goto out_put_audio;
        }
 
-       dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__);
+       dev_dbg(&pdev->dev, "%s ok\n", __func__);
 
        return ret;
 
index b95bb8b52e51214b6ecbfd8a83a8462e47aea6f3..3d1cf4784e87cd463039c7f32d13c22f8052301c 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/of_device.h>
@@ -95,6 +96,9 @@ struct ak4613_priv {
        struct mutex lock;
        const struct ak4613_interface *iface;
        struct snd_pcm_hw_constraint_list constraint;
+       struct work_struct dummy_write_work;
+       struct snd_soc_component *component;
+       unsigned int rate;
        unsigned int sysclk;
 
        unsigned int fmt;
@@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
        default:
                return -EINVAL;
        }
+       priv->rate = rate;
 
        /*
         * FIXME
@@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static void ak4613_dummy_write(struct work_struct *work)
+{
+       struct ak4613_priv *priv = container_of(work,
+                                               struct ak4613_priv,
+                                               dummy_write_work);
+       struct snd_soc_component *component = priv->component;
+       unsigned int mgmt1;
+       unsigned int mgmt3;
+
+       /*
+        * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+        *
+        * Note
+        *
+        * To avoid extra delay, we want to avoid preemption here,
+        * but we can't. Because it uses I2C access which is using IRQ
+        * and sleep. Thus, delay might be more than 5 LR clocks
+        * see also
+        *      ak4613_dai_trigger()
+        */
+       udelay(5000000 / priv->rate);
+
+       snd_soc_component_read(component, PW_MGMT1, &mgmt1);
+       snd_soc_component_read(component, PW_MGMT3, &mgmt3);
+
+       snd_soc_component_write(component, PW_MGMT1, mgmt1);
+       snd_soc_component_write(component, PW_MGMT3, mgmt3);
+}
+
+static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       /*
+        * FIXME
+        *
+        * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
+        * from Power Down Release. Otherwise, Playback volume will be 0dB.
+        * To avoid complex multiple delay/dummy_write method from
+        * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
+        * call it once here.
+        *
+        * But, unfortunately, we can't "write" here because here is atomic
+        * context (It uses I2C access for writing).
+        * Thus, use schedule_work() to switching to normal context
+        * immediately.
+        *
+        * Note
+        *
+        * Calling ak4613_dummy_write() function might be delayed.
+        * In such case, ak4613 volume might be temporarily 0dB when
+        * beggining of playback.
+        * see also
+        *      ak4613_dummy_write()
+        */
+
+       if ((cmd != SNDRV_PCM_TRIGGER_START) &&
+           (cmd != SNDRV_PCM_TRIGGER_RESUME))
+               return 0;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return  0;
+
+       priv->component = &codec->component;
+       schedule_work(&priv->dummy_write_work);
+
+       return 0;
+}
+
 static const struct snd_soc_dai_ops ak4613_dai_ops = {
        .startup        = ak4613_dai_startup,
        .shutdown       = ak4613_dai_shutdown,
        .set_sysclk     = ak4613_dai_set_sysclk,
        .set_fmt        = ak4613_dai_set_fmt,
+       .trigger        = ak4613_dai_trigger,
        .hw_params      = ak4613_dai_hw_params,
 };
 
@@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
        priv->iface             = NULL;
        priv->cnt               = 0;
        priv->sysclk            = 0;
+       INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
 
        mutex_init(&priv->lock);
 
index c88f974ebe3e2bc69dfd10e76caf4f47d3c68dd5..cf83c423394d775430e876d89721f300675365bf 100644 (file)
@@ -113,7 +113,7 @@ static int dmic_dev_probe(struct platform_device *pdev)
 
        if (pdev->dev.of_node) {
                err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
-               if (err && (err != -ENOENT))
+               if (err && (err != -EINVAL))
                        return err;
 
                if (!err) {
index 31b0864583e8fbd0c4bcb2d3240eb6b39209f0e7..562e887651291816c05c6585f17b4664fabfb2c3 100644 (file)
@@ -1,5 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (c) 2017, Maxim Integrated */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
 
 #include <linux/acpi.h>
 #include <linux/i2c.h>
index d0b359d0cf8c6c12dfd7571ae635109574d32aed..f6a37aa02f2665ea67dda2dcc4441763765b9f76 100644 (file)
@@ -1,5 +1,6 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (c) 2017, Maxim Integrated */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
 #ifndef _MAX98373_H
 #define _MAX98373_H
 
index 633cdcfc933d0c8cc4b4bb3ed07ebbf7ea2cc710..e1ab5537d27a80f702a2f8934b79c21ddf514d87 100644 (file)
@@ -1392,7 +1392,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 
                ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
                dev_info(&client->dev,
-                        "Using internal LDO instead of VDDD: check ER1\n");
+                        "Using internal LDO instead of VDDD: check ER1 erratum\n");
        } else {
                /* using external LDO for VDDD
                 * Clear startup powerup and simple powerup
index 8798182959c1d9338bde08d38d38b05089f83266..e4d7f397d361411bd7acc5d9a44f9908a6d0c51a 100644 (file)
@@ -2195,6 +2195,8 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
 static const struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
        .probe = twl4030_soc_probe,
        .remove = twl4030_soc_remove,
+       .read = twl4030_read,
+       .write = twl4030_write,
        .set_bias_level = twl4030_set_bias_level,
        .idle_bias_off = true,
 
index 3b895b4b451c1d319d61b6ca9b0d777ad6d2a49b..573a523ed0b3abe17eeb89e436f87d0584d82fa8 100644 (file)
@@ -1158,6 +1158,8 @@ static int twl6040_remove(struct snd_soc_codec *codec)
 static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
        .probe = twl6040_probe,
        .remove = twl6040_remove,
+       .read = twl6040_read,
+       .write = twl6040_write,
        .set_bias_level = twl6040_set_bias_level,
        .suspend_bias_off = true,
        .ignore_pmdown_time = true,
index f2c9e8c5970af08d9875d7fdcf47047b84f967a5..ceb105cbd461582196bff4e59c225cf6083952bd 100644 (file)
@@ -97,6 +97,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM
          codec, then enable this option by saying Y or m. This is a
          recommended option
 
+config SND_SOC_INTEL_SKYLAKE_SSP_CLK
+       tristate
+
 config SND_SOC_INTEL_SKYLAKE
        tristate "SKL/BXT/KBL/GLK/CNL... Platforms"
        depends on PCI && ACPI
index d4e103615f51eb6535b509bf6b29419afd7b3a5d..fefb1ee9fec6fea0233ef16292a1fe307fbeec9e 100644 (file)
@@ -235,6 +235,7 @@ config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH
        select SND_SOC_MAX98927
        select SND_SOC_DMIC
        select SND_SOC_HDAC_HDMI
+       select SND_SOC_INTEL_SKYLAKE_SSP_CLK
        help
          This adds support for ASoC Onboard Codec I2S machine driver. This will
          create an alsa sound card for RT5663 + MAX98927.
index bf7014ca486f7fdc49f5807f581aceff58c997a2..f5df6bca3156e8d3a42f21dcd36cf2e19c3682fc 100644 (file)
@@ -28,6 +28,9 @@
 #include "../../codecs/rt5663.h"
 #include "../../codecs/hdac_hdmi.h"
 #include "../skylake/skl.h"
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
 
 #define KBL_REALTEK_CODEC_DAI "rt5663-aif"
 #define KBL_MAXIM_CODEC_DAI "max98927-aif1"
@@ -48,6 +51,8 @@ struct kbl_hdmi_pcm {
 struct kbl_rt5663_private {
        struct snd_soc_jack kabylake_headset;
        struct list_head hdmi_pcm_list;
+       struct clk *mclk;
+       struct clk *sclk;
 };
 
 enum {
@@ -69,6 +74,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = {
        SOC_DAPM_PIN_SWITCH("Right Spk"),
 };
 
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int  event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct kbl_rt5663_private *priv = snd_soc_card_get_drvdata(card);
+       int ret = 0;
+
+       /*
+        * MCLK/SCLK need to be ON early for a successful synchronization of
+        * codec internal clock. And the clocks are turned off during
+        * POST_PMD after the stream is stopped.
+        */
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* Enable MCLK */
+               ret = clk_set_rate(priv->mclk, 24000000);
+               if (ret < 0) {
+                       dev_err(card->dev, "Can't set rate for mclk, err: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = clk_prepare_enable(priv->mclk);
+               if (ret < 0) {
+                       dev_err(card->dev, "Can't enable mclk, err: %d\n", ret);
+                       return ret;
+               }
+
+               /* Enable SCLK */
+               ret = clk_set_rate(priv->sclk, 3072000);
+               if (ret < 0) {
+                       dev_err(card->dev, "Can't set rate for sclk, err: %d\n",
+                               ret);
+                       clk_disable_unprepare(priv->mclk);
+                       return ret;
+               }
+
+               ret = clk_prepare_enable(priv->sclk);
+               if (ret < 0) {
+                       dev_err(card->dev, "Can't enable sclk, err: %d\n", ret);
+                       clk_disable_unprepare(priv->mclk);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               clk_disable_unprepare(priv->mclk);
+               clk_disable_unprepare(priv->sclk);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget kabylake_widgets[] = {
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -78,11 +138,14 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = {
        SND_SOC_DAPM_SPK("HDMI1", NULL),
        SND_SOC_DAPM_SPK("HDMI2", NULL),
        SND_SOC_DAPM_SPK("HDMI3", NULL),
-
+       SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+                       platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+                       SND_SOC_DAPM_POST_PMD),
 };
 
 static const struct snd_soc_dapm_route kabylake_map[] = {
        /* HP jack connectors - unknown if we have jack detection */
+       { "Headphone Jack", NULL, "Platform Clock" },
        { "Headphone Jack", NULL, "HPOL" },
        { "Headphone Jack", NULL, "HPOR" },
 
@@ -91,6 +154,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = {
        { "Right Spk", NULL, "Right BE_OUT" },
 
        /* other jacks */
+       { "Headset Mic", NULL, "Platform Clock" },
        { "IN1P", NULL, "Headset Mic" },
        { "IN1N", NULL, "Headset Mic" },
        { "DMic", NULL, "SoC DMIC" },
@@ -901,6 +965,7 @@ static int kabylake_audio_probe(struct platform_device *pdev)
 {
        struct kbl_rt5663_private *ctx;
        struct skl_machine_pdata *pdata;
+       int ret;
 
        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
        if (!ctx)
@@ -919,6 +984,34 @@ static int kabylake_audio_probe(struct platform_device *pdev)
                dmic_constraints = pdata->dmic_num == 2 ?
                        &constraints_dmic_2ch : &constraints_dmic_channels;
 
+       ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk");
+       if (IS_ERR(ctx->mclk)) {
+               ret = PTR_ERR(ctx->mclk);
+               if (ret == -ENOENT) {
+                       dev_info(&pdev->dev,
+                               "Failed to get ssp1_sclk, defer probe\n");
+                       return -EPROBE_DEFER;
+               }
+
+               dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n",
+                                                               ret);
+               return ret;
+       }
+
+       ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk");
+       if (IS_ERR(ctx->sclk)) {
+               ret = PTR_ERR(ctx->sclk);
+               if (ret == -ENOENT) {
+                       dev_info(&pdev->dev,
+                               "Failed to get ssp1_sclk, defer probe\n");
+                       return -EPROBE_DEFER;
+               }
+
+               dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n",
+                                                               ret);
+               return ret;
+       }
+
        return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card);
 }
 
index d1ccbecd141f92b612e71d822e3d2496db4a4af9..86f6e1d801af71385a1f6663814f09342285aa51 100644 (file)
@@ -14,3 +14,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \
                skl-sst-utils.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
+
+#Skylake Clock device support
+snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o
index dcf819bc688f3d7f05e9594be8a7ee61ddd648d7..ad0a1bbca13cbea7ff622439663124e033218706 100644 (file)
 #define SKL_SHIFT(x)                   (ffs(x) - 1)
 #define SKL_MCLK_DIV_RATIO_MASK                GENMASK(11, 0)
 
+#define is_legacy_blob(x) (x.signature != 0xEE)
+#define ext_to_legacy_blob(i2s_config_blob_ext) \
+       ((struct skl_i2s_config_blob_legacy *) i2s_config_blob_ext)
+
+#define get_clk_src(mclk, mask) \
+               ((mclk.mdivctrl & mask) >> SKL_SHIFT(mask))
 struct skl_i2s_config {
        u32 ssc0;
        u32 ssc1;
@@ -45,6 +51,24 @@ struct skl_i2s_config_mclk {
        u32 mdivr;
 };
 
+struct skl_i2s_config_mclk_ext {
+       u32 mdivctrl;
+       u32 mdivr_count;
+       u32 mdivr[0];
+} __packed;
+
+struct skl_i2s_config_blob_signature {
+       u32 minor_ver : 8;
+       u32 major_ver : 8;
+       u32 resvdz : 8;
+       u32 signature : 8;
+} __packed;
+
+struct skl_i2s_config_blob_header {
+       struct skl_i2s_config_blob_signature sig;
+       u32 size;
+};
+
 /**
  * struct skl_i2s_config_blob_legacy - Structure defines I2S Gateway
  * configuration legacy blob
@@ -61,4 +85,11 @@ struct skl_i2s_config_blob_legacy {
        struct skl_i2s_config_mclk mclk;
 };
 
+struct skl_i2s_config_blob_ext {
+       u32 gtw_attr;
+       struct skl_i2s_config_blob_header hdr;
+       u32 tdm_ts_group[SKL_I2S_MAX_TIME_SLOTS];
+       struct skl_i2s_config i2s_cfg;
+       struct skl_i2s_config_mclk_ext mclk;
+} __packed;
 #endif /* __SOUND_SOC_SKL_I2S_H */
index 8cbf080c38b3ef1acfcecb8f869bfbcee59eb732..60d76adade432c09db6133fd2a8796777f371318 100644 (file)
@@ -675,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
        kfree(dma_ctrl);
        return err;
 }
+EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control);
 
 static void skl_setup_out_format(struct skl_sst *ctx,
                        struct skl_module_cfg *mconfig,
index 3b1d2b828c1b6425c06614cce20293265c3c35ed..b9b140275be09908e93e1936a7d9d86772387106 100644 (file)
@@ -28,6 +28,7 @@ static guid_t osc_guid =
        GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
                  0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
 
+
 struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
 {
        acpi_handle handle;
@@ -287,6 +288,7 @@ void skl_nhlt_remove_sysfs(struct skl *skl)
 static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
                                struct nhlt_fmt *fmt, u8 id)
 {
+       struct skl_i2s_config_blob_ext *i2s_config_ext;
        struct skl_i2s_config_blob_legacy *i2s_config;
        struct skl_clk_parent_src *parent;
        struct skl_ssp_clk *sclk, *sclkfs;
@@ -347,12 +349,18 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
 
                /* Fill rate and parent for sclk/sclkfs */
                if (!present) {
-                       /* MCLK Divider Source Select */
-                       i2s_config = (struct skl_i2s_config_blob_legacy *)
+                       i2s_config_ext = (struct skl_i2s_config_blob_ext *)
                                                fmt->fmt_config[0].config.caps;
-                       clk_src = ((i2s_config->mclk.mdivctrl)
-                                       & SKL_MNDSS_DIV_CLK_SRC_MASK) >>
-                                       SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK);
+
+                       /* MCLK Divider Source Select */
+                       if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
+                               i2s_config = ext_to_legacy_blob(i2s_config_ext);
+                               clk_src = get_clk_src(i2s_config->mclk,
+                                               SKL_MNDSS_DIV_CLK_SRC_MASK);
+                       } else {
+                               clk_src = get_clk_src(i2s_config_ext->mclk,
+                                               SKL_MNDSS_DIV_CLK_SRC_MASK);
+                       }
 
                        parent = skl_get_parent_clk(clk_src);
 
@@ -378,6 +386,7 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
 static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
                                struct nhlt_fmt *fmt, u8 id)
 {
+       struct skl_i2s_config_blob_ext *i2s_config_ext;
        struct skl_i2s_config_blob_legacy *i2s_config;
        struct nhlt_specific_cfg *fmt_cfg;
        struct skl_clk_parent_src *parent;
@@ -385,13 +394,21 @@ static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
        u8 clk_src;
 
        fmt_cfg = &fmt->fmt_config[0].config;
-       i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps;
-
-       /* MCLK Divider Source Select */
-       clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >>
-                                       SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK);
-
-       clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK;
+       i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps;
+
+       /* MCLK Divider Source Select and divider */
+       if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
+               i2s_config = ext_to_legacy_blob(i2s_config_ext);
+               clk_src = get_clk_src(i2s_config->mclk,
+                               SKL_MCLK_DIV_CLK_SRC_MASK);
+               clkdiv = i2s_config->mclk.mdivr &
+                               SKL_MCLK_DIV_RATIO_MASK;
+       } else {
+               clk_src = get_clk_src(i2s_config_ext->mclk,
+                               SKL_MCLK_DIV_CLK_SRC_MASK);
+               clkdiv = i2s_config_ext->mclk.mdivr[0] &
+                               SKL_MCLK_DIV_RATIO_MASK;
+       }
 
        /* bypass divider */
        div_ratio = 1;
diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c
new file mode 100644 (file)
index 0000000..7fbddf5
--- /dev/null
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2015-17 Intel Corporation
+
+/*
+ *  skl-ssp-clk.c - ASoC skylake ssp clock driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "skl.h"
+#include "skl-ssp-clk.h"
+#include "skl-topology.h"
+
+#define to_skl_clk(_hw)        container_of(_hw, struct skl_clk, hw)
+
+struct skl_clk_parent {
+       struct clk_hw *hw;
+       struct clk_lookup *lookup;
+};
+
+struct skl_clk {
+       struct clk_hw hw;
+       struct clk_lookup *lookup;
+       unsigned long rate;
+       struct skl_clk_pdata *pdata;
+       u32 id;
+};
+
+struct skl_clk_data {
+       struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
+       struct skl_clk *clk[SKL_MAX_CLK_CNT];
+       u8 avail_clk_cnt;
+};
+
+static int skl_get_clk_type(u32 index)
+{
+       switch (index) {
+       case 0 ... (SKL_SCLK_OFS - 1):
+               return SKL_MCLK;
+
+       case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
+               return SKL_SCLK;
+
+       case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
+               return SKL_SCLK_FS;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int skl_get_vbus_id(u32 index, u8 clk_type)
+{
+       switch (clk_type) {
+       case SKL_MCLK:
+               return index;
+
+       case SKL_SCLK:
+               return index - SKL_SCLK_OFS;
+
+       case SKL_SCLK_FS:
+               return index - SKL_SCLKFS_OFS;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
+{
+       struct nhlt_fmt_cfg *fmt_cfg;
+       union skl_clk_ctrl_ipc *ipc;
+       struct wav_fmt *wfmt;
+
+       if (!rcfg)
+               return;
+
+       ipc = &rcfg->dma_ctl_ipc;
+       if (clk_type == SKL_SCLK_FS) {
+               fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
+               wfmt = &fmt_cfg->fmt_ext.fmt;
+
+               /* Remove TLV Header size */
+               ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
+                                               sizeof(struct skl_tlv_hdr);
+               ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
+               ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
+               ipc->sclk_fs.valid_bit_depth =
+                       fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
+               ipc->sclk_fs.number_of_channels = wfmt->channels;
+       } else {
+               ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
+               /* Remove TLV Header size */
+               ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
+                                               sizeof(struct skl_tlv_hdr);
+       }
+}
+
+/* Sends dma control IPC to turn the clock ON/OFF */
+static int skl_send_clk_dma_control(struct skl *skl,
+                               struct skl_clk_rate_cfg_table *rcfg,
+                               u32 vbus_id, u8 clk_type,
+                               bool enable)
+{
+       struct nhlt_specific_cfg *sp_cfg;
+       u32 i2s_config_size, node_id = 0;
+       struct nhlt_fmt_cfg *fmt_cfg;
+       union skl_clk_ctrl_ipc *ipc;
+       void *i2s_config = NULL;
+       u8 *data, size;
+       int ret;
+
+       if (!rcfg)
+               return -EIO;
+
+       ipc = &rcfg->dma_ctl_ipc;
+       fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
+       sp_cfg = &fmt_cfg->config;
+
+       if (clk_type == SKL_SCLK_FS) {
+               ipc->sclk_fs.hdr.type =
+                       enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
+               data = (u8 *)&ipc->sclk_fs;
+               size = sizeof(struct skl_dmactrl_sclkfs_cfg);
+       } else {
+               /* 1 to enable mclk, 0 to enable sclk */
+               if (clk_type == SKL_SCLK)
+                       ipc->mclk.mclk = 0;
+               else
+                       ipc->mclk.mclk = 1;
+
+               ipc->mclk.keep_running = enable;
+               ipc->mclk.warm_up_over = enable;
+               ipc->mclk.clk_stop_over = !enable;
+               data = (u8 *)&ipc->mclk;
+               size = sizeof(struct skl_dmactrl_mclk_cfg);
+       }
+
+       i2s_config_size = sp_cfg->size + size;
+       i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
+       if (!i2s_config)
+               return -ENOMEM;
+
+       /* copy blob */
+       memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
+
+       /* copy additional dma controls information */
+       memcpy(i2s_config + sp_cfg->size, data, size);
+
+       node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
+       ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config,
+                                       i2s_config_size, node_id);
+       kfree(i2s_config);
+
+       return ret;
+}
+
+static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
+               struct skl_clk_rate_cfg_table *rcfg,
+                               unsigned long rate)
+{
+       int i;
+
+       for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
+               if (rcfg[i].rate == rate)
+                       return &rcfg[i];
+       }
+
+       return NULL;
+}
+
+static int skl_clk_change_status(struct skl_clk *clkdev,
+                               bool enable)
+{
+       struct skl_clk_rate_cfg_table *rcfg;
+       int vbus_id, clk_type;
+
+       clk_type = skl_get_clk_type(clkdev->id);
+       if (clk_type < 0)
+               return clk_type;
+
+       vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
+       if (vbus_id < 0)
+               return vbus_id;
+
+       rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
+                                               clkdev->rate);
+       if (!rcfg)
+               return -EINVAL;
+
+       return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
+                                       vbus_id, clk_type, enable);
+}
+
+static int skl_clk_prepare(struct clk_hw *hw)
+{
+       struct skl_clk *clkdev = to_skl_clk(hw);
+
+       return skl_clk_change_status(clkdev, true);
+}
+
+static void skl_clk_unprepare(struct clk_hw *hw)
+{
+       struct skl_clk *clkdev = to_skl_clk(hw);
+
+       skl_clk_change_status(clkdev, false);
+}
+
+static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long parent_rate)
+{
+       struct skl_clk *clkdev = to_skl_clk(hw);
+       struct skl_clk_rate_cfg_table *rcfg;
+       int clk_type;
+
+       if (!rate)
+               return -EINVAL;
+
+       rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
+                                                       rate);
+       if (!rcfg)
+               return -EINVAL;
+
+       clk_type = skl_get_clk_type(clkdev->id);
+       if (clk_type < 0)
+               return clk_type;
+
+       skl_fill_clk_ipc(rcfg, clk_type);
+       clkdev->rate = rate;
+
+       return 0;
+}
+
+static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
+                               unsigned long parent_rate)
+{
+       struct skl_clk *clkdev = to_skl_clk(hw);
+
+       if (clkdev->rate)
+               return clkdev->rate;
+
+       return 0;
+}
+
+/* Not supported by clk driver. Implemented to satisfy clk fw */
+long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *parent_rate)
+{
+       return rate;
+}
+
+/*
+ * prepare/unprepare are used instead of enable/disable as IPC will be sent
+ * in non-atomic context.
+ */
+static const struct clk_ops skl_clk_ops = {
+       .prepare = skl_clk_prepare,
+       .unprepare = skl_clk_unprepare,
+       .set_rate = skl_clk_set_rate,
+       .round_rate = skl_clk_round_rate,
+       .recalc_rate = skl_clk_recalc_rate,
+};
+
+static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
+                                       unsigned int id)
+{
+       while (id--) {
+               clkdev_drop(pclk[id].lookup);
+               clk_hw_unregister_fixed_rate(pclk[id].hw);
+       }
+}
+
+static void unregister_src_clk(struct skl_clk_data *dclk)
+{
+       u8 cnt = dclk->avail_clk_cnt;
+
+       while (cnt--)
+               clkdev_drop(dclk->clk[cnt]->lookup);
+}
+
+static int skl_register_parent_clks(struct device *dev,
+                       struct skl_clk_parent *parent,
+                       struct skl_clk_parent_src *pclk)
+{
+       int i, ret;
+
+       for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
+
+               /* Register Parent clock */
+               parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
+                               pclk[i].parent_name, 0, pclk[i].rate);
+               if (IS_ERR(parent[i].hw)) {
+                       ret = PTR_ERR(parent[i].hw);
+                       goto err;
+               }
+
+               parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
+                                                                       NULL);
+               if (!parent[i].lookup) {
+                       clk_hw_unregister_fixed_rate(parent[i].hw);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       return 0;
+err:
+       unregister_parent_src_clk(parent, i);
+       return ret;
+}
+
+/* Assign fmt_config to clk_data */
+static struct skl_clk *register_skl_clk(struct device *dev,
+                       struct skl_ssp_clk *clk,
+                       struct skl_clk_pdata *clk_pdata, int id)
+{
+       struct clk_init_data init;
+       struct skl_clk *clkdev;
+       int ret;
+
+       clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
+       if (!clkdev)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = clk->name;
+       init.ops = &skl_clk_ops;
+       init.flags = CLK_SET_RATE_GATE;
+       init.parent_names = &clk->parent_name;
+       init.num_parents = 1;
+       clkdev->hw.init = &init;
+       clkdev->pdata = clk_pdata;
+
+       clkdev->id = id;
+       ret = devm_clk_hw_register(dev, &clkdev->hw);
+       if (ret) {
+               clkdev = ERR_PTR(ret);
+               return clkdev;
+       }
+
+       clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
+       if (!clkdev->lookup)
+               clkdev = ERR_PTR(-ENOMEM);
+
+       return clkdev;
+}
+
+static int skl_clk_dev_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device *parent_dev = dev->parent;
+       struct skl_clk_parent_src *parent_clks;
+       struct skl_clk_pdata *clk_pdata;
+       struct skl_clk_data *data;
+       struct skl_ssp_clk *clks;
+       int ret, i;
+
+       clk_pdata = dev_get_platdata(&pdev->dev);
+       parent_clks = clk_pdata->parent_clks;
+       clks = clk_pdata->ssp_clks;
+       if (!parent_clks || !clks)
+               return -EIO;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       /* Register Parent clock */
+       ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < clk_pdata->num_clks; i++) {
+               /*
+                * Only register valid clocks
+                * i.e. for which nhlt entry is present.
+                */
+               if (clks[i].rate_cfg[0].rate == 0)
+                       continue;
+
+               data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i);
+               if (IS_ERR(data->clk[i])) {
+                       ret = PTR_ERR(data->clk[i]);
+                       goto err_unreg_skl_clk;
+               }
+
+               data->avail_clk_cnt++;
+       }
+
+       platform_set_drvdata(pdev, data);
+
+       return 0;
+
+err_unreg_skl_clk:
+       unregister_src_clk(data);
+       unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
+
+       return ret;
+}
+
+static int skl_clk_dev_remove(struct platform_device *pdev)
+{
+       struct skl_clk_data *data;
+
+       data = platform_get_drvdata(pdev);
+       unregister_src_clk(data);
+       unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
+
+       return 0;
+}
+
+static struct platform_driver skl_clk_driver = {
+       .driver = {
+               .name = "skl-ssp-clk",
+       },
+       .probe = skl_clk_dev_probe,
+       .remove = skl_clk_dev_remove,
+};
+
+module_platform_driver(skl_clk_driver);
+
+MODULE_DESCRIPTION("Skylake clock driver");
+MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl-ssp-clk");
index c9ea8400426033065888e8d88b37379af738ed17..d1be50f96c05f6690cecf10b5339c1ca085f5052 100644 (file)
@@ -54,8 +54,46 @@ struct skl_clk_parent_src {
        const char *parent_name;
 };
 
+struct skl_tlv_hdr {
+       u32 type;
+       u32 size;
+};
+
+struct skl_dmactrl_mclk_cfg {
+       struct skl_tlv_hdr hdr;
+       /* DMA Clk TLV params */
+       u32 clk_warm_up:16;
+       u32 mclk:1;
+       u32 warm_up_over:1;
+       u32 rsvd0:14;
+       u32 clk_stop_delay:16;
+       u32 keep_running:1;
+       u32 clk_stop_over:1;
+       u32 rsvd1:14;
+};
+
+struct skl_dmactrl_sclkfs_cfg {
+       struct skl_tlv_hdr hdr;
+       /* DMA SClk&FS  TLV params */
+       u32 sampling_frequency;
+       u32 bit_depth;
+       u32 channel_map;
+       u32 channel_config;
+       u32 interleaving_style;
+       u32 number_of_channels : 8;
+       u32 valid_bit_depth : 8;
+       u32 sample_type : 8;
+       u32 reserved : 8;
+};
+
+union skl_clk_ctrl_ipc {
+       struct skl_dmactrl_mclk_cfg mclk;
+       struct skl_dmactrl_sclkfs_cfg sclk_fs;
+};
+
 struct skl_clk_rate_cfg_table {
        unsigned long rate;
+       union skl_clk_ctrl_ipc dma_ctl_ipc;
        void *config;
 };
 
index f411579bc7138ac1fce813cf8914720862ede50d..2d13f3fd988ad3881261257d162c9c5166f1bdbc 100644 (file)
 /* D0I3C Register fields */
 #define AZX_REG_VS_D0I3C_CIP      0x1 /* Command in progress */
 #define AZX_REG_VS_D0I3C_I3       0x4 /* D0i3 enable */
+#define SKL_MAX_DMACTRL_CFG    18
+#define DMA_CLK_CONTROLS       1
+#define DMA_TRANSMITION_START  2
+#define DMA_TRANSMITION_STOP   3
 
 struct skl_dsp_resource {
        u32 max_mcps;
@@ -147,6 +151,8 @@ int skl_nhlt_create_sysfs(struct skl *skl);
 void skl_nhlt_remove_sysfs(struct skl *skl);
 void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
 struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
+int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
+                               u32 caps_size, u32 node_id);
 
 struct skl_module_cfg;
 
index 5bc4e00a4a297728e3afcc54a9a4018d6e04eec6..d68b53f7cefe9334220a04183dbaa2bb997cdda5 100644 (file)
@@ -1405,9 +1405,24 @@ static int mt2701_afe_runtime_resume(struct device *dev)
        return mt2701_afe_enable_clock(afe);
 }
 
-static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt2701_afe_add_component(struct mtk_base_afe *afe)
 {
        struct snd_soc_component *component;
+
+       component = kzalloc(sizeof(*component), GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+
+       component->regmap = afe->regmap;
+
+       return snd_soc_add_component(afe->dev, component,
+                                    &mt2701_afe_pcm_dai_component,
+                                    mt2701_afe_pcm_dais,
+                                    ARRAY_SIZE(mt2701_afe_pcm_dais));
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
        struct mtk_base_afe *afe;
        struct mt2701_afe_private *afe_priv;
        struct device *dev;
@@ -1477,12 +1492,6 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
                        = &mt2701_i2s_data[i][I2S_IN];
        }
 
-       component = kzalloc(sizeof(*component), GFP_KERNEL);
-       if (!component)
-               return -ENOMEM;
-
-       component->regmap = afe->regmap;
-
        afe->mtk_afe_hardware = &mt2701_afe_hardware;
        afe->memif_fs = mt2701_memif_fs;
        afe->irq_fs = mt2701_irq_fs;
@@ -1495,7 +1504,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
        ret = mt2701_init_clock(afe);
        if (ret) {
                dev_err(dev, "init clock error\n");
-               goto err_init_clock;
+               return ret;
        }
 
        platform_set_drvdata(pdev, afe);
@@ -1514,10 +1523,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
                goto err_platform;
        }
 
-       ret = snd_soc_add_component(dev, component,
-                                   &mt2701_afe_pcm_dai_component,
-                                   mt2701_afe_pcm_dais,
-                                   ARRAY_SIZE(mt2701_afe_pcm_dais));
+       ret = mt2701_afe_add_component(afe);
        if (ret) {
                dev_warn(dev, "err_dai_component\n");
                goto err_dai_component;
@@ -1531,8 +1537,6 @@ err_platform:
        pm_runtime_put_sync(dev);
 err_pm_disable:
        pm_runtime_disable(dev);
-err_init_clock:
-       kfree(component);
 
        return ret;
 }
index 40ebefd625c1e4370e1f6527e3003b03fac4d34d..679fc8bea0a32fdff72dcef61673f42d401d3dd6 100644 (file)
@@ -272,15 +272,10 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
        }
        mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node;
 
-       if (of_find_node_by_name(platform_node, "codec-capture")) {
-               np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
-               if (!np) {
-                       dev_err(&pdev->dev,
-                               "%s: Can't find codec-capture DT node\n",
-                               __func__);
-                       return -EINVAL;
-               }
+       np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
+       if (np) {
                ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
+               of_node_put(np);
                if (ret < 0) {
                        dev_err(&pdev->dev,
                                "%s codec_capture_dai name fail %d\n",
index fa6cd1de828be95991dee4eaea9dec40f69d9fef..214bfc78cf5cd466659980db19caaa6d45922a7b 100644 (file)
@@ -368,7 +368,8 @@ static const struct snd_soc_dai_link rockchip_dais[] = {
        [DAILINK_RT5514_DSP] = {
                .name = "RT5514 DSP",
                .stream_name = "Wake on Voice",
-               .codec_dai_name = "rt5514-dsp-cpu-dai",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
        },
 };
 
@@ -529,7 +530,18 @@ static int rockchip_sound_of_parse_dais(struct device *dev,
                if (index < 0)
                        continue;
 
-               np_cpu = (index == DAILINK_CDNDP) ? np_cpu1 : np_cpu0;
+               switch (index) {
+               case DAILINK_CDNDP:
+                       np_cpu = np_cpu1;
+                       break;
+               case DAILINK_RT5514_DSP:
+                       np_cpu = np_codec;
+                       break;
+               default:
+                       np_cpu = np_cpu0;
+                       break;
+               }
+
                if (!np_cpu) {
                        dev_err(dev, "Missing 'rockchip,cpu' for %s\n",
                                rockchip_dais[index].name);
@@ -539,7 +551,8 @@ static int rockchip_sound_of_parse_dais(struct device *dev,
                dai = &card->dai_link[card->num_links++];
                *dai = rockchip_dais[index];
 
-               dai->codec_of_node = np_codec;
+               if (!dai->codec_name)
+                       dai->codec_of_node = np_codec;
                dai->platform_of_node = np_cpu;
                dai->cpu_of_node = np_cpu;
 
index 81232f4ab614b3089fe1af42a5612f3a1cbd8fea..82402688bd8ed33799a95f7ed4786cb5d840be72 100644 (file)
@@ -40,7 +40,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) {
                ret = cpu_dai->driver->cops->startup(cstream, cpu_dai);
                if (ret < 0) {
-                       dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n",
+                       dev_err(cpu_dai->dev,
+                               "Compress ASoC: can't open interface %s: %d\n",
                                cpu_dai->name, ret);
                        goto out;
                }
@@ -49,8 +50,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n",
-                               platform->component.name);
+                       dev_err(platform->dev,
+                               "Compress ASoC: can't open platform %s: %d\n",
+                               platform->component.name, ret);
                        goto plat_err;
                }
        }
@@ -68,8 +70,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
 
                __ret = component->driver->compr_ops->open(cstream);
                if (__ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n",
-                              component->name);
+                       dev_err(component->dev,
+                               "Compress ASoC: can't open platform %s: %d\n",
+                               component->name, __ret);
                        ret = __ret;
                }
        }
@@ -79,7 +82,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
                ret = rtd->dai_link->compr_ops->startup(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name);
+                       dev_err(rtd->dev,
+                               "Compress ASoC: %s startup failed: %d\n",
+                               rtd->dai_link->name, ret);
                        goto machine_err;
                }
        }
@@ -139,18 +144,19 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) {
                ret = cpu_dai->driver->cops->startup(cstream, cpu_dai);
                if (ret < 0) {
-                       dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n",
+                       dev_err(cpu_dai->dev,
+                               "Compress ASoC: can't open interface %s: %d\n",
                                cpu_dai->name, ret);
                        goto out;
                }
        }
 
-
        if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n",
-                               platform->component.name);
+                       dev_err(platform->dev,
+                               "Compress ASoC: can't open platform %s: %d\n",
+                               platform->component.name, ret);
                        goto plat_err;
                }
        }
@@ -168,8 +174,9 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 
                __ret = component->driver->compr_ops->open(cstream);
                if (__ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n",
-                              component->name);
+                       dev_err(component->dev,
+                               "Compress ASoC: can't open platform %s: %d\n",
+                               component->name, __ret);
                        ret = __ret;
                }
        }
@@ -179,7 +186,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) {
                ret = fe->dai_link->compr_ops->startup(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: %s startup failed\n", fe->dai_link->name);
+                       pr_err("Compress ASoC: %s startup failed: %d\n",
+                              fe->dai_link->name, ret);
                        goto machine_err;
                }
        }
@@ -190,7 +198,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        if (ret < 0)
                goto fe_err;
        else if (ret == 0)
-               dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
+               dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n",
                        fe->dai_link->name, stream ? "capture" : "playback");
 
        /* calculate valid and active FE <-> BE dpcms */
@@ -265,10 +273,11 @@ static void close_delayed_work(struct work_struct *work)
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
-       dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
-                codec_dai->driver->playback.stream_name,
-                codec_dai->playback_active ? "active" : "inactive",
-                rtd->pop_wait ? "yes" : "no");
+       dev_dbg(rtd->dev,
+               "Compress ASoC: pop wq checking: %s status: %s waiting: %s\n",
+               codec_dai->driver->playback.stream_name,
+               codec_dai->playback_active ? "active" : "inactive",
+               rtd->pop_wait ? "yes" : "no");
 
        /* are we waiting on this codec DAI stream */
        if (rtd->pop_wait == 1) {
@@ -307,7 +316,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
        if (!codec_dai->active)
                codec_dai->rate = 0;
 
-
        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
                rtd->dai_link->compr_ops->shutdown(cstream);
 
@@ -376,7 +384,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 
        ret = dpcm_be_dai_hw_free(fe, stream);
        if (ret < 0)
-               dev_err(fe->dev, "compressed hw_free failed %d\n", ret);
+               dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret);
 
        ret = dpcm_be_dai_shutdown(fe, stream);
 
@@ -460,7 +468,6 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger)
                cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai);
 
-
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
@@ -944,7 +951,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_component *component;
        struct snd_soc_rtdcom_list *rtdcom;
-       int ret = 0, __ret;
+       int ret = 0;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -965,10 +972,10 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
                    !component->driver->compr_ops->copy)
                        continue;
 
-               __ret = component->driver->compr_ops->copy(cstream, buf, count);
-               if (__ret < 0)
-                       ret = __ret;
+               ret = component->driver->compr_ops->copy(cstream, buf, count);
+               break;
        }
+
 err:
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
@@ -1108,7 +1115,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
        int playback = 0, capture = 0;
 
        if (rtd->num_codecs > 1) {
-               dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n");
+               dev_err(rtd->card->dev,
+                       "Compress ASoC: Multicodec not supported\n");
                return -EINVAL;
        }
 
@@ -1126,8 +1134,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
         * should be set, check for that (xor)
         */
        if (playback + capture != 1) {
-               dev_err(rtd->card->dev, "Invalid direction for compress P %d, C %d\n",
-                               playback, capture);
+               dev_err(rtd->card->dev,
+                       "Compress ASoC: Invalid direction for P %d, C %d\n",
+                       playback, capture);
                return -EINVAL;
        }
 
@@ -1155,8 +1164,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
                                rtd->dai_link->dpcm_playback,
                                rtd->dai_link->dpcm_capture, &be_pcm);
                if (ret < 0) {
-                       dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n",
-                               rtd->dai_link->name);
+                       dev_err(rtd->card->dev,
+                               "Compress ASoC: can't create compressed for %s: %d\n",
+                               rtd->dai_link->name, ret);
                        goto compr_err;
                }
 
@@ -1199,8 +1209,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
                                new_name, compr);
        if (ret < 0) {
                component = rtd->codec_dai->component;
-               pr_err("compress asoc: can't create compress for codec %s\n",
-                       component->name);
+               dev_err(component->dev,
+                       "Compress ASoC: can't create compress for codec %s: %d\n",
+                       component->name, ret);
                goto compr_err;
        }
 
@@ -1210,8 +1221,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
        rtd->compr = compr;
        compr->private_data = rtd;
 
-       printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
-               cpu_dai->name);
+       dev_info(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n",
+                codec_dai->name, cpu_dai->name);
        return ret;
 
 compr_err:
index 88efc84f3e7b4aa0d3a68e5d2d409aaca6358c45..96c44f6576c95f4dfae19547414040e2d2153235 100644 (file)
@@ -553,9 +553,17 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
 {
        struct snd_soc_rtdcom_list *rtdcom;
 
+       if (!driver_name)
+               return NULL;
+
        for_each_rtdcom(rtd, rtdcom) {
-               if ((rtdcom->component->driver->name == driver_name) ||
-                   strcmp(rtdcom->component->driver->name, driver_name) == 0)
+               const char *component_name = rtdcom->component->driver->name;
+
+               if (!component_name)
+                       continue;
+
+               if ((component_name == driver_name) ||
+                   strcmp(component_name, driver_name) == 0)
                        return rtdcom->component;
        }
 
index d1977ced895ff0217a33fd23eef7e0107d2f311c..92894d9cac19d54d835b6611c013b0ea527403a8 100644 (file)
@@ -2026,7 +2026,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        snd_soc_dapm_for_each_direction(dir) {
                rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
                snd_soc_dapm_widget_for_each_path(w, dir, p) {
-                       if (p->connected && !p->connected(w, p->node[rdir]))
+                       if (p->connected && !p->connected(p->source, p->sink))
                                continue;
 
                        if (!p->connect)
index 8075856668c23b10e0f16eebb78cb59b8c7a233a..084125463d101e96ab0aa119a964ac5609e6ab06 100644 (file)
@@ -144,7 +144,7 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
        for_each_rtdcom(rtd, rtdcom) {
                component = rtdcom->component;
 
-               ignore &= !component->driver->pmdown_time;
+               ignore &= !component->driver->use_pmdown_time;
        }
 
        /* this will be removed */
@@ -2831,10 +2831,9 @@ static void soc_pcm_private_free(struct snd_pcm *pcm)
        struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_component *component;
 
+       /* need to sync the delayed work before releasing resources */
+       flush_delayed_work(&rtd->delayed_work);
        for_each_rtdcom(rtd, rtdcom) {
-               /* need to sync the delayed work before releasing resources */
-
-               flush_delayed_work(&rtd->delayed_work);
                component = rtdcom->component;
 
                if (component->pcm_free)
index 3ad881fc40a16173bec38808d9b6819df9e23557..48f9ddd94016d12cc1b86cbcbee93cba8b6fc214 100644 (file)
@@ -2,7 +2,7 @@ menu "STMicroelectronics STM32 SOC audio support"
 
 config SND_SOC_STM32_SAI
        tristate "STM32 SAI interface (Serial Audio Interface) support"
-       depends on ARCH_STM32 || COMPILE_TEST
+       depends on (ARCH_STM32 && OF) || COMPILE_TEST
        depends on SND_SOC
        select SND_SOC_GENERIC_DMAENGINE_PCM
        select REGMAP_MMIO
@@ -11,7 +11,7 @@ config SND_SOC_STM32_SAI
 
 config SND_SOC_STM32_I2S
        tristate "STM32 I2S interface (SPI/I2S block) support"
-       depends on ARCH_STM32 || COMPILE_TEST
+       depends on (ARCH_STM32 && OF) || COMPILE_TEST
        depends on SND_SOC
        select SND_SOC_GENERIC_DMAENGINE_PCM
        select REGMAP_MMIO
@@ -20,7 +20,7 @@ config SND_SOC_STM32_I2S
 
 config SND_SOC_STM32_SPDIFRX
        tristate "STM32 S/PDIF receiver (SPDIFRX) support"
-       depends on ARCH_STM32 || COMPILE_TEST
+       depends on (ARCH_STM32 && OF) || COMPILE_TEST
        depends on SND_SOC
        select SND_SOC_GENERIC_DMAENGINE_PCM
        select REGMAP_MMIO
index 3dd183be08a40f8dc271f16eebab7f8d71ef0e1e..7a15df92431655831f89670125192d3670165f46 100644 (file)
 #define SUN8I_SYSCLK_CTL_SYSCLK_SRC                    0
 #define SUN8I_MOD_CLK_ENA                              0x010
 #define SUN8I_MOD_CLK_ENA_AIF1                         15
+#define SUN8I_MOD_CLK_ENA_ADC                          3
 #define SUN8I_MOD_CLK_ENA_DAC                          2
 #define SUN8I_MOD_RST_CTL                              0x014
 #define SUN8I_MOD_RST_CTL_AIF1                         15
+#define SUN8I_MOD_RST_CTL_ADC                          3
 #define SUN8I_MOD_RST_CTL_DAC                          2
 #define SUN8I_SYS_SR_CTRL                              0x018
 #define SUN8I_SYS_SR_CTRL_AIF1_FS                      12
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ               4
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16            (1 << 4)
 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT               2
+#define SUN8I_AIF1_ADCDAT_CTRL                         0x044
+#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA           15
+#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA           14
 #define SUN8I_AIF1_DACDAT_CTRL                         0x048
 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA           15
 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA           14
+#define SUN8I_AIF1_MXR_SRC                             0x04c
+#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L       15
+#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL       14
+#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL           13
+#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR       12
+#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R       11
+#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR       10
+#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR           9
+#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL       8
+#define SUN8I_ADC_DIG_CTRL                             0x100
+#define SUN8I_ADC_DIG_CTRL_ENDA                        15
+#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS                   2
+#define SUN8I_ADC_DIG_CTRL_ADOUT_DLY                   1
 #define SUN8I_DAC_DIG_CTRL                             0x120
 #define SUN8I_DAC_DIG_CTRL_ENDA                        15
 #define SUN8I_DAC_MXR_SRC                              0x130
@@ -338,10 +356,30 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
                        SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
 };
 
+static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
+       SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
+                       SUN8I_AIF1_MXR_SRC,
+                       SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L,
+                       SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
+       SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC,
+                       SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL,
+                       SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
+       SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
+                       SUN8I_AIF1_MXR_SRC,
+                       SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL,
+                       SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
+       SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
+                       SUN8I_AIF1_MXR_SRC,
+                       SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR,
+                       SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
+};
+
 static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
-       /* Digital parts of the DACs */
+       /* Digital parts of the DACs and ADC */
        SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
                            0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA,
+                           0, NULL, 0),
 
        /* Analog DAC AIF */
        SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0,
@@ -351,17 +389,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
                            SUN8I_AIF1_DACDAT_CTRL,
                            SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
 
-       /* DAC Mixers */
+       /* Analog ADC AIF */
+       SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0,
+                           SUN8I_AIF1_ADCDAT_CTRL,
+                           SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0),
+       SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0,
+                           SUN8I_AIF1_ADCDAT_CTRL,
+                           SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0),
+
+       /* DAC and ADC Mixers */
        SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
                        sun8i_dac_mixer_controls),
        SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
                        sun8i_dac_mixer_controls),
+       SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+                       sun8i_input_mixer_controls),
+       SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+                       sun8i_input_mixer_controls),
 
        /* Clocks */
        SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
                            SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA,
                            SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA,
+                           SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
                            SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
@@ -378,6 +430,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
                            SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL,
                            SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL,
+                           SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+
 };
 
 static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
@@ -387,11 +445,16 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
        { "RST AIF1", NULL, "AIF1 PLL" },
        { "MODCLK AFI1", NULL, "RST AIF1" },
        { "DAC", NULL, "MODCLK AFI1" },
+       { "ADC", NULL, "MODCLK AFI1" },
 
        { "RST DAC", NULL, "SYSCLK" },
        { "MODCLK DAC", NULL, "RST DAC" },
        { "DAC", NULL, "MODCLK DAC" },
 
+       { "RST ADC", NULL, "SYSCLK" },
+       { "MODCLK ADC", NULL, "RST ADC" },
+       { "ADC", NULL, "MODCLK ADC" },
+
        /* DAC Routes */
        { "AIF1 Slot 0 Right", NULL, "DAC" },
        { "AIF1 Slot 0 Left", NULL, "DAC" },
@@ -401,6 +464,12 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
          "AIF1 Slot 0 Left"},
        { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
          "AIF1 Slot 0 Right"},
+
+       /* ADC routes */
+       { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
+         "AIF1 Slot 0 Left ADC" },
+       { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
+         "AIF1 Slot 0 Right ADC" },
 };
 
 static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
@@ -418,6 +487,15 @@ static struct snd_soc_dai_driver sun8i_codec_dai = {
                .rates = SNDRV_PCM_RATE_8000_192000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
+       /* capture capabilities */
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               .sig_bits = 24,
+       },
        /* pcm operations */
        .ops = &sun8i_codec_dai_ops,
 };