]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge remote-tracking branches 'asoc/topic/rt5665', 'asoc/topic/rt5677', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Sun, 19 Feb 2017 16:36:54 +0000 (16:36 +0000)
committerMark Brown <broonie@kernel.org>
Sun, 19 Feb 2017 16:36:54 +0000 (16:36 +0000)
23 files changed:
Documentation/devicetree/bindings/sound/sun4i-i2s.txt
Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt
arch/arm/mach-s3c64xx/dev-audio.c
include/linux/platform_data/asoc-s3c.h
include/sound/simple_card_utils.h
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/rt5677-spi.c
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-card.c
sound/soc/generic/simple-scu-card.c
sound/soc/samsung/Kconfig
sound/soc/samsung/i2s.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/tm2_wm5110.c
sound/soc/sunxi/Kconfig
sound/soc/sunxi/Makefile
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun4i-i2s.c
sound/soc/sunxi/sun4i-spdif.c
sound/soc/sunxi/sun8i-codec-analog.c
sound/soc/sunxi/sun8i-codec.c [new file with mode: 0644]

index 7b526ec64991f3887803cc85f951c14f46673a2a..f4adc58f82baf04897313e2cf3cc981a0dc23e00 100644 (file)
@@ -7,6 +7,7 @@ Required properties:
 
 - compatible: should be one of the followings
    - "allwinner,sun4i-a10-i2s"
+   - "allwinner,sun6i-a31-i2s"
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: should contain the I2S interrupt.
@@ -19,6 +20,10 @@ Required properties:
    - "mod" : module clock for the I2S controller
 - #sound-dai-cells : Must be equal to 0
 
+Required properties for the following compatibles:
+       - "allwinner,sun6i-a31-i2s"
+- resets: phandle to the reset line for this codec
+
 Example:
 
 i2s0: i2s@01c22400 {
diff --git a/Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt b/Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt
new file mode 100644 (file)
index 0000000..399b1b4
--- /dev/null
@@ -0,0 +1,63 @@
+Allwinner SUN8I audio codec
+------------------------------------
+
+On Sun8i-A33 SoCs, the audio is separated in different parts:
+         - A DAI driver. It uses the "sun4i-i2s" driver which is
+         documented here:
+         Documentation/devicetree/bindings/sound/sun4i-i2s.txt
+         - An analog part of the codec which is handled as PRCM registers.
+         See Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
+         - An digital part of the codec which is documented in this current
+         binding documentation.
+         - And finally, an audio card which links all the above components.
+         The simple-audio card will be used.
+         See Documentation/devicetree/bindings/sound/simple-card.txt
+
+This bindings documentation exposes Sun8i codec (digital part).
+
+Required properties:
+- compatible: must be "allwinner,sun8i-a33-codec"
+- reg: must contain the registers location and length
+- interrupts: must contain the codec interrupt
+- clocks: a list of phandle + clock-specifer pairs, one for each entry
+  in clock-names.
+- clock-names: should contain followings:
+   - "bus": the parent APB clock for this controller
+   - "mod": the parent module clock
+
+Here is an example to add a sound card and the codec binding on sun8i SoCs that
+are similar to A33 using simple-card:
+
+       sound {
+               compatible = "simple-audio-card";
+               simple-audio-card,name = "sun8i-a33-audio";
+               simple-audio-card,format = "i2s";
+               simple-audio-card,frame-master = <&link_codec>;
+               simple-audio-card,bitclock-master = <&link_codec>;
+               simple-audio-card,mclk-fs = <512>;
+               simple-audio-card,aux-devs = <&codec_analog>;
+               simple-audio-card,routing =
+                               "Left DAC", "Digital Left DAC",
+                               "Right DAC", "Digital Right DAC";
+
+               simple-audio-card,cpu {
+                       sound-dai = <&dai>;
+               };
+
+               link_codec: simple-audio-card,codec {
+                       sound-dai = <&codec>;
+               };
+
+       soc@01c00000 {
+               [...]
+
+               audio-codec@1c22e00 {
+                       #sound-dai-cells = <0>;
+                       compatible = "allwinner,sun8i-a33-codec";
+                       reg = <0x01c22e00 0x400>;
+                       interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&ccu CLK_BUS_CODEC>, <&ccu CLK_AC_DIG>;
+                       clock-names = "bus", "mod";
+               };
+       };
+
index 0230c4d20506edaf919883afe352e089c6d8531e..fe0a65e6d629d407e6413054a8f7ac8e5dd6f026 100644 (file)
@@ -10,6 +10,7 @@ Required properties:
   - compatible         : should be one of the following:
     - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
     - "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC
+    - "allwinner,sun8i-h3-spdif": for the Allwinner H3 SoC
 
   - reg                        : Offset and length of the register set for the device.
 
index b57783371d525976946f3f47a2b37933308756f9..247dcc0b691e53a5e94539a0d490a25fc7afb20f 100644 (file)
@@ -106,9 +106,7 @@ static struct s3c_audio_pdata i2sv4_pdata = {
        .dma_playback = DMACH_HSI_I2SV40_TX,
        .dma_capture = DMACH_HSI_I2SV40_RX,
        .type = {
-               .i2s = {
-                       .quirks = QUIRK_PRI_6CHAN,
-               },
+               .quirks = QUIRK_PRI_6CHAN,
        },
 };
 
index 15bf56ee8af7d21d307c924df06d0f443d835975..90641a5daaf0218e4ead2af9ae4fe4bd9488159d 100644 (file)
@@ -18,7 +18,7 @@
 
 extern void s3c64xx_ac97_setup_gpio(int);
 
-struct samsung_i2s {
+struct samsung_i2s_type {
 /* If the Primary DAI has 5.1 Channels */
 #define QUIRK_PRI_6CHAN                (1 << 0)
 /* If the I2S block has a Stereo Overlay Channel */
@@ -47,7 +47,5 @@ struct s3c_audio_pdata {
        void *dma_capture;
        void *dma_play_sec;
        void *dma_capture_mic;
-       union {
-               struct samsung_i2s i2s;
-       } type;
+       struct samsung_i2s_type type;
 };
index 64e90ca9ad328079856bfa9adf840429949c619f..af58d236297554dc2ad87bcd9b6424fc59676d7a 100644 (file)
@@ -34,11 +34,12 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
 int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
                                     char *prefix);
 
-#define asoc_simple_card_parse_clk_cpu(node, dai_link, simple_dai)             \
-       asoc_simple_card_parse_clk(node, dai_link->cpu_of_node, simple_dai)
-#define asoc_simple_card_parse_clk_codec(node, dai_link, simple_dai)           \
-       asoc_simple_card_parse_clk(node, dai_link->codec_of_node, simple_dai)
-int asoc_simple_card_parse_clk(struct device_node *node,
+#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai)                \
+       asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai)
+#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai)      \
+       asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai)
+int asoc_simple_card_parse_clk(struct device *dev,
+                              struct device_node *node,
                               struct device_node *dai_of_node,
                               struct asoc_simple_dai *simple_dai);
 
index b456d31f7fc1bf2adf82028a68ed6bad0c585fe9..e49e9da7f1f6e41028a0ca832e76e7dce0bc1c01 100644 (file)
@@ -118,8 +118,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_RT5651 if I2C
        select SND_SOC_RT5659 if I2C
        select SND_SOC_RT5660 if I2C
-       select SND_SOC_RT5665 if I2C
        select SND_SOC_RT5663 if I2C
+       select SND_SOC_RT5665 if I2C
        select SND_SOC_RT5670 if I2C
        select SND_SOC_RT5677 if I2C && SPI_MASTER
        select SND_SOC_SGTL5000 if I2C
@@ -671,8 +671,8 @@ config SND_SOC_RL6231
        default y if SND_SOC_RT5651=y
        default y if SND_SOC_RT5659=y
        default y if SND_SOC_RT5660=y
-       default y if SND_SOC_RT5665=y
        default y if SND_SOC_RT5663=y
+       default y if SND_SOC_RT5665=y
        default y if SND_SOC_RT5670=y
        default y if SND_SOC_RT5677=y
        default m if SND_SOC_RT5514=m
@@ -682,8 +682,8 @@ config SND_SOC_RL6231
        default m if SND_SOC_RT5651=m
        default m if SND_SOC_RT5659=m
        default m if SND_SOC_RT5660=m
-       default m if SND_SOC_RT5665=m
        default m if SND_SOC_RT5663=m
+       default m if SND_SOC_RT5665=m
        default m if SND_SOC_RT5670=m
        default m if SND_SOC_RT5677=m
 
@@ -731,10 +731,10 @@ config SND_SOC_RT5659
 config SND_SOC_RT5660
        tristate
 
-config SND_SOC_RT5665
+config SND_SOC_RT5663
        tristate
 
-config SND_SOC_RT5663
+config SND_SOC_RT5665
        tristate
 
 config SND_SOC_RT5670
index 0a7f4ffe0d785cef79d5310785d6aaa08172f30d..1796cb987e712c78b0fe8f3aa8ec71177256b5b2 100644 (file)
@@ -119,8 +119,8 @@ snd-soc-rt5645-objs := rt5645.o
 snd-soc-rt5651-objs := rt5651.o
 snd-soc-rt5659-objs := rt5659.o
 snd-soc-rt5660-objs := rt5660.o
-snd-soc-rt5665-objs := rt5665.o
 snd-soc-rt5663-objs := rt5663.o
+snd-soc-rt5665-objs := rt5665.o
 snd-soc-rt5670-objs := rt5670.o
 snd-soc-rt5677-objs := rt5677.o
 snd-soc-rt5677-spi-objs := rt5677-spi.o
@@ -348,8 +348,8 @@ obj-$(CONFIG_SND_SOC_RT5645)        += snd-soc-rt5645.o
 obj-$(CONFIG_SND_SOC_RT5651)   += snd-soc-rt5651.o
 obj-$(CONFIG_SND_SOC_RT5659)   += snd-soc-rt5659.o
 obj-$(CONFIG_SND_SOC_RT5660)   += snd-soc-rt5660.o
-obj-$(CONFIG_SND_SOC_RT5665)   += snd-soc-rt5665.o
 obj-$(CONFIG_SND_SOC_RT5663)   += snd-soc-rt5663.o
+obj-$(CONFIG_SND_SOC_RT5665)   += snd-soc-rt5665.o
 obj-$(CONFIG_SND_SOC_RT5670)   += snd-soc-rt5670.o
 obj-$(CONFIG_SND_SOC_RT5677)   += snd-soc-rt5677.o
 obj-$(CONFIG_SND_SOC_RT5677_SPI)       += snd-soc-rt5677-spi.o
index ebd0f7c5ad3b19769bd542185a17c48cb3561678..bd51f3655ee3e54468ef6b09000cad9d95f16291 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/gpio.h>
 #include <linux/sched.h>
 #include <linux/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_qos.h>
 #include <linux/sysfs.h>
index cf026252cd4a4bded9b91301380e8678e5d37251..4924575d2e95d3d49c0bdad57bc8691c472da5b2 100644 (file)
@@ -98,7 +98,8 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
 
-int asoc_simple_card_parse_clk(struct device_node *node,
+int asoc_simple_card_parse_clk(struct device *dev,
+                              struct device_node *node,
                               struct device_node *dai_of_node,
                               struct asoc_simple_dai *simple_dai)
 {
@@ -111,14 +112,13 @@ int asoc_simple_card_parse_clk(struct device_node *node,
         *  or "system-clock-frequency = <xxx>"
         *  or device's module clock.
         */
-       clk = of_clk_get(node, 0);
+       clk = devm_get_clk_from_child(dev, node, NULL);
        if (!IS_ERR(clk)) {
                simple_dai->sysclk = clk_get_rate(clk);
-               simple_dai->clk = clk;
        } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
                simple_dai->sysclk = val;
        } else {
-               clk = of_clk_get(dai_of_node, 0);
+               clk = devm_get_clk_from_child(dev, dai_of_node, NULL);
                if (!IS_ERR(clk))
                        simple_dai->sysclk = clk_get_rate(clk);
        }
index a385ff6bfa4b3acbc1809176cd23eb6d4e0c5eb6..85b4f18065143cd0ad82f347d6e0959c9ad84e79 100644 (file)
@@ -278,11 +278,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        if (ret < 0)
                goto dai_link_of_err;
 
-       ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
+       ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
        if (ret < 0)
                goto dai_link_of_err;
 
-       ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
+       ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
        if (ret < 0)
                goto dai_link_of_err;
 
index bb86ee0424902d3f18bc0ad94bd33d20242416c0..308ff4c11a8d21293ddb5776e5e3e0eb900165f0 100644 (file)
@@ -128,7 +128,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
                if (ret)
                        return ret;
 
-               ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
+               ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai_props);
                if (ret < 0)
                        return ret;
 
@@ -153,7 +153,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
                if (ret < 0)
                        return ret;
 
-               ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
+               ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai_props);
                if (ret < 0)
                        return ret;
 
index 7c423151ef7d2f1a19e091cde5cfef5ba124e45b..f1f1d7959a1b986020beb3665f233404c73b2ab4 100644 (file)
@@ -111,6 +111,7 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380
 config SND_SOC_SMARTQ
        tristate "SoC I2S Audio support for SmartQ board"
        depends on MACH_SMARTQ || COMPILE_TEST
+       depends on GPIOLIB || COMPILE_TEST
        depends on I2C
        select SND_SAMSUNG_I2S
        select SND_SOC_WM8750
@@ -193,6 +194,7 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
 config SND_SOC_SAMSUNG_TM2_WM5110
        tristate "SoC I2S Audio support for WM5110 on TM2 board"
        depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+       depends on GPIOLIB || COMPILE_TEST
        select SND_SOC_MAX98504
        select SND_SOC_WM5110
        select SND_SAMSUNG_I2S
index 85324e61cbd56da5c07a55ae02151516ce4a8f00..52a47ed292a4739d485c40a977099b186a649281 100644 (file)
 
 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 
-enum samsung_dai_type {
-       TYPE_PRI,
-       TYPE_SEC,
-};
-
 struct samsung_i2s_variant_regs {
        unsigned int    bfs_off;
        unsigned int    rfs_off;
@@ -54,7 +49,6 @@ struct samsung_i2s_variant_regs {
 };
 
 struct samsung_i2s_dai_data {
-       int dai_type;
        u32 quirks;
        const struct samsung_i2s_variant_regs *i2s_variant_regs;
 };
@@ -483,6 +477,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
        unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
        u32 mod, mask, val = 0;
        unsigned long flags;
+       int ret = 0;
+
+       pm_runtime_get_sync(dai->dev);
 
        spin_lock_irqsave(i2s->lock, flags);
        mod = readl(i2s->addr + I2SMOD);
@@ -507,7 +504,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                        && (mod & cdcon_mask))))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
-                       return -EAGAIN;
+                       ret = -EAGAIN;
+                       goto err;
                }
 
                if (dir == SND_SOC_CLOCK_IN)
@@ -535,7 +533,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                } else {
                                        i2s->rclk_srcrate =
                                                clk_get_rate(i2s->op_clk);
-                                       return 0;
+                                       goto done;
                                }
                        }
 
@@ -546,8 +544,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                i2s->op_clk = clk_get(&i2s->pdev->dev,
                                                "i2s_opclk0");
 
-                       if (WARN_ON(IS_ERR(i2s->op_clk)))
-                               return PTR_ERR(i2s->op_clk);
+                       if (WARN_ON(IS_ERR(i2s->op_clk))) {
+                               ret = PTR_ERR(i2s->op_clk);
+                               i2s->op_clk = NULL;
+                               goto err;
+                       }
 
                        clk_prepare_enable(i2s->op_clk);
                        i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
@@ -561,12 +562,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                || (clk_id && !(mod & rsrc_mask))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
-                       return -EAGAIN;
+                       ret = -EAGAIN;
+                       goto err;
                } else {
                        /* Call can't be on the active DAI */
                        i2s->op_clk = other->op_clk;
                        i2s->rclk_srcrate = other->rclk_srcrate;
-                       return 0;
+                       goto done;
                }
 
                if (clk_id == 1)
@@ -574,7 +576,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                break;
        default:
                dev_err(&i2s->pdev->dev, "We don't serve that!\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        spin_lock_irqsave(i2s->lock, flags);
@@ -582,8 +585,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
        mod = (mod & ~mask) | val;
        writel(mod, i2s->addr + I2SMOD);
        spin_unlock_irqrestore(i2s->lock, flags);
+done:
+       pm_runtime_put(dai->dev);
 
        return 0;
+err:
+       pm_runtime_put(dai->dev);
+       return ret;
 }
 
 static int i2s_set_fmt(struct snd_soc_dai *dai,
@@ -652,6 +660,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
+       pm_runtime_get_sync(dai->dev);
        spin_lock_irqsave(i2s->lock, flags);
        mod = readl(i2s->addr + I2SMOD);
        /*
@@ -661,6 +670,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
        if (any_active(i2s) &&
                ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
                spin_unlock_irqrestore(i2s->lock, flags);
+               pm_runtime_put(dai->dev);
                dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                return -EAGAIN;
@@ -670,6 +680,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
        mod |= tmp;
        writel(mod, i2s->addr + I2SMOD);
        spin_unlock_irqrestore(i2s->lock, flags);
+       pm_runtime_put(dai->dev);
 
        return 0;
 }
@@ -681,6 +692,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
        u32 mod, mask = 0, val = 0;
        unsigned long flags;
 
+       WARN_ON(!pm_runtime_active(dai->dev));
+
        if (!is_secondary(i2s))
                mask |= (MOD_DC2_EN | MOD_DC1_EN);
 
@@ -769,6 +782,8 @@ static int i2s_startup(struct snd_pcm_substream *substream,
        struct i2s_dai *other = get_other_dai(i2s);
        unsigned long flags;
 
+       pm_runtime_get_sync(dai->dev);
+
        spin_lock_irqsave(&lock, flags);
 
        i2s->mode |= DAI_OPENED;
@@ -806,6 +821,8 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
        i2s->bfs = 0;
 
        spin_unlock_irqrestore(&lock, flags);
+
+       pm_runtime_put(dai->dev);
 }
 
 static int config_setup(struct i2s_dai *i2s)
@@ -880,6 +897,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               pm_runtime_get_sync(dai->dev);
                spin_lock_irqsave(i2s->lock, flags);
 
                if (config_setup(i2s)) {
@@ -908,6 +926,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
                }
 
                spin_unlock_irqrestore(i2s->lock, flags);
+               pm_runtime_put(dai->dev);
                break;
        }
 
@@ -922,13 +941,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
 
        switch (div_id) {
        case SAMSUNG_I2S_DIV_BCLK:
+               pm_runtime_get_sync(dai->dev);
                if ((any_active(i2s) && div && (get_bfs(i2s) != div))
                        || (other && other->bfs && (other->bfs != div))) {
+                       pm_runtime_put(dai->dev);
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                        return -EAGAIN;
                }
                i2s->bfs = div;
+               pm_runtime_put(dai->dev);
                break;
        default:
                dev_err(&i2s->pdev->dev,
@@ -947,6 +969,8 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
        snd_pcm_sframes_t delay;
        const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
+       WARN_ON(!pm_runtime_active(dai->dev));
+
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                delay = FIC_RXCOUNT(reg);
        else if (is_secondary(i2s))
@@ -960,24 +984,12 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
 #ifdef CONFIG_PM
 static int i2s_suspend(struct snd_soc_dai *dai)
 {
-       struct i2s_dai *i2s = to_info(dai);
-
-       i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
-       i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
-       i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
-
-       return 0;
+       return pm_runtime_force_suspend(dai->dev);
 }
 
 static int i2s_resume(struct snd_soc_dai *dai)
 {
-       struct i2s_dai *i2s = to_info(dai);
-
-       writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
-       writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
-       writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
-
-       return 0;
+       return pm_runtime_force_resume(dai->dev);
 }
 #else
 #define i2s_suspend NULL
@@ -990,6 +1002,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        struct i2s_dai *other = get_other_dai(i2s);
        unsigned long flags;
 
+       pm_runtime_get_sync(dai->dev);
+
        if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
                snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
                                           NULL);
@@ -1022,6 +1036,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (!is_opened(other))
                i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
                                0, SND_SOC_CLOCK_IN);
+       pm_runtime_put(dai->dev);
 
        return 0;
 }
@@ -1031,6 +1046,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
        struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
        unsigned long flags;
 
+       pm_runtime_get_sync(dai->dev);
+
        if (!is_secondary(i2s)) {
                if (i2s->quirks & QUIRK_NEED_RSTCLR) {
                        spin_lock_irqsave(i2s->lock, flags);
@@ -1039,6 +1056,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
                }
        }
 
+       pm_runtime_put(dai->dev);
+
        return 0;
 }
 
@@ -1066,7 +1085,6 @@ static const struct snd_soc_component_driver samsung_i2s_component = {
 static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
 {
        struct i2s_dai *i2s;
-       int ret;
 
        i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
        if (i2s == NULL)
@@ -1091,33 +1109,21 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
                i2s->i2s_dai_drv.capture.channels_max = 2;
                i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
                i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
-               dev_set_drvdata(&i2s->pdev->dev, i2s);
-       } else {        /* Create a new platform_device for Secondary */
-               i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1);
-               if (!i2s->pdev)
-                       return NULL;
-
-               i2s->pdev->dev.parent = &pdev->dev;
-
-               platform_set_drvdata(i2s->pdev, i2s);
-               ret = platform_device_add(i2s->pdev);
-               if (ret < 0)
-                       return NULL;
        }
-
        return i2s;
 }
 
-static void i2s_free_sec_dai(struct i2s_dai *i2s)
-{
-       platform_device_del(i2s->pdev);
-}
-
 #ifdef CONFIG_PM
 static int i2s_runtime_suspend(struct device *dev)
 {
        struct i2s_dai *i2s = dev_get_drvdata(dev);
 
+       i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
+       i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
+       i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
+
+       if (i2s->op_clk)
+               clk_disable_unprepare(i2s->op_clk);
        clk_disable_unprepare(i2s->clk);
 
        return 0;
@@ -1128,6 +1134,12 @@ static int i2s_runtime_resume(struct device *dev)
        struct i2s_dai *i2s = dev_get_drvdata(dev);
 
        clk_prepare_enable(i2s->clk);
+       if (i2s->op_clk)
+               clk_prepare_enable(i2s->op_clk);
+
+       writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
+       writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
+       writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
 
        return 0;
 }
@@ -1179,13 +1191,13 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
                u32 val = readl(i2s->addr + I2SPSR);
                writel(val | PSR_PSREN, i2s->addr + I2SPSR);
 
-               i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL,
+               i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
                                "i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
                                CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
                                i2s->addr + I2SMOD, reg_info->rclksrc_off,
                                1, 0, i2s->lock);
 
-               i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL,
+               i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
                                "i2s_presc", "i2s_rclksrc",
                                CLK_SET_RATE_PARENT,
                                i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
@@ -1196,7 +1208,7 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
        of_property_read_string_index(dev->of_node,
                                "clock-output-names", 0, &clk_name[0]);
 
-       i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0],
+       i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0],
                                p_names[0], CLK_SET_RATE_PARENT,
                                i2s->addr + I2SMOD, reg_info->cdclkcon_off,
                                CLK_GATE_SET_TO_DISABLE, i2s->lock);
@@ -1218,7 +1230,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
 {
        struct i2s_dai *pri_dai, *sec_dai = NULL;
        struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
-       struct samsung_i2s *i2s_cfg = NULL;
        struct resource *res;
        u32 regs_base, quirks = 0, idma_addr = 0;
        struct device_node *np = pdev->dev.of_node;
@@ -1231,22 +1242,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                i2s_dai_data = (struct samsung_i2s_dai_data *)
                                platform_get_device_id(pdev)->driver_data;
 
-       /* Call during the secondary interface registration */
-       if (i2s_dai_data->dai_type == TYPE_SEC) {
-               sec_dai = dev_get_drvdata(&pdev->dev);
-               if (!sec_dai) {
-                       dev_err(&pdev->dev, "Unable to get drvdata\n");
-                       return -EFAULT;
-               }
-               ret = samsung_asoc_dma_platform_register(&pdev->dev,
-                                       sec_dai->filter, "tx-sec", NULL);
-               if (ret != 0)
-                       return ret;
-
-               return devm_snd_soc_register_component(&sec_dai->pdev->dev,
-                                               &samsung_i2s_component,
-                                               &sec_dai->i2s_dai_drv, 1);
-       }
 
        pri_dai = i2s_alloc_dai(pdev, false);
        if (!pri_dai) {
@@ -1267,13 +1262,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
                pri_dai->filter = i2s_pdata->dma_filter;
 
-               if (&i2s_pdata->type)
-                       i2s_cfg = &i2s_pdata->type.i2s;
-
-               if (i2s_cfg) {
-                       quirks = i2s_cfg->quirks;
-                       idma_addr = i2s_cfg->idma_addr;
-               }
+               quirks = i2s_pdata->type.quirks;
+               idma_addr = i2s_pdata->type.idma_addr;
        } else {
                quirks = i2s_dai_data->quirks;
                if (of_property_read_u32(np, "samsung,idma-addr",
@@ -1320,6 +1310,12 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_disable_clk;
 
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                       &samsung_i2s_component,
+                                       &pri_dai->i2s_dai_drv, 1);
+       if (ret < 0)
+               goto err_disable_clk;
+
        if (quirks & QUIRK_SEC_DAI) {
                sec_dai = i2s_alloc_dai(pdev, true);
                if (!sec_dai) {
@@ -1345,6 +1341,17 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                sec_dai->idma_playback.addr = idma_addr;
                sec_dai->pri_dai = pri_dai;
                pri_dai->sec_dai = sec_dai;
+
+               ret = samsung_asoc_dma_platform_register(&pdev->dev,
+                                       sec_dai->filter, "tx-sec", NULL);
+               if (ret < 0)
+                       goto err_disable_clk;
+
+               ret = devm_snd_soc_register_component(&pdev->dev,
+                                               &samsung_i2s_component,
+                                               &sec_dai->i2s_dai_drv, 1);
+               if (ret < 0)
+                       goto err_disable_clk;
        }
 
        if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
@@ -1353,13 +1360,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                goto err_disable_clk;
        }
 
-       ret = devm_snd_soc_register_component(&pri_dai->pdev->dev,
-                                       &samsung_i2s_component,
-                                       &pri_dai->i2s_dai_drv, 1);
-       if (ret < 0)
-               goto err_free_dai;
-
+       dev_set_drvdata(&pdev->dev, pri_dai);
 
+       pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
        ret = i2s_register_clock_provider(pdev);
@@ -1367,9 +1370,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                return 0;
 
        pm_runtime_disable(&pdev->dev);
-err_free_dai:
-       if (sec_dai)
-               i2s_free_sec_dai(sec_dai);
 err_disable_clk:
        clk_disable_unprepare(pri_dai->clk);
        return ret;
@@ -1377,25 +1377,20 @@ err_disable_clk:
 
 static int samsung_i2s_remove(struct platform_device *pdev)
 {
-       struct i2s_dai *i2s, *other;
+       struct i2s_dai *pri_dai, *sec_dai;
 
-       i2s = dev_get_drvdata(&pdev->dev);
-       other = get_other_dai(i2s);
+       pri_dai = dev_get_drvdata(&pdev->dev);
+       sec_dai = pri_dai->sec_dai;
 
-       if (other) {
-               other->pri_dai = NULL;
-               other->sec_dai = NULL;
-       } else {
-               pm_runtime_disable(&pdev->dev);
-       }
+       pri_dai->sec_dai = NULL;
+       sec_dai->pri_dai = NULL;
 
-       if (!is_secondary(i2s)) {
-               i2s_unregister_clock_provider(pdev);
-               clk_disable_unprepare(i2s->clk);
-       }
+       pm_runtime_get_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
 
-       i2s->pri_dai = NULL;
-       i2s->sec_dai = NULL;
+       i2s_unregister_clock_provider(pdev);
+       clk_disable_unprepare(pri_dai->clk);
+       pm_runtime_put_noidle(&pdev->dev);
 
        return 0;
 }
@@ -1457,49 +1452,37 @@ static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
 };
 
 static const struct samsung_i2s_dai_data i2sv3_dai_type = {
-       .dai_type = TYPE_PRI,
        .quirks = QUIRK_NO_MUXPSR,
        .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv5_dai_type = {
-       .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
                        QUIRK_SUPPORTS_IDMA,
        .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv6_dai_type = {
-       .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
                        QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
        .i2s_variant_regs = &i2sv6_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv7_dai_type = {
-       .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
                        QUIRK_SUPPORTS_TDM,
        .i2s_variant_regs = &i2sv7_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
-       .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
        .i2s_variant_regs = &i2sv5_i2s1_regs,
 };
 
-static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
-       .dai_type = TYPE_SEC,
-};
-
 static const struct platform_device_id samsung_i2s_driver_ids[] = {
        {
                .name           = "samsung-i2s",
                .driver_data    = (kernel_ulong_t)&i2sv3_dai_type,
-       }, {
-               .name           = "samsung-i2s-sec",
-               .driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
        },
        {},
 };
@@ -1531,6 +1514,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match);
 static const struct dev_pm_ops samsung_i2s_pm = {
        SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
                                i2s_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                    pm_runtime_force_resume)
 };
 
 static struct platform_driver samsung_i2s_driver = {
index de724ce7b955e2935f9df628bd3f8bc533b67fd3..6e4dfa7e2c89434c26fc6eff4f21e72c5cadebbf 100644 (file)
@@ -32,14 +32,11 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        unsigned int pll_out;
-       int bfs, rfs, ret;
+       int rfs, ret;
 
        switch (params_width(params)) {
        case 8:
-               bfs = 16;
-               break;
        case 16:
-               bfs = 32;
                break;
        default:
                return -EINVAL;
index 5cdf7d19b87fd25fb72bb4b182104cdd811f0203..24cc9d63ce87f0e09716d655ce567916e766ca1c 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/clk.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <sound/pcm_params.h>
index 6c344e16aca41c090ed540bb6f43c0c1a82c191f..22408bc2d6ec686bccfc3982b223d8ea5387f6d1 100644 (file)
@@ -9,9 +9,20 @@ config SND_SUN4I_CODEC
          Select Y or M to add support for the Codec embedded in the Allwinner
          A10 and affiliated SoCs.
 
+config SND_SUN8I_CODEC
+       tristate "Allwinner SUN8I audio codec"
+       depends on OF
+       depends on MACH_SUN8I || COMPILE_TEST
+       select REGMAP_MMIO
+       help
+         This option enables the digital part of the internal audio codec for
+         Allwinner sun8i SoC (and particularly A33).
+
+         Say Y or M if you want to add sun8i digital audio codec support.
+
 config SND_SUN8I_CODEC_ANALOG
        tristate "Allwinner sun8i Codec Analog Controls Support"
-       depends on MACH_SUN8I || COMPILE_TEST
+       depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
        select REGMAP
        help
          Say Y or M if you want to add support for the analog controls for
index 241c0df9ca0cd48917939b77a015047aafb6694f..1f1af627173185a4f5e8bbf9eed484787c57e5c7 100644 (file)
@@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
 obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
 obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
 obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
+obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
index 848af01692a0ba579197d99b67fb1c3c7b6cd455..c3aab10fa085218aca24d2d6ca8ebf2ab2cc5f61 100644 (file)
@@ -1058,6 +1058,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
        { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
        { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
        { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
+       { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
        { "LINEOUT", NULL, "Line Out Source Playback Route" },
 
        /* ADC Routes */
index 4237323ef5945a37c11c0097f5f4ff62babca0f1..3635bbc72cbcd3f4053569195007f57762758e7f 100644 (file)
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
@@ -92,6 +94,7 @@ struct sun4i_i2s {
        struct clk      *bus_clk;
        struct clk      *mod_clk;
        struct regmap   *regmap;
+       struct reset_control *rst;
 
        unsigned int    mclk_freq;
 
@@ -651,9 +654,22 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)
        return 0;
 }
 
+struct sun4i_i2s_quirks {
+       bool has_reset;
+};
+
+static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
+       .has_reset      = false,
+};
+
+static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
+       .has_reset      = true,
+};
+
 static int sun4i_i2s_probe(struct platform_device *pdev)
 {
        struct sun4i_i2s *i2s;
+       const struct sun4i_i2s_quirks *quirks;
        struct resource *res;
        void __iomem *regs;
        int irq, ret;
@@ -674,6 +690,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                return irq;
        }
 
+       quirks = of_device_get_match_data(&pdev->dev);
+       if (!quirks) {
+               dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+               return -ENODEV;
+       }
+
        i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
        if (IS_ERR(i2s->bus_clk)) {
                dev_err(&pdev->dev, "Can't get our bus clock\n");
@@ -692,7 +714,24 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Can't get our mod clock\n");
                return PTR_ERR(i2s->mod_clk);
        }
-       
+
+       if (quirks->has_reset) {
+               i2s->rst = devm_reset_control_get(&pdev->dev, NULL);
+               if (IS_ERR(i2s->rst)) {
+                       dev_err(&pdev->dev, "Failed to get reset control\n");
+                       return PTR_ERR(i2s->rst);
+               }
+       }
+
+       if (!IS_ERR(i2s->rst)) {
+               ret = reset_control_deassert(i2s->rst);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to deassert the reset control\n");
+                       return -EINVAL;
+               }
+       }
+
        i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
        i2s->playback_dma_data.maxburst = 8;
 
@@ -727,23 +766,37 @@ err_suspend:
                sun4i_i2s_runtime_suspend(&pdev->dev);
 err_pm_disable:
        pm_runtime_disable(&pdev->dev);
+       if (!IS_ERR(i2s->rst))
+               reset_control_assert(i2s->rst);
 
        return ret;
 }
 
 static int sun4i_i2s_remove(struct platform_device *pdev)
 {
+       struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
        snd_dmaengine_pcm_unregister(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun4i_i2s_runtime_suspend(&pdev->dev);
 
+       if (!IS_ERR(i2s->rst))
+               reset_control_assert(i2s->rst);
+
        return 0;
 }
 
 static const struct of_device_id sun4i_i2s_match[] = {
-       { .compatible = "allwinner,sun4i-a10-i2s", },
+       {
+               .compatible = "allwinner,sun4i-a10-i2s",
+               .data = &sun4i_a10_i2s_quirks,
+       },
+       {
+               .compatible = "allwinner,sun6i-a31-i2s",
+               .data = &sun6i_a31_i2s_quirks,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
index 88fbb3a1e66015640a40ff61a70879911ddba38e..eaefd07a5ed080ecbe9373378a767cfffd6faf68 100644 (file)
        #define SUN4I_SPDIF_ISTA_RXOSTA                 BIT(1)
        #define SUN4I_SPDIF_ISTA_RXASTA                 BIT(0)
 
+#define SUN8I_SPDIF_TXFIFO     (0x20)
+
 #define SUN4I_SPDIF_TXCNT      (0x24)
 
 #define SUN4I_SPDIF_RXCNT      (0x28)
@@ -403,17 +405,38 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {
        .name = "spdif",
 };
 
-static const struct snd_soc_dapm_widget dit_widgets[] = {
-       SND_SOC_DAPM_OUTPUT("spdif-out"),
+struct sun4i_spdif_quirks {
+       unsigned int reg_dac_txdata;    /* TX FIFO offset for DMA config */
+       bool has_reset;
+};
+
+static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
+       .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
 };
 
-static const struct snd_soc_dapm_route dit_routes[] = {
-       { "spdif-out", NULL, "Playback" },
+static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
+       .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+       .has_reset      = true,
+};
+
+static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
+       .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+       .has_reset      = true,
 };
 
 static const struct of_device_id sun4i_spdif_of_match[] = {
-       { .compatible = "allwinner,sun4i-a10-spdif", },
-       { .compatible = "allwinner,sun6i-a31-spdif", },
+       {
+               .compatible = "allwinner,sun4i-a10-spdif",
+               .data = &sun4i_a10_spdif_quirks,
+       },
+       {
+               .compatible = "allwinner,sun6i-a31-spdif",
+               .data = &sun6i_a31_spdif_quirks,
+       },
+       {
+               .compatible = "allwinner,sun8i-h3-spdif",
+               .data = &sun8i_h3_spdif_quirks,
+       },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
@@ -446,6 +469,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
 {
        struct sun4i_spdif_dev *host;
        struct resource *res;
+       const struct sun4i_spdif_quirks *quirks;
        int ret;
        void __iomem *base;
 
@@ -467,6 +491,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
        if (IS_ERR(base))
                return PTR_ERR(base);
 
+       quirks = of_device_get_match_data(&pdev->dev);
+       if (quirks == NULL) {
+               dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+               return -ENODEV;
+       }
+
        host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
                                                &sun4i_spdif_regmap_config);
 
@@ -480,23 +510,21 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
        host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
        if (IS_ERR(host->spdif_clk)) {
                dev_err(&pdev->dev, "failed to get a spdif clock.\n");
-               ret = PTR_ERR(host->spdif_clk);
-               goto err_disable_apb_clk;
+               return PTR_ERR(host->spdif_clk);
        }
 
-       host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
+       host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;
        host->dma_params_tx.maxburst = 8;
        host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 
        platform_set_drvdata(pdev, host);
 
-       if (of_device_is_compatible(pdev->dev.of_node,
-                                   "allwinner,sun6i-a31-spdif")) {
+       if (quirks->has_reset) {
                host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
                if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
                        ret = -EPROBE_DEFER;
                        dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
-                       goto err_disable_apb_clk;
+                       return ret;
                }
                if (!IS_ERR(host->rst))
                        reset_control_deassert(host->rst);
@@ -505,7 +533,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
        ret = devm_snd_soc_register_component(&pdev->dev,
                                &sun4i_spdif_component, &sun4i_spdif_dai, 1);
        if (ret)
-               goto err_disable_apb_clk;
+               return ret;
 
        pm_runtime_enable(&pdev->dev);
        if (!pm_runtime_enabled(&pdev->dev)) {
@@ -523,9 +551,6 @@ err_suspend:
                sun4i_spdif_runtime_suspend(&pdev->dev);
 err_unregister:
        pm_runtime_disable(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-err_disable_apb_clk:
-       clk_disable_unprepare(host->apb_clk);
        return ret;
 }
 
@@ -535,9 +560,6 @@ static int sun4i_spdif_remove(struct platform_device *pdev)
        if (!pm_runtime_status_suspended(&pdev->dev))
                sun4i_spdif_runtime_suspend(&pdev->dev);
 
-       snd_soc_unregister_platform(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
        return 0;
 }
 
index af02290ebe49cc117377427e17b463a9f9aa479a..72331332b72ee94a4f9d5a268bc2ef9b94ad278f 100644 (file)
@@ -398,11 +398,37 @@ static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
                      sun8i_codec_hp_src_enum),
 };
 
+static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
+                                    struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
+                                             BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
+                                             BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
+               /*
+                * Need a delay to have the amplifier up. 700ms seems the best
+                * compromise between the time to let the amplifier up and the
+                * time not to feel this delay while playing a sound.
+                */
+               msleep(700);
+       } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
+                                             BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
+                                             0x0);
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
        SND_SOC_DAPM_MUX("Headphone Source Playback Route",
                         SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
-       SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
-                            SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
+                              SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
+                              sun8i_headphone_amp_event,
+                              SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
        SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
                            SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
        SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
new file mode 100644 (file)
index 0000000..b92bdc8
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * This driver supports the digital controls for the internal codec
+ * found in Allwinner's A33 SoCs.
+ *
+ * (C) Copyright 2010-2016
+ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
+ * huangxin <huangxin@Reuuimllatech.com>
+ * Mylène Josserand <mylene.josserand@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#define SUN8I_SYSCLK_CTL                               0x00c
+#define SUN8I_SYSCLK_CTL_AIF1CLK_ENA                   11
+#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL               9
+#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC                   8
+#define SUN8I_SYSCLK_CTL_SYSCLK_ENA                    3
+#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_DAC                          2
+#define SUN8I_MOD_RST_CTL                              0x014
+#define SUN8I_MOD_RST_CTL_AIF1                         15
+#define SUN8I_MOD_RST_CTL_DAC                          2
+#define SUN8I_SYS_SR_CTRL                              0x018
+#define SUN8I_SYS_SR_CTRL_AIF1_FS                      12
+#define SUN8I_SYS_SR_CTRL_AIF2_FS                      8
+#define SUN8I_AIF1CLK_CTRL                             0x040
+#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD               15
+#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV               14
+#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV               13
+#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV               9
+#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV               6
+#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16            (1 << 6)
+#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_DACDAT_CTRL                         0x048
+#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA           15
+#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA           14
+#define SUN8I_DAC_DIG_CTRL                             0x120
+#define SUN8I_DAC_DIG_CTRL_ENDA                        15
+#define SUN8I_DAC_MXR_SRC                              0x130
+#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L        15
+#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L        14
+#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL        13
+#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL            12
+#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R        11
+#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R        10
+#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR        9
+#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR            8
+
+#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK         GENMASK(15, 12)
+#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK         GENMASK(11, 8)
+#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK  GENMASK(5, 4)
+#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK  GENMASK(8, 6)
+
+struct sun8i_codec {
+       struct device   *dev;
+       struct regmap   *regmap;
+       struct clk      *clk_module;
+       struct clk      *clk_bus;
+};
+
+static int sun8i_codec_runtime_resume(struct device *dev)
+{
+       struct sun8i_codec *scodec = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(scodec->clk_module);
+       if (ret) {
+               dev_err(dev, "Failed to enable the module clock\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(scodec->clk_bus);
+       if (ret) {
+               dev_err(dev, "Failed to enable the bus clock\n");
+               goto err_disable_modclk;
+       }
+
+       regcache_cache_only(scodec->regmap, false);
+
+       ret = regcache_sync(scodec->regmap);
+       if (ret) {
+               dev_err(dev, "Failed to sync regmap cache\n");
+               goto err_disable_clk;
+       }
+
+       return 0;
+
+err_disable_clk:
+       clk_disable_unprepare(scodec->clk_bus);
+
+err_disable_modclk:
+       clk_disable_unprepare(scodec->clk_module);
+
+       return ret;
+}
+
+static int sun8i_codec_runtime_suspend(struct device *dev)
+{
+       struct sun8i_codec *scodec = dev_get_drvdata(dev);
+
+       regcache_cache_only(scodec->regmap, true);
+       regcache_mark_dirty(scodec->regmap);
+
+       clk_disable_unprepare(scodec->clk_module);
+       clk_disable_unprepare(scodec->clk_bus);
+
+       return 0;
+}
+
+static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+       unsigned int rate = params_rate(params);
+
+       switch (rate) {
+       case 8000:
+       case 7350:
+               return 0x0;
+       case 11025:
+               return 0x1;
+       case 12000:
+               return 0x2;
+       case 16000:
+               return 0x3;
+       case 22050:
+               return 0x4;
+       case 24000:
+               return 0x5;
+       case 32000:
+               return 0x6;
+       case 44100:
+               return 0x7;
+       case 48000:
+               return 0x8;
+       case 96000:
+               return 0x9;
+       case 192000:
+               return 0xa;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec);
+       u32 value;
+
+       /* clock masters */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */
+               value = 0x0; /* Codec Master */
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */
+               value = 0x1; /* Codec Slave */
+               break;
+       default:
+               return -EINVAL;
+       }
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
+                          value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF: /* Normal */
+               value = 0x0;
+               break;
+       case SND_SOC_DAIFMT_IB_IF: /* Inversion */
+               value = 0x1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
+                          value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
+                          value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
+
+       /* DAI format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               value = 0x0;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               value = 0x1;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               value = 0x2;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               value = 0x3;
+               break;
+       default:
+               return -EINVAL;
+       }
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT),
+                          value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
+
+       return 0;
+}
+
+static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec);
+       int sample_rate;
+
+       /*
+        * The CPU DAI handles only a sample of 16 bits. Configure the
+        * codec to handle this type of sample resolution.
+        */
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
+                          SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
+
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
+                          SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16);
+
+       sample_rate = sun8i_codec_get_hw_rate(params);
+       if (sample_rate < 0)
+               return sample_rate;
+
+       regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
+                          SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
+                          sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
+       regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
+                          SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
+                          sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new sun8i_output_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE("LSlot 0", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, 1, 0),
+       SOC_DAPM_SINGLE("LSlot 1", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, 1, 0),
+       SOC_DAPM_SINGLE("DACL", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL, 1, 0),
+       SOC_DAPM_SINGLE("ADCL", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun8i_output_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE("RSlot 0", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
+       SOC_DAPM_SINGLE("RSlot 1", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
+       SOC_DAPM_SINGLE("DACR", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
+       SOC_DAPM_SINGLE("ADCR", SUN8I_DAC_MXR_SRC,
+                       SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
+       /* Digital parts of the DACs */
+       SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
+                           0, NULL, 0),
+
+       /* Analog DAC */
+       SND_SOC_DAPM_DAC("Digital Left DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL,
+                        SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
+       SND_SOC_DAPM_DAC("Digital Right DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL,
+                        SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
+
+       /* DAC Mixers */
+       SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0,
+                          sun8i_output_left_mixer_controls,
+                          ARRAY_SIZE(sun8i_output_left_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0,
+                          sun8i_output_right_mixer_controls,
+                          ARRAY_SIZE(sun8i_output_right_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("AIF1", SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0),
+       /* Inversion as 0=AIF1, 1=AIF2 */
+       SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0),
+
+       /* Module reset */
+       SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL,
+                           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_OUTPUT("HP"),
+};
+
+static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
+       /* Clock Routes */
+       { "AIF1", NULL, "SYSCLK AIF1" },
+       { "AIF1 PLL", NULL, "AIF1" },
+       { "RST AIF1", NULL, "AIF1 PLL" },
+       { "MODCLK AFI1", NULL, "RST AIF1" },
+       { "DAC", NULL, "MODCLK AFI1" },
+
+       { "RST DAC", NULL, "SYSCLK" },
+       { "MODCLK DAC", NULL, "RST DAC" },
+       { "DAC", NULL, "MODCLK DAC" },
+
+       /* DAC Routes */
+       { "Digital Left DAC", NULL, "DAC" },
+       { "Digital Right DAC", NULL, "DAC" },
+
+       /* DAC Mixer Routes */
+       { "Left DAC Mixer", "LSlot 0", "Digital Left DAC"},
+       { "Right DAC Mixer", "RSlot 0", "Digital Right DAC"},
+
+       /* End of route : HP out */
+       { "HP", NULL, "Left DAC Mixer" },
+       { "HP", NULL, "Right DAC Mixer" },
+};
+
+static struct snd_soc_dai_ops sun8i_codec_dai_ops = {
+       .hw_params = sun8i_codec_hw_params,
+       .set_fmt = sun8i_set_fmt,
+};
+
+static struct snd_soc_dai_driver sun8i_codec_dai = {
+       .name = "sun8i",
+       /* playback capabilities */
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       /* pcm operations */
+       .ops = &sun8i_codec_dai_ops,
+};
+
+static struct snd_soc_codec_driver sun8i_soc_codec = {
+       .component_driver = {
+               .dapm_widgets           = sun8i_codec_dapm_widgets,
+               .num_dapm_widgets       = ARRAY_SIZE(sun8i_codec_dapm_widgets),
+               .dapm_routes            = sun8i_codec_dapm_routes,
+               .num_dapm_routes        = ARRAY_SIZE(sun8i_codec_dapm_routes),
+       },
+};
+
+static const struct regmap_config sun8i_codec_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = SUN8I_DAC_MXR_SRC,
+
+       .cache_type     = REGCACHE_FLAT,
+};
+
+static int sun8i_codec_probe(struct platform_device *pdev)
+{
+       struct resource *res_base;
+       struct sun8i_codec *scodec;
+       void __iomem *base;
+       int ret;
+
+       scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+       if (!scodec)
+               return -ENOMEM;
+
+       scodec->dev = &pdev->dev;
+
+       scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
+       if (IS_ERR(scodec->clk_module)) {
+               dev_err(&pdev->dev, "Failed to get the module clock\n");
+               return PTR_ERR(scodec->clk_module);
+       }
+
+       scodec->clk_bus = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(scodec->clk_bus)) {
+               dev_err(&pdev->dev, "Failed to get the bus clock\n");
+               return PTR_ERR(scodec->clk_bus);
+       }
+
+       res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res_base);
+       if (IS_ERR(base)) {
+               dev_err(&pdev->dev, "Failed to map the registers\n");
+               return PTR_ERR(base);
+       }
+
+       scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                              &sun8i_codec_regmap_config);
+       if (IS_ERR(scodec->regmap)) {
+               dev_err(&pdev->dev, "Failed to create our regmap\n");
+               return PTR_ERR(scodec->regmap);
+       }
+
+       platform_set_drvdata(pdev, scodec);
+
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev)) {
+               ret = sun8i_codec_runtime_resume(&pdev->dev);
+               if (ret)
+                       goto err_pm_disable;
+       }
+
+       ret = snd_soc_register_codec(&pdev->dev, &sun8i_soc_codec,
+                                    &sun8i_codec_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register codec\n");
+               goto err_suspend;
+       }
+
+       return ret;
+
+err_suspend:
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun8i_codec_runtime_suspend(&pdev->dev);
+
+err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+
+       return ret;
+}
+
+static int sun8i_codec_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct sun8i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun8i_codec_runtime_suspend(&pdev->dev);
+
+       snd_soc_unregister_codec(&pdev->dev);
+       clk_disable_unprepare(scodec->clk_module);
+       clk_disable_unprepare(scodec->clk_bus);
+
+       return 0;
+}
+
+static const struct of_device_id sun8i_codec_of_match[] = {
+       { .compatible = "allwinner,sun8i-a33-codec" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
+
+static const struct dev_pm_ops sun8i_codec_pm_ops = {
+       SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
+                          sun8i_codec_runtime_resume, NULL)
+};
+
+static struct platform_driver sun8i_codec_driver = {
+       .driver = {
+               .name = "sun8i-codec",
+               .of_match_table = sun8i_codec_of_match,
+               .pm = &sun8i_codec_pm_ops,
+       },
+       .probe = sun8i_codec_probe,
+       .remove = sun8i_codec_remove,
+};
+module_platform_driver(sun8i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
+MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sun8i-codec");