Merge branch 'asoc-4.22' into asoc-5.0
authorMark Brown <broonie@kernel.org>
Mon, 7 Jan 2019 12:18:14 +0000 (12:18 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 7 Jan 2019 12:18:14 +0000 (12:18 +0000)
53 files changed:
Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt [deleted file]
Documentation/devicetree/bindings/sound/cs4341.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/simple-scu-card.txt [deleted file]
Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt [new file with mode: 0644]
sound/core/compress_offload.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/cs4341.c [new file with mode: 0644]
sound/soc/codecs/es8316.c
sound/soc/codecs/es8316.h
sound/soc/codecs/pcm512x.c
sound/soc/codecs/rk3328_codec.c [new file with mode: 0644]
sound/soc/codecs/rk3328_codec.h [new file with mode: 0644]
sound/soc/codecs/rt274.c
sound/soc/codecs/rt5682.c
sound/soc/codecs/wm8904.c
sound/soc/generic/Kconfig
sound/soc/generic/Makefile
sound/soc/generic/audio-graph-card.c
sound/soc/generic/audio-graph-scu-card.c [deleted file]
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-card.c
sound/soc/generic/simple-scu-card.c [deleted file]
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst-atom-controls.c
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/atom/sst/sst_drv_interface.c
sound/soc/intel/atom/sst/sst_loader.c
sound/soc/intel/baytrail/sst-baytrail-ipc.c
sound/soc/intel/baytrail/sst-baytrail-pcm.c
sound/soc/intel/boards/broadwell.c
sound/soc/intel/boards/bytcht_es8316.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/glk_rt5682_max98357a.c
sound/soc/intel/boards/haswell.c
sound/soc/intel/boards/kbl_da7219_max98927.c
sound/soc/intel/common/soc-acpi-intel-byt-match.c
sound/soc/intel/haswell/sst-haswell-ipc.c
sound/soc/intel/haswell/sst-haswell-pcm.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/qcom/qdsp6/q6asm-dai.c
sound/soc/qcom/sdm845.c
sound/soc/sh/dma-sh7760.c
sound/soc/ti/davinci-mcasp.c
sound/soc/xilinx/Kconfig
sound/soc/xilinx/Makefile
sound/soc/xilinx/xlnx_formatter_pcm.c [new file with mode: 0644]
sound/soc/xilinx/xlnx_i2s.c

diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt
deleted file mode 100644 (file)
index 62d4276..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-Audio-Graph-SCU-Card:
-
-Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM".
-
-It is based on common bindings for device graphs.
-see ${LINUX}/Documentation/devicetree/bindings/graph.txt
-
-Basically, Audio-Graph-SCU-Card property is same as
-Simple-Card / Simple-SCU-Card / Audio-Graph-Card.
-see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
-    ${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt
-    ${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
-
-Below are same as Simple-Card / Audio-Graph-Card.
-
-- label
-- dai-format
-- frame-master
-- bitclock-master
-- bitclock-inversion
-- frame-inversion
-- dai-tdm-slot-num
-- dai-tdm-slot-width
-- clocks / system-clock-frequency
-
-Below are same as Simple-SCU-Card.
-
-- convert-rate
-- convert-channels
-- prefix
-- routing
-
-Required properties:
-
-- compatible                           : "audio-graph-scu-card";
-- dais                                 : list of CPU DAI port{s}
-
-Example 1. Sampling Rate Conversion
-
-       sound_card {
-               compatible = "audio-graph-scu-card";
-
-               label = "sound-card";
-               prefix = "codec";
-               routing = "codec Playback", "DAI0 Playback",
-                         "DAI0 Capture",   "codec Capture";
-               convert-rate = <48000>;
-
-               dais = <&cpu_port>;
-       };
-
-       audio-codec {
-               ...
-
-               port {
-                       codec_endpoint: endpoint {
-                               remote-endpoint = <&cpu_endpoint>;
-                       };
-               };
-       };
-
-       dai-controller {
-               ...
-               cpu_port: port {
-                       cpu_endpoint: endpoint {
-                               remote-endpoint = <&codec_endpoint>;
-
-                               dai-format = "left_j";
-                               ...
-                       };
-               };
-       };
-
-Example 2. 2 CPU 1 Codec (Mixing)
-
-       sound_card {
-               compatible = "audio-graph-scu-card";
-
-               label = "sound-card";
-               routing = "codec Playback", "DAI0 Playback",
-                         "codec Playback", "DAI1 Playback",
-                         "DAI0 Capture",   "codec Capture";
-
-               dais = <&cpu_port0
-                       &cpu_port1>;
-       };
-
-       audio-codec {
-               ...
-
-               audio-graph-card,prefix = "codec";
-               audio-graph-card,convert-rate = <48000>;
-               port {
-                       codec_endpoint0: endpoint {
-                               remote-endpoint = <&cpu_endpoint0>;
-                       };
-                       codec_endpoint1: endpoint {
-                               remote-endpoint = <&cpu_endpoint1>;
-                       };
-               };
-       };
-
-       dai-controller {
-               ...
-               ports {
-                       cpu_port0: port {
-                               cpu_endpoint0: endpoint {
-                                       remote-endpoint = <&codec_endpoint0>;
-
-                                       dai-format = "left_j";
-                                       ...
-                               };
-                       };
-                       cpu_port1: port {
-                               cpu_endpoint1: endpoint {
-                                       remote-endpoint = <&codec_endpoint1>;
-
-                                       dai-format = "left_j";
-                                       ...
-                               };
-                       };
-               };
-       };
diff --git a/Documentation/devicetree/bindings/sound/cs4341.txt b/Documentation/devicetree/bindings/sound/cs4341.txt
new file mode 100644 (file)
index 0000000..12b4aa8
--- /dev/null
@@ -0,0 +1,22 @@
+Cirrus Logic CS4341 audio DAC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+  - compatible: "cirrus,cs4341a"
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+For required properties on I2C-bus, please consult
+Documentation/devicetree/bindings/i2c/i2c.txt
+For required properties on SPI-bus, please consult
+Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+       codec: cs4341@0 {
+               #sound-dai-cells = <0>;
+               compatible = "cirrus,cs4341a";
+               reg = <0>;
+               spi-max-frequency = <6000000>;
+       };
index b279b6072bd549b4c9bfc108c9654a4d1484db51..a58f79f5345cd75aff5bc82acf28342e2ca6b555 100644 (file)
@@ -45,6 +45,23 @@ Optional properties:
 - fck_parent : Should contain a valid clock name which will be used as parent
               for the McASP fck
 
+Optional GPIO support:
+If any McASP pin need to be used as GPIO then the McASP node must have:
+...
+  gpio-controller
+  #gpio-cells = <2>;
+...
+
+When requesting a GPIO, the first parameter is the PIN index in McASP_P*
+registers.
+For example to request the AXR2 pin of mcasp8:
+function-gpios = <&mcasp8 2 0>;
+
+Or to request the ACLKR pin of mcasp8:
+function-gpios = <&mcasp8 29 0>;
+
+For generic gpio information, please refer to bindings/gpio/gpio.txt
+
 Example:
 
 mcasp0: mcasp0@1d00000 {
diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt
new file mode 100644 (file)
index 0000000..2469588
--- /dev/null
@@ -0,0 +1,23 @@
+* Rockchip Rk3328 internal codec
+
+Required properties:
+
+- compatible: "rockchip,rk3328-codec"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- rockchip,grf: the phandle of the syscon node for GRF register.
+- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
+- clock-names: should be "pclk".
+- spk-depop-time-ms: speak depop time msec.
+
+Example for rk3328 internal codec:
+
+codec: codec@ff410000 {
+       compatible = "rockchip,rk3328-codec";
+       reg = <0x0 0xff410000 0x0 0x1000>;
+       rockchip,grf = <&grf>;
+       clocks = <&cru PCLK_ACODEC>;
+       clock-names = "pclk";
+       spk-depop-time-ms = 100;
+       status = "disabled";
+};
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt
deleted file mode 100644 (file)
index 3a2f716..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-ASoC Simple SCU Sound Card
-
-Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
-For example, you can use this driver if you want to exchange sampling rate convert,
-Mixing, etc...
-
-Required properties:
-
-- compatible                           : "simple-scu-audio-card"
-                                         "renesas,rsrc-card"
-Optional properties:
-
-- simple-audio-card,name               : see simple-audio-card.txt
-- simple-audio-card,cpu                        : see simple-audio-card.txt
-- simple-audio-card,codec              : see simple-audio-card.txt
-
-Optional subnode properties:
-
-- simple-audio-card,format             : see simple-audio-card.txt
-- simple-audio-card,frame-master       : see simple-audio-card.txt
-- simple-audio-card,bitclock-master    : see simple-audio-card.txt
-- simple-audio-card,bitclock-inversion : see simple-audio-card.txt
-- simple-audio-card,frame-inversion    : see simple-audio-card.txt
-- simple-audio-card,convert-rate       : platform specified sampling rate convert
-- simple-audio-card,convert-channels   : platform specified converted channel size (2 - 8 ch)
-- simple-audio-card,prefix             : see routing
-- simple-audio-card,widgets            : Please refer to widgets.txt.
-- simple-audio-card,routing            : A list of the connections between audio components.
-                                         Each entry is a pair of strings, the first being the connection's sink,
-                                         the second being the connection's source. Valid names for sources.
-                                         use audio-prefix if some components is using same sink/sources naming.
-                                         it can be used if compatible was "renesas,rsrc-card";
-
-Required CPU/CODEC subnodes properties:
-
-- sound-dai                            : see simple-audio-card.txt
-
-Optional CPU/CODEC subnodes properties:
-
-- clocks / system-clock-frequency      : see simple-audio-card.txt
-
-Example 1. Sampling Rate Conversion
-
-sound {
-       compatible = "simple-scu-audio-card";
-
-       simple-audio-card,name = "rsnd-ak4643";
-       simple-audio-card,format = "left_j";
-       simple-audio-card,bitclock-master = <&sndcodec>;
-       simple-audio-card,frame-master = <&sndcodec>;
-
-       simple-audio-card,convert-rate = <48000>;
-
-       simple-audio-card,prefix = "ak4642";
-       simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
-                       "DAI0 Capture", "ak4642 Capture";
-
-       sndcpu: simple-audio-card,cpu {
-               sound-dai = <&rcar_sound>;
-       };
-
-       sndcodec: simple-audio-card,codec {
-               sound-dai = <&ak4643>;
-               system-clock-frequency = <11289600>;
-       };
-};
-
-Example 2. 2 CPU 1 Codec (Mixing)
-
-sound {
-       compatible = "simple-scu-audio-card";
-
-       simple-audio-card,name = "rsnd-ak4643";
-       simple-audio-card,format = "left_j";
-       simple-audio-card,bitclock-master = <&dpcmcpu>;
-       simple-audio-card,frame-master = <&dpcmcpu>;
-
-       simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
-                       "ak4642 Playback", "DAI1 Playback";
-
-       dpcmcpu: cpu@0 {
-               sound-dai = <&rcar_sound 0>;
-       };
-
-       cpu@1 {
-               sound-dai = <&rcar_sound 1>;
-       };
-
-       codec {
-               prefix = "ak4642";
-               sound-dai = <&ak4643>;
-               clocks = <&audio_clock>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt b/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt
new file mode 100644 (file)
index 0000000..cbc93c8
--- /dev/null
@@ -0,0 +1,29 @@
+Device-Tree bindings for Xilinx PL audio formatter
+
+The IP core supports DMA, data formatting(AES<->PCM conversion)
+of audio samples.
+
+Required properties:
+ - compatible: "xlnx,audio-formatter-1.0"
+ - interrupt-names: Names specified to list of interrupts in same
+                   order mentioned under "interrupts".
+                   List of supported interrupt names are:
+                   "irq_mm2s" : interrupt from MM2S block
+                   "irq_s2mm" : interrupt from S2MM block
+ - interrupts-parent: Phandle for interrupt controller.
+ - interrupts: List of Interrupt numbers.
+ - reg: Base address and size of the IP core instance.
+ - clock-names: List of input clocks.
+   Required elements: "s_axi_lite_aclk", "aud_mclk"
+ - clocks: Input clock specifier. Refer to common clock bindings.
+
+Example:
+       audio_ss_0_audio_formatter_0: audio_formatter@80010000 {
+               compatible = "xlnx,audio-formatter-1.0";
+               interrupt-names = "irq_mm2s", "irq_s2mm";
+               interrupt-parent = <&gic>;
+               interrupts = <0 104 4>, <0 105 4>;
+               reg = <0x0 0x80010000 0x0 0x1000>;
+               clock-names = "s_axi_lite_aclk", "aud_mclk";
+               clocks = <&clk 71>, <&clk_wiz_1 0>;
+       };
index a5b09e75e7874bb6a1facc9a9fd70e6fdac10791..f7d2b373da0aaebd869b06773c24b7994c0cb472 100644 (file)
@@ -541,7 +541,8 @@ static int snd_compress_check_input(struct snd_compr_params *params)
 {
        /* first let's check the buffer parameter's */
        if (params->buffer.fragment_size == 0 ||
-           params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
+           params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
+           params->buffer.fragments == 0)
                return -EINVAL;
 
        /* now codec parameters */
index 62bdb7e333b84fabd8d42890d2e07d9d261914fa..87cb9c51e6f5a481ca9ed569db524450e1cca86f 100644 (file)
@@ -65,6 +65,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS4271_SPI if SPI_MASTER
        select SND_SOC_CS42XX8_I2C if I2C
        select SND_SOC_CS43130 if I2C
+       select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI
        select SND_SOC_CS4349 if I2C
        select SND_SOC_CS47L24 if MFD_CS47L24
        select SND_SOC_CS53L30 if I2C
@@ -129,6 +130,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM5102A
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
+       select SND_SOC_RK3328
        select SND_SOC_RT274 if I2C
        select SND_SOC_RT286 if I2C
        select SND_SOC_RT298 if I2C
@@ -542,6 +544,12 @@ config SND_SOC_CS43130
         tristate "Cirrus Logic CS43130 CODEC"
         depends on I2C
 
+config SND_SOC_CS4341
+       tristate "Cirrus Logic CS4341 CODEC"
+       depends on I2C || SPI_MASTER
+       select REGMAP_I2C if I2C
+       select REGMAP_SPI if SPI_MASTER
+
 # Cirrus Logic CS4349 HiFi DAC
 config SND_SOC_CS4349
        tristate "Cirrus Logic CS4349 CODEC"
@@ -799,6 +807,10 @@ config SND_SOC_PCM512x_SPI
        select SND_SOC_PCM512x
        select REGMAP_SPI
 
+config SND_SOC_RK3328
+       tristate "Rockchip RK3328 audio CODEC"
+       select REGMAP_MMIO
+
 config SND_SOC_RL6231
        tristate
        default y if SND_SOC_RT5514=y
@@ -1211,7 +1223,8 @@ config SND_SOC_WM8903
        depends on I2C
 
 config SND_SOC_WM8904
-       tristate
+       tristate "Wolfson Microelectronics WM8904 CODEC"
+       depends on I2C
 
 config SND_SOC_WM8940
         tristate
index 66f55d18562074ff77f21e4ccb6f2eab02906e63..9bb3346fab2fee7d0d7b4f5d6583a92155647781 100644 (file)
@@ -60,6 +60,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
 snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cs43130-objs := cs43130.o
+snd-soc-cs4341-objs := cs4341.o
 snd-soc-cs4349-objs := cs4349.o
 snd-soc-cs47l24-objs := cs47l24.o
 snd-soc-cs53l30-objs := cs53l30.o
@@ -132,6 +133,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o
 snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
+snd-soc-rk3328-objs := rk3328_codec.o
 snd-soc-rl6231-objs := rl6231.o
 snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt1305-objs := rt1305.o
@@ -326,6 +328,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI)    += snd-soc-cs4271-spi.o
 obj-$(CONFIG_SND_SOC_CS42XX8)  += snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CS43130)   += snd-soc-cs43130.o
+obj-$(CONFIG_SND_SOC_CS4341)   += snd-soc-cs4341.o
 obj-$(CONFIG_SND_SOC_CS4349)   += snd-soc-cs4349.o
 obj-$(CONFIG_SND_SOC_CS47L24)  += snd-soc-cs47l24.o
 obj-$(CONFIG_SND_SOC_CS53L30)  += snd-soc-cs53l30.o
@@ -398,6 +401,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A)      += snd-soc-pcm5102a.o
 obj-$(CONFIG_SND_SOC_PCM512x)  += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_RK3328)   += snd-soc-rk3328.o
 obj-$(CONFIG_SND_SOC_RL6231)   += snd-soc-rl6231.o
 obj-$(CONFIG_SND_SOC_RL6347A)  += snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT1305)   += snd-soc-rt1305.o
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
new file mode 100644 (file)
index 0000000..d2e616a
--- /dev/null
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Cirrus Logic CS4341A ALSA SoC Codec Driver
+ *  Author: Alexander Shiyan <shc_work@mail.ru>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define CS4341_REG_MODE1       0x00
+#define CS4341_REG_MODE2       0x01
+#define CS4341_REG_MIX         0x02
+#define CS4341_REG_VOLA                0x03
+#define CS4341_REG_VOLB                0x04
+
+#define CS4341_MODE2_DIF       (7 << 4)
+#define CS4341_MODE2_DIF_I2S_24        (0 << 4)
+#define CS4341_MODE2_DIF_I2S_16        (1 << 4)
+#define CS4341_MODE2_DIF_LJ_24 (2 << 4)
+#define CS4341_MODE2_DIF_RJ_24 (3 << 4)
+#define CS4341_MODE2_DIF_RJ_16 (5 << 4)
+#define CS4341_VOLX_MUTE       (1 << 7)
+
+struct cs4341_priv {
+       unsigned int            fmt;
+       struct regmap           *regmap;
+       struct regmap_config    regcfg;
+};
+
+static const struct reg_default cs4341_reg_defaults[] = {
+       { CS4341_REG_MODE1,     0x00 },
+       { CS4341_REG_MODE2,     0x82 },
+       { CS4341_REG_MIX,       0x49 },
+       { CS4341_REG_VOLA,      0x80 },
+       { CS4341_REG_VOLB,      0x80 },
+};
+
+static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+       struct snd_soc_component *component = dai->component;
+       struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+
+       switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_RIGHT_J:
+               cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cs4341_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);
+       unsigned int mode = 0;
+       int b24 = 0;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S24_LE:
+               b24 = 1;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       default:
+               dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",
+                       params_format(params));
+               return -EINVAL;
+       }
+
+       switch (cs4341->fmt) {
+       case SND_SOC_DAIFMT_I2S:
+               mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               mode = CS4341_MODE2_DIF_LJ_24;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;
+               break;
+       default:
+               dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",
+                       cs4341->fmt);
+               return -EINVAL;
+       }
+
+       return snd_soc_component_update_bits(component, CS4341_REG_MODE2,
+                                            CS4341_MODE2_DIF, mode);
+}
+
+static int cs4341_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_component *component = dai->component;
+       int ret;
+
+       ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,
+                                           CS4341_VOLX_MUTE,
+                                           mute ? CS4341_VOLX_MUTE : 0);
+       if (ret < 0)
+               return ret;
+
+       return snd_soc_component_update_bits(component, CS4341_REG_VOLB,
+                                            CS4341_VOLX_MUTE,
+                                            mute ? CS4341_VOLX_MUTE : 0);
+}
+
+static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);
+
+static const char * const deemph[] = {
+       "None", "44.1k", "48k", "32k",
+};
+
+static const struct soc_enum deemph_enum =
+       SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);
+
+static const char * const srzc[] = {
+       "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",
+};
+
+static const struct soc_enum srzc_enum =
+       SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);
+
+
+static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_OUTPUT("OutA"),
+       SND_SOC_DAPM_OUTPUT("OutB"),
+};
+
+static const struct snd_soc_dapm_route cs4341_routes[] = {
+       { "OutA", NULL, "HiFi DAC" },
+       { "OutB", NULL, "HiFi DAC" },
+       { "DAC Playback", NULL, "OutA" },
+       { "DAC Playback", NULL, "OutB" },
+};
+
+static const struct snd_kcontrol_new cs4341_controls[] = {
+       SOC_DOUBLE_R_TLV("Master Playback Volume",
+                        CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),
+       SOC_ENUM("De-Emphasis Control", deemph_enum),
+       SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),
+       SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),
+       SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),
+};
+
+static const struct snd_soc_dai_ops cs4341_dai_ops = {
+       .set_fmt        = cs4341_set_fmt,
+       .hw_params      = cs4341_hw_params,
+       .digital_mute   = cs4341_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs4341_dai = {
+       .name                   = "cs4341a-hifi",
+       .playback               = {
+               .stream_name    = "DAC Playback",
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rates          = SNDRV_PCM_RATE_8000_96000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE,
+       },
+       .ops                    = &cs4341_dai_ops,
+       .symmetric_rates        = 1,
+};
+
+static const struct snd_soc_component_driver soc_component_cs4341 = {
+       .controls               = cs4341_controls,
+       .num_controls           = ARRAY_SIZE(cs4341_controls),
+       .dapm_widgets           = cs4341_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(cs4341_dapm_widgets),
+       .dapm_routes            = cs4341_routes,
+       .num_dapm_routes        = ARRAY_SIZE(cs4341_routes),
+       .idle_bias_on           = 1,
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+       .non_legacy_dai_naming  = 1,
+};
+
+static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {
+       { .compatible = "cirrus,cs4341a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, cs4341_dt_ids);
+
+static int cs4341_probe(struct device *dev)
+{
+       struct cs4341_priv *cs4341 = dev_get_drvdata(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)
+               regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,
+                            cs4341_reg_defaults[i].def);
+
+       return devm_snd_soc_register_component(dev, &soc_component_cs4341,
+                                              &cs4341_dai, 1);
+}
+
+#if defined(CONFIG_I2C)
+static int cs4341_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct cs4341_priv *cs4341;
+
+       cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);
+       if (!cs4341)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, cs4341);
+
+       cs4341->regcfg.reg_bits         = 8;
+       cs4341->regcfg.val_bits         = 8;
+       cs4341->regcfg.max_register     = CS4341_REG_VOLB;
+       cs4341->regcfg.cache_type       = REGCACHE_FLAT;
+       cs4341->regcfg.reg_defaults     = cs4341_reg_defaults;
+       cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+       cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);
+       if (IS_ERR(cs4341->regmap))
+               return PTR_ERR(cs4341->regmap);
+
+       return cs4341_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id cs4341_i2c_id[] = {
+       { "cs4341", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);
+
+static struct i2c_driver cs4341_i2c_driver = {
+       .driver = {
+               .name = "cs4341-i2c",
+               .of_match_table = of_match_ptr(cs4341_dt_ids),
+       },
+       .probe = cs4341_i2c_probe,
+       .id_table = cs4341_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static bool cs4341_reg_readable(struct device *dev, unsigned int reg)
+{
+       return false;
+}
+
+static int cs4341_spi_probe(struct spi_device *spi)
+{
+       struct cs4341_priv *cs4341;
+       int ret;
+
+       cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);
+       if (!cs4341)
+               return -ENOMEM;
+
+       if (!spi->bits_per_word)
+               spi->bits_per_word = 8;
+       if (!spi->max_speed_hz)
+               spi->max_speed_hz = 6000000;
+       ret = spi_setup(spi);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, cs4341);
+
+       cs4341->regcfg.reg_bits         = 16;
+       cs4341->regcfg.val_bits         = 8;
+       cs4341->regcfg.write_flag_mask  = 0x20;
+       cs4341->regcfg.max_register     = CS4341_REG_VOLB;
+       cs4341->regcfg.cache_type       = REGCACHE_FLAT;
+       cs4341->regcfg.readable_reg     = cs4341_reg_readable;
+       cs4341->regcfg.reg_defaults     = cs4341_reg_defaults;
+       cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);
+       cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);
+       if (IS_ERR(cs4341->regmap))
+               return PTR_ERR(cs4341->regmap);
+
+       return cs4341_probe(&spi->dev);
+}
+
+static struct spi_driver cs4341_spi_driver = {
+       .driver = {
+               .name = "cs4341-spi",
+               .of_match_table = of_match_ptr(cs4341_dt_ids),
+       },
+       .probe = cs4341_spi_probe,
+};
+#endif
+
+static int __init cs4341_init(void)
+{
+       int ret = 0;
+
+#if defined(CONFIG_I2C)
+       ret = i2c_add_driver(&cs4341_i2c_driver);
+       if (ret)
+               return ret;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&cs4341_spi_driver);
+#endif
+
+       return ret;
+}
+module_init(cs4341_init);
+
+static void __exit cs4341_exit(void)
+{
+#if defined(CONFIG_I2C)
+       i2c_del_driver(&cs4341_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&cs4341_spi_driver);
+#endif
+}
+module_exit(cs4341_exit);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
index e97d12d578b00c1a287209e24fed9d29688d777e..98464ba1046c1845d3bef77acfbfe3c6e5f803bd 100644 (file)
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
 #include <linux/regmap.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <sound/jack.h>
 #include "es8316.h"
 
 /* In slave mode at single speed, the codec is documented as accepting 5
@@ -33,6 +35,11 @@ static const unsigned int supported_mclk_lrck_ratios[] = {
 };
 
 struct es8316_priv {
+       struct mutex lock;
+       struct regmap *regmap;
+       struct snd_soc_component *component;
+       struct snd_soc_jack *jack;
+       int irq;
        unsigned int sysclk;
        unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
        struct snd_pcm_hw_constraint_list sysclk_constraints;
@@ -94,6 +101,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = {
        SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
        SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
        SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
+       SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0),
 
        SOC_ENUM("Capture Polarity", adcpol),
        SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
@@ -529,8 +537,162 @@ static struct snd_soc_dai_driver es8316_dai = {
        .symmetric_rates = 1,
 };
 
+static void es8316_enable_micbias_for_mic_gnd_short_detect(
+       struct snd_soc_component *component)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+       snd_soc_dapm_mutex_lock(dapm);
+       snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias");
+       snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power");
+       snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias");
+       snd_soc_dapm_sync_unlocked(dapm);
+       snd_soc_dapm_mutex_unlock(dapm);
+
+       msleep(20);
+}
+
+static void es8316_disable_micbias_for_mic_gnd_short_detect(
+       struct snd_soc_component *component)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+       snd_soc_dapm_mutex_lock(dapm);
+       snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias");
+       snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power");
+       snd_soc_dapm_disable_pin_unlocked(dapm, "Bias");
+       snd_soc_dapm_sync_unlocked(dapm);
+       snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t es8316_irq(int irq, void *data)
+{
+       struct es8316_priv *es8316 = data;
+       struct snd_soc_component *comp = es8316->component;
+       unsigned int flags;
+
+       mutex_lock(&es8316->lock);
+
+       regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+       if (flags == 0x00)
+               goto out; /* Powered-down / reset */
+
+       /* Catch spurious IRQ before set_jack is called */
+       if (!es8316->jack)
+               goto out;
+
+       dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+       if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+               /* Jack removed, or spurious IRQ? */
+               if (es8316->jack->status & SND_JACK_MICROPHONE)
+                       es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+
+               if (es8316->jack->status & SND_JACK_HEADPHONE) {
+                       snd_soc_jack_report(es8316->jack, 0,
+                                           SND_JACK_HEADSET | SND_JACK_BTN_0);
+                       dev_dbg(comp->dev, "jack unplugged\n");
+               }
+       } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) {
+               /* Jack inserted, determine type */
+               es8316_enable_micbias_for_mic_gnd_short_detect(comp);
+               regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+               dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
+               if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
+                       /* Jack unplugged underneath us */
+                       es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+               } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+                       /* Open, headset */
+                       snd_soc_jack_report(es8316->jack,
+                                           SND_JACK_HEADSET,
+                                           SND_JACK_HEADSET);
+                       /* Keep mic-gnd-short detection on for button press */
+               } else {
+                       /* Shorted, headphones */
+                       snd_soc_jack_report(es8316->jack,
+                                           SND_JACK_HEADPHONE,
+                                           SND_JACK_HEADSET);
+                       /* No longer need mic-gnd-short detection */
+                       es8316_disable_micbias_for_mic_gnd_short_detect(comp);
+               }
+       } else if (es8316->jack->status & SND_JACK_MICROPHONE) {
+               /* Interrupt while jack inserted, report button state */
+               if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) {
+                       /* Open, button release */
+                       snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+               } else {
+                       /* Short, button press */
+                       snd_soc_jack_report(es8316->jack,
+                                           SND_JACK_BTN_0,
+                                           SND_JACK_BTN_0);
+               }
+       }
+
+out:
+       mutex_unlock(&es8316->lock);
+       return IRQ_HANDLED;
+}
+
+static void es8316_enable_jack_detect(struct snd_soc_component *component,
+                                     struct snd_soc_jack *jack)
+{
+       struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+       mutex_lock(&es8316->lock);
+
+       es8316->jack = jack;
+
+       if (es8316->jack->status & SND_JACK_MICROPHONE)
+               es8316_enable_micbias_for_mic_gnd_short_detect(component);
+
+       snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+                                     ES8316_GPIO_ENABLE_INTERRUPT,
+                                     ES8316_GPIO_ENABLE_INTERRUPT);
+
+       mutex_unlock(&es8316->lock);
+
+       /* Enable irq and sync initial jack state */
+       enable_irq(es8316->irq);
+       es8316_irq(es8316->irq, es8316);
+}
+
+static void es8316_disable_jack_detect(struct snd_soc_component *component)
+{
+       struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+       disable_irq(es8316->irq);
+
+       mutex_lock(&es8316->lock);
+
+       snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE,
+                                     ES8316_GPIO_ENABLE_INTERRUPT, 0);
+
+       if (es8316->jack->status & SND_JACK_MICROPHONE) {
+               es8316_disable_micbias_for_mic_gnd_short_detect(component);
+               snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0);
+       }
+
+       es8316->jack = NULL;
+
+       mutex_unlock(&es8316->lock);
+}
+
+static int es8316_set_jack(struct snd_soc_component *component,
+                          struct snd_soc_jack *jack, void *data)
+{
+       if (jack)
+               es8316_enable_jack_detect(component, jack);
+       else
+               es8316_disable_jack_detect(component);
+
+       return 0;
+}
+
 static int es8316_probe(struct snd_soc_component *component)
 {
+       struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+       es8316->component = component;
+
        /* Reset codec and enable current state machine */
        snd_soc_component_write(component, ES8316_RESET, 0x3f);
        usleep_range(5000, 5500);
@@ -555,6 +717,7 @@ static int es8316_probe(struct snd_soc_component *component)
 
 static const struct snd_soc_component_driver soc_component_dev_es8316 = {
        .probe                  = es8316_probe,
+       .set_jack               = es8316_set_jack,
        .controls               = es8316_snd_controls,
        .num_controls           = ARRAY_SIZE(es8316_snd_controls),
        .dapm_widgets           = es8316_dapm_widgets,
@@ -566,18 +729,29 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
        .non_legacy_dai_naming  = 1,
 };
 
+static const struct regmap_range es8316_volatile_ranges[] = {
+       regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG),
+};
+
+static const struct regmap_access_table es8316_volatile_table = {
+       .yes_ranges     = es8316_volatile_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(es8316_volatile_ranges),
+};
+
 static const struct regmap_config es8316_regmap = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = 0x53,
+       .volatile_table = &es8316_volatile_table,
        .cache_type = REGCACHE_RBTREE,
 };
 
 static int es8316_i2c_probe(struct i2c_client *i2c_client,
                            const struct i2c_device_id *id)
 {
+       struct device *dev = &i2c_client->dev;
        struct es8316_priv *es8316;
-       struct regmap *regmap;
+       int ret;
 
        es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
                              GFP_KERNEL);
@@ -586,9 +760,23 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client,
 
        i2c_set_clientdata(i2c_client, es8316);
 
-       regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
-       if (IS_ERR(regmap))
-               return PTR_ERR(regmap);
+       es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
+       if (IS_ERR(es8316->regmap))
+               return PTR_ERR(es8316->regmap);
+
+       es8316->irq = i2c_client->irq;
+       mutex_init(&es8316->lock);
+
+       ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
+                                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                       "es8316", es8316);
+       if (ret == 0) {
+               /* Gets re-enabled by es8316_set_jack() */
+               disable_irq(es8316->irq);
+       } else {
+               dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
+               es8316->irq = -ENXIO;
+       }
 
        return devm_snd_soc_register_component(&i2c_client->dev,
                                      &soc_component_dev_es8316,
index 6bcdd63ea45948d7d08ed4583917e757a0f05dbc..439a0130cbb7d51a5f227e24a3d4586ac78f89e0 100644 (file)
 #define ES8316_SERDATA2_LEN_16         0x0c
 #define ES8316_SERDATA2_LEN_32         0x10
 
+/* ES8316_GPIO_DEBOUNCE        */
+#define ES8316_GPIO_ENABLE_INTERRUPT           0x02
+
+/* ES8316_GPIO_FLAG */
+#define ES8316_GPIO_FLAG_GM_NOT_SHORTED                0x02
+#define ES8316_GPIO_FLAG_HP_NOT_INSERTED       0x04
+
 #endif
index 6cb1653be80417ae65ceddbc52791dcc92a4a367..4cc24a5d5c3167cf625a4135e068b6798b12a842 100644 (file)
@@ -1400,24 +1400,20 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
                if (ret != 0) {
                        dev_err(component->dev,
                                "Failed to set digital mute: %d\n", ret);
-                       mutex_unlock(&pcm512x->mutex);
-                       return ret;
+                       goto unlock;
                }
 
                regmap_read_poll_timeout(pcm512x->regmap,
                                         PCM512x_ANALOG_MUTE_DET,
                                         mute_det, (mute_det & 0x3) == 0,
                                         200, 10000);
-
-               mutex_unlock(&pcm512x->mutex);
        } else {
                pcm512x->mute &= ~0x1;
                ret = pcm512x_update_mute(pcm512x);
                if (ret != 0) {
                        dev_err(component->dev,
                                "Failed to update digital mute: %d\n", ret);
-                       mutex_unlock(&pcm512x->mutex);
-                       return ret;
+                       goto unlock;
                }
 
                regmap_read_poll_timeout(pcm512x->regmap,
@@ -1428,9 +1424,10 @@ static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
                                         200, 10000);
        }
 
+unlock:
        mutex_unlock(&pcm512x->mutex);
 
-       return 0;
+       return ret;
 }
 
 static const struct snd_soc_dai_ops pcm512x_dai_ops = {
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644 (file)
index 0000000..24f8f86
--- /dev/null
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk3328 ALSA SoC Audio driver
+//
+// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include "rk3328_codec.h"
+
+/*
+ * volume setting
+ * 0: -39dB
+ * 26: 0dB
+ * 31: 6dB
+ * Step: 1.5dB
+ */
+#define OUT_VOLUME     (0x18)
+#define RK3328_GRF_SOC_CON2    (0x0408)
+#define RK3328_GRF_SOC_CON10   (0x0428)
+#define INITIAL_FREQ   (11289600)
+
+struct rk3328_codec_priv {
+       struct regmap *regmap;
+       struct regmap *grf;
+       struct clk *mclk;
+       struct clk *pclk;
+       unsigned int sclk;
+       int spk_depop_time; /* msec */
+};
+
+static const struct reg_default rk3328_codec_reg_defaults[] = {
+       { CODEC_RESET, 0x03 },
+       { DAC_INIT_CTRL1, 0x00 },
+       { DAC_INIT_CTRL2, 0x50 },
+       { DAC_INIT_CTRL3, 0x0e },
+       { DAC_PRECHARGE_CTRL, 0x01 },
+       { DAC_PWR_CTRL, 0x00 },
+       { DAC_CLK_CTRL, 0x00 },
+       { HPMIX_CTRL, 0x00 },
+       { HPOUT_CTRL, 0x00 },
+       { HPOUTL_GAIN_CTRL, 0x00 },
+       { HPOUTR_GAIN_CTRL, 0x00 },
+       { HPOUT_POP_CTRL, 0x11 },
+};
+
+static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328)
+{
+       regmap_write(rk3328->regmap, CODEC_RESET, 0x00);
+       mdelay(10);
+       regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
+
+       return 0;
+}
+
+static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(dai->component);
+       unsigned int val;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
+                          PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               val = DAC_MODE_PCM;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val = DAC_MODE_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               val = DAC_MODE_RJM;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val = DAC_MODE_LJM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
+                          DAC_MODE_MASK, val);
+
+       return 0;
+}
+
+static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute)
+{
+       unsigned int val = BIT(17);
+
+       if (mute)
+               val |= BIT(1);
+
+       regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10, val);
+}
+
+static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(dai->component);
+       unsigned int val;
+
+       if (mute)
+               val = HPOUTL_MUTE | HPOUTR_MUTE;
+       else
+               val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
+
+       regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
+                          HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
+
+       return 0;
+}
+
+static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
+       mdelay(10);
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_ALL_ON);
+       mdelay(wait_ms);
+
+       return 0;
+}
+
+static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms)
+{
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
+       mdelay(10);
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_ALL_ON);
+       mdelay(wait_ms);
+
+       return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_open_list[] = {
+       { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
+       { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+         DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
+       { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK,
+         HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
+       { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+         HPOUTR_POP_WORK | HPOUTL_POP_WORK },
+       { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
+       { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+         HPMIXL_INIT_EN | HPMIXR_INIT_EN },
+       { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
+       { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+         HPOUTL_INIT_EN | HPOUTR_INIT_EN },
+       { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+         DACL_REFV_ON | DACR_REFV_ON },
+       { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+         DACL_CLK_ON | DACR_CLK_ON },
+       { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
+       { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+         DACL_INIT_ON | DACR_INIT_ON },
+       { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+         DACL_SELECT | DACR_SELECT },
+       { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+         HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
+       { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+         HPOUTL_UNMUTE | HPOUTR_UNMUTE },
+};
+
+static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328)
+{
+       int i;
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_I);
+
+       for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) {
+               regmap_update_bits(rk3328->regmap,
+                                  playback_open_list[i].reg,
+                                  playback_open_list[i].msk,
+                                  playback_open_list[i].val);
+               mdelay(1);
+       }
+
+       msleep(rk3328->spk_depop_time);
+       rk3328_analog_output(rk3328, 1);
+
+       regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+                          HPOUTL_GAIN_MASK, OUT_VOLUME);
+       regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+                          HPOUTR_GAIN_MASK, OUT_VOLUME);
+
+       return 0;
+}
+
+static const struct rk3328_reg_msk_val playback_close_list[] = {
+       { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+         HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
+       { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+         DACL_UNSELECT | DACR_UNSELECT },
+       { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+         HPOUTL_MUTE | HPOUTR_MUTE },
+       { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+         HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
+       { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
+       { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
+       { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
+       { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+         DACL_CLK_OFF | DACR_CLK_OFF },
+       { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+         DACL_REFV_OFF | DACR_REFV_OFF },
+       { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+         HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
+       { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+         DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
+       { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
+       { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+         HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
+       { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+         DACL_INIT_OFF | DACR_INIT_OFF },
+};
+
+static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328)
+{
+       size_t i;
+
+       rk3328_analog_output(rk3328, 0);
+
+       regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+                          HPOUTL_GAIN_MASK, 0);
+       regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+                          HPOUTR_GAIN_MASK, 0);
+
+       for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) {
+               regmap_update_bits(rk3328->regmap,
+                                  playback_close_list[i].reg,
+                                  playback_close_list[i].msk,
+                                  playback_close_list[i].val);
+               mdelay(1);
+       }
+
+       /* Workaround for silence when changed Fs 48 -> 44.1kHz */
+       rk3328_codec_reset(rk3328);
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_ALL_ON);
+
+       return 0;
+}
+
+static int rk3328_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(dai->component);
+       unsigned int val = 0;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               val = DAC_VDL_16BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val = DAC_VDL_20BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val = DAC_VDL_24BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               val = DAC_VDL_32BITS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
+
+       val = DAC_WL_32BITS | DAC_RST_DIS;
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
+                          DAC_WL_MASK | DAC_RST_MASK, val);
+
+       return 0;
+}
+
+static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(dai->component);
+
+       return rk3328_codec_open_playback(rk3328);
+}
+
+static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(dai->component);
+
+       rk3328_codec_close_playback(rk3328);
+}
+
+static const struct snd_soc_dai_ops rk3328_dai_ops = {
+       .hw_params = rk3328_hw_params,
+       .set_fmt = rk3328_set_dai_fmt,
+       .digital_mute = rk3328_digital_mute,
+       .startup = rk3328_pcm_startup,
+       .shutdown = rk3328_pcm_shutdown,
+};
+
+static struct snd_soc_dai_driver rk3328_dai[] = {
+       {
+               .name = "rk3328-hifi",
+               .id = RK3328_HIFI,
+               .playback = {
+                       .stream_name = "HIFI Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+                                   SNDRV_PCM_FMTBIT_S20_3LE |
+                                   SNDRV_PCM_FMTBIT_S24_LE |
+                                   SNDRV_PCM_FMTBIT_S32_LE),
+               },
+               .capture = {
+                       .stream_name = "HIFI Capture",
+                       .channels_min = 2,
+                       .channels_max = 8,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+                                   SNDRV_PCM_FMTBIT_S20_3LE |
+                                   SNDRV_PCM_FMTBIT_S24_LE |
+                                   SNDRV_PCM_FMTBIT_S32_LE),
+               },
+               .ops = &rk3328_dai_ops,
+       },
+};
+
+static int rk3328_codec_probe(struct snd_soc_component *component)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(component);
+
+       rk3328_codec_reset(rk3328);
+       rk3328_codec_power_on(rk3328, 0);
+
+       return 0;
+}
+
+static void rk3328_codec_remove(struct snd_soc_component *component)
+{
+       struct rk3328_codec_priv *rk3328 =
+               snd_soc_component_get_drvdata(component);
+
+       rk3328_codec_close_playback(rk3328);
+       rk3328_codec_power_off(rk3328, 0);
+}
+
+static const struct snd_soc_component_driver soc_codec_rk3328 = {
+       .probe = rk3328_codec_probe,
+       .remove = rk3328_codec_remove,
+};
+
+static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CODEC_RESET:
+       case DAC_INIT_CTRL1:
+       case DAC_INIT_CTRL2:
+       case DAC_INIT_CTRL3:
+       case DAC_PRECHARGE_CTRL:
+       case DAC_PWR_CTRL:
+       case DAC_CLK_CTRL:
+       case HPMIX_CTRL:
+       case DAC_SELECT:
+       case HPOUT_CTRL:
+       case HPOUTL_GAIN_CTRL:
+       case HPOUTR_GAIN_CTRL:
+       case HPOUT_POP_CTRL:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CODEC_RESET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config rk3328_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = HPOUT_POP_CTRL,
+       .writeable_reg = rk3328_codec_write_read_reg,
+       .readable_reg = rk3328_codec_write_read_reg,
+       .volatile_reg = rk3328_codec_volatile_reg,
+       .reg_defaults = rk3328_codec_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
+       .cache_type = REGCACHE_FLAT,
+};
+
+static int rk3328_platform_probe(struct platform_device *pdev)
+{
+       struct device_node *rk3328_np = pdev->dev.of_node;
+       struct rk3328_codec_priv *rk3328;
+       struct resource *res;
+       struct regmap *grf;
+       void __iomem *base;
+       int ret = 0;
+
+       rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
+       if (!rk3328)
+               return -ENOMEM;
+
+       grf = syscon_regmap_lookup_by_phandle(rk3328_np,
+                                             "rockchip,grf");
+       if (IS_ERR(grf)) {
+               dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
+               return PTR_ERR(grf);
+       }
+       rk3328->grf = grf;
+       /* enable i2s_acodec_en */
+       regmap_write(grf, RK3328_GRF_SOC_CON2,
+                    (BIT(14) << 16 | BIT(14)));
+
+       ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
+                                  &rk3328->spk_depop_time);
+       if (ret < 0) {
+               dev_info(&pdev->dev, "spk_depop_time use default value.\n");
+               rk3328->spk_depop_time = 200;
+       }
+
+       rk3328_analog_output(rk3328, 0);
+
+       rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
+       if (IS_ERR(rk3328->mclk))
+               return PTR_ERR(rk3328->mclk);
+
+       ret = clk_prepare_enable(rk3328->mclk);
+       if (ret)
+               return ret;
+       clk_set_rate(rk3328->mclk, INITIAL_FREQ);
+
+       rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(rk3328->pclk)) {
+               dev_err(&pdev->dev, "can't get acodec pclk\n");
+               return PTR_ERR(rk3328->pclk);
+       }
+
+       ret = clk_prepare_enable(rk3328->pclk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to enable acodec pclk\n");
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                              &rk3328_codec_regmap_config);
+       if (IS_ERR(rk3328->regmap))
+               return PTR_ERR(rk3328->regmap);
+
+       platform_set_drvdata(pdev, rk3328);
+
+       return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
+                                              rk3328_dai,
+                                              ARRAY_SIZE(rk3328_dai));
+}
+
+static const struct of_device_id rk3328_codec_of_match[] = {
+               { .compatible = "rockchip,rk3328-codec", },
+               {},
+};
+MODULE_DEVICE_TABLE(of, rk3328_codec_of_match);
+
+static struct platform_driver rk3328_codec_driver = {
+       .driver = {
+                  .name = "rk3328-codec",
+                  .of_match_table = of_match_ptr(rk3328_codec_of_match),
+       },
+       .probe = rk3328_platform_probe,
+};
+module_platform_driver(rk3328_codec_driver);
+
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("ASoC rk3328 codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h
new file mode 100644 (file)
index 0000000..6551035
--- /dev/null
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * rk3328 ALSA SoC Audio driver
+ *
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ */
+
+#ifndef _RK3328_CODEC_H
+#define _RK3328_CODEC_H
+
+#include <linux/bitfield.h>
+
+/* codec register */
+#define CODEC_RESET                    (0x00 << 2)
+#define DAC_INIT_CTRL1                 (0x03 << 2)
+#define DAC_INIT_CTRL2                 (0x04 << 2)
+#define DAC_INIT_CTRL3                 (0x05 << 2)
+#define DAC_PRECHARGE_CTRL             (0x22 << 2)
+#define DAC_PWR_CTRL                   (0x23 << 2)
+#define DAC_CLK_CTRL                   (0x24 << 2)
+#define HPMIX_CTRL                     (0x25 << 2)
+#define DAC_SELECT                     (0x26 << 2)
+#define HPOUT_CTRL                     (0x27 << 2)
+#define HPOUTL_GAIN_CTRL               (0x28 << 2)
+#define HPOUTR_GAIN_CTRL               (0x29 << 2)
+#define HPOUT_POP_CTRL                 (0x2a << 2)
+
+/* REG00: CODEC_RESET */
+#define PWR_RST_BYPASS_DIS             (0x0 << 6)
+#define PWR_RST_BYPASS_EN              (0x1 << 6)
+#define DIG_CORE_RST                   (0x0 << 1)
+#define DIG_CORE_WORK                  (0x1 << 1)
+#define SYS_RST                                (0x0 << 0)
+#define SYS_WORK                       (0x1 << 0)
+
+/* REG03: DAC_INIT_CTRL1 */
+#define PIN_DIRECTION_MASK             BIT(5)
+#define PIN_DIRECTION_IN               (0x0 << 5)
+#define PIN_DIRECTION_OUT              (0x1 << 5)
+#define DAC_I2S_MODE_MASK              BIT(4)
+#define DAC_I2S_MODE_SLAVE             (0x0 << 4)
+#define DAC_I2S_MODE_MASTER            (0x1 << 4)
+
+/* REG04: DAC_INIT_CTRL2 */
+#define DAC_I2S_LRP_MASK               BIT(7)
+#define DAC_I2S_LRP_NORMAL             (0x0 << 7)
+#define DAC_I2S_LRP_REVERSAL           (0x1 << 7)
+#define DAC_VDL_MASK                   GENMASK(6, 5)
+#define DAC_VDL_16BITS                 (0x0 << 5)
+#define DAC_VDL_20BITS                 (0x1 << 5)
+#define DAC_VDL_24BITS                 (0x2 << 5)
+#define DAC_VDL_32BITS                 (0x3 << 5)
+#define DAC_MODE_MASK                  GENMASK(4, 3)
+#define DAC_MODE_RJM                   (0x0 << 3)
+#define DAC_MODE_LJM                   (0x1 << 3)
+#define DAC_MODE_I2S                   (0x2 << 3)
+#define DAC_MODE_PCM                   (0x3 << 3)
+#define DAC_LR_SWAP_MASK               BIT(2)
+#define DAC_LR_SWAP_DIS                        (0x0 << 2)
+#define DAC_LR_SWAP_EN                 (0x1 << 2)
+
+/* REG05: DAC_INIT_CTRL3 */
+#define DAC_WL_MASK                    GENMASK(3, 2)
+#define DAC_WL_16BITS                  (0x0 << 2)
+#define DAC_WL_20BITS                  (0x1 << 2)
+#define DAC_WL_24BITS                  (0x2 << 2)
+#define DAC_WL_32BITS                  (0x3 << 2)
+#define DAC_RST_MASK                   BIT(1)
+#define DAC_RST_EN                     (0x0 << 1)
+#define DAC_RST_DIS                    (0x1 << 1)
+#define DAC_BCP_MASK                   BIT(0)
+#define DAC_BCP_NORMAL                 (0x0 << 0)
+#define DAC_BCP_REVERSAL               (0x1 << 0)
+
+/* REG22: DAC_PRECHARGE_CTRL */
+#define DAC_CHARGE_XCHARGE_MASK                BIT(7)
+#define DAC_CHARGE_DISCHARGE           (0x0 << 7)
+#define DAC_CHARGE_PRECHARGE           (0x1 << 7)
+#define DAC_CHARGE_CURRENT_64I_MASK    BIT(6)
+#define DAC_CHARGE_CURRENT_64I         (0x1 << 6)
+#define DAC_CHARGE_CURRENT_32I_MASK    BIT(5)
+#define DAC_CHARGE_CURRENT_32I         (0x1 << 5)
+#define DAC_CHARGE_CURRENT_16I_MASK    BIT(4)
+#define DAC_CHARGE_CURRENT_16I         (0x1 << 4)
+#define DAC_CHARGE_CURRENT_08I_MASK    BIT(3)
+#define DAC_CHARGE_CURRENT_08I         (0x1 << 3)
+#define DAC_CHARGE_CURRENT_04I_MASK    BIT(2)
+#define DAC_CHARGE_CURRENT_04I         (0x1 << 2)
+#define DAC_CHARGE_CURRENT_02I_MASK    BIT(1)
+#define DAC_CHARGE_CURRENT_02I         (0x1 << 1)
+#define DAC_CHARGE_CURRENT_I_MASK      BIT(0)
+#define DAC_CHARGE_CURRENT_I           (0x1 << 0)
+#define DAC_CHARGE_CURRENT_ALL_MASK    GENMASK(6, 0)
+#define DAC_CHARGE_CURRENT_ALL_OFF     0x00
+#define DAC_CHARGE_CURRENT_ALL_ON      0x7f
+
+/* REG23: DAC_PWR_CTRL */
+#define DAC_PWR_MASK                   BIT(6)
+#define DAC_PWR_OFF                    (0x0 << 6)
+#define DAC_PWR_ON                     (0x1 << 6)
+#define DACL_PATH_REFV_MASK            BIT(5)
+#define DACL_PATH_REFV_OFF             (0x0 << 5)
+#define DACL_PATH_REFV_ON              (0x1 << 5)
+#define HPOUTL_ZERO_CROSSING_MASK      BIT(4)
+#define HPOUTL_ZERO_CROSSING_OFF       (0x0 << 4)
+#define HPOUTL_ZERO_CROSSING_ON                (0x1 << 4)
+#define DACR_PATH_REFV_MASK            BIT(1)
+#define DACR_PATH_REFV_OFF             (0x0 << 1)
+#define DACR_PATH_REFV_ON              (0x1 << 1)
+#define HPOUTR_ZERO_CROSSING_MASK      BIT(0)
+#define HPOUTR_ZERO_CROSSING_OFF       (0x0 << 0)
+#define HPOUTR_ZERO_CROSSING_ON                (0x1 << 0)
+
+/* REG24: DAC_CLK_CTRL */
+#define DACL_REFV_MASK                 BIT(7)
+#define DACL_REFV_OFF                  (0x0 << 7)
+#define DACL_REFV_ON                   (0x1 << 7)
+#define DACL_CLK_MASK                  BIT(6)
+#define DACL_CLK_OFF                   (0x0 << 6)
+#define DACL_CLK_ON                    (0x1 << 6)
+#define DACL_MASK                      BIT(5)
+#define DACL_OFF                       (0x0 << 5)
+#define DACL_ON                                (0x1 << 5)
+#define DACL_INIT_MASK                 BIT(4)
+#define DACL_INIT_OFF                  (0x0 << 4)
+#define DACL_INIT_ON                   (0x1 << 4)
+#define DACR_REFV_MASK                 BIT(3)
+#define DACR_REFV_OFF                  (0x0 << 3)
+#define DACR_REFV_ON                   (0x1 << 3)
+#define DACR_CLK_MASK                  BIT(2)
+#define DACR_CLK_OFF                   (0x0 << 2)
+#define DACR_CLK_ON                    (0x1 << 2)
+#define DACR_MASK                      BIT(1)
+#define DACR_OFF                       (0x0 << 1)
+#define DACR_ON                                (0x1 << 1)
+#define DACR_INIT_MASK                 BIT(0)
+#define DACR_INIT_OFF                  (0x0 << 0)
+#define DACR_INIT_ON                   (0x1 << 0)
+
+/* REG25: HPMIX_CTRL*/
+#define HPMIXL_MASK                    BIT(6)
+#define HPMIXL_DIS                     (0x0 << 6)
+#define HPMIXL_EN                      (0x1 << 6)
+#define HPMIXL_INIT_MASK               BIT(5)
+#define HPMIXL_INIT_DIS                        (0x0 << 5)
+#define HPMIXL_INIT_EN                 (0x1 << 5)
+#define HPMIXL_INIT2_MASK              BIT(4)
+#define HPMIXL_INIT2_DIS               (0x0 << 4)
+#define HPMIXL_INIT2_EN                        (0x1 << 4)
+#define HPMIXR_MASK                    BIT(2)
+#define HPMIXR_DIS                     (0x0 << 2)
+#define HPMIXR_EN                      (0x1 << 2)
+#define HPMIXR_INIT_MASK               BIT(1)
+#define HPMIXR_INIT_DIS                        (0x0 << 1)
+#define HPMIXR_INIT_EN                 (0x1 << 1)
+#define HPMIXR_INIT2_MASK              BIT(0)
+#define HPMIXR_INIT2_DIS               (0x0 << 0)
+#define HPMIXR_INIT2_EN                        (0x1 << 0)
+
+/* REG26: DAC_SELECT */
+#define DACL_SELECT_MASK               BIT(4)
+#define DACL_UNSELECT                  (0x0 << 4)
+#define DACL_SELECT                    (0x1 << 4)
+#define DACR_SELECT_MASK               BIT(0)
+#define DACR_UNSELECT                  (0x0 << 0)
+#define DACR_SELECT                    (0x1 << 0)
+
+/* REG27: HPOUT_CTRL */
+#define HPOUTL_MASK                    BIT(7)
+#define HPOUTL_DIS                     (0x0 << 7)
+#define HPOUTL_EN                      (0x1 << 7)
+#define HPOUTL_INIT_MASK               BIT(6)
+#define HPOUTL_INIT_DIS                        (0x0 << 6)
+#define HPOUTL_INIT_EN                 (0x1 << 6)
+#define HPOUTL_MUTE_MASK               BIT(5)
+#define HPOUTL_MUTE                    (0x0 << 5)
+#define HPOUTL_UNMUTE                  (0x1 << 5)
+#define HPOUTR_MASK                    BIT(4)
+#define HPOUTR_DIS                     (0x0 << 4)
+#define HPOUTR_EN                      (0x1 << 4)
+#define HPOUTR_INIT_MASK               BIT(3)
+#define HPOUTR_INIT_DIS                        (0x0 << 3)
+#define HPOUTR_INIT_EN                 (0x1 << 3)
+#define HPOUTR_MUTE_MASK               BIT(2)
+#define HPOUTR_MUTE                    (0x0 << 2)
+#define HPOUTR_UNMUTE                  (0x1 << 2)
+
+/* REG28: HPOUTL_GAIN_CTRL */
+#define HPOUTL_GAIN_MASK               GENMASK(4, 0)
+
+/* REG29: HPOUTR_GAIN_CTRL */
+#define HPOUTR_GAIN_MASK               GENMASK(4, 0)
+
+/* REG2a: HPOUT_POP_CTRL */
+#define HPOUTR_POP_MASK                        GENMASK(5, 4)
+#define HPOUTR_POP_XCHARGE             (0x1 << 4)
+#define HPOUTR_POP_WORK                        (0x2 << 4)
+#define HPOUTL_POP_MASK                        GENMASK(1, 0)
+#define HPOUTL_POP_XCHARGE             (0x1 << 0)
+#define HPOUTL_POP_WORK                        (0x2 << 0)
+
+#define RK3328_HIFI                    0
+
+struct rk3328_reg_msk_val {
+       unsigned int reg;
+       unsigned int msk;
+       unsigned int val;
+};
+
+#endif
index 0ef966d56bac300f00f1fac7aa92f54fa9949e7e..e2855ab9a2c6b5f84a4e179d254564d62cb6efff 100644 (file)
@@ -1128,8 +1128,11 @@ static int rt274_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
-       regmap_read(rt274->regmap,
+       ret = regmap_read(rt274->regmap,
                RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+       if (ret)
+               return ret;
+
        if (val != RT274_VENDOR_ID) {
                dev_err(&i2c->dev,
                        "Device with ID register %#x is not rt274\n", val);
index 34cfaf8f6f3452b9839614a03c4b72c9cb82cd17..89c43b26c3790814645172e8165e1ac971972319 100644 (file)
@@ -2512,6 +2512,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682)
        regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
        regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
        regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
+       regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
 
        mutex_unlock(&rt5682->calibrate_mutex);
 
index 2a3e5fbd04e46f4debc2b1267dc15ccf34fe2c4c..9e0f96e0f8ece35a76d7cb830d8b7d4cba0894b5 100644 (file)
@@ -1837,9 +1837,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               ret = clk_prepare_enable(wm8904->mclk);
-               if (ret)
-                       return ret;
                break;
 
        case SND_SOC_BIAS_PREPARE:
@@ -1864,6 +1861,15 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
                                return ret;
                        }
 
+                       ret = clk_prepare_enable(wm8904->mclk);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to enable MCLK: %d\n", ret);
+                               regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
+                                                      wm8904->supplies);
+                               return ret;
+                       }
+
                        regcache_cache_only(wm8904->regmap, false);
                        regcache_sync(wm8904->regmap);
 
@@ -2108,16 +2114,13 @@ static const struct regmap_config wm8904_regmap = {
 };
 
 #ifdef CONFIG_OF
-static enum wm8904_type wm8904_data = WM8904;
-static enum wm8904_type wm8912_data = WM8912;
-
 static const struct of_device_id wm8904_of_match[] = {
        {
                .compatible = "wlf,wm8904",
-               .data = &wm8904_data,
+               .data = (void *)WM8904,
        }, {
                .compatible = "wlf,wm8912",
-               .data = &wm8912_data,
+               .data = (void *)WM8912,
        }, {
                /* sentinel */
        }
@@ -2158,7 +2161,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
                match = of_match_node(wm8904_of_match, i2c->dev.of_node);
                if (match == NULL)
                        return -EINVAL;
-               wm8904->devtype = *((enum wm8904_type *)match->data);
+               wm8904->devtype = (enum wm8904_type)match->data;
        } else {
                wm8904->devtype = id->driver_data;
        }
index 92c2cf06f40ac89bebdd7a9f193ae1ed51983907..83f1243145b00b603462656c8bb1cedf8ce8414d 100644 (file)
@@ -8,14 +8,6 @@ config SND_SIMPLE_CARD
          This option enables generic simple sound card support
          It also support DPCM of multi CPU single Codec ststem.
 
-config SND_SIMPLE_SCU_CARD
-       tristate "ASoC Simple SCU sound card support"
-       depends on OF
-       select SND_SIMPLE_CARD_UTILS
-       help
-         This option enables generic simple SCU sound card support.
-         It supports DPCM of multi CPU single Codec system.
-
 config SND_AUDIO_GRAPH_CARD
        tristate "ASoC Audio Graph sound card support"
        depends on OF
@@ -24,12 +16,3 @@ config SND_AUDIO_GRAPH_CARD
          This option enables generic simple sound card support
          with OF-graph DT bindings.
          It also support DPCM of multi CPU single Codec ststem.
-
-config SND_AUDIO_GRAPH_SCU_CARD
-       tristate "ASoC Audio Graph SCU sound card support"
-       depends on OF
-       select SND_SIMPLE_CARD_UTILS
-       help
-         This option enables generic simple SCU sound card support
-         with OF-graph DT bindings.
-         It supports DPCM of multi CPU single Codec ststem.
index 9dec293a4c4de941d2cfc689f048bb38e7a31846..21c29e5e0671e2f61f7b6fbf39dfedb5da67568a 100644 (file)
@@ -1,12 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 snd-soc-simple-card-utils-objs := simple-card-utils.o
 snd-soc-simple-card-objs       := simple-card.o
-snd-soc-simple-scu-card-objs   := simple-scu-card.o
 snd-soc-audio-graph-card-objs  := audio-graph-card.o
-snd-soc-audio-graph-scu-card-objs      := audio-graph-scu-card.o
 
 obj-$(CONFIG_SND_SIMPLE_CARD_UTILS)    += snd-soc-simple-card-utils.o
 obj-$(CONFIG_SND_SIMPLE_CARD)          += snd-soc-simple-card.o
-obj-$(CONFIG_SND_SIMPLE_SCU_CARD)      += snd-soc-simple-scu-card.o
 obj-$(CONFIG_SND_AUDIO_GRAPH_CARD)     += snd-soc-audio-graph-card.o
-obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o
index 0d6144560a1e5d6ee31d0bfb8c0b186945c88d17..3ec96cdc683b5a1da1ba07939632d772c9e16247 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/string.h>
 #include <sound/simple_card_utils.h>
 
-struct graph_card_data {
+struct graph_priv {
        struct snd_soc_card snd_card;
        struct graph_dai_props {
                struct asoc_simple_dai *cpu_dai;
@@ -39,6 +39,13 @@ struct graph_card_data {
        struct gpio_desc *pa_gpio;
 };
 
+struct link_info {
+       int dais; /* number of dai  */
+       int link; /* number of link */
+       int conf; /* number of codec_conf */
+       int cpu;  /* turn for CPU / Codec */
+};
+
 #define graph_priv_to_card(priv) (&(priv)->snd_card)
 #define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
 #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
@@ -46,12 +53,12 @@ struct graph_card_data {
 
 #define PREFIX "audio-graph-card,"
 
-static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
-                                       struct snd_kcontrol *kcontrol,
-                                       int event)
+static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *kcontrol,
+                             int event)
 {
        struct snd_soc_dapm_context *dapm = w->dapm;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(dapm->card);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
@@ -67,16 +74,16 @@ static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
        SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
-                              0, 0, NULL, 0, asoc_graph_card_outdrv_event,
+                              0, 0, NULL, 0, graph_outdrv_event,
                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 };
 
-static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
+static int graph_startup(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
        int ret;
 
@@ -91,10 +98,10 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
        return ret;
 }
 
-static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
+static void graph_shutdown(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
 
        asoc_simple_card_clk_disable(dai_props->cpu_dai);
@@ -102,13 +109,13 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
        asoc_simple_card_clk_disable(dai_props->codec_dai);
 }
 
-static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream,
-                                    struct snd_pcm_hw_params *params)
+static int graph_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
        unsigned int mclk, mclk_fs = 0;
        int ret = 0;
@@ -133,15 +140,15 @@ err:
        return ret;
 }
 
-static const struct snd_soc_ops asoc_graph_card_ops = {
-       .startup = asoc_graph_card_startup,
-       .shutdown = asoc_graph_card_shutdown,
-       .hw_params = asoc_graph_card_hw_params,
+static const struct snd_soc_ops graph_ops = {
+       .startup        = graph_startup,
+       .shutdown       = graph_shutdown,
+       .hw_params      = graph_hw_params,
 };
 
-static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+static int graph_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct graph_card_data *priv =  snd_soc_card_get_drvdata(rtd->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
        int ret = 0;
 
@@ -158,10 +165,10 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
-static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                             struct snd_pcm_hw_params *params)
+static int graph_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                                   struct snd_pcm_hw_params *params)
 {
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
 
        asoc_simple_card_convert_fixup(&dai_props->adata, params);
@@ -169,41 +176,64 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
-                                           struct device_node *cpu_ep,
-                                           struct device_node *codec_ep,
-                                           struct graph_card_data *priv,
-                                           int *dai_idx, int link_idx,
-                                           int *conf_idx, int is_cpu)
+static void graph_get_conversion(struct device *dev,
+                                struct device_node *ep,
+                                struct asoc_simple_card_data *adata)
 {
-       struct device *dev = graph_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
-       struct device_node *ep = is_cpu ? cpu_ep : codec_ep;
+       struct device_node *top = dev->of_node;
        struct device_node *port = of_get_parent(ep);
        struct device_node *ports = of_get_parent(port);
        struct device_node *node = of_graph_get_port_parent(ep);
+
+       asoc_simple_card_parse_convert(dev, top,   NULL,   adata);
+       asoc_simple_card_parse_convert(dev, node,  PREFIX, adata);
+       asoc_simple_card_parse_convert(dev, ports, NULL,   adata);
+       asoc_simple_card_parse_convert(dev, port,  NULL,   adata);
+       asoc_simple_card_parse_convert(dev, ep,    NULL,   adata);
+}
+
+static int graph_dai_link_of_dpcm(struct graph_priv *priv,
+                                 struct device_node *cpu_ep,
+                                 struct device_node *codec_ep,
+                                 struct link_info *li,
+                                 int dup_codec)
+{
+       struct device *dev = graph_priv_to_dev(priv);
+       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
+       struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
+       struct device_node *top = dev->of_node;
+       struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
+       struct device_node *port;
+       struct device_node *ports;
+       struct device_node *node;
        struct asoc_simple_dai *dai;
        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
        int ret;
 
-       dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec");
+       /* Do it all CPU endpoint, and 1st Codec endpoint */
+       if (!li->cpu && dup_codec)
+               return 0;
+
+       port    = of_get_parent(ep);
+       ports   = of_get_parent(port);
+       node    = of_graph_get_port_parent(ep);
+
+       li->link++;
+
+       dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
 
        of_property_read_u32(top,   "mclk-fs", &dai_props->mclk_fs);
        of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs);
        of_property_read_u32(port,  "mclk-fs", &dai_props->mclk_fs);
        of_property_read_u32(ep,    "mclk-fs", &dai_props->mclk_fs);
 
-       asoc_simple_card_parse_convert(dev, top,   NULL,   &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, node,  PREFIX, &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, ports, NULL,   &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, port,  NULL,   &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, ep,    NULL,   &dai_props->adata);
+       graph_get_conversion(dev, ep, &dai_props->adata);
 
        of_node_put(ports);
        of_node_put(port);
+       of_node_put(node);
 
-       if (is_cpu) {
+       if (li->cpu) {
 
                /* BE is dummy */
                codecs->of_node         = NULL;
@@ -215,7 +245,7 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
                dai_link->dpcm_merged_format    = 1;
 
                dai =
-               dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
+               dai_props->cpu_dai      = &priv->dais[li->dais++];
 
                ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
                if (ret)
@@ -244,13 +274,13 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
 
                /* BE settings */
                dai_link->no_pcm                = 1;
-               dai_link->be_hw_params_fixup    = asoc_graph_card_be_hw_params_fixup;
+               dai_link->be_hw_params_fixup    = graph_be_hw_params_fixup;
 
                dai =
-               dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
+               dai_props->codec_dai    = &priv->dais[li->dais++];
 
                cconf =
-               dai_props->codec_conf   = &priv->codec_conf[(*conf_idx)++];
+               dai_props->codec_conf   = &priv->codec_conf[li->conf++];
 
                ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
                if (ret < 0)
@@ -292,35 +322,46 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
 
        dai_link->dpcm_playback         = 1;
        dai_link->dpcm_capture          = 1;
-       dai_link->ops                   = &asoc_graph_card_ops;
-       dai_link->init                  = asoc_graph_card_dai_init;
+       dai_link->ops                   = &graph_ops;
+       dai_link->init                  = graph_dai_init;
 
        return 0;
 }
 
-static int asoc_graph_card_dai_link_of(struct device_node *top,
-                                       struct device_node *cpu_ep,
-                                       struct device_node *codec_ep,
-                                       struct graph_card_data *priv,
-                                       int *dai_idx, int link_idx)
+static int graph_dai_link_of(struct graph_priv *priv,
+                            struct device_node *cpu_ep,
+                            struct device_node *codec_ep,
+                            struct link_info *li)
 {
        struct device *dev = graph_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
-       struct device_node *cpu_port = of_get_parent(cpu_ep);
-       struct device_node *codec_port = of_get_parent(codec_ep);
-       struct device_node *cpu_ports = of_get_parent(cpu_port);
-       struct device_node *codec_ports = of_get_parent(codec_port);
+       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
+       struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
+       struct device_node *top = dev->of_node;
+       struct device_node *cpu_port;
+       struct device_node *cpu_ports;
+       struct device_node *codec_port;
+       struct device_node *codec_ports;
        struct asoc_simple_dai *cpu_dai;
        struct asoc_simple_dai *codec_dai;
        int ret;
 
-       dev_dbg(dev, "link_of\n");
+       /* Do it only CPU turn */
+       if (!li->cpu)
+               return 0;
+
+       cpu_port        = of_get_parent(cpu_ep);
+       cpu_ports       = of_get_parent(cpu_port);
+       codec_port      = of_get_parent(codec_ep);
+       codec_ports     = of_get_parent(codec_port);
+
+       dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
+
+       li->link++;
 
        cpu_dai                 =
-       dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
+       dai_props->cpu_dai      = &priv->dais[li->dais++];
        codec_dai               =
-       dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
+       dai_props->codec_dai    = &priv->dais[li->dais++];
 
        /* Factor to mclk, used in hw_params() */
        of_property_read_u32(top,         "mclk-fs", &dai_props->mclk_fs);
@@ -375,8 +416,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *top,
        if (ret < 0)
                return ret;
 
-       dai_link->ops = &asoc_graph_card_ops;
-       dai_link->init = asoc_graph_card_dai_init;
+       dai_link->ops = &graph_ops;
+       dai_link->init = graph_dai_init;
 
        asoc_simple_card_canonicalize_cpu(dai_link,
                of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
@@ -384,21 +425,79 @@ static int asoc_graph_card_dai_link_of(struct device_node *top,
        return 0;
 }
 
-static int asoc_graph_card_parse_of(struct graph_card_data *priv)
+static int graph_for_each_link(struct graph_priv *priv,
+                       struct link_info *li,
+                       int (*func_noml)(struct graph_priv *priv,
+                                        struct device_node *cpu_ep,
+                                        struct device_node *codec_ep,
+                                        struct link_info *li),
+                       int (*func_dpcm)(struct graph_priv *priv,
+                                        struct device_node *cpu_ep,
+                                        struct device_node *codec_ep,
+                                        struct link_info *li, int dup_codec))
 {
        struct of_phandle_iterator it;
        struct device *dev = graph_priv_to_dev(priv);
-       struct snd_soc_card *card = graph_priv_to_card(priv);
-       struct device_node *top = dev->of_node;
-       struct device_node *node = top;
+       struct device_node *node = dev->of_node;
        struct device_node *cpu_port;
-       struct device_node *cpu_ep              = NULL;
-       struct device_node *codec_ep            = NULL;
-       struct device_node *codec_port          = NULL;
-       struct device_node *codec_port_old      = NULL;
+       struct device_node *cpu_ep;
+       struct device_node *codec_ep;
+       struct device_node *codec_port;
+       struct device_node *codec_port_old = NULL;
+       struct asoc_simple_card_data adata;
        int rc, ret;
-       int link_idx, dai_idx, conf_idx;
-       int cpu;
+
+       /* loop for all listed CPU port */
+       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+               cpu_port = it.node;
+               cpu_ep   = NULL;
+
+               /* loop for all CPU endpoint */
+               while (1) {
+                       cpu_ep = of_get_next_child(cpu_port, cpu_ep);
+                       if (!cpu_ep)
+                               break;
+
+                       /* get codec */
+                       codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+                       codec_port = of_get_parent(codec_ep);
+
+                       of_node_put(codec_ep);
+                       of_node_put(codec_port);
+
+                       /* get convert-xxx property */
+                       memset(&adata, 0, sizeof(adata));
+                       graph_get_conversion(dev, codec_ep, &adata);
+                       graph_get_conversion(dev, cpu_ep,   &adata);
+
+                       /*
+                        * It is DPCM
+                        * if Codec port has many endpoints,
+                        * or has convert-xxx property
+                        */
+                       if ((of_get_child_count(codec_port) > 1) ||
+                           adata.convert_rate || adata.convert_channels)
+                               ret = func_dpcm(priv, cpu_ep, codec_ep, li,
+                                               (codec_port_old == codec_port));
+                       /* else normal sound */
+                       else
+                               ret = func_noml(priv, cpu_ep, codec_ep, li);
+
+                       if (ret < 0)
+                               return ret;
+
+                       codec_port_old = codec_port;
+               }
+       }
+
+       return 0;
+}
+
+static int graph_parse_of(struct graph_priv *priv)
+{
+       struct snd_soc_card *card = graph_priv_to_card(priv);
+       struct link_info li;
+       int ret;
 
        ret = asoc_simple_card_of_parse_widgets(card, NULL);
        if (ret < 0)
@@ -408,11 +507,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
        if (ret < 0)
                return ret;
 
-       link_idx        = 0;
-       dai_idx         = 0;
-       conf_idx        = 0;
-       codec_port_old  = NULL;
-       for (cpu = 1; cpu >= 0; cpu--) {
+       memset(&li, 0, sizeof(li));
+       for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
                /*
                 * Detect all CPU first, and Detect all Codec 2nd.
                 *
@@ -425,66 +521,57 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
                 * To avoid random sub-device numbering,
                 * detect "dummy-Codec" in last;
                 */
-               of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
-                       cpu_port = it.node;
-                       cpu_ep   = NULL;
-                       while (1) {
-                               cpu_ep = of_get_next_child(cpu_port, cpu_ep);
-                               if (!cpu_ep)
-                                       break;
-
-                               codec_ep   = of_graph_get_remote_endpoint(cpu_ep);
-                               codec_port = of_get_parent(codec_ep);
-
-                               of_node_put(codec_ep);
-                               of_node_put(codec_port);
-
-                               dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep);
-
-                               if (of_get_child_count(codec_port) > 1) {
-                                       /*
-                                        * for DPCM sound
-                                        */
-                                       if (!cpu) {
-                                               if (codec_port_old == codec_port)
-                                                       continue;
-                                               codec_port_old = codec_port;
-                                       }
-                                       ret = asoc_graph_card_dai_link_of_dpcm(
-                                               top, cpu_ep, codec_ep, priv,
-                                               &dai_idx, link_idx++,
-                                               &conf_idx, cpu);
-                               } else if (cpu) {
-                                       /*
-                                        * for Normal sound
-                                        */
-                                       ret = asoc_graph_card_dai_link_of(
-                                               top, cpu_ep, codec_ep, priv,
-                                               &dai_idx, link_idx++);
-                               }
-                               if (ret < 0)
-                                       return ret;
-                       }
-               }
+               ret = graph_for_each_link(priv, &li,
+                                         graph_dai_link_of,
+                                         graph_dai_link_of_dpcm);
+               if (ret < 0)
+                       return ret;
        }
 
        return asoc_simple_card_parse_card_name(card, NULL);
 }
 
-static void asoc_graph_get_dais_count(struct device *dev,
-                                     int *link_num,
-                                     int *dais_num,
-                                     int *ccnf_num)
+static int graph_count_noml(struct graph_priv *priv,
+                           struct device_node *cpu_ep,
+                           struct device_node *codec_ep,
+                           struct link_info *li)
 {
-       struct of_phandle_iterator it;
-       struct device_node *node = dev->of_node;
-       struct device_node *cpu_port;
-       struct device_node *cpu_ep;
-       struct device_node *codec_ep;
-       struct device_node *codec_port;
-       struct device_node *codec_port_old;
-       struct device_node *codec_port_old2;
-       int rc;
+       struct device *dev = graph_priv_to_dev(priv);
+
+       li->link += 1; /* 1xCPU-Codec */
+       li->dais += 2; /* 1xCPU + 1xCodec */
+
+       dev_dbg(dev, "Count As Normal\n");
+
+       return 0;
+}
+
+static int graph_count_dpcm(struct graph_priv *priv,
+                           struct device_node *cpu_ep,
+                           struct device_node *codec_ep,
+                           struct link_info *li,
+                           int dup_codec)
+{
+       struct device *dev = graph_priv_to_dev(priv);
+
+       li->link++; /* 1xCPU-dummy */
+       li->dais++; /* 1xCPU */
+
+       if (!dup_codec) {
+               li->link++; /* 1xdummy-Codec */
+               li->conf++; /* 1xdummy-Codec */
+               li->dais++; /* 1xCodec */
+       }
+
+       dev_dbg(dev, "Count As DPCM\n");
+
+       return 0;
+}
+
+static void graph_get_dais_count(struct graph_priv *priv,
+                                struct link_info *li)
+{
+       struct device *dev = graph_priv_to_dev(priv);
 
        /*
         * link_num :   number of links.
@@ -522,45 +609,26 @@ static void asoc_graph_get_dais_count(struct device *dev,
         *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
         *      => 6 DAIs  = 4xCPU + 2xCodec
         *      => 2 ccnf  = 2xdummy-Codec
+        *
+        * ex4)
+        * CPU0 --- Codec0 (convert-rate)       link : 3
+        * CPU1 --- Codec1                      dais : 4
+        *                                      ccnf : 1
+        *
+        *      => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
+        *      => 4 DAIs  = 2xCPU + 2xCodec
+        *      => 1 ccnf  = 1xdummy-Codec
         */
-       codec_port_old = NULL;
-       codec_port_old2 = NULL;
-       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
-               cpu_port = it.node;
-               cpu_ep   = NULL;
-               while (1) {
-                       cpu_ep = of_get_next_child(cpu_port, cpu_ep);
-                       if (!cpu_ep)
-                               break;
-
-                       codec_ep = of_graph_get_remote_endpoint(cpu_ep);
-                       codec_port = of_get_parent(codec_ep);
-
-                       of_node_put(codec_ep);
-                       of_node_put(codec_port);
-
-                       (*link_num)++;
-                       (*dais_num)++;
-
-                       if (codec_port_old == codec_port) {
-                               if (codec_port_old2 != codec_port_old) {
-                                       (*link_num)++;
-                                       (*ccnf_num)++;
-                               }
-
-                               codec_port_old2 = codec_port_old;
-                               continue;
-                       }
-
-                       (*dais_num)++;
-                       codec_port_old = codec_port;
-               }
-       }
+       graph_for_each_link(priv, li,
+                           graph_count_noml,
+                           graph_count_dpcm);
+       dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
+               li->link, li->dais, li->conf);
 }
 
-static int asoc_graph_soc_card_probe(struct snd_soc_card *card)
+static int graph_card_probe(struct snd_soc_card *card)
 {
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(card);
+       struct graph_priv *priv = snd_soc_card_get_drvdata(card);
        int ret;
 
        ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL);
@@ -574,16 +642,16 @@ static int asoc_graph_soc_card_probe(struct snd_soc_card *card)
        return 0;
 }
 
-static int asoc_graph_card_probe(struct platform_device *pdev)
+static int graph_probe(struct platform_device *pdev)
 {
-       struct graph_card_data *priv;
+       struct graph_priv *priv;
        struct snd_soc_dai_link *dai_link;
        struct graph_dai_props *dai_props;
        struct asoc_simple_dai *dais;
        struct device *dev = &pdev->dev;
        struct snd_soc_card *card;
        struct snd_soc_codec_conf *cconf;
-       int lnum = 0, dnum = 0, cnum = 0;
+       struct link_info li;
        int ret, i;
 
        /* Allocate the private data and the DAI link array */
@@ -591,14 +659,22 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
-       asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
-       if (!lnum || !dnum)
+       card = graph_priv_to_card(priv);
+       card->owner             = THIS_MODULE;
+       card->dev               = dev;
+       card->dapm_widgets      = graph_dapm_widgets;
+       card->num_dapm_widgets  = ARRAY_SIZE(graph_dapm_widgets);
+       card->probe             = graph_card_probe;
+
+       memset(&li, 0, sizeof(li));
+       graph_get_dais_count(priv, &li);
+       if (!li.link || !li.dais)
                return -EINVAL;
 
-       dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
-       dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link),  GFP_KERNEL);
-       dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
-       cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
+       dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
+       dai_link  = devm_kcalloc(dev, li.link, sizeof(*dai_link),  GFP_KERNEL);
+       dais      = devm_kcalloc(dev, li.dais, sizeof(*dais),      GFP_KERNEL);
+       cconf     = devm_kcalloc(dev, li.conf, sizeof(*cconf),     GFP_KERNEL);
        if (!dai_props || !dai_link || !dais)
                return -ENOMEM;
 
@@ -608,7 +684,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
         * see
         *      soc-core.c :: snd_soc_init_multicodec()
         */
-       for (i = 0; i < lnum; i++) {
+       for (i = 0; i < li.link; i++) {
                dai_link[i].codecs      = &dai_props[i].codecs;
                dai_link[i].num_codecs  = 1;
                dai_link[i].platform    = &dai_props[i].platform;
@@ -621,24 +697,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
                return ret;
        }
 
-       priv->dai_props                 = dai_props;
-       priv->dai_link                  = dai_link;
-       priv->dais                      = dais;
-       priv->codec_conf                = cconf;
+       priv->dai_props         = dai_props;
+       priv->dai_link          = dai_link;
+       priv->dais              = dais;
+       priv->codec_conf        = cconf;
 
-       /* Init snd_soc_card */
-       card = graph_priv_to_card(priv);
-       card->owner             = THIS_MODULE;
-       card->dev               = dev;
        card->dai_link          = dai_link;
-       card->num_links         = lnum;
-       card->dapm_widgets      = asoc_graph_card_dapm_widgets;
-       card->num_dapm_widgets  = ARRAY_SIZE(asoc_graph_card_dapm_widgets);
-       card->probe             = asoc_graph_soc_card_probe;
+       card->num_links         = li.link;
        card->codec_conf        = cconf;
-       card->num_configs       = cnum;
+       card->num_configs       = li.conf;
 
-       ret = asoc_graph_card_parse_of(priv);
+       ret = graph_parse_of(priv);
        if (ret < 0) {
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "parse error %d\n", ret);
@@ -658,30 +727,30 @@ err:
        return ret;
 }
 
-static int asoc_graph_card_remove(struct platform_device *pdev)
+static int graph_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        return asoc_simple_card_clean_reference(card);
 }
 
-static const struct of_device_id asoc_graph_of_match[] = {
+static const struct of_device_id graph_of_match[] = {
        { .compatible = "audio-graph-card", },
        { .compatible = "audio-graph-scu-card", },
        {},
 };
-MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
+MODULE_DEVICE_TABLE(of, graph_of_match);
 
-static struct platform_driver asoc_graph_card = {
+static struct platform_driver graph_card = {
        .driver = {
                .name = "asoc-audio-graph-card",
                .pm = &snd_soc_pm_ops,
-               .of_match_table = asoc_graph_of_match,
+               .of_match_table = graph_of_match,
        },
-       .probe = asoc_graph_card_probe,
-       .remove = asoc_graph_card_remove,
+       .probe = graph_probe,
+       .remove = graph_remove,
 };
-module_platform_driver(asoc_graph_card);
+module_platform_driver(graph_card);
 
 MODULE_ALIAS("platform:asoc-audio-graph-card");
 MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c
deleted file mode 100644 (file)
index e1b192e..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// ASoC audio graph SCU sound card support
-//
-// Copyright (C) 2017 Renesas Solutions Corp.
-// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
-//
-// based on
-//     ${LINUX}/sound/soc/generic/simple-scu-card.c
-//     ${LINUX}/sound/soc/generic/audio-graph-card.c
-
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/string.h>
-#include <sound/jack.h>
-#include <sound/simple_card_utils.h>
-
-struct graph_card_data {
-       struct snd_soc_card snd_card;
-       struct graph_dai_props {
-               struct asoc_simple_dai *cpu_dai;
-               struct asoc_simple_dai *codec_dai;
-               struct snd_soc_dai_link_component codecs;
-               struct snd_soc_dai_link_component platform;
-               struct asoc_simple_card_data adata;
-               struct snd_soc_codec_conf *codec_conf;
-       } *dai_props;
-       struct snd_soc_dai_link *dai_link;
-       struct asoc_simple_dai *dais;
-       struct asoc_simple_card_data adata;
-       struct snd_soc_codec_conf *codec_conf;
-};
-
-#define graph_priv_to_card(priv) (&(priv)->snd_card)
-#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
-#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
-
-#define PREFIX "audio-graph-card,"
-
-static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
-       int ret = 0;
-
-       ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
-       if (ret)
-               return ret;
-
-       ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
-       if (ret)
-               asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
-       return ret;
-}
-
-static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
-
-       asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
-       asoc_simple_card_clk_disable(dai_props->codec_dai);
-}
-
-static const struct snd_soc_ops asoc_graph_card_ops = {
-       .startup = asoc_graph_card_startup,
-       .shutdown = asoc_graph_card_shutdown,
-};
-
-static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct graph_card_data *priv =  snd_soc_card_get_drvdata(rtd->card);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
-       int ret = 0;
-
-       ret = asoc_simple_card_init_dai(rtd->codec_dai,
-                                       dai_props->codec_dai);
-       if (ret < 0)
-               return ret;
-
-       ret = asoc_simple_card_init_dai(rtd->cpu_dai,
-                                       dai_props->cpu_dai);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                              struct snd_pcm_hw_params *params)
-{
-       struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
-
-       asoc_simple_card_convert_fixup(&dai_props->adata, params);
-
-       /* overwrite by top level adata if exist */
-       asoc_simple_card_convert_fixup(&priv->adata, params);
-
-       return 0;
-}
-
-static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep,
-                                      struct device_node *codec_ep,
-                                      struct graph_card_data *priv,
-                                      int *dai_idx, int link_idx,
-                                      int *conf_idx, int is_fe)
-{
-       struct device *dev = graph_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
-       struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
-       struct snd_soc_card *card = graph_priv_to_card(priv);
-       struct device_node *ep = is_fe ? cpu_ep : codec_ep;
-       struct device_node *node = of_graph_get_port_parent(ep);
-       struct asoc_simple_dai *dai;
-       int ret;
-
-       if (is_fe) {
-               struct snd_soc_dai_link_component *codecs;
-
-               /* BE is dummy */
-               codecs                  = dai_link->codecs;
-               codecs->of_node         = NULL;
-               codecs->dai_name        = "snd-soc-dummy-dai";
-               codecs->name            = "snd-soc-dummy";
-
-               /* FE settings */
-               dai_link->dynamic               = 1;
-               dai_link->dpcm_merged_format    = 1;
-
-               dai =
-               dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
-
-               ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
-               if (ret)
-                       return ret;
-
-               ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
-                                                       "fe.%s",
-                                                       dai_link->cpu_dai_name);
-               if (ret < 0)
-                       return ret;
-
-               /* card->num_links includes Codec */
-               asoc_simple_card_canonicalize_cpu(dai_link,
-                       of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
-       } else {
-               struct snd_soc_codec_conf *cconf;
-
-               /* FE is dummy */
-               dai_link->cpu_of_node           = NULL;
-               dai_link->cpu_dai_name          = "snd-soc-dummy-dai";
-               dai_link->cpu_name              = "snd-soc-dummy";
-
-               /* BE settings */
-               dai_link->no_pcm                = 1;
-               dai_link->be_hw_params_fixup    = asoc_graph_card_be_hw_params_fixup;
-
-               dai =
-               dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
-
-               cconf =
-               dai_props->codec_conf   = &priv->codec_conf[(*conf_idx)++];
-
-               ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
-                                                       "be.%s",
-                                                       dai_link->codecs->dai_name);
-               if (ret < 0)
-                       return ret;
-
-               /* check "prefix" from top node */
-               snd_soc_of_parse_audio_prefix(card, cconf,
-                                             dai_link->codecs->of_node,
-                                             "prefix");
-               /* check "prefix" from each node if top doesn't have */
-               if (!cconf->of_node)
-                       snd_soc_of_parse_node_prefix(node, cconf,
-                                                    dai_link->codecs->of_node,
-                                                    PREFIX "prefix");
-       }
-
-       asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata);
-
-       ret = asoc_simple_card_of_parse_tdm(ep, dai);
-       if (ret)
-               return ret;
-
-       ret = asoc_simple_card_canonicalize_dailink(dai_link);
-       if (ret < 0)
-               return ret;
-
-       ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
-                                           NULL, &dai_link->dai_fmt);
-       if (ret < 0)
-               return ret;
-
-       dai_link->dpcm_playback         = 1;
-       dai_link->dpcm_capture          = 1;
-       dai_link->ops                   = &asoc_graph_card_ops;
-       dai_link->init                  = asoc_graph_card_dai_init;
-
-       return 0;
-}
-
-static int asoc_graph_card_parse_of(struct graph_card_data *priv)
-{
-       struct of_phandle_iterator it;
-       struct device *dev = graph_priv_to_dev(priv);
-       struct snd_soc_card *card = graph_priv_to_card(priv);
-       struct device_node *node = dev->of_node;
-       struct device_node *cpu_port;
-       struct device_node *cpu_ep;
-       struct device_node *codec_ep;
-       struct device_node *codec_port;
-       struct device_node *codec_port_old;
-       int dai_idx, link_idx, conf_idx, ret;
-       int rc, codec;
-
-       if (!node)
-               return -EINVAL;
-
-       /*
-        * we need to consider "widgets", "mclk-fs" around here
-        * see simple-card
-        */
-
-       ret = asoc_simple_card_of_parse_routing(card, NULL);
-       if (ret < 0)
-               return ret;
-
-       asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata);
-
-       /*
-        * it supports multi CPU, single CODEC only here
-        * see asoc_graph_get_dais_count
-        */
-
-       link_idx = 0;
-       dai_idx = 0;
-       conf_idx = 0;
-       codec_port_old = NULL;
-       for (codec = 0; codec < 2; codec++) {
-               /*
-                * To listup valid sounds continuously,
-                * detect all CPU-dummy first, and
-                * detect all dummy-Codec second
-                */
-               of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
-                       cpu_port = it.node;
-                       cpu_ep   = of_get_next_child(cpu_port, NULL);
-                       codec_ep = of_graph_get_remote_endpoint(cpu_ep);
-                       codec_port = of_graph_get_port_parent(codec_ep);
-
-                       of_node_put(cpu_ep);
-                       of_node_put(codec_ep);
-                       of_node_put(cpu_port);
-                       of_node_put(codec_port);
-                       it.node = NULL;
-
-                       if (codec) {
-                               if (codec_port_old == codec_port)
-                                       continue;
-
-                               codec_port_old = codec_port;
-                       }
-
-                       ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep,
-                                                         priv, &dai_idx,
-                                                         link_idx++, &conf_idx,
-                                                         !codec);
-                       if (ret < 0)
-                               goto parse_of_err;
-               }
-       }
-
-       ret = asoc_simple_card_parse_card_name(card, NULL);
-       if (ret)
-               goto parse_of_err;
-
-       if ((card->num_links   != link_idx) ||
-           (card->num_configs != conf_idx)) {
-               dev_err(dev, "dai_link or codec_config wrong (%d/%d, %d/%d)\n",
-                       card->num_links, link_idx, card->num_configs, conf_idx);
-               ret = -EINVAL;
-               goto parse_of_err;
-       }
-
-       ret = 0;
-
-parse_of_err:
-       return ret;
-}
-
-static void asoc_graph_get_dais_count(struct device *dev,
-                                     int *link_num,
-                                     int *dais_num,
-                                     int *ccnf_num)
-{
-       struct of_phandle_iterator it;
-       struct device_node *node = dev->of_node;
-       struct device_node *cpu_port;
-       struct device_node *cpu_ep;
-       struct device_node *codec_ep;
-       struct device_node *codec_port;
-       struct device_node *codec_port_old;
-       struct device_node *codec_port_old2;
-       int rc;
-
-       /*
-        * link_num :   number of links.
-        *              CPU-Codec / CPU-dummy / dummy-Codec
-        * dais_num :   number of DAIs
-        * ccnf_num :   number of codec_conf
-        *              same number for dummy-Codec
-        *
-        * ex1)
-        * CPU0 --- Codec0      link : 5
-        * CPU1 --- Codec1      dais : 7
-        * CPU2 -/              ccnf : 1
-        * CPU3 --- Codec2
-        *
-        *      => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
-        *      => 7 DAIs  = 4xCPU + 3xCodec
-        *      => 1 ccnf  = 1xdummy-Codec
-        *
-        * ex2)
-        * CPU0 --- Codec0      link : 5
-        * CPU1 --- Codec1      dais : 6
-        * CPU2 -/              ccnf : 1
-        * CPU3 -/
-        *
-        *      => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
-        *      => 6 DAIs  = 4xCPU + 2xCodec
-        *      => 1 ccnf  = 1xdummy-Codec
-        *
-        * ex3)
-        * CPU0 --- Codec0      link : 6
-        * CPU1 -/              dais : 6
-        * CPU2 --- Codec1      ccnf : 2
-        * CPU3 -/
-        *
-        *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
-        *      => 6 DAIs  = 4xCPU + 2xCodec
-        *      => 2 ccnf  = 2xdummy-Codec
-        */
-       codec_port_old = NULL;
-       codec_port_old2 = NULL;
-       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
-               cpu_port = it.node;
-               cpu_ep   = of_get_next_child(cpu_port, NULL);
-               codec_ep = of_graph_get_remote_endpoint(cpu_ep);
-               codec_port = of_graph_get_port_parent(codec_ep);
-
-               of_node_put(cpu_ep);
-               of_node_put(codec_ep);
-               of_node_put(codec_port);
-
-               (*link_num)++;
-               (*dais_num)++;
-
-               if (codec_port_old == codec_port) {
-                       if (codec_port_old2 != codec_port_old) {
-                               (*link_num)++;
-                               (*ccnf_num)++;
-                       }
-
-                       codec_port_old2 = codec_port_old;
-                       continue;
-               }
-
-               (*dais_num)++;
-               codec_port_old = codec_port;
-       }
-}
-
-static int asoc_graph_card_probe(struct platform_device *pdev)
-{
-       struct graph_card_data *priv;
-       struct snd_soc_dai_link *dai_link;
-       struct graph_dai_props *dai_props;
-       struct asoc_simple_dai *dais;
-       struct device *dev = &pdev->dev;
-       struct snd_soc_card *card;
-       struct snd_soc_codec_conf *cconf;
-       int lnum = 0, dnum = 0, cnum = 0;
-       int ret, i;
-
-       /* Allocate the private data and the DAI link array */
-       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
-       if (!lnum || !dnum)
-               return -EINVAL;
-
-       dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
-       dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link),  GFP_KERNEL);
-       dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
-       cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
-       if (!dai_props || !dai_link || !dais)
-               return -ENOMEM;
-
-       /*
-        * Use snd_soc_dai_link_component instead of legacy style
-        * It is codec only. but cpu/platform will be supported in the future.
-        * see
-        *      soc-core.c :: snd_soc_init_multicodec()
-        */
-       for (i = 0; i < lnum; i++) {
-               dai_link[i].codecs      = &dai_props[i].codecs;
-               dai_link[i].num_codecs  = 1;
-               dai_link[i].platform    = &dai_props[i].platform;
-       }
-
-       priv->dai_props                 = dai_props;
-       priv->dai_link                  = dai_link;
-       priv->dais                      = dais;
-       priv->codec_conf                = cconf;
-
-       /* Init snd_soc_card */
-       card = graph_priv_to_card(priv);
-       card->owner             = THIS_MODULE;
-       card->dev               = dev;
-       card->dai_link          = priv->dai_link;
-       card->num_links         = lnum;
-       card->codec_conf        = cconf;
-       card->num_configs       = cnum;
-
-       ret = asoc_graph_card_parse_of(priv);
-       if (ret < 0) {
-               if (ret != -EPROBE_DEFER)
-                       dev_err(dev, "parse error %d\n", ret);
-               goto err;
-       }
-
-       snd_soc_card_set_drvdata(card, priv);
-
-       ret = devm_snd_soc_register_card(dev, card);
-       if (ret < 0)
-               goto err;
-
-       return 0;
-err:
-       asoc_simple_card_clean_reference(card);
-
-       return ret;
-}
-
-static int asoc_graph_card_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       return asoc_simple_card_clean_reference(card);
-}
-
-static const struct of_device_id asoc_graph_of_match[] = {
-       { .compatible = "audio-graph-scu-card", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
-
-static struct platform_driver asoc_graph_card = {
-       .driver = {
-               .name = "asoc-audio-graph-scu-card",
-               .pm = &snd_soc_pm_ops,
-               .of_match_table = asoc_graph_of_match,
-       },
-       .probe = asoc_graph_card_probe,
-       .remove = asoc_graph_card_remove,
-};
-module_platform_driver(asoc_graph_card);
-
-MODULE_ALIAS("platform:asoc-audio-graph-scu-card");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
index b807a47515ebc85b3b37dc14ccc11911d8b4bf3f..336895f7fd1e9e93941f483a8ce1fc56da465ea3 100644 (file)
@@ -283,12 +283,20 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep)
        /* use endpoint/port reg if exist */
        ret = of_graph_parse_endpoint(ep, &info);
        if (ret == 0) {
-               if (info.id)
+               /*
+                * Because it will count port/endpoint if it doesn't have "reg".
+                * But, we can't judge whether it has "no reg", or "reg = <0>"
+                * only of_graph_parse_endpoint().
+                * We need to check "reg" property
+                */
+               if (of_get_property(ep,   "reg", NULL))
                        return info.id;
-               if (info.port)
+
+               node = of_get_parent(ep);
+               of_node_put(node);
+               if (of_get_property(node, "reg", NULL))
                        return info.port;
        }
-
        node = of_graph_get_port_parent(ep);
 
        /*
index 37e001cf9cd1dc5f09c019b63e1719d061a36cd1..479de236e694e213dea18f3ff8bd5da393a534e9 100644 (file)
@@ -15,7 +15,7 @@
 #include <sound/soc-dai.h>
 #include <sound/soc.h>
 
-struct simple_card_data {
+struct simple_priv {
        struct snd_soc_card snd_card;
        struct simple_dai_props {
                struct asoc_simple_dai *cpu_dai;
@@ -33,6 +33,13 @@ struct simple_card_data {
        struct snd_soc_codec_conf *codec_conf;
 };
 
+struct link_info {
+       int dais; /* number of dai  */
+       int link; /* number of link */
+       int conf; /* number of codec_conf */
+       int cpu;  /* turn for CPU / Codec */
+};
+
 #define simple_priv_to_card(priv) (&(priv)->snd_card)
 #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
 #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
@@ -42,10 +49,10 @@ struct simple_card_data {
 #define CELL   "#sound-dai-cells"
 #define PREFIX "simple-audio-card,"
 
-static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
+static int simple_startup(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct simple_dai_props *dai_props =
                simple_priv_to_props(priv, rtd->num);
        int ret;
@@ -61,10 +68,10 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
        return ret;
 }
 
-static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
+static void simple_shutdown(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct simple_dai_props *dai_props =
                simple_priv_to_props(priv, rtd->num);
 
@@ -73,8 +80,8 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
        asoc_simple_card_clk_disable(dai_props->codec_dai);
 }
 
-static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
-                                   unsigned long rate)
+static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
+                              unsigned long rate)
 {
        if (!simple_dai)
                return 0;
@@ -88,13 +95,13 @@ static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
        return clk_set_rate(simple_dai->clk, rate);
 }
 
-static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
-                                     struct snd_pcm_hw_params *params)
+static int simple_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct simple_dai_props *dai_props =
                simple_priv_to_props(priv, rtd->num);
        unsigned int mclk, mclk_fs = 0;
@@ -106,11 +113,11 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
        if (mclk_fs) {
                mclk = params_rate(params) * mclk_fs;
 
-               ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
+               ret = simple_set_clk_rate(dai_props->codec_dai, mclk);
                if (ret < 0)
                        return ret;
 
-               ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
+               ret = simple_set_clk_rate(dai_props->cpu_dai, mclk);
                if (ret < 0)
                        return ret;
 
@@ -129,15 +136,15 @@ err:
        return ret;
 }
 
-static const struct snd_soc_ops asoc_simple_card_ops = {
-       .startup = asoc_simple_card_startup,
-       .shutdown = asoc_simple_card_shutdown,
-       .hw_params = asoc_simple_card_hw_params,
+static const struct snd_soc_ops simple_ops = {
+       .startup        = simple_startup,
+       .shutdown       = simple_shutdown,
+       .hw_params      = simple_hw_params,
 };
 
-static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+static int simple_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
        int ret;
 
@@ -154,10 +161,10 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
-static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                              struct snd_pcm_hw_params *params)
+static int simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                                    struct snd_pcm_hw_params *params)
 {
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
 
        asoc_simple_card_convert_fixup(&dai_props->adata, params);
@@ -165,30 +172,58 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
-                                            struct device_node *node,
-                                            struct device_node *np,
-                                            struct device_node *codec,
-                                            struct simple_card_data *priv,
-                                            int *dai_idx, int link_idx,
-                                            int *conf_idx, int is_fe,
-                                            bool is_top_level_node)
+static void simple_get_conversion(struct device *dev,
+                                 struct device_node *np,
+                                 struct asoc_simple_card_data *adata)
+{
+       struct device_node *top = dev->of_node;
+       struct device_node *node = of_get_parent(np);
+
+       asoc_simple_card_parse_convert(dev, top,  PREFIX, adata);
+       asoc_simple_card_parse_convert(dev, node, PREFIX, adata);
+       asoc_simple_card_parse_convert(dev, node, NULL,   adata);
+       asoc_simple_card_parse_convert(dev, np,   NULL,   adata);
+
+       of_node_put(node);
+}
+
+static int simple_dai_link_of_dpcm(struct simple_priv *priv,
+                                  struct device_node *np,
+                                  struct device_node *codec,
+                                  struct link_info *li,
+                                  bool is_top)
 {
        struct device *dev = simple_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
-       struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
+       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+       struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
        struct asoc_simple_dai *dai;
        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
-
+       struct device_node *top = dev->of_node;
+       struct device_node *node = of_get_parent(np);
        char prop[128];
        char *prefix = "";
        int ret;
 
+       /*
+        *       |CPU   |Codec   : turn
+        * CPU   |Pass  |return
+        * Codec |return|Pass
+        * np
+        */
+       if (li->cpu == (np == codec))
+               return 0;
+
+       dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
+
+       li->link++;
+
+       of_node_put(node);
+
        /* For single DAI link & old style of DT node */
-       if (is_top_level_node)
+       if (is_top)
                prefix = PREFIX;
 
-       if (is_fe) {
+       if (li->cpu) {
                int is_single_links = 0;
 
                /* BE is dummy */
@@ -201,7 +236,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
                dai_link->dpcm_merged_format    = 1;
 
                dai =
-               dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
+               dai_props->cpu_dai      = &priv->dais[li->dais++];
 
                ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
                                                 &is_single_links);
@@ -229,13 +264,13 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
 
                /* BE settings */
                dai_link->no_pcm                = 1;
-               dai_link->be_hw_params_fixup    = asoc_simple_card_be_hw_params_fixup;
+               dai_link->be_hw_params_fixup    = simple_be_hw_params_fixup;
 
                dai =
-               dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
+               dai_props->codec_dai    = &priv->dais[li->dais++];
 
                cconf =
-               dai_props->codec_conf   = &priv->codec_conf[(*conf_idx)++];
+               dai_props->codec_conf   = &priv->codec_conf[li->conf++];
 
                ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
                if (ret < 0)
@@ -260,9 +295,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
                                             "prefix");
        }
 
-       asoc_simple_card_parse_convert(dev, top,  PREFIX, &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata);
-       asoc_simple_card_parse_convert(dev, np,   NULL,   &dai_props->adata);
+       simple_get_conversion(dev, np, &dai_props->adata);
 
        ret = asoc_simple_card_of_parse_tdm(np, dai);
        if (ret)
@@ -284,59 +317,57 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
 
        dai_link->dpcm_playback         = 1;
        dai_link->dpcm_capture          = 1;
-       dai_link->ops                   = &asoc_simple_card_ops;
-       dai_link->init                  = asoc_simple_card_dai_init;
+       dai_link->ops                   = &simple_ops;
+       dai_link->init                  = simple_dai_init;
 
        return 0;
 }
 
-static int asoc_simple_card_dai_link_of(struct device_node *top,
-                                       struct device_node *node,
-                                       struct simple_card_data *priv,
-                                       int *dai_idx, int link_idx,
-                                       bool is_top_level_node)
+static int simple_dai_link_of(struct simple_priv *priv,
+                             struct device_node *np,
+                             struct device_node *codec,
+                             struct link_info *li,
+                             bool is_top)
 {
        struct device *dev = simple_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
-       struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
+       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+       struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
        struct asoc_simple_dai *cpu_dai;
        struct asoc_simple_dai *codec_dai;
+       struct device_node *top = dev->of_node;
        struct device_node *cpu = NULL;
+       struct device_node *node = NULL;
        struct device_node *plat = NULL;
-       struct device_node *codec = NULL;
        char prop[128];
        char *prefix = "";
        int ret, single_cpu;
 
-       /* For single DAI link & old style of DT node */
-       if (is_top_level_node)
-               prefix = PREFIX;
+       /*
+        *       |CPU   |Codec   : turn
+        * CPU   |Pass  |return
+        * Codec |return|return
+        * np
+        */
+       if (!li->cpu || np == codec)
+               return 0;
 
-       snprintf(prop, sizeof(prop), "%scpu", prefix);
-       cpu = of_get_child_by_name(node, prop);
+       cpu  = np;
+       node = of_get_parent(np);
+       li->link++;
 
-       if (!cpu) {
-               ret = -EINVAL;
-               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
-               goto dai_link_of_err;
-       }
+       dev_dbg(dev, "link_of (%pOF)\n", node);
+
+       /* For single DAI link & old style of DT node */
+       if (is_top)
+               prefix = PREFIX;
 
        snprintf(prop, sizeof(prop), "%splat", prefix);
        plat = of_get_child_by_name(node, prop);
 
-       snprintf(prop, sizeof(prop), "%scodec", prefix);
-       codec = of_get_child_by_name(node, prop);
-
-       if (!codec) {
-               ret = -EINVAL;
-               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
-               goto dai_link_of_err;
-       }
-
        cpu_dai                 =
-       dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
+       dai_props->cpu_dai      = &priv->dais[li->dais++];
        codec_dai               =
-       dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
+       dai_props->codec_dai    = &priv->dais[li->dais++];
 
        ret = asoc_simple_card_parse_daifmt(dev, node, codec,
                                            prefix, &dai_link->dai_fmt);
@@ -389,20 +420,87 @@ static int asoc_simple_card_dai_link_of(struct device_node *top,
        if (ret < 0)
                goto dai_link_of_err;
 
-       dai_link->ops = &asoc_simple_card_ops;
-       dai_link->init = asoc_simple_card_dai_init;
+       dai_link->ops = &simple_ops;
+       dai_link->init = simple_dai_init;
 
        asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
 
 dai_link_of_err:
-       of_node_put(cpu);
-       of_node_put(codec);
+       of_node_put(node);
 
        return ret;
 }
 
-static int asoc_simple_card_parse_aux_devs(struct device_node *node,
-                                          struct simple_card_data *priv)
+static int simple_for_each_link(struct simple_priv *priv,
+                       struct link_info *li,
+                       int (*func_noml)(struct simple_priv *priv,
+                                        struct device_node *np,
+                                        struct device_node *codec,
+                                        struct link_info *li, bool is_top),
+                       int (*func_dpcm)(struct simple_priv *priv,
+                                        struct device_node *np,
+                                        struct device_node *codec,
+                                        struct link_info *li, bool is_top))
+{
+       struct device *dev = simple_priv_to_dev(priv);
+       struct device_node *top = dev->of_node;
+       struct device_node *node;
+       bool is_top = 0;
+
+       /* Check if it has dai-link */
+       node = of_get_child_by_name(top, PREFIX "dai-link");
+       if (!node) {
+               node = top;
+               is_top = 1;
+       }
+
+       /* loop for all dai-link */
+       do {
+               struct asoc_simple_card_data adata;
+               struct device_node *codec;
+               struct device_node *np;
+               int num = of_get_child_count(node);
+               int ret;
+
+               /* get codec */
+               codec = of_get_child_by_name(node, is_top ?
+                                            PREFIX "codec" : "codec");
+               if (!codec)
+                       return -ENODEV;
+
+               of_node_put(codec);
+
+               /* get convert-xxx property */
+               memset(&adata, 0, sizeof(adata));
+               for_each_child_of_node(node, np)
+                       simple_get_conversion(dev, np, &adata);
+
+               /* loop for all CPU/Codec node */
+               for_each_child_of_node(node, np) {
+                       /*
+                        * It is DPCM
+                        * if it has many CPUs,
+                        * or has convert-xxx property
+                        */
+                       if (num > 2 ||
+                           adata.convert_rate || adata.convert_channels)
+                               ret = func_dpcm(priv, np, codec, li, is_top);
+                       /* else normal sound */
+                       else
+                               ret = func_noml(priv, np, codec, li, is_top);
+
+                       if (ret < 0)
+                               return ret;
+               }
+
+               node = of_get_next_child(top, node);
+       } while (!is_top && node);
+
+       return 0;
+}
+
+static int simple_parse_aux_devs(struct device_node *node,
+                                struct simple_priv *priv)
 {
        struct device *dev = simple_priv_to_dev(priv);
        struct device_node *aux_node;
@@ -432,17 +530,13 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
        return 0;
 }
 
-static int asoc_simple_card_parse_of(struct simple_card_data *priv)
+static int simple_parse_of(struct simple_priv *priv)
 {
        struct device *dev = simple_priv_to_dev(priv);
        struct device_node *top = dev->of_node;
        struct snd_soc_card *card = simple_priv_to_card(priv);
-       struct device_node *node;
-       struct device_node *np;
-       struct device_node *codec;
-       bool is_fe;
-       int ret, loop;
-       int dai_idx, link_idx, conf_idx;
+       struct link_info li;
+       int ret;
 
        if (!top)
                return -EINVAL;
@@ -456,62 +550,66 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
                return ret;
 
        /* Single/Muti DAI link(s) & New style of DT node */
-       loop            = 1;
-       link_idx        = 0;
-       dai_idx         = 0;
-       conf_idx        = 0;
-       node = of_get_child_by_name(top, PREFIX "dai-link");
-       if (!node) {
-               node = dev->of_node;
-               loop = 0;
-       }
-
-       do  {
-               /* DPCM */
-               if (of_get_child_count(node) > 2) {
-                       for_each_child_of_node(node, np) {
-                               codec = of_get_child_by_name(node,
-                                                       loop ?  "codec" :
-                                                               PREFIX "codec");
-                               if (!codec)
-                                       return -ENODEV;
-
-                               is_fe = (np != codec);
-
-                               ret = asoc_simple_card_dai_link_of_dpcm(
-                                               top, node, np, codec, priv,
-                                               &dai_idx, link_idx++, &conf_idx,
-                                               is_fe, !loop);
-                       }
-               } else {
-                       ret = asoc_simple_card_dai_link_of(
-                                               top, node, priv,
-                                               &dai_idx, link_idx++, !loop);
-               }
+       memset(&li, 0, sizeof(li));
+       for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
+               /*
+                * Detect all CPU first, and Detect all Codec 2nd.
+                *
+                * In Normal sound case, all DAIs are detected
+                * as "CPU-Codec".
+                *
+                * In DPCM sound case,
+                * all CPUs   are detected as "CPU-dummy", and
+                * all Codecs are detected as "dummy-Codec".
+                * To avoid random sub-device numbering,
+                * detect "dummy-Codec" in last;
+                */
+               ret = simple_for_each_link(priv, &li,
+                                          simple_dai_link_of,
+                                          simple_dai_link_of_dpcm);
                if (ret < 0)
                        return ret;
-
-               node = of_get_next_child(top, node);
-       } while (loop && node);
+       }
 
        ret = asoc_simple_card_parse_card_name(card, PREFIX);
        if (ret < 0)
                return ret;
 
-       ret = asoc_simple_card_parse_aux_devs(top, priv);
+       ret = simple_parse_aux_devs(top, priv);
 
        return ret;
 }
 
-static void asoc_simple_card_get_dais_count(struct device *dev,
-                                           int *link_num,
-                                           int *dais_num,
-                                           int *ccnf_num)
+static int simple_count_noml(struct simple_priv *priv,
+                            struct device_node *np,
+                            struct device_node *codec,
+                            struct link_info *li, bool is_top)
 {
+       li->dais++; /* CPU or Codec */
+       if (np != codec)
+               li->link++; /* CPU-Codec */
+
+       return 0;
+}
+
+static int simple_count_dpcm(struct simple_priv *priv,
+                            struct device_node *np,
+                            struct device_node *codec,
+                            struct link_info *li, bool is_top)
+{
+       li->dais++; /* CPU or Codec */
+       li->link++; /* CPU-dummy or dummy-Codec */
+       if (np == codec)
+               li->conf++;
+
+       return 0;
+}
+
+static void simple_get_dais_count(struct simple_priv *priv,
+                                 struct link_info *li)
+{
+       struct device *dev = simple_priv_to_dev(priv);
        struct device_node *top = dev->of_node;
-       struct device_node *node;
-       int loop;
-       int num;
 
        /*
         * link_num :   number of links.
@@ -549,37 +647,34 @@ static void asoc_simple_card_get_dais_count(struct device *dev,
         *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
         *      => 6 DAIs  = 4xCPU + 2xCodec
         *      => 2 ccnf  = 2xdummy-Codec
+        *
+        * ex4)
+        * CPU0 --- Codec0 (convert-rate)       link : 3
+        * CPU1 --- Codec1                      dais : 4
+        *                                      ccnf : 1
+        *
+        *      => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
+        *      => 4 DAIs  = 2xCPU + 2xCodec
+        *      => 1 ccnf  = 1xdummy-Codec
         */
        if (!top) {
-               (*link_num) = 1;
-               (*dais_num) = 2;
-               (*ccnf_num) = 0;
+               li->link = 1;
+               li->dais = 2;
+               li->conf = 0;
                return;
        }
 
-       loop = 1;
-       node = of_get_child_by_name(top, PREFIX "dai-link");
-       if (!node) {
-               node = top;
-               loop = 0;
-       }
+       simple_for_each_link(priv, li,
+                            simple_count_noml,
+                            simple_count_dpcm);
 
-       do {
-               num = of_get_child_count(node);
-               (*dais_num) += num;
-               if (num > 2) {
-                       (*link_num) += num;
-                       (*ccnf_num)++;
-               } else {
-                       (*link_num)++;
-               }
-               node = of_get_next_child(top, node);
-       } while (loop && node);
+       dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
+               li->link, li->dais, li->conf);
 }
 
-static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
+static int simple_soc_probe(struct snd_soc_card *card)
 {
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
+       struct simple_priv *priv = snd_soc_card_get_drvdata(card);
        int ret;
 
        ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX);
@@ -593,9 +688,9 @@ static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
        return 0;
 }
 
-static int asoc_simple_card_probe(struct platform_device *pdev)
+static int simple_probe(struct platform_device *pdev)
 {
-       struct simple_card_data *priv;
+       struct simple_priv *priv;
        struct snd_soc_dai_link *dai_link;
        struct simple_dai_props *dai_props;
        struct asoc_simple_dai *dais;
@@ -603,7 +698,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        struct snd_soc_card *card;
        struct snd_soc_codec_conf *cconf;
-       int lnum = 0, dnum = 0, cnum = 0;
+       struct link_info li;
        int ret, i;
 
        /* Allocate the private data and the DAI link array */
@@ -611,14 +706,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
-       asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
-       if (!lnum || !dnum)
+       card = simple_priv_to_card(priv);
+       card->owner             = THIS_MODULE;
+       card->dev               = dev;
+       card->probe             = simple_soc_probe;
+
+       memset(&li, 0, sizeof(li));
+       simple_get_dais_count(priv, &li);
+       if (!li.link || !li.dais)
                return -EINVAL;
 
-       dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
-       dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link),  GFP_KERNEL);
-       dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
-       cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
+       dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
+       dai_link  = devm_kcalloc(dev, li.link, sizeof(*dai_link),  GFP_KERNEL);
+       dais      = devm_kcalloc(dev, li.dais, sizeof(*dais),      GFP_KERNEL);
+       cconf     = devm_kcalloc(dev, li.conf, sizeof(*cconf),     GFP_KERNEL);
        if (!dai_props || !dai_link || !dais)
                return -ENOMEM;
 
@@ -628,30 +729,25 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
         * see
         *      soc-core.c :: snd_soc_init_multicodec()
         */
-       for (i = 0; i < lnum; i++) {
+       for (i = 0; i < li.link; i++) {
                dai_link[i].codecs      = &dai_props[i].codecs;
                dai_link[i].num_codecs  = 1;
                dai_link[i].platform    = &dai_props[i].platform;
        }
 
-       priv->dai_props                 = dai_props;
-       priv->dai_link                  = dai_link;
-       priv->dais                      = dais;
-       priv->codec_conf                = cconf;
+       priv->dai_props         = dai_props;
+       priv->dai_link          = dai_link;
+       priv->dais              = dais;
+       priv->codec_conf        = cconf;
 
-       /* Init snd_soc_card */
-       card = simple_priv_to_card(priv);
-       card->owner             = THIS_MODULE;
-       card->dev               = dev;
        card->dai_link          = priv->dai_link;
-       card->num_links         = lnum;
+       card->num_links         = li.link;
        card->codec_conf        = cconf;
-       card->num_configs       = cnum;
-       card->probe             = asoc_simple_soc_card_probe;
+       card->num_configs       = li.conf;
 
        if (np && of_device_is_available(np)) {
 
-               ret = asoc_simple_card_parse_of(priv);
+               ret = simple_parse_of(priv);
                if (ret < 0) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "parse error %d\n", ret);
@@ -694,7 +790,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
                dai_link->stream_name   = cinfo->name;
                dai_link->cpu_dai_name  = cinfo->cpu_dai.name;
                dai_link->dai_fmt       = cinfo->daifmt;
-               dai_link->init          = asoc_simple_card_dai_init;
+               dai_link->init          = simple_dai_init;
                memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai,
                                        sizeof(*priv->dai_props->cpu_dai));
                memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai,
@@ -714,28 +810,28 @@ err:
        return ret;
 }
 
-static int asoc_simple_card_remove(struct platform_device *pdev)
+static int simple_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
        return asoc_simple_card_clean_reference(card);
 }
 
-static const struct of_device_id asoc_simple_of_match[] = {
+static const struct of_device_id simple_of_match[] = {
        { .compatible = "simple-audio-card", },
        { .compatible = "simple-scu-audio-card", },
        {},
 };
-MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
+MODULE_DEVICE_TABLE(of, simple_of_match);
 
 static struct platform_driver asoc_simple_card = {
        .driver = {
                .name = "asoc-simple-card",
                .pm = &snd_soc_pm_ops,
-               .of_match_table = asoc_simple_of_match,
+               .of_match_table = simple_of_match,
        },
-       .probe = asoc_simple_card_probe,
-       .remove = asoc_simple_card_remove,
+       .probe = simple_probe,
+       .remove = simple_remove,
 };
 
 module_platform_driver(asoc_simple_card);
diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c
deleted file mode 100644 (file)
index 9d7299d..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// ASoC simple SCU sound card support
-//
-// Copyright (C) 2015 Renesas Solutions Corp.
-// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
-//
-// based on ${LINUX}/sound/soc/generic/simple-card.c
-
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/string.h>
-#include <sound/jack.h>
-#include <sound/soc.h>
-#include <sound/soc-dai.h>
-#include <sound/simple_card_utils.h>
-
-struct simple_card_data {
-       struct snd_soc_card snd_card;
-       struct simple_dai_props {
-               struct asoc_simple_dai *cpu_dai;
-               struct asoc_simple_dai *codec_dai;
-               struct snd_soc_dai_link_component codecs;
-               struct snd_soc_dai_link_component platform;
-               struct asoc_simple_card_data adata;
-               struct snd_soc_codec_conf *codec_conf;
-       } *dai_props;
-       struct snd_soc_dai_link *dai_link;
-       struct asoc_simple_dai *dais;
-       struct asoc_simple_card_data adata;
-       struct snd_soc_codec_conf *codec_conf;
-};
-
-#define simple_priv_to_card(priv) (&(priv)->snd_card)
-#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
-#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
-
-#define DAI    "sound-dai"
-#define CELL   "#sound-dai-cells"
-#define PREFIX "simple-audio-card,"
-
-static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct simple_dai_props *dai_props =
-               simple_priv_to_props(priv, rtd->num);
-       int ret;
-
-       ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
-       if (ret)
-               return ret;
-
-       ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
-       if (ret)
-               asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
-       return ret;
-}
-
-static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct simple_dai_props *dai_props =
-               simple_priv_to_props(priv, rtd->num);
-
-       asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
-       asoc_simple_card_clk_disable(dai_props->codec_dai);
-}
-
-static const struct snd_soc_ops asoc_simple_card_ops = {
-       .startup = asoc_simple_card_startup,
-       .shutdown = asoc_simple_card_shutdown,
-};
-
-static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
-       int ret;
-
-       ret = asoc_simple_card_init_dai(rtd->codec_dai,
-                                       dai_props->codec_dai);
-       if (ret < 0)
-               return ret;
-
-       ret = asoc_simple_card_init_dai(rtd->cpu_dai,
-                                       dai_props->cpu_dai);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
-                                       struct snd_pcm_hw_params *params)
-{
-       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-       struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
-
-       asoc_simple_card_convert_fixup(&dai_props->adata, params);
-
-       /* overwrite by top level adata if exist */
-       asoc_simple_card_convert_fixup(&priv->adata, params);
-
-       return 0;
-}
-
-static int asoc_simple_card_dai_link_of(struct device_node *link,
-                                       struct device_node *np,
-                                       struct device_node *codec,
-                                       struct simple_card_data *priv,
-                                       int *dai_idx, int link_idx,
-                                       int *conf_idx, int is_fe,
-                                       bool is_top_level_node)
-{
-       struct device *dev = simple_priv_to_dev(priv);
-       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
-       struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
-       struct snd_soc_card *card = simple_priv_to_card(priv);
-       struct asoc_simple_dai *dai;
-       char *prefix = "";
-       int ret;
-
-       /* For single DAI link & old style of DT node */
-       if (is_top_level_node)
-               prefix = PREFIX;
-
-       if (is_fe) {
-               int is_single_links = 0;
-               struct snd_soc_dai_link_component *codecs;
-
-               /* BE is dummy */
-               codecs                  = dai_link->codecs;
-               codecs->of_node         = NULL;
-               codecs->dai_name        = "snd-soc-dummy-dai";
-               codecs->name            = "snd-soc-dummy";
-
-               /* FE settings */
-               dai_link->dynamic               = 1;
-               dai_link->dpcm_merged_format    = 1;
-
-               dai =
-               dai_props->cpu_dai      = &priv->dais[(*dai_idx)++];
-
-               ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
-                                                &is_single_links);
-               if (ret)
-                       return ret;
-
-               ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
-                                                       "fe.%s",
-                                                       dai_link->cpu_dai_name);
-               if (ret < 0)
-                       return ret;
-
-               asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
-       } else {
-               struct snd_soc_codec_conf *cconf;
-
-               /* FE is dummy */
-               dai_link->cpu_of_node           = NULL;
-               dai_link->cpu_dai_name          = "snd-soc-dummy-dai";
-               dai_link->cpu_name              = "snd-soc-dummy";
-
-               /* BE settings */
-               dai_link->no_pcm                = 1;
-               dai_link->be_hw_params_fixup    = asoc_simple_card_be_hw_params_fixup;
-
-               dai =
-               dai_props->codec_dai    = &priv->dais[(*dai_idx)++];
-
-               cconf =
-               dai_props->codec_conf   = &priv->codec_conf[(*conf_idx)++];
-
-               ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai);
-               if (ret < 0)
-                       return ret;
-
-               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
-                                                       "be.%s",
-                                                       dai_link->codecs->dai_name);
-               if (ret < 0)
-                       return ret;
-
-               /* check "prefix" from top node */
-               snd_soc_of_parse_audio_prefix(card, cconf,
-                                             dai_link->codecs->of_node,
-                                             PREFIX "prefix");
-               /* check "prefix" from each node if top doesn't have */
-               if (!cconf->of_node)
-                       snd_soc_of_parse_node_prefix(np, cconf,
-                                                    dai_link->codecs->of_node,
-                                                    "prefix");
-       }
-
-       asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata);
-
-       ret = asoc_simple_card_of_parse_tdm(np, dai);
-       if (ret)
-               return ret;
-
-       ret = asoc_simple_card_canonicalize_dailink(dai_link);
-       if (ret < 0)
-               return ret;
-
-       ret = asoc_simple_card_parse_daifmt(dev, link, codec,
-                                           prefix, &dai_link->dai_fmt);
-       if (ret < 0)
-               return ret;
-
-       dai_link->dpcm_playback         = 1;
-       dai_link->dpcm_capture          = 1;
-       dai_link->ops                   = &asoc_simple_card_ops;
-       dai_link->init                  = asoc_simple_card_dai_init;
-
-       return 0;
-}
-
-static int asoc_simple_card_parse_of(struct simple_card_data *priv)
-
-{
-       struct device *dev = simple_priv_to_dev(priv);
-       struct device_node *top = dev->of_node;
-       struct device_node *node;
-       struct device_node *np;
-       struct device_node *codec;
-       struct snd_soc_card *card = simple_priv_to_card(priv);
-       bool is_fe;
-       int ret, loop;
-       int dai_idx, link_idx, conf_idx;
-
-       if (!top)
-               return -EINVAL;
-
-       ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
-       if (ret < 0)
-               return ret;
-
-       ret = asoc_simple_card_of_parse_routing(card, PREFIX);
-       if (ret < 0)
-               return ret;
-
-       asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata);
-
-       loop = 1;
-       link_idx = 0;
-       dai_idx = 0;
-       conf_idx = 0;
-       node = of_get_child_by_name(top, PREFIX "dai-link");
-       if (!node) {
-               node = dev->of_node;
-               loop = 0;
-       }
-
-       do  {
-               codec = of_get_child_by_name(node,
-                                            loop ? "codec" : PREFIX "codec");
-               if (!codec)
-                       return -ENODEV;
-
-               for_each_child_of_node(node, np) {
-                       is_fe = (np != codec);
-
-                       ret = asoc_simple_card_dai_link_of(node, np, codec, priv,
-                                                          &dai_idx, link_idx++,
-                                                          &conf_idx,
-                                                          is_fe, !loop);
-                       if (ret < 0)
-                               return ret;
-               }
-               node = of_get_next_child(top, node);
-       } while (loop && node);
-
-       ret = asoc_simple_card_parse_card_name(card, PREFIX);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static void asoc_simple_card_get_dais_count(struct device *dev,
-                                           int *link_num,
-                                           int *dais_num,
-                                           int *ccnf_num)
-{
-       struct device_node *top = dev->of_node;
-       struct device_node *node;
-       int loop;
-       int num;
-
-       /*
-        * link_num :   number of links.
-        *              CPU-Codec / CPU-dummy / dummy-Codec
-        * dais_num :   number of DAIs
-        * ccnf_num :   number of codec_conf
-        *              same number for "dummy-Codec"
-        *
-        * ex1)
-        * CPU0 --- Codec0      link : 5
-        * CPU1 --- Codec1      dais : 7
-        * CPU2 -/              ccnf : 1
-        * CPU3 --- Codec2
-        *
-        *      => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
-        *      => 7 DAIs  = 4xCPU + 3xCodec
-        *      => 1 ccnf  = 1xdummy-Codec
-        *
-        * ex2)
-        * CPU0 --- Codec0      link : 5
-        * CPU1 --- Codec1      dais : 6
-        * CPU2 -/              ccnf : 1
-        * CPU3 -/
-        *
-        *      => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
-        *      => 6 DAIs  = 4xCPU + 2xCodec
-        *      => 1 ccnf  = 1xdummy-Codec
-        *
-        * ex3)
-        * CPU0 --- Codec0      link : 6
-        * CPU1 -/              dais : 6
-        * CPU2 --- Codec1      ccnf : 2
-        * CPU3 -/
-        *
-        *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
-        *      => 6 DAIs  = 4xCPU + 2xCodec
-        *      => 2 ccnf  = 2xdummy-Codec
-        */
-       if (!top) {
-               (*link_num) = 1;
-               (*dais_num) = 2;
-               (*ccnf_num) = 0;
-               return;
-       }
-
-       loop = 1;
-       node = of_get_child_by_name(top, PREFIX "dai-link");
-       if (!node) {
-               node = top;
-               loop = 0;
-       }
-
-       do {
-               num = of_get_child_count(node);
-               (*dais_num) += num;
-               if (num > 2) {
-                       (*link_num) += num;
-                       (*ccnf_num)++;
-               } else {
-                       (*link_num)++;
-               }
-               node = of_get_next_child(top, node);
-       } while (loop && node);
-}
-
-static int asoc_simple_card_probe(struct platform_device *pdev)
-{
-       struct simple_card_data *priv;
-       struct snd_soc_dai_link *dai_link;
-       struct simple_dai_props *dai_props;
-       struct asoc_simple_dai *dais;
-       struct snd_soc_card *card;
-       struct snd_soc_codec_conf *cconf;
-       struct device *dev = &pdev->dev;
-       int ret, i;
-       int lnum = 0, dnum = 0, cnum = 0;
-
-       /* Allocate the private data */
-       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
-       if (!lnum || !dnum)
-               return -EINVAL;
-
-       dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
-       dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
-       dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
-       cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
-       if (!dai_props || !dai_link || !dais)
-               return -ENOMEM;
-
-       /*
-        * Use snd_soc_dai_link_component instead of legacy style
-        * It is codec only. but cpu/platform will be supported in the future.
-        * see
-        *      soc-core.c :: snd_soc_init_multicodec()
-        */
-       for (i = 0; i < lnum; i++) {
-               dai_link[i].codecs      = &dai_props[i].codecs;
-               dai_link[i].num_codecs  = 1;
-               dai_link[i].platform    = &dai_props[i].platform;
-       }
-
-       priv->dai_props                         = dai_props;
-       priv->dai_link                          = dai_link;
-       priv->dais                              = dais;
-       priv->codec_conf                        = cconf;
-
-       /* Init snd_soc_card */
-       card = simple_priv_to_card(priv);
-       card->owner             = THIS_MODULE;
-       card->dev               = dev;
-       card->dai_link          = priv->dai_link;
-       card->num_links         = lnum;
-       card->codec_conf        = cconf;
-       card->num_configs       = cnum;
-
-       ret = asoc_simple_card_parse_of(priv);
-       if (ret < 0) {
-               if (ret != -EPROBE_DEFER)
-                       dev_err(dev, "parse error %d\n", ret);
-               goto err;
-       }
-
-       snd_soc_card_set_drvdata(card, priv);
-
-       ret = devm_snd_soc_register_card(dev, card);
-       if (ret < 0)
-               goto err;
-
-       return 0;
-err:
-       asoc_simple_card_clean_reference(card);
-
-       return ret;
-}
-
-static int asoc_simple_card_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       return asoc_simple_card_clean_reference(card);
-}
-
-static const struct of_device_id asoc_simple_of_match[] = {
-       { .compatible = "renesas,rsrc-card", },
-       { .compatible = "simple-scu-audio-card", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
-
-static struct platform_driver asoc_simple_card = {
-       .driver = {
-               .name = "simple-scu-audio-card",
-               .pm = &snd_soc_pm_ops,
-               .of_match_table = asoc_simple_of_match,
-       },
-       .probe = asoc_simple_card_probe,
-       .remove = asoc_simple_card_remove,
-};
-
-module_platform_driver(asoc_simple_card);
-
-MODULE_ALIAS("platform:asoc-simple-scu-card");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
index 99a62ba409df83424bc84031f067e4e70a0db03d..bd9fd2035c554b9479500113816483b4d128b2c9 100644 (file)
@@ -91,7 +91,7 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI
 config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
        tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
        default ACPI
-       depends on X86 && ACPI
+       depends on X86 && ACPI && PCI
        select SND_SST_IPC_ACPI
        select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SOC_ACPI_INTEL_MATCH
index 3672d36b4b66f4fcd62ed863ef7a8b44ac9b0c4b..d1207ea53523d4fdb940ae1a1db2cab266f4c8df 100644 (file)
@@ -647,7 +647,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
                set_mixer = false;
        }
 
-       if (set_mixer == false)
+       if (!set_mixer)
                return 0;
 
        if (SND_SOC_DAPM_EVENT_ON(event) ||
index afc5598660955a462f1f2bfdf45fe9905a23e339..aefa5ce4cb592e6dc24a003b2ae8058bb39aa87a 100644 (file)
@@ -190,7 +190,7 @@ int sst_fill_stream_params(void *substream,
        map = ctx->pdata->pdev_strm_map;
        map_size = ctx->pdata->strm_map_size;
 
-       if (is_compress == true)
+       if (is_compress)
                cstream = (struct snd_compr_stream *)substream;
        else
                pstream = (struct snd_pcm_substream *)substream;
index ac542535b9d53fb036151e35da1e9aee063e7793..ae17ce4677a5c07b8da5c5b8760bb548ed597fdb 100644 (file)
@@ -255,18 +255,16 @@ static int is_byt(void)
        return status;
 }
 
-static int is_byt_cr(struct device *dev, bool *bytcr)
+static bool is_byt_cr(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        int status = 0;
 
-       if (IS_ENABLED(CONFIG_IOSF_MBI)) {
-               u32 bios_status;
-
-               if (!is_byt() || !iosf_mbi_available()) {
-                       /* bail silently */
-                       return status;
-               }
+       if (!is_byt())
+               return false;
 
+       if (iosf_mbi_available()) {
+               u32 bios_status;
                status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
                                       MBI_REG_READ, /* 0x10 */
                                       0x006, /* BIOS_CONFIG */
@@ -278,15 +276,28 @@ static int is_byt_cr(struct device *dev, bool *bytcr)
                        /* bits 26:27 mirror PMIC options */
                        bios_status = (bios_status >> 26) & 3;
 
-                       if ((bios_status == 1) || (bios_status == 3))
-                               *bytcr = true;
-                       else
-                               dev_info(dev, "BYT-CR not detected\n");
+                       if (bios_status == 1 || bios_status == 3) {
+                               dev_info(dev, "Detected Baytrail-CR platform\n");
+                               return true;
+                       }
+
+                       dev_info(dev, "BYT-CR not detected\n");
                }
        } else {
-               dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n");
+               dev_info(dev, "IOSF_MBI not available, no BYT-CR detection\n");
        }
-       return status;
+
+       if (platform_get_resource(pdev, IORESOURCE_IRQ, 5) == NULL) {
+               /*
+                * Some devices detected as BYT-T have only a single IRQ listed,
+                * causing platform_get_irq with index 5 to return -ENXIO.
+                * The correct IRQ in this case is at index 0, as on BYT-CR.
+                */
+               dev_info(dev, "Falling back to Baytrail-CR platform\n");
+               return true;
+       }
+
+       return false;
 }
 
 
@@ -301,7 +312,6 @@ static int sst_acpi_probe(struct platform_device *pdev)
        struct platform_device *plat_dev;
        struct sst_platform_info *pdata;
        unsigned int dev_id;
-       bool bytcr = false;
 
        id = acpi_match_device(dev->driver->acpi_match_table, dev);
        if (!id)
@@ -333,10 +343,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
-       ret = is_byt_cr(dev, &bytcr);
-       if (!((ret < 0) || (bytcr == false))) {
-               dev_info(dev, "Detected Baytrail-CR platform\n");
-
+       if (is_byt_cr(pdev)) {
                /* override resource info */
                byt_rvp_platform_data.res_info = &bytcr_res_info;
        }
index 5455d6e0ab53cc60271c90f16afaed3689bdf0fd..a592df06aa58eb001f58f54655e2858340751084 100644 (file)
@@ -146,7 +146,7 @@ static int sst_power_control(struct device *dev, bool state)
        int ret = 0;
        int usage_count = 0;
 
-       if (state == true) {
+       if (state) {
                ret = pm_runtime_get_sync(dev);
                usage_count = GET_USAGE_COUNT(dev);
                dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
index b8c456753f015ff66557447a15ee196536641df2..321c783cf833cab58db7eda72c3f6365270bf0b3 100644 (file)
@@ -269,7 +269,7 @@ static void sst_do_memcpy(struct list_head *memcpy_list)
        struct sst_memcpy_list *listnode;
 
        list_for_each_entry(listnode, memcpy_list, memcpylist) {
-               if (listnode->is_io == true)
+               if (listnode->is_io)
                        memcpy32_toio((void __iomem *)listnode->dstn,
                                        listnode->src, listnode->size);
                else
index 260447da32b8709b5ff72799141b99775adba93b..2cd8f9668b504635cb8456fdbf07d99d2f9a6115 100644 (file)
@@ -278,7 +278,6 @@ static int sst_byt_process_notification(struct sst_byt *byt,
        struct sst_byt_stream *stream;
        u64 header;
        u8 msg_id, stream_id;
-       int handled = 1;
 
        header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
        msg_id = sst_byt_header_msg_id(header);
@@ -298,7 +297,7 @@ static int sst_byt_process_notification(struct sst_byt *byt,
                break;
        }
 
-       return handled;
+       return 1;
 }
 
 static irqreturn_t sst_byt_irq_thread(int irq, void *context)
index aabb35bf6b963d86ab6f7e26d8f17f4c8460d37b..498fb5346f1a81cfd63a0cdfdc1e4b9ab32ed3b7 100644 (file)
@@ -188,7 +188,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                sst_byt_stream_start(byt, pcm_data->stream, 0);
                break;
        case SNDRV_PCM_TRIGGER_RESUME:
-               if (pdata->restore_stream == true)
+               if (pdata->restore_stream)
                        schedule_work(&pcm_data->work);
                else
                        sst_byt_stream_resume(byt, pcm_data->stream);
index 68e6543e6cb026aa20b3a339c9f7c3f24c3820f5..99f2a0156ae88cb509b70994cc6784902901161d 100644 (file)
@@ -192,7 +192,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .stream_name = "Loopback",
                .cpu_dai_name = "Loopback Pin",
                .platform_name = "haswell-pcm-audio",
-               .dynamic = 0,
+               .dynamic = 1,
                .codec_name = "snd-soc-dummy",
                .codec_dai_name = "snd-soc-dummy-dai",
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
index adc26dfc7d654d3064d0999366ac99ea71c5f742..cdf2061e7613d9522a889ec45d09ec6721aeb446 100644 (file)
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
 #include <linux/init.h>
+#include <linux/input.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/device.h>
 #include <linux/slab.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
 #include <asm/platform_sst_audio.h>
-#include <linux/clk.h>
+#include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
 struct byt_cht_es8316_private {
        struct clk *mclk;
+       struct snd_soc_jack jack;
+       struct gpio_desc *speaker_en_gpio;
+       bool speaker_en;
 };
 
+enum {
+       BYT_CHT_ES8316_INTMIC_IN1_MAP,
+       BYT_CHT_ES8316_INTMIC_IN2_MAP,
+};
+
+#define BYT_CHT_ES8316_MAP(quirk)              ((quirk) & GENMASK(3, 0))
+#define BYT_CHT_ES8316_SSP0                    BIT(16)
+#define BYT_CHT_ES8316_MONO_SPEAKER            BIT(17)
+
+static int quirk;
+
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+       if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP)
+               dev_info(dev, "quirk IN1_MAP enabled");
+       if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP)
+               dev_info(dev, "quirk IN2_MAP enabled");
+       if (quirk & BYT_CHT_ES8316_SSP0)
+               dev_info(dev, "quirk SSP0 enabled");
+       if (quirk & BYT_CHT_ES8316_MONO_SPEAKER)
+               dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+}
+
+static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_card *card = w->dapm->card;
+       struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               priv->speaker_en = true;
+       else
+               priv->speaker_en = false;
+
+       gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
+       SND_SOC_DAPM_SPK("Speaker", NULL),
        SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Internal Mic", NULL),
 
-       /*
-        * The codec supports two analog microphone inputs. I have only
-        * tested MIC1. A DMIC route could also potentially be added
-        * if such functionality is found on another platform.
-        */
-       SND_SOC_DAPM_MIC("Microphone 1", NULL),
-       SND_SOC_DAPM_MIC("Microphone 2", NULL),
+       SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+                           byt_cht_es8316_speaker_power_event,
+                           SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 };
 
 static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
-       {"MIC1", NULL, "Microphone 1"},
-       {"MIC2", NULL, "Microphone 2"},
-
        {"Headphone", NULL, "HPOL"},
        {"Headphone", NULL, "HPOR"},
 
+       /*
+        * There is no separate speaker output instead the speakers are muxed to
+        * the HP outputs. The mux is controlled by the "Speaker Power" supply.
+        */
+       {"Speaker", NULL, "HPOL"},
+       {"Speaker", NULL, "HPOR"},
+       {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = {
+       {"MIC1", NULL, "Internal Mic"},
+       {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = {
+       {"MIC2", NULL, "Internal Mic"},
+       {"MIC1", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = {
+       {"Playback", NULL, "ssp0 Tx"},
+       {"ssp0 Tx", NULL, "modem_out"},
+       {"modem_in", NULL, "ssp0 Rx"},
+       {"ssp0 Rx", NULL, "Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = {
        {"Playback", NULL, "ssp2 Tx"},
        {"ssp2 Tx", NULL, "codec_out0"},
        {"ssp2 Tx", NULL, "codec_out1"},
@@ -65,19 +141,60 @@ static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
 };
 
 static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
        SOC_DAPM_PIN_SWITCH("Headphone"),
-       SOC_DAPM_PIN_SWITCH("Microphone 1"),
-       SOC_DAPM_PIN_SWITCH("Microphone 2"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = {
+       {
+               .pin    = "Headphone",
+               .mask   = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin    = "Headset Mic",
+               .mask   = SND_JACK_MICROPHONE,
+       },
 };
 
 static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
 {
+       struct snd_soc_component *codec = runtime->codec_dai->component;
        struct snd_soc_card *card = runtime->card;
        struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+       const struct snd_soc_dapm_route *custom_map;
+       int num_routes;
        int ret;
 
        card->dapm.idle_bias_off = true;
 
+       switch (BYT_CHT_ES8316_MAP(quirk)) {
+       case BYT_CHT_ES8316_INTMIC_IN1_MAP:
+       default:
+               custom_map = byt_cht_es8316_intmic_in1_map;
+               num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map);
+               break;
+       case BYT_CHT_ES8316_INTMIC_IN2_MAP:
+               custom_map = byt_cht_es8316_intmic_in2_map;
+               num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map);
+               break;
+       }
+       ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+       if (ret)
+               return ret;
+
+       if (quirk & BYT_CHT_ES8316_SSP0) {
+               custom_map = byt_cht_es8316_ssp0_map;
+               num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map);
+       } else {
+               custom_map = byt_cht_es8316_ssp2_map;
+               num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map);
+       }
+       ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+       if (ret)
+               return ret;
+
        /*
         * The firmware might enable the clock at boot (this information
         * may or may not be reflected in the enable clock register).
@@ -105,6 +222,18 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
                return ret;
        }
 
+       ret = snd_soc_card_jack_new(card, "Headset",
+                                   SND_JACK_HEADSET | SND_JACK_BTN_0,
+                                   &priv->jack, byt_cht_es8316_jack_pins,
+                                   ARRAY_SIZE(byt_cht_es8316_jack_pins));
+       if (ret) {
+               dev_err(card->dev, "jack creation failed %d\n", ret);
+               return ret;
+       }
+
+       snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+       snd_soc_component_set_jack(codec, &priv->jack, NULL);
+
        return 0;
 }
 
@@ -123,14 +252,21 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                        SNDRV_PCM_HW_PARAM_RATE);
        struct snd_interval *channels = hw_param_interval(params,
                                                SNDRV_PCM_HW_PARAM_CHANNELS);
-       int ret;
+       int ret, bits;
 
        /* The DSP will covert the FE rate to 48k, stereo */
        rate->min = rate->max = 48000;
        channels->min = channels->max = 2;
 
-       /* set SSP2 to 24-bit */
-       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+       if (quirk & BYT_CHT_ES8316_SSP0) {
+               /* set SSP0 to 16-bit */
+               params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+               bits = 16;
+       } else {
+               /* set SSP2 to 24-bit */
+               params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+               bits = 24;
+       }
 
        /*
         * Default mode for SSP configuration is TDM 4 slot, override config
@@ -147,7 +283,7 @@ static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                return ret;
        }
 
-       ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+       ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits);
        if (ret < 0) {
                dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
                return ret;
@@ -218,6 +354,59 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
 
 
 /* SoC card */
+static char codec_name[SND_ACPI_I2C_ID_LEN];
+static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */
+
+static int byt_cht_es8316_suspend(struct snd_soc_card *card)
+{
+       struct snd_soc_component *component;
+
+       for_each_card_components(card, component) {
+               if (!strcmp(component->name, codec_name)) {
+                       dev_dbg(component->dev, "disabling jack detect before suspend\n");
+                       snd_soc_component_set_jack(component, NULL, NULL);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int byt_cht_es8316_resume(struct snd_soc_card *card)
+{
+       struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_component *component;
+
+       for_each_card_components(card, component) {
+               if (!strcmp(component->name, codec_name)) {
+                       dev_dbg(component->dev, "re-enabling jack detect after resume\n");
+                       snd_soc_component_set_jack(component, &priv->jack, NULL);
+                       break;
+               }
+       }
+
+       /*
+        * Some Cherry Trail boards with an ES8316 codec have a bug in their
+        * ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods
+        * wrongly also set the speaker-enable GPIO to 1/0. Testing has shown
+        * that this really is a bug and the GPIO has no influence on the
+        * touchscreen at all.
+        *
+        * The silead.c touchscreen driver does not support runtime suspend, so
+        * the GPIO can only be changed underneath us during a system suspend.
+        * This resume() function runs from a pm complete() callback, and thus
+        * is guaranteed to run after the touchscreen driver/ACPI-subsys has
+        * brought the touchscreen back up again (and thus changed the GPIO).
+        *
+        * So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when
+        * requesting the GPIO and we set its value here to undo any changes
+        * done by the touchscreen's broken _PS0 ACPI method.
+        */
+       gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en);
+
+       return 0;
+}
+
 static struct snd_soc_card byt_cht_es8316_card = {
        .name = "bytcht-es8316",
        .owner = THIS_MODULE,
@@ -230,24 +419,39 @@ static struct snd_soc_card byt_cht_es8316_card = {
        .controls = byt_cht_es8316_controls,
        .num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
        .fully_routed = true,
+       .suspend_pre = byt_cht_es8316_suspend,
+       .resume_post = byt_cht_es8316_resume,
 };
 
-static char codec_name[SND_ACPI_I2C_ID_LEN];
+static const struct x86_cpu_id baytrail_cpu_ids[] = {
+       { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT }, /* Valleyview */
+       {}
+};
+
+static const struct acpi_gpio_params first_gpio = { 0, 0, false };
+
+static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = {
+       { "speaker-enable-gpios", &first_gpio, 1 },
+       { },
+};
 
 static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
 {
+       const char * const mic_name[] = { "in1", "in2" };
        struct byt_cht_es8316_private *priv;
+       struct device *dev = &pdev->dev;
        struct snd_soc_acpi_mach *mach;
        const char *i2c_name = NULL;
+       struct device *codec_dev;
        int dai_index = 0;
        int i;
        int ret = 0;
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       mach = (&pdev->dev)->platform_data;
+       mach = dev->platform_data;
        /* fix index of codec dai */
        for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
                if (!strcmp(byt_cht_es8316_dais[i].codec_name,
@@ -265,26 +469,85 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
                byt_cht_es8316_dais[dai_index].codec_name = codec_name;
        }
 
-       /* register the soc card */
-       byt_cht_es8316_card.dev = &pdev->dev;
-       snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
+       /* Check for BYTCR or other platform and setup quirks */
+       if (x86_match_cpu(baytrail_cpu_ids) &&
+           mach->mach_params.acpi_ipc_irq_index == 0) {
+               /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */
+               quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP |
+                       BYT_CHT_ES8316_MONO_SPEAKER;
+       } else {
+               /* Others default to internal-mic-in1-map, mono-speaker */
+               quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP |
+                       BYT_CHT_ES8316_MONO_SPEAKER;
+       }
+       if (quirk_override != -1) {
+               dev_info(dev, "Overriding quirk 0x%x => 0x%x\n", quirk,
+                        quirk_override);
+               quirk = quirk_override;
+       }
+       log_quirks(dev);
+
+       if (quirk & BYT_CHT_ES8316_SSP0)
+               byt_cht_es8316_dais[dai_index].cpu_dai_name = "ssp0-port";
 
-       priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+       /* get the clock */
+       priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3");
        if (IS_ERR(priv->mclk)) {
                ret = PTR_ERR(priv->mclk);
-               dev_err(&pdev->dev,
-                       "Failed to get MCLK from pmc_plt_clk_3: %d\n",
-                       ret);
+               dev_err(dev, "clk_get pmc_plt_clk_3 failed: %d\n", ret);
                return ret;
        }
 
-       ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card);
+       /* get speaker enable GPIO */
+       codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name);
+       if (!codec_dev)
+               return -EPROBE_DEFER;
+
+       devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios);
+       priv->speaker_en_gpio =
+               gpiod_get_index(codec_dev, "speaker-enable", 0,
+                               /* see comment in byt_cht_es8316_resume */
+                               GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+       put_device(codec_dev);
+
+       if (IS_ERR(priv->speaker_en_gpio)) {
+               ret = PTR_ERR(priv->speaker_en_gpio);
+               switch (ret) {
+               case -ENOENT:
+                       priv->speaker_en_gpio = NULL;
+                       break;
+               default:
+                       dev_err(dev, "get speaker GPIO failed: %d\n", ret);
+                       /* fall through */
+               case -EPROBE_DEFER:
+                       return ret;
+               }
+       }
+
+       /* register the soc card */
+       snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic",
+                (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo",
+                mic_name[BYT_CHT_ES8316_MAP(quirk)]);
+       byt_cht_es8316_card.long_name = long_name;
+       byt_cht_es8316_card.dev = dev;
+       snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
+
+       ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card);
        if (ret) {
-               dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+               gpiod_put(priv->speaker_en_gpio);
+               dev_err(dev, "snd_soc_register_card failed: %d\n", ret);
                return ret;
        }
        platform_set_drvdata(pdev, &byt_cht_es8316_card);
-       return ret;
+       return 0;
+}
+
+static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev)
+{
+       struct byt_cht_es8316_private *priv = platform_get_drvdata(pdev);
+
+       gpiod_put(priv->speaker_en_gpio);
+       return 0;
 }
 
 static struct platform_driver snd_byt_cht_es8316_mc_driver = {
@@ -292,6 +555,7 @@ static struct platform_driver snd_byt_cht_es8316_mc_driver = {
                .name = "bytcht_es8316",
        },
        .probe = snd_byt_cht_es8316_mc_probe,
+       .remove = snd_byt_cht_es8316_mc_remove,
 };
 
 module_platform_driver(snd_byt_cht_es8316_mc_driver);
index a22366ce33c40389694b42b96f937f516c425a87..ca8b4d5ff70f9f1744e6d10e65d8f6945d8e0f20 100644 (file)
@@ -428,6 +428,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
                                        BYT_RT5640_SSP0_AIF1 |
                                        BYT_RT5640_MCLK_EN),
        },
+       {
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
+               },
+               .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+                                       BYT_RT5640_JD_SRC_JD2_IN4N |
+                                       BYT_RT5640_OVCD_TH_2000UA |
+                                       BYT_RT5640_OVCD_SF_0P75 |
+                                       BYT_RT5640_SSP0_AIF1 |
+                                       BYT_RT5640_MCLK_EN),
+       },
        {
                .matches = {
                        DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
index c74c4f17316fe898ff29e16a2be4982499d7f612..0739e3a75083f1d31b8182c42ba2f9236e5f0d5f 100644 (file)
@@ -164,7 +164,7 @@ static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
 
        /* set SSP to 24 bit */
        snd_mask_none(fmt);
-       snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+       snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
 
        return 0;
 }
index eab1f439dd3f1ad9a4553d03fa2190d673ec94a3..a4022983a7ce0050b3a5d857b0edd6ed691e0636 100644 (file)
@@ -146,7 +146,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .stream_name = "Loopback",
                .cpu_dai_name = "Loopback Pin",
                .platform_name = "haswell-pcm-audio",
-               .dynamic = 0,
+               .dynamic = 1,
                .codec_name = "snd-soc-dummy",
                .codec_dai_name = "snd-soc-dummy-dai",
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
index 723a4935ed76ed287f93e4f21605fb8b325c6ab6..6dd5c69671b3f667d914a58cc07a669a4814249d 100644 (file)
@@ -221,7 +221,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
                rate->min = rate->max = 48000;
                channels->min = channels->max = 2;
                snd_mask_none(fmt);
-               snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
        }
 
        /*
@@ -229,7 +229,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
         * thus changing the mask here
         */
        if (!strcmp(be_dai_link->name, "SSP0-Codec"))
-               snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE);
+               snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
 
        return 0;
 }
index 097dc06377baaf14ff8f1b68a69cdb2700abd56f..47a90909b956893113ee2e235b6320e827c7d9b3 100644 (file)
@@ -154,6 +154,15 @@ struct snd_soc_acpi_mach  snd_soc_acpi_intel_baytrail_machines[] = {
                .sof_tplg_filename = "intel/sof-byt-da7213.tplg",
                .asoc_plat_name = "sst-mfld-platform",
        },
+       {
+               .id = "ESSX8316",
+               .drv_name = "bytcht_es8316",
+               .fw_filename = "intel/fw_sst_0f28.bin",
+               .board = "bytcht_es8316",
+               .sof_fw_filename = "intel/sof-byt.ri",
+               .sof_tplg_filename = "intel/sof-byt-es8316.tplg",
+               .asoc_plat_name = "sst-mfld-platform",
+       },
        /* some Baytrail platforms rely on RT5645, use CHT machine driver */
        {
                .id = "10EC5645",
index d33bdaf92c57c8ab9fb7648f51a1ccc185f9e324..31fcdf12c67def96f9f00591cde3b6f022ab5786 100644 (file)
@@ -1216,7 +1216,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
                return ret;
        }
 
-       stream->commited = 1;
+       stream->commited = true;
        trace_hsw_stream_alloc_reply(stream);
 
        return 0;
index fe2c826e710c73bba7053900514504d3abd71d1f..2debcc2ed99a1ffb4bd02d0588869659f216d02b 100644 (file)
@@ -544,7 +544,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                dev_err(rtd->dev, "error: invalid DAI ID %d\n",
                        rtd->cpu_dai->id);
                return -EINVAL;
-       };
+       }
 
        ret = sst_hsw_stream_format(hsw, pcm_data->stream,
                path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
@@ -861,7 +861,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
                dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
                goto out;
        }
-       pcm_data->allocated = 0;
+       pcm_data->allocated = false;
        pcm_data->stream = NULL;
 
 out:
index b0e6fb93eaf83ce9e1bcaf01c2cb405784e72e73..28c4806b196a2fc3505ce88e298b834cf64e1c56 100644 (file)
@@ -416,7 +416,7 @@ int skl_resume_dsp(struct skl *skl)
        snd_hdac_ext_bus_ppcap_int_enable(bus, true);
 
        /* check if DSP 1st boot is done */
-       if (skl->skl_sst->is_first_boot == true)
+       if (skl->skl_sst->is_first_boot)
                return 0;
 
        /*
index 557f80c0bfe5307e4ceb842c1d22707180485d95..8e589d698c588e2349ff270de4c7a30a6507d9b9 100644 (file)
@@ -1423,7 +1423,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component)
                if (!ops)
                        return -EIO;
 
-               if (skl->skl_sst->is_first_boot == false) {
+               if (!skl->skl_sst->is_first_boot) {
                        dev_err(component->dev, "DSP reports first boot done!!!\n");
                        return -EIO;
                }
index cf8848b779dcc2a84a2c15a35d29ad49808a0692..389f1862bc43918489b4035146070a507599a54a 100644 (file)
@@ -3103,7 +3103,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
        ac->size = dfw_ac->max;
 
        if (ac->max) {
-               ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
+               ac->params = devm_kzalloc(dev, ac->max, GFP_KERNEL);
                if (!ac->params)
                        return -ENOMEM;
 
index 5b986b74dd36f96d0a4a37bc8714f0b741c39486..548eb4fa2da64415ccf7f9db83f84575a251093f 100644 (file)
@@ -570,10 +570,10 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
        prtd->audio_client = q6asm_audio_client_alloc(dev,
                                        (q6asm_cb)compress_event_handler,
                                        prtd, stream_id, LEGACY_PCM_MODE);
-       if (!prtd->audio_client) {
+       if (IS_ERR(prtd->audio_client)) {
                dev_err(dev, "Could not allocate memory\n");
-               kfree(prtd);
-               return -ENOMEM;
+               ret = PTR_ERR(prtd->audio_client);
+               goto free_prtd;
        }
 
        size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
@@ -582,7 +582,7 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
                                  &prtd->dma_buffer);
        if (ret) {
                dev_err(dev, "Cannot allocate buffer(s)\n");
-               return ret;
+               goto free_client;
        }
 
        if (pdata->sid < 0)
@@ -595,6 +595,13 @@ static int q6asm_dai_compr_open(struct snd_compr_stream *stream)
        runtime->private_data = prtd;
 
        return 0;
+
+free_client:
+       q6asm_audio_client_free(prtd->audio_client);
+free_prtd:
+       kfree(prtd);
+
+       return ret;
 }
 
 static int q6asm_dai_compr_free(struct snd_compr_stream *stream)
@@ -874,7 +881,7 @@ static int of_q6asm_parse_dai_data(struct device *dev,
 
        for_each_child_of_node(dev->of_node, node) {
                ret = of_property_read_u32(node, "reg", &id);
-               if (ret || id > MAX_SESSIONS || id < 0) {
+               if (ret || id >= MAX_SESSIONS || id < 0) {
                        dev_err(dev, "valid dai id not found:%d\n", ret);
                        continue;
                }
index 1db8ef6682233feaf38145efb07af877c28a1ef2..6f66a58e23caa1178bec669a14eb54c4dec72a95 100644 (file)
@@ -158,17 +158,24 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static void sdm845_jack_free(struct snd_jack *jack)
+{
+       struct snd_soc_component *component = jack->private_data;
+
+       snd_soc_component_set_jack(component, NULL, NULL);
+}
+
 static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_component *component;
-       struct snd_soc_dai_link *dai_link = rtd->dai_link;
        struct snd_soc_card *card = rtd->card;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
-       int i, rval;
+       struct snd_jack *jack;
+       int rval;
 
        if (!pdata->jack_setup) {
-               struct snd_jack *jack;
-
                rval = snd_soc_card_jack_new(card, "Headset Jack",
                                SND_JACK_HEADSET |
                                SND_JACK_HEADPHONE |
@@ -190,16 +197,22 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
                pdata->jack_setup = true;
        }
 
-       for (i = 0 ; i < dai_link->num_codecs; i++) {
-               struct snd_soc_dai *dai = rtd->codec_dais[i];
+       switch (cpu_dai->id) {
+       case PRIMARY_MI2S_RX:
+               jack  = pdata->jack.jack;
+               component = codec_dai->component;
 
-               component = dai->component;
-               rval = snd_soc_component_set_jack(
-                               component, &pdata->jack, NULL);
+               jack->private_data = component;
+               jack->private_free = sdm845_jack_free;
+               rval = snd_soc_component_set_jack(component,
+                                                 &pdata->jack, NULL);
                if (rval != 0 && rval != -ENOTSUPP) {
                        dev_warn(card->dev, "Failed to set jack: %d\n", rval);
                        return rval;
                }
+               break;
+       default:
+               break;
        }
 
        return 0;
index 922fb6aa3ed191c5047ae21ecb5d708287a81f35..5aee11c94f2a749f13c2957fb4eeb35c263ed482 100644 (file)
@@ -202,7 +202,7 @@ static int camelot_prepare(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 
-       pr_debug("PCM data: addr 0x%08ulx len %d\n",
+       pr_debug("PCM data: addr 0x%08lx len %d\n",
                 (u32)runtime->dma_addr, runtime->dma_bytes);
  
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
index eeda6d5565bccc5bffa00873a9a3c517dcacf41d..a3a67a8f0f543ab047addec631d3f3743a7fb0fd 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/platform_data/davinci_asp.h>
 #include <linux/math64.h>
 #include <linux/bitmap.h>
+#include <linux/gpio/driver.h>
 
 #include <sound/asoundef.h>
 #include <sound/core.h>
@@ -54,6 +55,7 @@ static u32 context_regs[] = {
        DAVINCI_MCASP_AHCLKXCTL_REG,
        DAVINCI_MCASP_AHCLKRCTL_REG,
        DAVINCI_MCASP_PDIR_REG,
+       DAVINCI_MCASP_PFUNC_REG,
        DAVINCI_MCASP_RXMASK_REG,
        DAVINCI_MCASP_TXMASK_REG,
        DAVINCI_MCASP_RXTDM_REG,
@@ -108,7 +110,11 @@ struct davinci_mcasp {
        /* Used for comstraint setting on the second stream */
        u32     channels;
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_GPIOLIB
+       struct gpio_chip gpio_chip;
+#endif
+
+#ifdef CONFIG_PM
        struct davinci_mcasp_context context;
 #endif
 
@@ -818,9 +824,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
        if (mcasp->version < MCASP_VERSION_3)
                mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
 
-       /* All PINS as McASP */
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
-
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
@@ -1486,74 +1489,6 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
-{
-       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-       struct davinci_mcasp_context *context = &mcasp->context;
-       u32 reg;
-       int i;
-
-       context->pm_state = pm_runtime_active(mcasp->dev);
-       if (!context->pm_state)
-               pm_runtime_get_sync(mcasp->dev);
-
-       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
-               context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
-
-       if (mcasp->txnumevt) {
-               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-               context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
-       }
-       if (mcasp->rxnumevt) {
-               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-               context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
-       }
-
-       for (i = 0; i < mcasp->num_serializer; i++)
-               context->xrsr_regs[i] = mcasp_get_reg(mcasp,
-                                               DAVINCI_MCASP_XRSRCTL_REG(i));
-
-       pm_runtime_put_sync(mcasp->dev);
-
-       return 0;
-}
-
-static int davinci_mcasp_resume(struct snd_soc_dai *dai)
-{
-       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-       struct davinci_mcasp_context *context = &mcasp->context;
-       u32 reg;
-       int i;
-
-       pm_runtime_get_sync(mcasp->dev);
-
-       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
-               mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
-
-       if (mcasp->txnumevt) {
-               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-               mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
-       }
-       if (mcasp->rxnumevt) {
-               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-               mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
-       }
-
-       for (i = 0; i < mcasp->num_serializer; i++)
-               mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
-                             context->xrsr_regs[i]);
-
-       if (!context->pm_state)
-               pm_runtime_put_sync(mcasp->dev);
-
-       return 0;
-}
-#else
-#define davinci_mcasp_suspend NULL
-#define davinci_mcasp_resume NULL
-#endif
-
 #define DAVINCI_MCASP_RATES    SNDRV_PCM_RATE_8000_192000
 
 #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
@@ -1571,8 +1506,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
        {
                .name           = "davinci-mcasp.0",
                .probe          = davinci_mcasp_dai_probe,
-               .suspend        = davinci_mcasp_suspend,
-               .resume         = davinci_mcasp_resume,
                .playback       = {
                        .channels_min   = 1,
                        .channels_max   = 32 * 16,
@@ -1915,6 +1848,147 @@ static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
        return offset;
 }
 
+#ifdef CONFIG_GPIOLIB
+static int davinci_mcasp_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+
+       if (mcasp->num_serializer && offset < mcasp->num_serializer &&
+           mcasp->serial_dir[offset] != INACTIVE_MODE) {
+               dev_err(mcasp->dev, "AXR%u pin is  used for audio\n", offset);
+               return -EBUSY;
+       }
+
+       /* Do not change the PIN yet */
+
+       return pm_runtime_get_sync(mcasp->dev);
+}
+
+static void davinci_mcasp_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+
+       /* Set the direction to input */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
+
+       /* Set the pin as McASP pin */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
+
+       pm_runtime_put_sync(mcasp->dev);
+}
+
+static int davinci_mcasp_gpio_direction_out(struct gpio_chip *chip,
+                                           unsigned offset, int value)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+       u32 val;
+
+       if (value)
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
+       else
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
+
+       val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
+       if (!(val & BIT(offset))) {
+               /* Set the pin as GPIO pin */
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
+
+               /* Set the direction to output */
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
+       }
+
+       return 0;
+}
+
+static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset,
+                                 int value)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+
+       if (value)
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
+       else
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
+}
+
+static int davinci_mcasp_gpio_direction_in(struct gpio_chip *chip,
+                                          unsigned offset)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+       u32 val;
+
+       val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
+       if (!(val & BIT(offset))) {
+               /* Set the direction to input */
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
+
+               /* Set the pin as GPIO pin */
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
+       }
+
+       return 0;
+}
+
+static int davinci_mcasp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+       u32 val;
+
+       val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDSET_REG);
+       if (val & BIT(offset))
+               return 1;
+
+       return 0;
+}
+
+static int davinci_mcasp_gpio_get_direction(struct gpio_chip *chip,
+                                           unsigned offset)
+{
+       struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
+       u32 val;
+
+       val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
+       if (val & BIT(offset))
+               return 0;
+
+       return 1;
+}
+
+static const struct gpio_chip davinci_mcasp_template_chip = {
+       .owner                  = THIS_MODULE,
+       .request                = davinci_mcasp_gpio_request,
+       .free                   = davinci_mcasp_gpio_free,
+       .direction_output       = davinci_mcasp_gpio_direction_out,
+       .set                    = davinci_mcasp_gpio_set,
+       .direction_input        = davinci_mcasp_gpio_direction_in,
+       .get                    = davinci_mcasp_gpio_get,
+       .get_direction          = davinci_mcasp_gpio_get_direction,
+       .base                   = -1,
+       .ngpio                  = 32,
+};
+
+static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
+{
+       if (!of_property_read_bool(mcasp->dev->of_node, "gpio-controller"))
+               return 0;
+
+       mcasp->gpio_chip = davinci_mcasp_template_chip;
+       mcasp->gpio_chip.label = dev_name(mcasp->dev);
+       mcasp->gpio_chip.parent = mcasp->dev;
+#ifdef CONFIG_OF_GPIO
+       mcasp->gpio_chip.of_node = mcasp->dev->of_node;
+#endif
+
+       return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp);
+}
+
+#else /* CONFIG_GPIOLIB */
+static inline int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
+{
+       return 0;
+}
+#endif /* CONFIG_GPIOLIB */
+
 static int davinci_mcasp_probe(struct platform_device *pdev)
 {
        struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1976,7 +2050,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        }
 
        mcasp->num_serializer = pdata->num_serializer;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
                                        mcasp->num_serializer, sizeof(u32),
                                        GFP_KERNEL);
@@ -2139,6 +2213,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
        mcasp_reparent_fck(pdev);
 
+       /* All PINS as McASP */
+       pm_runtime_get_sync(mcasp->dev);
+       mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+       pm_runtime_put(mcasp->dev);
+
+       ret = davinci_mcasp_init_gpiochip(mcasp);
+       if (ret)
+               goto err;
+
        ret = devm_snd_soc_register_component(&pdev->dev,
                                        &davinci_mcasp_component,
                                        &davinci_mcasp_dai[pdata->op_mode], 1);
@@ -2149,26 +2232,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        ret = davinci_mcasp_get_dma_type(mcasp);
        switch (ret) {
        case PCM_EDMA:
-#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \
-       (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
-        IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM))
                ret = edma_pcm_platform_register(&pdev->dev);
-#else
-               dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
-               ret = -EINVAL;
-               goto err;
-#endif
                break;
        case PCM_SDMA:
-#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \
-       (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
-        IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM))
                ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
-#else
-               dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
-               ret = -EINVAL;
-               goto err;
-#endif
                break;
        default:
                dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
@@ -2196,11 +2263,73 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int davinci_mcasp_runtime_suspend(struct device *dev)
+{
+       struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
+       struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
+       }
+
+       for (i = 0; i < mcasp->num_serializer; i++)
+               context->xrsr_regs[i] = mcasp_get_reg(mcasp,
+                                               DAVINCI_MCASP_XRSRCTL_REG(i));
+
+       return 0;
+}
+
+static int davinci_mcasp_runtime_resume(struct device *dev)
+{
+       struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
+       struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
+       }
+
+       for (i = 0; i < mcasp->num_serializer; i++)
+               mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+                             context->xrsr_regs[i]);
+
+       return 0;
+}
+
+#endif
+
+static const struct dev_pm_ops davinci_mcasp_pm_ops = {
+       SET_RUNTIME_PM_OPS(davinci_mcasp_runtime_suspend,
+                          davinci_mcasp_runtime_resume,
+                          NULL)
+};
+
 static struct platform_driver davinci_mcasp_driver = {
        .probe          = davinci_mcasp_probe,
        .remove         = davinci_mcasp_remove,
        .driver         = {
                .name   = "davinci-mcasp",
+               .pm     = &davinci_mcasp_pm_ops,
                .of_match_table = mcasp_dt_ids,
        },
 };
index 25e287feb58c33e845db60054e16ed49856e5679..ac48d6a00c36d1d83dd95e948576fa08a74f4bf1 100644 (file)
@@ -1,8 +1,15 @@
 config SND_SOC_XILINX_I2S
-       tristate "Audio support for the the Xilinx I2S"
+       tristate "Audio support for the Xilinx I2S"
        help
          Select this option to enable Xilinx I2S Audio. This enables
          I2S playback and capture using xilinx soft IP. In transmitter
          mode, IP receives audio in AES format, extracts PCM and sends
          PCM data. In receiver mode, IP receives PCM audio and
          encapsulates PCM in AES format and sends AES data.
+
+config SND_SOC_XILINX_AUDIO_FORMATTER
+        tristate "Audio support for the the Xilinx audio formatter"
+        help
+          Select this option to enable Xilinx audio formatter
+          support. This provides DMA platform device support for
+          audio functionality.
index 6c1209b9ee750f79c5e248d2bf06caf34fb3c80c..432693b1cc79223ba63342f2eabf36b144d9c208 100644 (file)
@@ -1,2 +1,4 @@
 snd-soc-xlnx-i2s-objs      := xlnx_i2s.o
 obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o
+snd-soc-xlnx-formatter-pcm-objs := xlnx_formatter_pcm.o
+obj-$(CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER) += snd-soc-xlnx-formatter-pcm.o
diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c
new file mode 100644 (file)
index 0000000..d2194da
--- /dev/null
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Xilinx ASoC audio formatter support
+//
+// Copyright (C) 2018 Xilinx, Inc.
+//
+// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sizes.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#define DRV_NAME "xlnx_formatter_pcm"
+
+#define XLNX_S2MM_OFFSET       0
+#define XLNX_MM2S_OFFSET       0x100
+
+#define XLNX_AUD_CORE_CONFIG   0x4
+#define XLNX_AUD_CTRL          0x10
+#define XLNX_AUD_STS           0x14
+
+#define AUD_CTRL_RESET_MASK    BIT(1)
+#define AUD_CFG_MM2S_MASK      BIT(15)
+#define AUD_CFG_S2MM_MASK      BIT(31)
+
+#define XLNX_AUD_FS_MULTIPLIER 0x18
+#define XLNX_AUD_PERIOD_CONFIG 0x1C
+#define XLNX_AUD_BUFF_ADDR_LSB 0x20
+#define XLNX_AUD_BUFF_ADDR_MSB 0x24
+#define XLNX_AUD_XFER_COUNT    0x28
+#define XLNX_AUD_CH_STS_START  0x2C
+#define XLNX_BYTES_PER_CH      0x44
+
+#define AUD_STS_IOC_IRQ_MASK   BIT(31)
+#define AUD_STS_CH_STS_MASK    BIT(29)
+#define AUD_CTRL_IOC_IRQ_MASK  BIT(13)
+#define AUD_CTRL_TOUT_IRQ_MASK BIT(14)
+#define AUD_CTRL_DMA_EN_MASK   BIT(0)
+
+#define CFG_MM2S_CH_MASK       GENMASK(11, 8)
+#define CFG_MM2S_CH_SHIFT      8
+#define CFG_MM2S_XFER_MASK     GENMASK(14, 13)
+#define CFG_MM2S_XFER_SHIFT    13
+#define CFG_MM2S_PKG_MASK      BIT(12)
+
+#define CFG_S2MM_CH_MASK       GENMASK(27, 24)
+#define CFG_S2MM_CH_SHIFT      24
+#define CFG_S2MM_XFER_MASK     GENMASK(30, 29)
+#define CFG_S2MM_XFER_SHIFT    29
+#define CFG_S2MM_PKG_MASK      BIT(28)
+
+#define AUD_CTRL_DATA_WIDTH_SHIFT      16
+#define AUD_CTRL_ACTIVE_CH_SHIFT       19
+#define PERIOD_CFG_PERIODS_SHIFT       16
+
+#define PERIODS_MIN            2
+#define PERIODS_MAX            6
+#define PERIOD_BYTES_MIN       192
+#define PERIOD_BYTES_MAX       (50 * 1024)
+
+enum bit_depth {
+       BIT_DEPTH_8,
+       BIT_DEPTH_16,
+       BIT_DEPTH_20,
+       BIT_DEPTH_24,
+       BIT_DEPTH_32,
+};
+
+struct xlnx_pcm_drv_data {
+       void __iomem *mmio;
+       bool s2mm_presence;
+       bool mm2s_presence;
+       int s2mm_irq;
+       int mm2s_irq;
+       struct snd_pcm_substream *play_stream;
+       struct snd_pcm_substream *capture_stream;
+       struct clk *axi_clk;
+};
+
+/*
+ * struct xlnx_pcm_stream_param - stream configuration
+ * @mmio: base address offset
+ * @interleaved: audio channels arrangement in buffer
+ * @xfer_mode: data formatting mode during transfer
+ * @ch_limit: Maximum channels supported
+ * @buffer_size: stream ring buffer size
+ */
+struct xlnx_pcm_stream_param {
+       void __iomem *mmio;
+       bool interleaved;
+       u32 xfer_mode;
+       u32 ch_limit;
+       u64 buffer_size;
+};
+
+static const struct snd_pcm_hardware xlnx_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_RESUME,
+       .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+                  SNDRV_PCM_FMTBIT_S24_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .rates = SNDRV_PCM_RATE_8000_192000,
+       .rate_min = 8000,
+       .rate_max = 192000,
+       .buffer_bytes_max = PERIODS_MAX * PERIOD_BYTES_MAX,
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = PERIOD_BYTES_MAX,
+       .periods_min = PERIODS_MIN,
+       .periods_max = PERIODS_MAX,
+};
+
+static int xlnx_formatter_pcm_reset(void __iomem *mmio_base)
+{
+       u32 val, retries = 0;
+
+       val = readl(mmio_base + XLNX_AUD_CTRL);
+       val |= AUD_CTRL_RESET_MASK;
+       writel(val, mmio_base + XLNX_AUD_CTRL);
+
+       val = readl(mmio_base + XLNX_AUD_CTRL);
+       /* Poll for maximum timeout of approximately 100ms (1 * 100)*/
+       while ((val & AUD_CTRL_RESET_MASK) && (retries < 100)) {
+               mdelay(1);
+               retries++;
+               val = readl(mmio_base + XLNX_AUD_CTRL);
+       }
+       if (val & AUD_CTRL_RESET_MASK)
+               return -ENODEV;
+
+       return 0;
+}
+
+static void xlnx_formatter_disable_irqs(void __iomem *mmio_base, int stream)
+{
+       u32 val;
+
+       val = readl(mmio_base + XLNX_AUD_CTRL);
+       val &= ~AUD_CTRL_IOC_IRQ_MASK;
+       if (stream == SNDRV_PCM_STREAM_CAPTURE)
+               val &= ~AUD_CTRL_TOUT_IRQ_MASK;
+
+       writel(val, mmio_base + XLNX_AUD_CTRL);
+}
+
+static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg)
+{
+       u32 val;
+       void __iomem *reg;
+       struct device *dev = arg;
+       struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev);
+
+       reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS;
+       val = readl(reg);
+       if (val & AUD_STS_IOC_IRQ_MASK) {
+               writel(val & AUD_STS_IOC_IRQ_MASK, reg);
+               if (adata->play_stream)
+                       snd_pcm_period_elapsed(adata->play_stream);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg)
+{
+       u32 val;
+       void __iomem *reg;
+       struct device *dev = arg;
+       struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev);
+
+       reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS;
+       val = readl(reg);
+       if (val & AUD_STS_IOC_IRQ_MASK) {
+               writel(val & AUD_STS_IOC_IRQ_MASK, reg);
+               if (adata->capture_stream)
+                       snd_pcm_period_elapsed(adata->capture_stream);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream)
+{
+       int err;
+       u32 val, data_format_mode;
+       u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift;
+       struct xlnx_pcm_stream_param *stream_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *prtd = substream->private_data;
+       struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
+                                                                   DRV_NAME);
+       struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           !adata->mm2s_presence)
+               return -ENODEV;
+       else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+                !adata->s2mm_presence)
+               return -ENODEV;
+
+       stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL);
+       if (!stream_data)
+               return -ENOMEM;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ch_count_mask = CFG_MM2S_CH_MASK;
+               ch_count_shift = CFG_MM2S_CH_SHIFT;
+               data_xfer_mode = CFG_MM2S_XFER_MASK;
+               data_xfer_shift = CFG_MM2S_XFER_SHIFT;
+               data_format_mode = CFG_MM2S_PKG_MASK;
+               stream_data->mmio = adata->mmio + XLNX_MM2S_OFFSET;
+               adata->play_stream = substream;
+
+       } else {
+               ch_count_mask = CFG_S2MM_CH_MASK;
+               ch_count_shift = CFG_S2MM_CH_SHIFT;
+               data_xfer_mode = CFG_S2MM_XFER_MASK;
+               data_xfer_shift = CFG_S2MM_XFER_SHIFT;
+               data_format_mode = CFG_S2MM_PKG_MASK;
+               stream_data->mmio = adata->mmio + XLNX_S2MM_OFFSET;
+               adata->capture_stream = substream;
+       }
+
+       val = readl(adata->mmio + XLNX_AUD_CORE_CONFIG);
+
+       if (!(val & data_format_mode))
+               stream_data->interleaved = true;
+
+       stream_data->xfer_mode = (val & data_xfer_mode) >> data_xfer_shift;
+       stream_data->ch_limit = (val & ch_count_mask) >> ch_count_shift;
+       dev_info(component->dev,
+                "stream %d : format = %d mode = %d ch_limit = %d\n",
+                substream->stream, stream_data->interleaved,
+                stream_data->xfer_mode, stream_data->ch_limit);
+
+       snd_soc_set_runtime_hwparams(substream, &xlnx_pcm_hardware);
+       runtime->private_data = stream_data;
+
+       /* Resize the period size divisible by 64 */
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+       if (err) {
+               dev_err(component->dev,
+                       "unable to set constraint on period bytes\n");
+               return err;
+       }
+
+       /* enable DMA IOC irq */
+       val = readl(stream_data->mmio + XLNX_AUD_CTRL);
+       val |= AUD_CTRL_IOC_IRQ_MASK;
+       writel(val, stream_data->mmio + XLNX_AUD_CTRL);
+
+       return 0;
+}
+
+static int xlnx_formatter_pcm_close(struct snd_pcm_substream *substream)
+{
+       int ret;
+       struct xlnx_pcm_stream_param *stream_data =
+                       substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *prtd = substream->private_data;
+       struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
+                                                                   DRV_NAME);
+
+       ret = xlnx_formatter_pcm_reset(stream_data->mmio);
+       if (ret) {
+               dev_err(component->dev, "audio formatter reset failed\n");
+               goto err_reset;
+       }
+       xlnx_formatter_disable_irqs(stream_data->mmio, substream->stream);
+
+err_reset:
+       kfree(stream_data);
+       return 0;
+}
+
+static snd_pcm_uframes_t
+xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       u32 pos;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct xlnx_pcm_stream_param *stream_data = runtime->private_data;
+
+       pos = readl(stream_data->mmio + XLNX_AUD_XFER_COUNT);
+
+       if (pos >= stream_data->buffer_size)
+               pos = 0;
+
+       return bytes_to_frames(runtime, pos);
+}
+
+static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample;
+       int status;
+       u64 size;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct xlnx_pcm_stream_param *stream_data = runtime->private_data;
+
+       active_ch = params_channels(params);
+       if (active_ch > stream_data->ch_limit)
+               return -EINVAL;
+
+       size = params_buffer_bytes(params);
+       status = snd_pcm_lib_malloc_pages(substream, size);
+       if (status < 0)
+               return status;
+
+       stream_data->buffer_size = size;
+
+       low = lower_32_bits(substream->dma_buffer.addr);
+       high = upper_32_bits(substream->dma_buffer.addr);
+       writel(low, stream_data->mmio + XLNX_AUD_BUFF_ADDR_LSB);
+       writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB);
+
+       val = readl(stream_data->mmio + XLNX_AUD_CTRL);
+       bits_per_sample = params_width(params);
+       switch (bits_per_sample) {
+       case 8:
+               val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT);
+               break;
+       case 16:
+               val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT);
+               break;
+       case 20:
+               val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT);
+               break;
+       case 24:
+               val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT);
+               break;
+       case 32:
+               val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT;
+       writel(val, stream_data->mmio + XLNX_AUD_CTRL);
+
+       val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT)
+               | params_period_bytes(params);
+       writel(val, stream_data->mmio + XLNX_AUD_PERIOD_CONFIG);
+       bytes_per_ch = DIV_ROUND_UP(params_period_bytes(params), active_ch);
+       writel(bytes_per_ch, stream_data->mmio + XLNX_BYTES_PER_CH);
+
+       return 0;
+}
+
+static int xlnx_formatter_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream,
+                                     int cmd)
+{
+       u32 val;
+       struct xlnx_pcm_stream_param *stream_data =
+                       substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               val = readl(stream_data->mmio + XLNX_AUD_CTRL);
+               val |= AUD_CTRL_DMA_EN_MASK;
+               writel(val, stream_data->mmio + XLNX_AUD_CTRL);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               val = readl(stream_data->mmio + XLNX_AUD_CTRL);
+               val &= ~AUD_CTRL_DMA_EN_MASK;
+               writel(val, stream_data->mmio + XLNX_AUD_CTRL);
+               break;
+       }
+
+       return 0;
+}
+
+static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
+                                                                   DRV_NAME);
+       return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+                       SNDRV_DMA_TYPE_DEV, component->dev,
+                       xlnx_pcm_hardware.buffer_bytes_max,
+                       xlnx_pcm_hardware.buffer_bytes_max);
+}
+
+static const struct snd_pcm_ops xlnx_formatter_pcm_ops = {
+       .open = xlnx_formatter_pcm_open,
+       .close = xlnx_formatter_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = xlnx_formatter_pcm_hw_params,
+       .hw_free = xlnx_formatter_pcm_hw_free,
+       .trigger = xlnx_formatter_pcm_trigger,
+       .pointer = xlnx_formatter_pcm_pointer,
+};
+
+static const struct snd_soc_component_driver xlnx_asoc_component = {
+       .name = DRV_NAME,
+       .ops = &xlnx_formatter_pcm_ops,
+       .pcm_new = xlnx_formatter_pcm_new,
+};
+
+static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
+{
+       int ret;
+       u32 val;
+       struct xlnx_pcm_drv_data *aud_drv_data;
+       struct resource *res;
+       struct device *dev = &pdev->dev;
+
+       aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL);
+       if (!aud_drv_data)
+               return -ENOMEM;
+
+       aud_drv_data->axi_clk = devm_clk_get(dev, "s_axi_lite_aclk");
+       if (IS_ERR(aud_drv_data->axi_clk)) {
+               ret = PTR_ERR(aud_drv_data->axi_clk);
+               dev_err(dev, "failed to get s_axi_lite_aclk(%d)\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(aud_drv_data->axi_clk);
+       if (ret) {
+               dev_err(dev,
+                       "failed to enable s_axi_lite_aclk(%d)\n", ret);
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "audio formatter node:addr to resource failed\n");
+               ret = -ENXIO;
+               goto clk_err;
+       }
+       aud_drv_data->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(aud_drv_data->mmio)) {
+               dev_err(dev, "audio formatter ioremap failed\n");
+               ret = PTR_ERR(aud_drv_data->mmio);
+               goto clk_err;
+       }
+
+       val = readl(aud_drv_data->mmio + XLNX_AUD_CORE_CONFIG);
+       if (val & AUD_CFG_MM2S_MASK) {
+               aud_drv_data->mm2s_presence = true;
+               ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
+                                              XLNX_MM2S_OFFSET);
+               if (ret) {
+                       dev_err(dev, "audio formatter reset failed\n");
+                       goto clk_err;
+               }
+               xlnx_formatter_disable_irqs(aud_drv_data->mmio +
+                                           XLNX_MM2S_OFFSET,
+                                           SNDRV_PCM_STREAM_PLAYBACK);
+
+               aud_drv_data->mm2s_irq = platform_get_irq_byname(pdev,
+                                                                "irq_mm2s");
+               if (aud_drv_data->mm2s_irq < 0) {
+                       dev_err(dev, "xlnx audio mm2s irq resource failed\n");
+                       ret = aud_drv_data->mm2s_irq;
+                       goto clk_err;
+               }
+               ret = devm_request_irq(dev, aud_drv_data->mm2s_irq,
+                                      xlnx_mm2s_irq_handler, 0,
+                                      "xlnx_formatter_pcm_mm2s_irq", dev);
+               if (ret) {
+                       dev_err(dev, "xlnx audio mm2s irq request failed\n");
+                       goto clk_err;
+               }
+       }
+       if (val & AUD_CFG_S2MM_MASK) {
+               aud_drv_data->s2mm_presence = true;
+               ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
+                                              XLNX_S2MM_OFFSET);
+               if (ret) {
+                       dev_err(dev, "audio formatter reset failed\n");
+                       goto clk_err;
+               }
+               xlnx_formatter_disable_irqs(aud_drv_data->mmio +
+                                           XLNX_S2MM_OFFSET,
+                                           SNDRV_PCM_STREAM_CAPTURE);
+
+               aud_drv_data->s2mm_irq = platform_get_irq_byname(pdev,
+                                                                "irq_s2mm");
+               if (aud_drv_data->s2mm_irq < 0) {
+                       dev_err(dev, "xlnx audio s2mm irq resource failed\n");
+                       ret = aud_drv_data->s2mm_irq;
+                       goto clk_err;
+               }
+               ret = devm_request_irq(dev, aud_drv_data->s2mm_irq,
+                                      xlnx_s2mm_irq_handler, 0,
+                                      "xlnx_formatter_pcm_s2mm_irq",
+                                      dev);
+               if (ret) {
+                       dev_err(dev, "xlnx audio s2mm irq request failed\n");
+                       goto clk_err;
+               }
+       }
+
+       dev_set_drvdata(dev, aud_drv_data);
+
+       ret = devm_snd_soc_register_component(dev, &xlnx_asoc_component,
+                                             NULL, 0);
+       if (ret) {
+               dev_err(dev, "pcm platform device register failed\n");
+               goto clk_err;
+       }
+
+       return 0;
+
+clk_err:
+       clk_disable_unprepare(aud_drv_data->axi_clk);
+       return ret;
+}
+
+static int xlnx_formatter_pcm_remove(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct xlnx_pcm_drv_data *adata = dev_get_drvdata(&pdev->dev);
+
+       if (adata->s2mm_presence)
+               ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_S2MM_OFFSET);
+
+       /* Try MM2S reset, even if S2MM  reset fails */
+       if (adata->mm2s_presence)
+               ret = xlnx_formatter_pcm_reset(adata->mmio + XLNX_MM2S_OFFSET);
+
+       if (ret)
+               dev_err(&pdev->dev, "audio formatter reset failed\n");
+
+       clk_disable_unprepare(adata->axi_clk);
+       return ret;
+}
+
+static const struct of_device_id xlnx_formatter_pcm_of_match[] = {
+       { .compatible = "xlnx,audio-formatter-1.0"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, xlnx_formatter_pcm_of_match);
+
+static struct platform_driver xlnx_formatter_pcm_driver = {
+       .probe  = xlnx_formatter_pcm_probe,
+       .remove = xlnx_formatter_pcm_remove,
+       .driver = {
+               .name   = DRV_NAME,
+               .of_match_table = xlnx_formatter_pcm_of_match,
+       },
+};
+
+module_platform_driver(xlnx_formatter_pcm_driver);
+MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>");
+MODULE_LICENSE("GPL v2");
index d4ae9eff41ce51cb2f2777263b5190edf49c3e49..8b353166ad447699480baae7944fe087a3bb79d8 100644 (file)
@@ -1,12 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
-/*
- * Xilinx ASoC I2S audio support
- *
- * Copyright (C) 2018 Xilinx, Inc.
- *
- * Author: Praveen Vuppala <praveenv@xilinx.com>
- * Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
- */
+//
+// Xilinx ASoC I2S audio support
+//
+// Copyright (C) 2018 Xilinx, Inc.
+//
+// Author: Praveen Vuppala <praveenv@xilinx.com>
+// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
 
 #include <linux/io.h>
 #include <linux/module.h>