]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next
authorMark Brown <broonie@kernel.org>
Mon, 3 Jul 2017 15:15:04 +0000 (16:15 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 3 Jul 2017 15:15:04 +0000 (16:15 +0100)
28 files changed:
Documentation/devicetree/bindings/sound/audio-graph-card.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/simple-scu-card.txt
drivers/of/base.c
include/linux/of_graph.h
include/sound/simple_card_utils.h
include/sound/soc.h
sound/soc/generic/Kconfig
sound/soc/generic/Makefile
sound/soc/generic/audio-graph-card.c [new file with mode: 0644]
sound/soc/generic/audio-graph-scu-card.c [new file with mode: 0644]
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-card.c
sound/soc/generic/simple-scu-card.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/cmd.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ctu.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c
sound/soc/soc-core.c

diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt
new file mode 100644 (file)
index 0000000..bac4b1b
--- /dev/null
@@ -0,0 +1,124 @@
+Audio Graph Card:
+
+Audio Graph Card specifies audio DAI connections of SoC <-> codec.
+It is based on common bindings for device graphs.
+see ${LINUX}/Documentation/devicetree/bindings/graph.txt
+
+Basically, Audio Graph Card property is same as Simple Card.
+see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
+
+Below are same as Simple-Card.
+
+- label
+- dai-format
+- frame-master
+- bitclock-master
+- bitclock-inversion
+- frame-inversion
+- dai-tdm-slot-num
+- dai-tdm-slot-width
+- clocks / system-clock-frequency
+
+Required properties:
+
+- compatible                           : "audio-graph-card";
+- dais                                 : list of CPU DAI port{s}
+
+Example: Single DAI case
+
+       sound_card {
+               compatible = "audio-graph-card";
+
+               dais = <&cpu_port>;
+       };
+
+       dai-controller {
+               ...
+               cpu_port: port {
+                       cpu_endpoint: endpoint {
+                               remote-endpoint = <&codec_endpoint>;
+
+                               dai-format = "left_j";
+                               ...
+                       };
+               };
+       };
+
+       audio-codec {
+               ...
+               port {
+                       codec_endpoint: endpoint {
+                               remote-endpoint = <&cpu_endpoint>;
+                       };
+               };
+       };
+
+Example: Multi DAI case
+
+       sound-card {
+               compatible = "audio-graph-card";
+
+               label = "sound-card";
+
+               dais = <&cpu_port0
+                       &cpu_port1
+                       &cpu_port2>;
+       };
+
+       audio-codec@0 {
+               ...
+               port {
+                       codec0_endpoint: endpoint {
+                               remote-endpoint = <&cpu_endpoint0>;
+                       };
+               };
+       };
+
+       audio-codec@1 {
+               ...
+               port {
+                       codec1_endpoint: endpoint {
+                               remote-endpoint = <&cpu_endpoint1>;
+                       };
+               };
+       };
+
+       audio-codec@2 {
+               ...
+               port {
+                       codec2_endpoint: endpoint {
+                               remote-endpoint = <&cpu_endpoint2>;
+                       };
+               };
+       };
+
+       dai-controller {
+               ...
+               ports {
+                       cpu_port0: port@0 {
+                               cpu_endpoint0: endpoint {
+                                       remote-endpoint = <&codec0_endpoint>;
+
+                                       dai-format = "left_j";
+                                       ...
+                               };
+                       };
+                       cpu_port1: port@1 {
+                               cpu_endpoint1: endpoint {
+                                       remote-endpoint = <&codec1_endpoint>;
+
+                                       dai-format = "i2s";
+                                       ...
+                               };
+                       };
+                       cpu_port2: port@2 {
+                               cpu_endpoint2: endpoint {
+                                       remote-endpoint = <&codec2_endpoint>;
+
+                                       dai-format = "i2s";
+                                       ...
+                               };
+                       };
+               };
+       };
+
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt
new file mode 100644 (file)
index 0000000..b63c559
--- /dev/null
@@ -0,0 +1,117 @@
+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",
+                       "codec Playback", "DAI1 Playback";
+               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";
+               prefix = "codec";
+               routing = "codec Playback", "DAI0 Playback",
+                       "codec Playback", "DAI1 Playback";
+               convert-rate = <48000>;
+
+               dais = <&cpu_port0
+                       &cpu_port1>;
+       };
+
+       audio-codec {
+               ...
+
+               port {
+                       codec_endpoint: endpoint {
+                               remote-endpoint = <&cpu_endpoint0>;
+                       };
+               };
+       };
+
+       dai-controller {
+               ...
+               ports {
+                       cpu_port0: port {
+                               cpu_endpoint0: endpoint {
+                                       remote-endpoint = <&codec_endpoint>;
+
+                                       dai-format = "left_j";
+                                       ...
+                               };
+                       };
+                       cpu_port1: port {
+                               cpu_endpoint1: endpoint {
+                                       dai-format = "left_j";
+                                       ...
+                               };
+                       };
+               };
+       };
index 15a7316e4c913c837fc8f03358c94a466447fe38..7246bb268bf9bab6a49c78af481455a8fb744e42 100644 (file)
@@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
 **     Asynchronous mode
 ------------------
 
-You need to use "renesas,rsrc-card" sound card for it.
+You need to use "simple-scu-audio-card" sound card for it.
 example)
 
        sound {
-               compatible = "renesas,rsrc-card";
+               compatible = "simple-scu-audio-card";
                ...
                /*
                 * SRC Asynchronous mode setting
@@ -97,12 +97,12 @@ example)
                 * Inputed 48kHz data will be converted to
                 * system specified Hz
                 */
-               convert-rate = <48000>;
+               simple-audio-card,convert-rate = <48000>;
                ...
-               cpu {
+               simple-audio-card,cpu {
                        sound-dai = <&rcar_sound>;
                };
-               codec {
+               simple-audio-card,codec {
                        ...
                };
        };
@@ -141,23 +141,23 @@ For more detail information, see below
        ${LINUX}/sound/soc/sh/rcar/ctu.c
         - comment of header
 
-You need to use "renesas,rsrc-card" sound card for it.
+You need to use "simple-scu-audio-card" sound card for it.
 example)
 
        sound {
-               compatible = "renesas,rsrc-card";
+               compatible = "simple-scu-audio-card";
                ...
                /*
                 * CTU setting
                 * All input data will be converted to 2ch
                 * as output data
                 */
-               convert-channels = <2>;
+               simple-audio-card,convert-channels = <2>;
                ...
-               cpu {
+               simple-audio-card,cpu {
                        sound-dai = <&rcar_sound>;
                };
-               codec {
+               simple-audio-card,codec {
                        ...
                };
        };
@@ -190,22 +190,22 @@ and these sounds will be merged by MIX.
        aplay -D plughw:0,0 xxxx.wav &
        aplay -D plughw:0,1 yyyy.wav
 
-You need to use "renesas,rsrc-card" sound card for it.
+You need to use "simple-scu-audio-card" sound card for it.
 Ex)
        [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
                                    |
        [MEM] -> [SRC2] -> [CTU03] -+
 
        sound {
-               compatible = "renesas,rsrc-card";
+               compatible = "simple-scu-audio-card";
                ...
-               cpu@0 {
+               simple-audio-card,cpu@0 {
                        sound-dai = <&rcar_sound 0>;
                };
-               cpu@1 {
+               simple-audio-card,cpu@1 {
                        sound-dai = <&rcar_sound 1>;
                };
-               codec {
+               simple-audio-card,codec {
                        ...
                };
        };
@@ -368,6 +368,10 @@ Required properties:
                                  see below for detail.
 - #sound-dai-cells             : it must be 0 if your system is using single DAI
                                  it must be 1 if your system is using multi  DAI
+- clocks                       : References to SSI/SRC/MIX/CTU/DVC/AUDIO_CLK clocks.
+- clock-names                  : List of necessary clock names.
+                                 "ssi-all", "ssi.X", "src.X", "mix.X", "ctu.X",
+                                 "dvc.X", "clk_a", "clk_b", "clk_c", "clk_i"
 
 Optional properties:
 - #clock-cells                 : it must be 0 if your system has audio_clkout
@@ -375,6 +379,9 @@ Optional properties:
 - clock-frequency              : for all audio_clkout0/1/2/3
 - clkout-lr-asynchronous       : boolean property. it indicates that audio_clkoutn
                                  is asynchronizes with lr-clock.
+- resets                       : References to SSI resets.
+- reset-names                  : List of valid reset names.
+                                 "ssi-all", "ssi.X"
 
 SSI subnode properties:
 - interrupts                   : Should contain SSI interrupt for PIO transfer
index d6fe47ed09af4bfb22698f92c9f77499581f8832..327d229a51b27e126fad58678a75dd2c803fed54 100644 (file)
@@ -1,35 +1,29 @@
-ASoC simple SCU Sound Card
+ASoC Simple SCU Sound Card
 
-Simple-Card specifies audio DAI connections of SoC <-> codec.
+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               : User specified audio sound card name, one string
-                                         property.
-- simple-audio-card,cpu                        : CPU   sub-node
-- simple-audio-card,codec              : CODEC sub-node
+- 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             : CPU/CODEC common audio format.
-                                         "i2s", "right_j", "left_j" , "dsp_a"
-                                         "dsp_b", "ac97", "pdm", "msb", "lsb"
-- simple-audio-card,frame-master       : Indicates dai-link frame master.
-                                         phandle to a cpu or codec subnode.
-- simple-audio-card,bitclock-master    : Indicates dai-link bit clock master.
-                                         phandle to a cpu or codec subnode.
-- simple-audio-card,bitclock-inversion : bool property. Add this if the
-                                         dai-link uses bit clock inversion.
-- simple-audio-card,frame-inversion    : bool property. Add this if the
-                                         dai-link uses frame clock inversion.
+- 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 audio-routing
+- simple-audio-card,prefix             : see routing
 - 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.
@@ -38,32 +32,23 @@ Optional subnode properties:
 
 Required CPU/CODEC subnodes properties:
 
-- sound-dai                            : phandle and port of CPU/CODEC
+- sound-dai                            : see simple-audio-card.txt
 
 Optional CPU/CODEC subnodes properties:
 
-- clocks / system-clock-frequency      : specify subnode's clock if needed.
-                                         it can be specified via "clocks" if system has
-                                         clock node (= common clock), or "system-clock-frequency"
-                                         (if system doens't support common clock)
-                                         If a clock is specified, it is
-                                         enabled with clk_prepare_enable()
-                                         in dai startup() and disabled with
-                                         clk_disable_unprepare() in dai
-                                         shutdown().
+- clocks / system-clock-frequency      : see simple-audio-card.txt
 
-Example 1. Sampling Rate Covert
+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,format = "left_j";
        simple-audio-card,bitclock-master = <&sndcodec>;
        simple-audio-card,frame-master = <&sndcodec>;
 
-       simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */
+       simple-audio-card,convert-rate = <48000>;
 
        simple-audio-card,prefix = "ak4642";
        simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
@@ -79,20 +64,18 @@ sound {
        };
 };
 
-Example 2. 2 CPU 1 Codec
+Example 2. 2 CPU 1 Codec (Mixing)
 
 sound {
-       compatible = "renesas,rsrc-card";
-
-       card-name = "rsnd-ak4643";
-       format = "left_j";
-       bitclock-master = <&dpcmcpu>;
-       frame-master = <&dpcmcpu>;
+       compatible = "simple-scu-audio-card";
 
-       convert-rate = <48000>;  /* see audio_clk_a */
+       simple-audio-card,name = "rsnd-ak4643";
+       simple-audio-card,format = "left_j";
+       simple-audio-card,bitclock-master = <&dpcmcpu>;
+       simple-audio-card,frame-master = <&dpcmcpu>;
 
-       audio-prefix = "ak4642";
-       audio-routing = "ak4642 Playback", "DAI0 Playback",
+       simple-audio-card,prefix = "ak4642";
+       simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
                        "ak4642 Playback", "DAI1 Playback";
 
        dpcmcpu: cpu@0 {
index 28d5f53bc631cadc9af2ca4ba4c54372de47b2a2..cb1c49ae3b889e87e5c585aab3349b3b30e472b2 100644 (file)
@@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(of_phandle_iterator_init);
 
 int of_phandle_iterator_next(struct of_phandle_iterator *it)
 {
@@ -1670,6 +1671,7 @@ err:
 
        return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(of_phandle_iterator_next);
 
 int of_phandle_iterator_args(struct of_phandle_iterator *it,
                             uint32_t *args,
@@ -2484,6 +2486,41 @@ struct device_node *of_graph_get_endpoint_by_regs(
 }
 EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
 
+/**
+ * of_graph_get_remote_endpoint() - get remote endpoint node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote endpoint node associated with remote endpoint node linked
+ *        to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_endpoint(const struct device_node *node)
+{
+       /* Get remote endpoint node. */
+       return of_parse_phandle(node, "remote-endpoint", 0);
+}
+EXPORT_SYMBOL(of_graph_get_remote_endpoint);
+
+/**
+ * of_graph_get_port_parent() - get port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: device node associated with endpoint node linked
+ *        to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_port_parent(struct device_node *node)
+{
+       unsigned int depth;
+
+       /* Walk 3 levels up only if there is 'ports' node. */
+       for (depth = 3; depth && node; depth--) {
+               node = of_get_next_parent(node);
+               if (depth == 2 && of_node_cmp(node->name, "ports"))
+                       break;
+       }
+       return node;
+}
+EXPORT_SYMBOL(of_graph_get_port_parent);
+
 /**
  * of_graph_get_remote_port_parent() - get remote port's parent node
  * @node: pointer to a local endpoint device_node
@@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent(
                               const struct device_node *node)
 {
        struct device_node *np;
-       unsigned int depth;
 
        /* Get remote endpoint node. */
-       np = of_parse_phandle(node, "remote-endpoint", 0);
+       np = of_graph_get_remote_endpoint(node);
 
-       /* Walk 3 levels up only if there is 'ports' node. */
-       for (depth = 3; depth && np; depth--) {
-               np = of_get_next_parent(np);
-               if (depth == 2 && of_node_cmp(np->name, "ports"))
-                       break;
-       }
-       return np;
+       return of_graph_get_port_parent(np);
 }
 EXPORT_SYMBOL(of_graph_get_remote_port_parent);
 
@@ -2522,13 +2552,25 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
        struct device_node *np;
 
        /* Get remote endpoint node. */
-       np = of_parse_phandle(node, "remote-endpoint", 0);
+       np = of_graph_get_remote_endpoint(node);
        if (!np)
                return NULL;
        return of_get_next_parent(np);
 }
 EXPORT_SYMBOL(of_graph_get_remote_port);
 
+int of_graph_get_endpoint_count(const struct device_node *np)
+{
+       struct device_node *endpoint;
+       int num = 0;
+
+       for_each_endpoint_of_node(np, endpoint)
+               num++;
+
+       return num;
+}
+EXPORT_SYMBOL(of_graph_get_endpoint_count);
+
 /**
  * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
  * @node: pointer to parent device_node containing graph port/endpoint
index abdb02eaef0689a64fcb2acab45625057097ecda..3e058f05ab04793e81f0b40f8b9cf9400d03c824 100644 (file)
@@ -43,11 +43,15 @@ struct of_endpoint {
 #ifdef CONFIG_OF
 int of_graph_parse_endpoint(const struct device_node *node,
                                struct of_endpoint *endpoint);
+int of_graph_get_endpoint_count(const struct device_node *np);
 struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
                                        struct device_node *previous);
 struct device_node *of_graph_get_endpoint_by_regs(
                const struct device_node *parent, int port_reg, int reg);
+struct device_node *of_graph_get_remote_endpoint(
+                                       const struct device_node *node);
+struct device_node *of_graph_get_port_parent(struct device_node *node);
 struct device_node *of_graph_get_remote_port_parent(
                                        const struct device_node *node);
 struct device_node *of_graph_get_remote_port(const struct device_node *node);
@@ -61,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
        return -ENOSYS;
 }
 
+static inline int of_graph_get_endpoint_count(const struct device_node *np)
+{
+       return 0;
+}
+
 static inline struct device_node *of_graph_get_port_by_id(
                                        struct device_node *node, u32 id)
 {
@@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs(
        return NULL;
 }
 
+static inline struct device_node *of_graph_get_remote_endpoint(
+                                       const struct device_node *node)
+{
+       return NULL;
+}
+
+static inline struct device_node *of_graph_get_port_parent(
+       struct device_node *node)
+{
+       return NULL;
+}
+
 static inline struct device_node *of_graph_get_remote_port_parent(
                                        const struct device_node *node)
 {
index af58d236297554dc2ad87bcd9b6424fc59676d7a..108cae459ed097c04c3e39a4b10882ccd6f3c190 100644 (file)
@@ -35,13 +35,16 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
                                     char *prefix);
 
 #define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai)                \
-       asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai)
+       asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
+                                  dai_link->cpu_dai_name)
 #define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai)      \
-       asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai)
+       asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
+                                  dai_link->codec_dai_name)
 int asoc_simple_card_parse_clk(struct device *dev,
                               struct device_node *node,
                               struct device_node *dai_of_node,
-                              struct asoc_simple_dai *simple_dai);
+                              struct asoc_simple_dai *simple_dai,
+                              const char *name);
 
 #define asoc_simple_card_parse_cpu(node, dai_link,                             \
                                   list_name, cells_name, is_single_link)       \
@@ -60,6 +63,16 @@ int asoc_simple_card_parse_dai(struct device_node *node,
                                  const char *cells_name,
                                  int *is_single_links);
 
+#define asoc_simple_card_parse_graph_cpu(ep, dai_link)                 \
+       asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node,    \
+                                        &dai_link->cpu_dai_name)
+#define asoc_simple_card_parse_graph_codec(ep, dai_link)               \
+       asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node,  \
+                                        &dai_link->codec_dai_name)
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
+                                    struct device_node **endpoint_np,
+                                    const char **dai_name);
+
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
                              struct asoc_simple_dai *simple_dai);
 
index 5170fd81e1fd0886c465225b72748c44792240d5..9c94b97c17f8ebc830f9ccac9e00c9a0b0e1d463 100644 (file)
@@ -803,6 +803,8 @@ struct snd_soc_component_driver {
        int (*of_xlate_dai_name)(struct snd_soc_component *component,
                                 struct of_phandle_args *args,
                                 const char **dai_name);
+       int (*of_xlate_dai_id)(struct snd_soc_component *comment,
+                              struct device_node *endpoint);
        void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
                int subseq);
        int (*stream_event)(struct snd_soc_component *, int event);
@@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                                     const char *prefix,
                                     struct device_node **bitclkmaster,
                                     struct device_node **framemaster);
+int snd_soc_get_dai_id(struct device_node *ep);
 int snd_soc_get_dai_name(struct of_phandle_args *args,
                         const char **dai_name);
 int snd_soc_of_get_dai_name(struct device_node *of_node,
index d023959b8cd68e4de9172bce1c2816729d82bc79..c954be0a0f96b052496dae219173cc46e9e14993 100644 (file)
@@ -14,3 +14,20 @@ config SND_SIMPLE_SCU_CARD
        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
+       select SND_SIMPLE_CARD_UTILS
+       help
+         This option enables generic simple simple sound card support
+         with OF-graph DT bindings.
+
+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 ee750f3023baf8acdb8ecd3b2dcf7bc943d6dcd9..9e000523a3b49a4b0dbf10e77fea3f07c5030262 100644 (file)
@@ -1,7 +1,11 @@
 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
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
new file mode 100644 (file)
index 0000000..0180b28
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * ASoC audio graph sound card support
+ *
+ * Copyright (C) 2016 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on ${LINUX}/sound/soc/generic/simple-card.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#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;
+       } *dai_props;
+       struct snd_soc_dai_link *dai_link;
+};
+
+#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))
+
+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;
+
+       ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(dai_props->codec_dai.clk);
+       if (ret)
+               clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+       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);
+
+       clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+       clk_disable_unprepare(dai_props->codec_dai.clk);
+}
+
+static 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 snd_soc_dai *codec = rtd->codec_dai;
+       struct snd_soc_dai *cpu = rtd->cpu_dai;
+       struct graph_dai_props *dai_props =
+               graph_priv_to_props(priv, rtd->num);
+       int ret;
+
+       ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
+       if (ret < 0)
+               return ret;
+
+       ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
+                                       struct graph_card_data *priv,
+                                       int idx)
+{
+       struct device *dev = graph_priv_to_dev(priv);
+       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
+       struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
+       struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+       struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
+       struct snd_soc_card *card = graph_priv_to_card(priv);
+       struct device_node *cpu_ep    = of_get_next_child(cpu_port, NULL);
+       struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+       struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
+       int ret;
+
+       if (rcpu_ep != cpu_ep) {
+               dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n",
+                       cpu_ep->name, codec_ep->name, rcpu_ep->name);
+               ret = -EINVAL;
+               goto dai_link_of_err;
+       }
+
+       ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
+                                           NULL, &dai_link->dai_fmt);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       /*
+        * we need to consider "mclk-fs" around here
+        * see simple-card
+        */
+
+       ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = snd_soc_of_parse_tdm_slot(cpu_ep,
+                                       &cpu_dai->tx_slot_mask,
+                                       &cpu_dai->rx_slot_mask,
+                                       &cpu_dai->slots,
+                                       &cpu_dai->slot_width);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = snd_soc_of_parse_tdm_slot(codec_ep,
+                                       &codec_dai->tx_slot_mask,
+                                       &codec_dai->rx_slot_mask,
+                                       &codec_dai->slots,
+                                       &codec_dai->slot_width);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_canonicalize_dailink(dai_link);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+                                               "%s-%s",
+                                               dai_link->cpu_dai_name,
+                                               dai_link->codec_dai_name);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       dai_link->ops = &asoc_graph_card_ops;
+       dai_link->init = asoc_graph_card_dai_init;
+
+       asoc_simple_card_canonicalize_cpu(dai_link,
+                                         card->num_links == 1);
+
+dai_link_of_err:
+       of_node_put(cpu_ep);
+       of_node_put(rcpu_ep);
+       of_node_put(codec_ep);
+
+       return ret;
+}
+
+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;
+       int rc, idx = 0;
+       int ret;
+
+       /*
+        * we need to consider "widgets", "routing", "mclk-fs" around here
+        * see simple-card
+        */
+
+       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+               ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
+               of_node_put(it.node);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return asoc_simple_card_parse_card_name(card, NULL);
+}
+
+static int asoc_graph_get_dais_count(struct device *dev)
+{
+       struct of_phandle_iterator it;
+       struct device_node *node = dev->of_node;
+       int count = 0;
+       int rc;
+
+       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+               count++;
+               of_node_put(it.node);
+       }
+
+       return count;
+}
+
+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 device *dev = &pdev->dev;
+       struct snd_soc_card *card;
+       int num, ret;
+
+       /* Allocate the private data and the DAI link array */
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       num = asoc_graph_get_dais_count(dev);
+       if (num == 0)
+               return -EINVAL;
+
+       dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
+       dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
+       if (!dai_props || !dai_link)
+               return -ENOMEM;
+
+       priv->dai_props                 = dai_props;
+       priv->dai_link                  = dai_link;
+
+       /* 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 = num;
+
+       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-card", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
+
+static struct platform_driver asoc_graph_card = {
+       .driver = {
+               .name = "asoc-audio-graph-card",
+               .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-card");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c
new file mode 100644 (file)
index 0000000..0066102
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * 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
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#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 snd_soc_codec_conf codec_conf;
+       struct asoc_simple_dai *dai_props;
+       struct snd_soc_dai_link *dai_link;
+       u32 convert_rate;
+       u32 convert_channels;
+};
+
+#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))
+
+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 asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
+
+       return clk_prepare_enable(dai_props->clk);
+}
+
+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 asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
+
+       clk_disable_unprepare(dai_props->clk);
+}
+
+static 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 snd_soc_dai *dai;
+       struct snd_soc_dai_link *dai_link;
+       struct asoc_simple_dai *dai_props;
+       int num = rtd->num;
+
+       dai_link        = graph_priv_to_link(priv, num);
+       dai_props       = graph_priv_to_props(priv, num);
+       dai             = dai_link->dynamic ?
+                               rtd->cpu_dai :
+                               rtd->codec_dai;
+
+       return asoc_simple_card_init_dai(dai, dai_props);
+}
+
+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 snd_interval *rate = hw_param_interval(params,
+                                                     SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                                         SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       if (priv->convert_rate)
+               rate->min =
+               rate->max = priv->convert_rate;
+
+       if (priv->convert_channels)
+               channels->min =
+               channels->max = priv->convert_channels;
+
+       return 0;
+}
+
+static int asoc_graph_card_dai_link_of(struct device_node *ep,
+                                      struct graph_card_data *priv,
+                                      unsigned int daifmt,
+                                      int idx, int is_fe)
+{
+       struct device *dev = graph_priv_to_dev(priv);
+       struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
+       struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, idx);
+       struct snd_soc_card *card = graph_priv_to_card(priv);
+       int ret;
+
+       if (is_fe) {
+               /* BE is dummy */
+               dai_link->codec_of_node         = NULL;
+               dai_link->codec_dai_name        = "snd-soc-dummy-dai";
+               dai_link->codec_name            = "snd-soc-dummy";
+
+               /* FE settings */
+               dai_link->dynamic               = 1;
+               dai_link->dpcm_merged_format    = 1;
+
+               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_props);
+               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,
+                                       (card->num_links - 1) == 1);
+       } else {
+               /* 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;
+
+               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_props);
+               if (ret < 0)
+                       return ret;
+
+               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+                                                       "be.%s",
+                                                       dai_link->codec_dai_name);
+               if (ret < 0)
+                       return ret;
+
+               snd_soc_of_parse_audio_prefix(card,
+                                             &priv->codec_conf,
+                                             dai_link->codec_of_node,
+                                             "prefix");
+       }
+
+       ret = snd_soc_of_parse_tdm_slot(ep,
+                                       &dai_props->tx_slot_mask,
+                                       &dai_props->rx_slot_mask,
+                                       &dai_props->slots,
+                                       &dai_props->slot_width);
+       if (ret)
+               return ret;
+
+       ret = asoc_simple_card_canonicalize_dailink(dai_link);
+       if (ret < 0)
+               return ret;
+
+       dai_link->dai_fmt               = daifmt;
+       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 *rcpu_ep;
+       unsigned int daifmt = 0;
+       int dai_idx, ret;
+       int rc, codec;
+
+       if (!node)
+               return -EINVAL;
+
+       /*
+        * we need to consider "widgets", "mclk-fs" around here
+        * see simple-card
+        */
+
+       ret = snd_soc_of_parse_audio_routing(card, "routing");
+       if (ret)
+               return ret;
+
+       /* sampling rate convert */
+       of_property_read_u32(node, "convert-rate", &priv->convert_rate);
+
+       /* channels transfer */
+       of_property_read_u32(node, "convert-channels", &priv->convert_channels);
+
+       /*
+        * it supports multi CPU, single CODEC only here
+        * see asoc_graph_get_dais_count
+        */
+
+       /* find 1st codec */
+       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);
+               rcpu_ep  = of_graph_get_remote_endpoint(codec_ep);
+
+               of_node_put(cpu_port);
+               of_node_put(cpu_ep);
+               of_node_put(codec_ep);
+               of_node_put(rcpu_ep);
+
+               if (!codec_ep)
+                       continue;
+
+               if (rcpu_ep != cpu_ep) {
+                       dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n",
+                               cpu_ep->name, codec_ep->name, rcpu_ep->name);
+                       ret = -EINVAL;
+                       goto parse_of_err;
+               }
+
+               ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
+                                                           NULL, &daifmt);
+               if (ret < 0)
+                       goto parse_of_err;
+       }
+
+       dai_idx = 0;
+       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);
+
+                       of_node_put(cpu_port);
+                       of_node_put(cpu_ep);
+                       of_node_put(codec_ep);
+
+                       if (codec) {
+                               if (!codec_ep)
+                                       continue;
+
+                               /* Back-End (= Codec) */
+                               ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0);
+                               if (ret < 0)
+                                       goto parse_of_err;
+                       } else {
+                               /* Front-End (= CPU) */
+                               ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1);
+                               if (ret < 0)
+                                       goto parse_of_err;
+                       }
+               }
+       }
+
+       ret = asoc_simple_card_parse_card_name(card, NULL);
+       if (ret)
+               goto parse_of_err;
+
+       dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
+       dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
+
+       ret = 0;
+
+parse_of_err:
+       return ret;
+}
+
+static int asoc_graph_get_dais_count(struct device *dev)
+{
+       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;
+       int count = 0;
+       int rc;
+
+       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);
+
+               of_node_put(cpu_port);
+               of_node_put(cpu_ep);
+               of_node_put(codec_ep);
+
+               if (cpu_ep)
+                       count++;
+               if (codec_ep)
+                       count++;
+       }
+
+       return count;
+}
+
+static int asoc_graph_card_probe(struct platform_device *pdev)
+{
+       struct graph_card_data *priv;
+       struct snd_soc_dai_link *dai_link;
+       struct asoc_simple_dai *dai_props;
+       struct device *dev = &pdev->dev;
+       struct snd_soc_card *card;
+       int num, ret;
+
+       /* Allocate the private data and the DAI link array */
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       num = asoc_graph_get_dais_count(dev);
+       if (num == 0)
+               return -EINVAL;
+
+       dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
+       dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
+       if (!dai_props || !dai_link)
+               return -ENOMEM;
+
+       priv->dai_props                 = dai_props;
+       priv->dai_link                  = dai_link;
+
+       /* 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         = num;
+       card->codec_conf        = &priv->codec_conf;
+       card->num_configs       = 1;
+
+       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",
+               .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 343b291fc3725f46b4d5270b65d5100ca083ef61..d9d8b8a58348e27304a05763e42c12d168134c0a 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <sound/simple_card_utils.h>
 
 int asoc_simple_card_parse_daifmt(struct device *dev,
@@ -20,14 +21,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
 {
        struct device_node *bitclkmaster = NULL;
        struct device_node *framemaster = NULL;
-       int prefix_len = prefix ? strlen(prefix) : 0;
        unsigned int daifmt;
 
        daifmt = snd_soc_of_parse_daifmt(node, prefix,
                                         &bitclkmaster, &framemaster);
        daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
 
-       if (prefix_len && !bitclkmaster && !framemaster) {
+       if (!bitclkmaster && !framemaster) {
                /*
                 * No dai-link level and master setting was not found from
                 * sound node level, revert back to legacy DT parsing and
@@ -51,6 +51,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
 
        *retfmt = daifmt;
 
+       dev_dbg(dev, "format : %04x\n", daifmt);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
@@ -72,6 +74,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
 
                dai_link->name          = name;
                dai_link->stream_name   = name;
+
+               dev_dbg(dev, "name : %s\n", name);
        }
 
        return ret;
@@ -81,19 +85,27 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
 int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
                                     char *prefix)
 {
-       char prop[128];
        int ret;
 
-       snprintf(prop, sizeof(prop), "%sname", prefix);
+       if (!prefix)
+               prefix = "";
 
        /* Parse the card name from DT */
-       ret = snd_soc_of_parse_card_name(card, prop);
-       if (ret < 0)
-               return ret;
+       ret = snd_soc_of_parse_card_name(card, "label");
+       if (ret < 0) {
+               char prop[128];
+
+               snprintf(prop, sizeof(prop), "%sname", prefix);
+               ret = snd_soc_of_parse_card_name(card, prop);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (!card->name && card->dai_link)
                card->name = card->dai_link->name;
 
+       dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
@@ -101,7 +113,8 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
 int asoc_simple_card_parse_clk(struct device *dev,
                               struct device_node *node,
                               struct device_node *dai_of_node,
-                              struct asoc_simple_dai *simple_dai)
+                              struct asoc_simple_dai *simple_dai,
+                              const char *name)
 {
        struct clk *clk;
        u32 val;
@@ -124,6 +137,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
                        simple_dai->sysclk = clk_get_rate(clk);
        }
 
+       dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
@@ -165,6 +180,71 @@ int asoc_simple_card_parse_dai(struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
 
+static int asoc_simple_card_get_dai_id(struct device_node *ep)
+{
+       struct device_node *node;
+       struct device_node *endpoint;
+       int i, id;
+       int ret;
+
+       ret = snd_soc_get_dai_id(ep);
+       if (ret != -ENOTSUPP)
+               return ret;
+
+       node = of_graph_get_port_parent(ep);
+
+       /*
+        * Non HDMI sound case, counting port/endpoint on its DT
+        * is enough. Let's count it.
+        */
+       i = 0;
+       id = -1;
+       for_each_endpoint_of_node(node, endpoint) {
+               if (endpoint == ep)
+                       id = i;
+               i++;
+       }
+       if (id < 0)
+               return -ENODEV;
+
+       return id;
+}
+
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
+                                    struct device_node **dai_of_node,
+                                    const char **dai_name)
+{
+       struct device_node *node;
+       struct of_phandle_args args;
+       int ret;
+
+       if (!ep)
+               return 0;
+       if (!dai_name)
+               return 0;
+
+       /*
+        * of_graph_get_port_parent() will call
+        * of_node_put(). So, call of_node_get() here
+        */
+       of_node_get(ep);
+       node = of_graph_get_port_parent(ep);
+
+       /* Get dai->name */
+       args.np         = node;
+       args.args[0]    = asoc_simple_card_get_dai_id(ep);
+       args.args_count = (of_graph_get_endpoint_count(node) > 1);
+
+       ret = snd_soc_get_dai_name(&args, dai_name);
+       if (ret < 0)
+               return ret;
+
+       *dai_of_node = node;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
+
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
                              struct asoc_simple_dai *simple_dai)
 {
index bc136d2bd7cdeb68b5ca7a24db94a193ede323b3..1df69701eb12885122a4155227b64aeb44bb1089 100644 (file)
@@ -301,15 +301,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        dai_link->ops = &asoc_simple_card_ops;
        dai_link->init = asoc_simple_card_dai_init;
 
-       dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
-       dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
-       dev_dbg(dev, "\tcpu : %s / %d\n",
-               dai_link->cpu_dai_name,
-               dai_props->cpu_dai.sysclk);
-       dev_dbg(dev, "\tcodec : %s / %d\n",
-               dai_link->codec_dai_name,
-               dai_props->codec_dai.sysclk);
-
        asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
 
 dai_link_of_err:
@@ -497,8 +488,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        snd_soc_card_set_drvdata(card, priv);
 
        ret = devm_snd_soc_register_card(dev, card);
-       if (ret >= 0)
-               return ret;
+       if (ret < 0)
+               goto err;
+
+       return 0;
 err:
        asoc_simple_card_clean_reference(card);
 
index dcbcab230d1b0e0f08c87ba21b5439c39b90c088..5faf5d6c48a2f89f7697be11bcbdb7b3f8390bb9 100644 (file)
@@ -189,21 +189,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
        dai_link->ops                   = &asoc_simple_card_ops;
        dai_link->init                  = asoc_simple_card_dai_init;
 
-       dev_dbg(dev, "\t%s / %04x / %d\n",
-               dai_link->name,
-               dai_link->dai_fmt,
-               dai_props->sysclk);
-
        return 0;
 }
 
-static int asoc_simple_card_parse_of(struct device_node *node,
-                                    struct simple_card_data *priv)
+static int asoc_simple_card_parse_of(struct simple_card_data *priv)
 
 {
        struct device *dev = simple_priv_to_dev(priv);
        struct device_node *np;
        struct snd_soc_card *card = simple_priv_to_card(priv);
+       struct device_node *node = dev->of_node;
        unsigned int daifmt = 0;
        bool is_fe;
        int ret, i;
@@ -246,8 +241,6 @@ static int asoc_simple_card_parse_of(struct device_node *node,
        if (ret < 0)
                return ret;
 
-       dev_dbg(dev, "New card: %s\n",
-               card->name ? card->name : "");
        dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
        dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
 
@@ -288,7 +281,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        card->codec_conf        = &priv->codec_conf;
        card->num_configs       = 1;
 
-       ret = asoc_simple_card_parse_of(np, priv);
+       ret = asoc_simple_card_parse_of(priv);
        if (ret < 0) {
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "parse error %d\n", ret);
@@ -298,8 +291,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        snd_soc_card_set_drvdata(card, priv);
 
        ret = devm_snd_soc_register_card(dev, card);
-       if (ret >= 0)
-               return ret;
+       if (ret < 0)
+               goto err;
+
+       return 0;
 err:
        asoc_simple_card_clean_reference(card);
 
index 147ebecfed9438068cb4c65560e937a34dbbe065..1aa5cd77ca24a06f75d28ed2e6aec23baf0011ba 100644 (file)
@@ -38,7 +38,7 @@ config SND_SOC_RCAR
        tristate "R-Car series SRU/SCU/SSIU/SSI support"
        depends on COMMON_CLK
        depends on OF || COMPILE_TEST
-       select SND_SIMPLE_CARD
+       select SND_SIMPLE_CARD_UTILS
        select REGMAP_MMIO
        help
          This option enables R-Car SRU/SCU/SSIU/SSI sound support
index d3b0dc145a560c8a35ddc4320810039f481db5aa..197cb3ec075f3de5905d4ccf002b0700311154e0 100644 (file)
@@ -308,23 +308,12 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
        }
 }
 
-int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
-{
-       rsnd_adg_set_ssi_clk(ssi_mod, 0);
-
-       return 0;
-}
-
-int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
-       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct clk *clk;
        int i;
-       u32 data;
-       u32 ckr = 0;
        int sel_table[] = {
                [CLKA] = 0x1,
                [CLKB] = 0x2,
@@ -338,30 +327,42 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
         * find suitable clock from
         * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
         */
-       data = 0;
        for_each_rsnd_clk(clk, adg, i) {
-               if (rate == clk_get_rate(clk)) {
-                       data = sel_table[i];
-                       goto found_clock;
-               }
+               if (rate == clk_get_rate(clk))
+                       return sel_table[i];
        }
 
        /*
         * find divided clock from BRGA/BRGB
         */
-       if (rate  == adg->rbga_rate_for_441khz) {
-               data = 0x10;
-               goto found_clock;
-       }
+       if (rate == adg->rbga_rate_for_441khz)
+               return 0x10;
 
-       if (rate == adg->rbgb_rate_for_48khz) {
-               data = 0x20;
-               goto found_clock;
-       }
+       if (rate == adg->rbgb_rate_for_48khz)
+               return 0x20;
 
        return -EIO;
+}
 
-found_clock:
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
+{
+       rsnd_adg_set_ssi_clk(ssi_mod, 0);
+
+       return 0;
+}
+
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+       int data;
+       u32 ckr = 0;
+
+       data = rsnd_adg_clk_query(priv, rate);
+       if (data < 0)
+               return data;
 
        rsnd_adg_set_ssi_clk(ssi_mod, data);
 
@@ -480,6 +481,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
        if (req_rate[0] % 48000 == 0)
                adg->flags = AUDIO_OUT_48;
 
+       if (of_get_property(np, "clkout-lr-asynchronous", NULL))
+               adg->flags = LRCLK_ASYNC;
+
        /*
         * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
         * have 44.1kHz or 48kHz base clocks for now.
@@ -555,7 +559,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
                        clk = clk_register_fixed_rate(dev, clkout_name[i],
                                                      parent_clk_name, 0,
                                                      req_rate[0]);
-                       adg->clkout[i] = ERR_PTR(-ENOENT);
                        if (!IS_ERR(clk))
                                adg->clkout[i] = clk;
                }
@@ -580,7 +583,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
-       struct device_node *np = dev->of_node;
        int ret;
 
        adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
@@ -597,9 +599,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
        rsnd_adg_get_clkin(priv, adg);
        rsnd_adg_get_clkout(priv, adg);
 
-       if (of_get_property(np, "clkout-lr-asynchronous", NULL))
-               adg->flags = LRCLK_ASYNC;
-
        priv->adg = adg;
 
        rsnd_adg_clk_enable(priv);
index d879c010cf03c4607ebdab3c854a582d816ddf62..f1d4fb5668921bfd5eafa024643cfc729c288e1e 100644 (file)
@@ -31,7 +31,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
        struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 data;
-       u32 path[] = {
+       static const u32 path[] = {
                [1] = 1 << 0,
                [5] = 1 << 8,
                [6] = 1 << 12,
@@ -71,7 +71,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
        } else {
                struct rsnd_mod *src = rsnd_io_to_mod_src(io);
 
-               u8 cmd_case[] = {
+               static const u8 cmd_case[] = {
                        [0] = 0x3,
                        [1] = 0x3,
                        [2] = 0x4,
@@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
                        [9] = 0x2,
                };
 
+               if (unlikely(!src))
+                       return -EIO;
+
                data = path[rsnd_mod_id(src)] |
                        cmd_case[rsnd_mod_id(src)] << 16;
        }
index 8c1f4e2e0c4fb8c3ac09a641b4b8a928defb871c..3f2ced26ed3755b0eae995689fd13ae00fab9779 100644 (file)
@@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
        return !!io->substream;
 }
 
-void rsnd_set_slot(struct rsnd_dai *rdai,
-                  int slots, int num)
-{
-       rdai->slots     = slots;
-       rdai->slots_num = num;
-}
-
-int rsnd_get_slot(struct rsnd_dai_stream *io)
-{
-       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-
-       return rdai->slots;
-}
-
-int rsnd_get_slot_num(struct rsnd_dai_stream *io)
-{
-       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-
-       return rdai->slots_num;
-}
-
 int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
 {
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
@@ -248,13 +227,14 @@ int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
 
 int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
 {
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        int chan = rsnd_io_is_play(io) ?
                rsnd_runtime_channel_after_ctu(io) :
                rsnd_runtime_channel_original(io);
 
        /* Use Multi SSI */
        if (rsnd_runtime_is_ssi_multi(io))
-               chan /= rsnd_get_slot_num(io);
+               chan /= rsnd_rdai_ssi_lane_get(rdai);
 
        /* TDM Extend Mode needs 8ch */
        if (chan == 6)
@@ -265,12 +245,13 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
 
 int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
 {
-       int slots = rsnd_get_slot_num(io);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+       int lane = rsnd_rdai_ssi_lane_get(rdai);
        int chan = rsnd_io_is_play(io) ?
                rsnd_runtime_channel_after_ctu(io) :
                rsnd_runtime_channel_original(io);
 
-       return (chan >= 6) && (slots > 1);
+       return (chan > 2) && (lane > 1);
 }
 
 int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
@@ -310,6 +291,24 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
        u32 val = 0x76543210;
        u32 mask = ~0;
 
+       /*
+        * *Hardware* L/R and *Software* L/R are inverted.
+        * We need to care about inversion timing to control
+        * Playback/Capture correctly.
+        * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R
+        *
+        * sL/R : software L/R
+        * hL/R : hardware L/R
+        * (*)  : conversion timing
+        *
+        * Playback
+        *           sL/R (*) hL/R     hL/R     hL/R      hL/R     hL/R
+        *      [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec
+        *
+        * Capture
+        *           hL/R     hL/R      hL/R     hL/R     hL/R (*) sL/R
+        *      codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM]
+        */
        if (rsnd_io_is_play(io)) {
                struct rsnd_mod *src = rsnd_io_to_mod_src(io);
 
@@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status,
 
 #define rsnd_dai_call(fn, io, param...)                                        \
 ({                                                                     \
-       struct rsnd_priv *priv = rsnd_io_to_priv(io);                   \
-       struct device *dev = rsnd_priv_to_dev(priv);                    \
+       struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));     \
        struct rsnd_mod *mod;                                           \
        int is_play = rsnd_io_is_play(io);                              \
        int ret = 0, i;                                                 \
@@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod,
        io->mod[type] = NULL;
 }
 
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+                           int max_channels)
+{
+       if (max_channels > 0)
+               rdai->max_channels = max_channels;
+
+       return rdai->max_channels;
+}
+
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+                           int ssi_lane)
+{
+       if (ssi_lane > 0)
+               rdai->ssi_lane = ssi_lane;
+
+       return rdai->ssi_lane;
+}
+
 struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
 {
        if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
@@ -551,40 +567,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
 /*
  *     rsnd_soc_dai functions
  */
-int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
-{
-       struct snd_pcm_substream *substream = io->substream;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int pos = io->byte_pos + additional;
-
-       pos %= (runtime->periods * io->byte_per_period);
-
-       return pos;
-}
-
-bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
-{
-       io->byte_pos += byte;
-
-       if (io->byte_pos >= io->next_period_byte) {
-               struct snd_pcm_substream *substream = io->substream;
-               struct snd_pcm_runtime *runtime = substream->runtime;
-
-               io->period_pos++;
-               io->next_period_byte += io->byte_per_period;
-
-               if (io->period_pos >= runtime->periods) {
-                       io->byte_pos = 0;
-                       io->period_pos = 0;
-                       io->next_period_byte = io->byte_per_period;
-               }
-
-               return true;
-       }
-
-       return false;
-}
-
 void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
 {
        struct snd_pcm_substream *substream = io->substream;
@@ -602,15 +584,7 @@ void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
 static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
                                struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
        io->substream           = substream;
-       io->byte_pos            = 0;
-       io->period_pos          = 0;
-       io->byte_per_period     = runtime->period_size *
-                                 runtime->channels *
-                                 samples_to_bytes(runtime, 1);
-       io->next_period_byte    = io->byte_per_period;
 }
 
 static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
@@ -749,9 +723,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
        struct device *dev = rsnd_priv_to_dev(priv);
 
        switch (slots) {
+       case 2:
        case 6:
+       case 8:
+       case 16:
                /* TDM Extend Mode */
-               rsnd_set_slot(rdai, slots, 1);
+               rsnd_rdai_channels_set(rdai, slots);
+               rsnd_rdai_ssi_lane_set(rdai, 1);
                break;
        default:
                dev_err(dev, "unsupported TDM slots (%d)\n", slots);
@@ -761,22 +739,177 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
        return 0;
 }
 
+static unsigned int rsnd_soc_hw_channels_list[] = {
+       2, 6, 8, 16,
+};
+
+static unsigned int rsnd_soc_hw_rate_list[] = {
+         8000,
+        11025,
+        16000,
+        22050,
+        32000,
+        44100,
+        48000,
+        64000,
+        88200,
+        96000,
+       176400,
+       192000,
+};
+
+static int rsnd_soc_hw_rule(struct rsnd_priv *priv,
+                           unsigned int *list, int list_num,
+                           struct snd_interval *baseline, struct snd_interval *iv)
+{
+       struct snd_interval p;
+       unsigned int rate;
+       int i;
+
+       snd_interval_any(&p);
+       p.min = UINT_MAX;
+       p.max = 0;
+
+       for (i = 0; i < list_num; i++) {
+
+               if (!snd_interval_test(iv, list[i]))
+                       continue;
+
+               rate = rsnd_ssi_clk_query(priv,
+                                         baseline->min, list[i], NULL);
+               if (rate > 0) {
+                       p.min = min(p.min, list[i]);
+                       p.max = max(p.max, list[i]);
+               }
+
+               rate = rsnd_ssi_clk_query(priv,
+                                         baseline->max, list[i], NULL);
+               if (rate > 0) {
+                       p.min = min(p.min, list[i]);
+                       p.max = max(p.max, list[i]);
+               }
+       }
+
+       return snd_interval_refine(iv, &p);
+}
+
+static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params,
+                                struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval ic;
+       struct snd_soc_dai *dai = rule->private;
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+
+       /*
+        * possible sampling rate limitation is same as
+        * 2ch if it supports multi ssi
+        */
+       ic = *ic_;
+       if (1 < rsnd_rdai_ssi_lane_get(rdai)) {
+               ic.min = 2;
+               ic.max = 2;
+       }
+
+       return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list,
+                               ARRAY_SIZE(rsnd_soc_hw_rate_list),
+                               &ic, ir);
+}
+
+
+static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
+                                    struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval ic;
+       struct snd_soc_dai *dai = rule->private;
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+
+       /*
+        * possible sampling rate limitation is same as
+        * 2ch if it supports multi ssi
+        */
+       ic = *ic_;
+       if (1 < rsnd_rdai_ssi_lane_get(rdai)) {
+               ic.min = 2;
+               ic.max = 2;
+       }
+
+       return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list,
+                               ARRAY_SIZE(rsnd_soc_hw_channels_list),
+                               ir, &ic);
+}
+
+static void rsnd_soc_hw_constraint(struct snd_pcm_runtime *runtime,
+                                  struct snd_soc_dai *dai)
+{
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint;
+       unsigned int max_channels = rsnd_rdai_channels_get(rdai);
+       int i;
+
+       /*
+        * Channel Limitation
+        * It depends on Platform design
+        */
+       constraint->list        = rsnd_soc_hw_channels_list;
+       constraint->count       = 0;
+       constraint->mask        = 0;
+
+       for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) {
+               if (rsnd_soc_hw_channels_list[i] > max_channels)
+                       break;
+               constraint->count = i + 1;
+       }
+
+       snd_pcm_hw_constraint_list(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
+
+       /*
+        * Sampling Rate / Channel Limitation
+        * It depends on Clock Master Mode
+        */
+       if (!rsnd_rdai_is_clk_master(rdai))
+               return;
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                           rsnd_soc_hw_rule_rate, dai,
+                           SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                           rsnd_soc_hw_rule_channels, dai,
+                           SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+
 static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
        struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       int ret;
+
+       /* rsnd_io_to_runtime() is not yet enabled here */
+       rsnd_soc_hw_constraint(substream->runtime, dai);
 
        /*
         * call rsnd_dai_call without spinlock
         */
-       return rsnd_dai_call(nolock_start, io, priv);
+       ret = rsnd_dai_call(nolock_start, io, priv);
+       if (ret < 0)
+               rsnd_dai_call(nolock_stop, io, priv);
+
+       return ret;
 }
 
 static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
        struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 
        /*
@@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
        of_node_put(node);
 }
 
-static int rsnd_dai_probe(struct rsnd_priv *priv)
+static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
+                                           int *is_graph)
 {
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct device_node *np = dev->of_node;
        struct device_node *dai_node;
-       struct device_node *dai_np;
+       struct device_node *ret;
+
+       *is_graph = 0;
+
+       /*
+        * parse both previous dai (= rcar_sound,dai), and
+        * graph dai (= ports/port)
+        */
+       dai_node = of_get_child_by_name(np, RSND_NODE_DAI);
+       if (dai_node) {
+               ret = dai_node;
+               goto of_node_compatible;
+       }
+
+       ret = np;
+
+       dai_node = of_graph_get_next_endpoint(np, NULL);
+       if (dai_node)
+               goto of_node_graph;
+
+       return NULL;
+
+of_node_graph:
+       *is_graph = 1;
+of_node_compatible:
+       of_node_put(dai_node);
+
+       return ret;
+}
+
+static void __rsnd_dai_probe(struct rsnd_priv *priv,
+                            struct device_node *dai_np,
+                            int dai_i, int is_graph)
+{
        struct device_node *playback, *capture;
        struct rsnd_dai_stream *io_playback;
        struct rsnd_dai_stream *io_capture;
-       struct snd_soc_dai_driver *rdrv, *drv;
+       struct snd_soc_dai_driver *drv;
        struct rsnd_dai *rdai;
        struct device *dev = rsnd_priv_to_dev(priv);
-       int nr, dai_i, io_i;
-       int ret;
+       int io_i;
+
+       rdai            = rsnd_rdai_get(priv, dai_i);
+       drv             = priv->daidrv + dai_i;
+       io_playback     = &rdai->playback;
+       io_capture      = &rdai->capture;
+
+       snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+
+       rdai->priv      = priv;
+       drv->name       = rdai->name;
+       drv->ops        = &rsnd_soc_dai_ops;
+
+       snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
+                "DAI%d Playback", dai_i);
+       drv->playback.rates             = RSND_RATES;
+       drv->playback.formats           = RSND_FMTS;
+       drv->playback.channels_min      = 2;
+       drv->playback.channels_max      = 16;
+       drv->playback.stream_name       = rdai->playback.name;
+
+       snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
+                "DAI%d Capture", dai_i);
+       drv->capture.rates              = RSND_RATES;
+       drv->capture.formats            = RSND_FMTS;
+       drv->capture.channels_min       = 2;
+       drv->capture.channels_max       = 16;
+       drv->capture.stream_name        = rdai->capture.name;
+
+       rdai->playback.rdai             = rdai;
+       rdai->capture.rdai              = rdai;
+       rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
+       rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */
+
+       for (io_i = 0;; io_i++) {
+               playback = of_parse_phandle(dai_np, "playback", io_i);
+               capture  = of_parse_phandle(dai_np, "capture", io_i);
+
+               if (!playback && !capture)
+                       break;
 
-       dai_node = rsnd_dai_of_node(priv);
-       nr = of_get_child_count(dai_node);
-       if (!nr) {
-               ret = -EINVAL;
-               goto rsnd_dai_probe_done;
+               rsnd_parse_connect_ssi(rdai, playback, capture);
+               rsnd_parse_connect_src(rdai, playback, capture);
+               rsnd_parse_connect_ctu(rdai, playback, capture);
+               rsnd_parse_connect_mix(rdai, playback, capture);
+               rsnd_parse_connect_dvc(rdai, playback, capture);
+
+               of_node_put(playback);
+               of_node_put(capture);
        }
 
+       dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
+               rsnd_io_to_mod_ssi(io_playback) ? "play"    : " -- ",
+               rsnd_io_to_mod_ssi(io_capture) ? "capture" : "  --   ");
+}
+
+static int rsnd_dai_probe(struct rsnd_priv *priv)
+{
+       struct device_node *dai_node;
+       struct device_node *dai_np;
+       struct snd_soc_dai_driver *rdrv;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_dai *rdai;
+       int nr;
+       int is_graph;
+       int dai_i;
+
+       dai_node = rsnd_dai_of_node(priv, &is_graph);
+       if (is_graph)
+               nr = of_graph_get_endpoint_count(dai_node);
+       else
+               nr = of_get_child_count(dai_node);
+
+       if (!nr)
+               return -EINVAL;
+
        rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
        rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
-       if (!rdrv || !rdai) {
-               ret = -ENOMEM;
-               goto rsnd_dai_probe_done;
-       }
+       if (!rdrv || !rdai)
+               return -ENOMEM;
 
        priv->rdai_nr   = nr;
        priv->daidrv    = rdrv;
@@ -855,68 +1088,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
         * parse all dai
         */
        dai_i = 0;
-       for_each_child_of_node(dai_node, dai_np) {
-               rdai            = rsnd_rdai_get(priv, dai_i);
-               drv             = rdrv + dai_i;
-               io_playback     = &rdai->playback;
-               io_capture      = &rdai->capture;
-
-               snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
-
-               rdai->priv      = priv;
-               drv->name       = rdai->name;
-               drv->ops        = &rsnd_soc_dai_ops;
-
-               snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
-                        "DAI%d Playback", dai_i);
-               drv->playback.rates             = RSND_RATES;
-               drv->playback.formats           = RSND_FMTS;
-               drv->playback.channels_min      = 2;
-               drv->playback.channels_max      = 6;
-               drv->playback.stream_name       = rdai->playback.name;
-
-               snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
-                        "DAI%d Capture", dai_i);
-               drv->capture.rates              = RSND_RATES;
-               drv->capture.formats            = RSND_FMTS;
-               drv->capture.channels_min       = 2;
-               drv->capture.channels_max       = 6;
-               drv->capture.stream_name        = rdai->capture.name;
-
-               rdai->playback.rdai             = rdai;
-               rdai->capture.rdai              = rdai;
-               rsnd_set_slot(rdai, 2, 1); /* default */
-
-               for (io_i = 0;; io_i++) {
-                       playback = of_parse_phandle(dai_np, "playback", io_i);
-                       capture  = of_parse_phandle(dai_np, "capture", io_i);
-
-                       if (!playback && !capture)
-                               break;
-
-                       rsnd_parse_connect_ssi(rdai, playback, capture);
-                       rsnd_parse_connect_src(rdai, playback, capture);
-                       rsnd_parse_connect_ctu(rdai, playback, capture);
-                       rsnd_parse_connect_mix(rdai, playback, capture);
-                       rsnd_parse_connect_dvc(rdai, playback, capture);
-
-                       of_node_put(playback);
-                       of_node_put(capture);
+       if (is_graph) {
+               for_each_endpoint_of_node(dai_node, dai_np) {
+                       __rsnd_dai_probe(priv, dai_np, dai_i, is_graph);
+                       rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i);
+                       dai_i++;
                }
-
-               dai_i++;
-
-               dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
-                       rsnd_io_to_mod_ssi(io_playback) ? "play"    : " -- ",
-                       rsnd_io_to_mod_ssi(io_capture) ? "capture" : "  --   ");
+       } else {
+               for_each_child_of_node(dai_node, dai_np)
+                       __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph);
        }
 
-       ret = 0;
-
-rsnd_dai_probe_done:
-       of_node_put(dai_node);
-
-       return ret;
+       return 0;
 }
 
 /*
@@ -965,12 +1148,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream,
 
 static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
        struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       snd_pcm_uframes_t pointer = 0;
+
+       rsnd_dai_call(pointer, io, &pointer);
 
-       return bytes_to_frames(runtime, io->byte_pos);
+       return pointer;
 }
 
 static struct snd_pcm_ops rsnd_pcm_ops = {
@@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
        struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
        int i, change = 0;
 
+       if (!cfg->accept(cfg->io))
+               return 0;
+
        for (i = 0; i < cfg->size; i++) {
                if (cfg->texts) {
                        change |= (uc->value.enumerated.item[i] != cfg->val[i]);
@@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
        return change;
 }
 
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io)
+{
+       return 1;
+}
+
+int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io)
+{
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+       return !!runtime;
+}
+
 struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
 {
        cfg->cfg.val = cfg->val;
@@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
                   struct rsnd_dai_stream *io,
                   struct snd_soc_pcm_runtime *rtd,
                   const unsigned char *name,
+                  int (*accept)(struct rsnd_dai_stream *io),
                   void (*update)(struct rsnd_dai_stream *io,
                                  struct rsnd_mod *mod),
                   struct rsnd_kctrl_cfg *cfg,
@@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
        cfg->texts      = texts;
        cfg->max        = max;
        cfg->size       = size;
+       cfg->accept     = accept;
        cfg->update     = update;
        cfg->card       = card;
        cfg->kctrl      = kctrl;
@@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops rsnd_pm_ops = {
+static const struct dev_pm_ops rsnd_pm_ops = {
        .suspend                = rsnd_suspend,
        .resume                 = rsnd_resume,
 };
index 9dcc1f9db026f6b76d7849ff3108ac3335543116..4ba8f2fe7a4c47f08f046de4062e8ff7e9279cc4 100644 (file)
@@ -279,12 +279,14 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
 
        /* CTU Pass */
        ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+                              rsnd_kctrl_accept_anytime,
                               NULL,
                               &ctu->pass, RSND_MAX_CHANNELS,
                               0xC);
 
        /* ROW0 */
        ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+                              rsnd_kctrl_accept_anytime,
                               NULL,
                               &ctu->sv0, RSND_MAX_CHANNELS,
                               0x00FFFFFF);
@@ -293,6 +295,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
 
        /* ROW1 */
        ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+                              rsnd_kctrl_accept_anytime,
                               NULL,
                               &ctu->sv1, RSND_MAX_CHANNELS,
                               0x00FFFFFF);
@@ -301,6 +304,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
 
        /* ROW2 */
        ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+                              rsnd_kctrl_accept_anytime,
                               NULL,
                               &ctu->sv2, RSND_MAX_CHANNELS,
                               0x00FFFFFF);
@@ -309,6 +313,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
 
        /* ROW3 */
        ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+                              rsnd_kctrl_accept_anytime,
                               NULL,
                               &ctu->sv3, RSND_MAX_CHANNELS,
                               0x00FFFFFF);
@@ -317,6 +322,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
 
        /* Reset */
        ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+                              rsnd_kctrl_accept_anytime,
                               rsnd_ctu_value_reset,
                               &ctu->reset, 1);
 
index 241cb3b08a0755dc93c06034cca814e9c509c090..60aa5e96a49f97b3b3c3d384a7a36b4ad89a7041 100644 (file)
@@ -25,6 +25,7 @@
 
 struct rsnd_dmaen {
        struct dma_chan         *chan;
+       dma_cookie_t            cookie;
        dma_addr_t              dma_buf;
        unsigned int            dma_len;
        unsigned int            dma_period;
@@ -103,10 +104,6 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
         * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
         * But, Audio-DMAC-peri-peri doesn't have interrupt,
         * and this driver is assuming that here.
-        *
-        * If Audio-DMAC-peri-peri has interrpt,
-        * rsnd_dai_pointer_update() will be called twice,
-        * ant it will breaks io->byte_pos
         */
        spin_lock_irqsave(&priv->lock, flags);
 
@@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
                 */
                rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
 
-               elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
+               elapsed = true;
 
                dmaen->dma_cnt++;
        }
@@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
        for (i = 0; i < 2; i++)
                rsnd_dmaen_sync(dmaen, io, i);
 
-       if (dmaengine_submit(desc) < 0) {
+       dmaen->cookie = dmaengine_submit(desc);
+       if (dmaen->cookie < 0) {
                dev_err(dev, "dmaengine_submit() fail\n");
                return -EIO;
        }
@@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
        return 0;
 }
 
+static int rsnd_dmaen_pointer(struct rsnd_mod *mod,
+                             struct rsnd_dai_stream *io,
+                             snd_pcm_uframes_t *pointer)
+{
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+       struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+       struct dma_tx_state state;
+       enum dma_status status;
+       unsigned int pos = 0;
+
+       status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state);
+       if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+               if (state.residue > 0 && state.residue <= dmaen->dma_len)
+                       pos = dmaen->dma_len - state.residue;
+       }
+       *pointer = bytes_to_frames(runtime, pos);
+
+       return 0;
+}
+
 static struct rsnd_mod_ops rsnd_dmaen_ops = {
        .name   = "audmac",
        .nolock_start = rsnd_dmaen_nolock_start,
        .nolock_stop  = rsnd_dmaen_nolock_stop,
        .start  = rsnd_dmaen_start,
        .stop   = rsnd_dmaen_stop,
+       .pointer= rsnd_dmaen_pointer,
 };
 
 /*
index 463de8360985c5518f90f6f102a2ccccd560ff0b..99d2d9459e755a46f630b36f93611e88c4feeae6 100644 (file)
@@ -249,16 +249,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                            struct snd_soc_pcm_runtime *rtd)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        int is_play = rsnd_io_is_play(io);
-       int slots = rsnd_get_slot(io);
+       int channels = rsnd_rdai_channels_get(rdai);
        int ret;
 
        /* Volume */
        ret = rsnd_kctrl_new_m(mod, io, rtd,
                        is_play ?
                        "DVC Out Playback Volume" : "DVC In Capture Volume",
+                       rsnd_kctrl_accept_anytime,
                        rsnd_dvc_volume_update,
-                       &dvc->volume, slots,
+                       &dvc->volume, channels,
                        0x00800000 - 1);
        if (ret < 0)
                return ret;
@@ -267,8 +269,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        ret = rsnd_kctrl_new_m(mod, io, rtd,
                        is_play ?
                        "DVC Out Mute Switch" : "DVC In Mute Switch",
+                       rsnd_kctrl_accept_anytime,
                        rsnd_dvc_volume_update,
-                       &dvc->mute,  slots,
+                       &dvc->mute, channels,
                        1);
        if (ret < 0)
                return ret;
@@ -277,6 +280,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        ret = rsnd_kctrl_new_s(mod, io, rtd,
                        is_play ?
                        "DVC Out Ramp Switch" : "DVC In Ramp Switch",
+                       rsnd_kctrl_accept_anytime,
                        rsnd_dvc_volume_update,
                        &dvc->ren, 1);
        if (ret < 0)
@@ -285,6 +289,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        ret = rsnd_kctrl_new_e(mod, io, rtd,
                        is_play ?
                        "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+                       rsnd_kctrl_accept_anytime,
                        rsnd_dvc_volume_update,
                        &dvc->rup,
                        dvc_ramp_rate);
@@ -294,6 +299,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        ret = rsnd_kctrl_new_e(mod, io, rtd,
                        is_play ?
                        "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+                       rsnd_kctrl_accept_anytime,
                        rsnd_dvc_volume_update,
                        &dvc->rdown,
                        dvc_ramp_rate);
index 4b0980728e13ec75f18ac07135ab4971290b5310..ee00e3516911ef32b6fbdd3113f49776231b96a1 100644 (file)
@@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
                RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
                RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
                RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
+               RSND_GEN_S_REG(HDMI0_SEL,       0x9e0),
+               RSND_GEN_S_REG(HDMI1_SEL,       0x9e4),
 
                /* FIXME: it needs SSI_MODE2/3 in the future */
                RSND_GEN_M_REG(SSI_BUSIF_MODE,  0x0,    0x80),
index 323af41ecfcb8ffea222f25fbe6c524968f29fd5..99c57611df886a0700f7978cb93b5279b7c2498d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_graph.h>
 #include <linux/of_irq.h>
 #include <linux/sh_dma.h>
 #include <linux/workqueue.h>
@@ -170,6 +171,8 @@ enum rsnd_reg {
        RSND_REG_SSI_SYS_STATUS5,
        RSND_REG_SSI_SYS_STATUS6,
        RSND_REG_SSI_SYS_STATUS7,
+       RSND_REG_HDMI0_SEL,
+       RSND_REG_HDMI1_SEL,
 
        /* SSI */
        RSND_REG_SSICR,
@@ -268,6 +271,9 @@ struct rsnd_mod_ops {
                         struct rsnd_dai_stream *io,
                         struct snd_pcm_substream *substream,
                         struct snd_pcm_hw_params *hw_params);
+       int (*pointer)(struct rsnd_mod *mod,
+                      struct rsnd_dai_stream *io,
+                      snd_pcm_uframes_t *pointer);
        int (*fallback)(struct rsnd_mod *mod,
                        struct rsnd_dai_stream *io,
                        struct rsnd_priv *priv);
@@ -305,6 +311,7 @@ struct rsnd_mod {
  * H   0: pcm_new
  * H   0: fallback
  * H   0: hw_params
+ * H   0: pointer
  */
 #define __rsnd_mod_shift_nolock_start  0
 #define __rsnd_mod_shift_nolock_stop   0
@@ -318,6 +325,7 @@ struct rsnd_mod {
 #define __rsnd_mod_shift_pcm_new       28 /* always called */
 #define __rsnd_mod_shift_fallback      28 /* always called */
 #define __rsnd_mod_shift_hw_params     28 /* always called */
+#define __rsnd_mod_shift_pointer       28 /* always called */
 
 #define __rsnd_mod_add_probe           0
 #define __rsnd_mod_add_remove          0
@@ -331,6 +339,7 @@ struct rsnd_mod {
 #define __rsnd_mod_add_pcm_new         0
 #define __rsnd_mod_add_fallback                0
 #define __rsnd_mod_add_hw_params       0
+#define __rsnd_mod_add_pointer         0
 
 #define __rsnd_mod_call_probe          0
 #define __rsnd_mod_call_remove         0
@@ -342,6 +351,7 @@ struct rsnd_mod {
 #define __rsnd_mod_call_pcm_new                0
 #define __rsnd_mod_call_fallback       0
 #define __rsnd_mod_call_hw_params      0
+#define __rsnd_mod_call_pointer                0
 #define __rsnd_mod_call_nolock_start   0
 #define __rsnd_mod_call_nolock_stop    1
 
@@ -389,11 +399,6 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
                struct device_node *playback,
                struct device_node *capture);
 
-void rsnd_set_slot(struct rsnd_dai *rdai,
-                  int slots, int slots_total);
-int rsnd_get_slot(struct rsnd_dai_stream *io);
-int rsnd_get_slot_num(struct rsnd_dai_stream *io);
-
 int rsnd_runtime_channel_original(struct rsnd_dai_stream *io);
 int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io);
 int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
@@ -420,13 +425,8 @@ struct rsnd_dai_stream {
        char name[RSND_DAI_NAME_SIZE];
        struct snd_pcm_substream *substream;
        struct rsnd_mod *mod[RSND_MOD_MAX];
-       struct rsnd_dai_path_info *info; /* rcar_snd.h */
        struct rsnd_dai *rdai;
        u32 parent_ssi_status;
-       int byte_pos;
-       int period_pos;
-       int byte_per_period;
-       int next_period_byte;
 };
 #define rsnd_io_to_mod(io, i)  ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
 #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
@@ -449,9 +449,10 @@ struct rsnd_dai {
        struct rsnd_dai_stream playback;
        struct rsnd_dai_stream capture;
        struct rsnd_priv *priv;
+       struct snd_pcm_hw_constraint_list constraint;
 
-       int slots;
-       int slots_num;
+       int max_channels;       /* 2ch - 16ch */
+       int ssi_lane;           /* 1lane - 4lane */
 
        unsigned int clk_master:1;
        unsigned int bit_clk_inv:1;
@@ -471,13 +472,24 @@ struct rsnd_dai {
 
 struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
 
-bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+#define rsnd_rdai_channels_set(rdai, max_channels) \
+       rsnd_rdai_channels_ctrl(rdai, max_channels)
+#define rsnd_rdai_channels_get(rdai) \
+       rsnd_rdai_channels_ctrl(rdai, 0)
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+                           int max_channels);
+
+#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \
+       rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane)
+#define rsnd_rdai_ssi_lane_get(rdai) \
+       rsnd_rdai_ssi_lane_ctrl(rdai, 0)
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+                           int ssi_lane);
+
 void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
-int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
 int rsnd_dai_connect(struct rsnd_mod *mod,
                     struct rsnd_dai_stream *io,
                     enum rsnd_mod_type type);
-#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI)
 
 /*
  *     R-Car Gen1/Gen2
@@ -491,6 +503,7 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
 /*
  *     R-Car ADG
  */
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate);
 int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
 int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
 int rsnd_adg_probe(struct rsnd_priv *priv);
@@ -596,6 +609,7 @@ struct rsnd_kctrl_cfg {
        unsigned int size;
        u32 *val;
        const char * const *texts;
+       int (*accept)(struct rsnd_dai_stream *io);
        void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
        struct rsnd_dai_stream *io;
        struct snd_card *card;
@@ -613,12 +627,15 @@ struct rsnd_kctrl_cfg_s {
        u32 val;
 };
 
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io);
+int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io);
 struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg);
 struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
 int rsnd_kctrl_new(struct rsnd_mod *mod,
                   struct rsnd_dai_stream *io,
                   struct snd_soc_pcm_runtime *rtd,
                   const unsigned char *name,
+                  int (*accept)(struct rsnd_dai_stream *io),
                   void (*update)(struct rsnd_dai_stream *io,
                                  struct rsnd_mod *mod),
                   struct rsnd_kctrl_cfg *cfg,
@@ -626,16 +643,16 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
                   int size,
                   u32 max);
 
-#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \
-       rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \
+#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \
+       rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \
                       NULL, size, max)
 
-#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max) \
-       rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \
+       rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
                       NULL, 1, max)
 
-#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts)       \
-       rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts)       \
+       rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
                       texts, 1, ARRAY_SIZE(texts))
 
 /*
@@ -648,6 +665,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
 u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
 
+#define RSND_SSI_HDMI_PORT0    0xf0
+#define RSND_SSI_HDMI_PORT1    0xf1
+int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io);
+void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+                                   struct device_node *endpoint,
+                                   int dai_i);
+
 #define rsnd_ssi_is_pin_sharing(io)    \
        __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
 int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
@@ -656,6 +680,8 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
                            struct device_node *playback,
                            struct device_node *capture);
+unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
+                      int param1, int param2, int *idx);
 
 /*
  *     R-Car SSIU
index 76a477a3ccb5d88e18fd8398d9ad2b2616a99f48..7aa239e28491525053d4dd957bf2cc4d2f4b056c 100644 (file)
 
 #define SRC_NAME "src"
 
-/* SRCx_STATUS */
-#define OUF_SRCO       ((1 << 12) | (1 << 13))
-#define OUF_SRCI       ((1 <<  9) | (1 <<  8))
-
 /* SCU_SYSTEM_STATUS0/1 */
 #define OUF_SRC(id)    ((1 << (id + 16)) | (1 << id))
 
@@ -55,20 +51,6 @@ struct rsnd_src {
  *
  */
 
-/*
- * src.c is caring...
- *
- * Gen1
- *
- * [mem] -> [SRU] -> [SSI]
- *        |--------|
- *
- * Gen2
- *
- * [mem] -> [SRC] -> [SSIU] -> [SSI]
- *        |-----------------|
- */
-
 static void rsnd_src_activation(struct rsnd_mod *mod)
 {
        rsnd_mod_write(mod, SRC_SWRSR, 0);
@@ -515,6 +497,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
                               rsnd_io_is_play(io) ?
                               "SRC Out Rate Switch" :
                               "SRC In Rate Switch",
+                              rsnd_kctrl_accept_anytime,
                               rsnd_src_set_convert_rate,
                               &src->sen, 1);
        if (ret < 0)
@@ -524,6 +507,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
                               rsnd_io_is_play(io) ?
                               "SRC Out Rate" :
                               "SRC In Rate",
+                              rsnd_kctrl_accept_runtime,
                               rsnd_src_set_convert_rate,
                               &src->sync, 192000);
 
index 91e5c07911b4a5b14364becf64c568d7a61cc1c4..46feddd78ee26f0e7fa90e40500914ef5b4edfe3 100644 (file)
@@ -11,6 +11,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <sound/simple_card_utils.h>
 #include <linux/delay.h>
 #include "rsnd.h"
 #define RSND_SSI_NAME_SIZE 16
@@ -76,11 +77,18 @@ struct rsnd_ssi {
        int rate;
        int irq;
        unsigned int usrcnt;
+
+       int byte_pos;
+       int period_pos;
+       int byte_per_period;
+       int next_period_byte;
 };
 
 /* flags */
 #define RSND_SSI_CLK_PIN_SHARE         (1 << 0)
 #define RSND_SSI_NO_BUSIF              (1 << 1) /* SSI+DMA without BUSIF */
+#define RSND_SSI_HDMI0                 (1 << 2) /* for HDMI0 */
+#define RSND_SSI_HDMI1                 (1 << 3) /* for HDMI1 */
 
 #define for_each_rsnd_ssi(pos, priv, i)                                        \
        for (i = 0;                                                     \
@@ -99,6 +107,20 @@ struct rsnd_ssi {
 #define rsnd_ssi_is_run_mods(mod, io) \
        (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
 
+int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0)
+               return RSND_SSI_HDMI_PORT0;
+
+       if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1)
+               return RSND_SSI_HDMI_PORT1;
+
+       return 0;
+}
+
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
 {
        struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
@@ -186,6 +208,46 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
        return 0;
 }
 
+unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
+                      int param1, int param2, int *idx)
+{
+       int ssi_clk_mul_table[] = {
+               1, 2, 4, 8, 16, 6, 12,
+       };
+       int j, ret;
+       unsigned int main_rate;
+
+       for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+               /*
+                * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+                * with it is not allowed. (SSIWSR.WS_MODE with
+                * SSICR.CKDV = 000 is not allowed either).
+                * Skip it. See SSICR.CKDV
+                */
+               if (j == 0)
+                       continue;
+
+               /*
+                * this driver is assuming that
+                * system word is 32bit x chan
+                * see rsnd_ssi_init()
+                */
+               main_rate = 32 * param1 * param2 * ssi_clk_mul_table[j];
+
+               ret = rsnd_adg_clk_query(priv, main_rate);
+               if (ret < 0)
+                       continue;
+
+               if (idx)
+                       *idx = j;
+
+               return main_rate;
+       }
+
+       return 0;
+}
+
 static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
                                     struct rsnd_dai_stream *io)
 {
@@ -195,10 +257,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
        int chan = rsnd_runtime_channel_for_ssi(io);
-       int j, ret;
-       int ssi_clk_mul_table[] = {
-               1, 2, 4, 8, 16, 6, 12,
-       };
+       int idx, ret;
        unsigned int main_rate;
        unsigned int rate = rsnd_io_is_play(io) ?
                rsnd_src_get_out_rate(priv, io) :
@@ -222,45 +281,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
                return 0;
        }
 
-       /*
-        * Find best clock, and try to start ADG
-        */
-       for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
-               /*
-                * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
-                * with it is not allowed. (SSIWSR.WS_MODE with
-                * SSICR.CKDV = 000 is not allowed either).
-                * Skip it. See SSICR.CKDV
-                */
-               if (j == 0)
-                       continue;
-
-               /*
-                * this driver is assuming that
-                * system word is 32bit x chan
-                * see rsnd_ssi_init()
-                */
-               main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
-
-               ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
-               if (0 == ret) {
-                       ssi->cr_clk     = FORCE | SWL_32 |
-                               SCKD | SWSD | CKDV(j);
-                       ssi->wsr = CONT;
+       main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx);
+       if (!main_rate) {
+               dev_err(dev, "unsupported clock rate\n");
+               return -EIO;
+       }
 
-                       ssi->rate = rate;
+       ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+       if (ret < 0)
+               return ret;
 
-                       dev_dbg(dev, "%s[%d] outputs %u Hz\n",
-                               rsnd_mod_name(mod),
-                               rsnd_mod_id(mod), rate);
+       ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx);
+       ssi->wsr = CONT;
+       ssi->rate = rate;
 
-                       return 0;
-               }
-       }
+       dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+               rsnd_mod_name(mod),
+               rsnd_mod_id(mod), rate);
 
-       dev_err(dev, "unsupported clock rate\n");
-       return -EIO;
+       return 0;
 }
 
 static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
@@ -357,6 +396,59 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
                                        ssi->cr_mode); /* without EN */
 }
 
+static void rsnd_ssi_pointer_init(struct rsnd_mod *mod,
+                                 struct rsnd_dai_stream *io)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+       ssi->byte_pos           = 0;
+       ssi->period_pos         = 0;
+       ssi->byte_per_period    = runtime->period_size *
+                                 runtime->channels *
+                                 samples_to_bytes(runtime, 1);
+       ssi->next_period_byte   = ssi->byte_per_period;
+}
+
+static int rsnd_ssi_pointer_offset(struct rsnd_mod *mod,
+                                  struct rsnd_dai_stream *io,
+                                  int additional)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       int pos = ssi->byte_pos + additional;
+
+       pos %= (runtime->periods * ssi->byte_per_period);
+
+       return pos;
+}
+
+static bool rsnd_ssi_pointer_update(struct rsnd_mod *mod,
+                                   struct rsnd_dai_stream *io,
+                                   int byte)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       ssi->byte_pos += byte;
+
+       if (ssi->byte_pos >= ssi->next_period_byte) {
+               struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+               ssi->period_pos++;
+               ssi->next_period_byte += ssi->byte_per_period;
+
+               if (ssi->period_pos >= runtime->periods) {
+                       ssi->byte_pos = 0;
+                       ssi->period_pos = 0;
+                       ssi->next_period_byte = ssi->byte_per_period;
+               }
+
+               return true;
+       }
+
+       return false;
+}
+
 /*
  *     SSI mod common functions
  */
@@ -370,6 +462,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
        if (!rsnd_ssi_is_run_mods(mod, io))
                return 0;
 
+       rsnd_ssi_pointer_init(mod, io);
+
        ssi->usrcnt++;
 
        rsnd_mod_power_on(mod);
@@ -549,7 +643,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
        if (!is_dma && (status & DIRQ)) {
                struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
                u32 *buf = (u32 *)(runtime->dma_area +
-                                  rsnd_dai_pointer_offset(io, 0));
+                                  rsnd_ssi_pointer_offset(mod, io, 0));
                int shift = 0;
 
                switch (runtime->sample_bits) {
@@ -568,7 +662,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
                else
                        *buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
 
-               elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
+               elapsed = rsnd_ssi_pointer_update(mod, io, sizeof(*buf));
        }
 
        /* DMA only */
@@ -675,6 +769,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
        return ret;
 }
 
+static int rsnd_ssi_pointer(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           snd_pcm_uframes_t *pointer)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+       *pointer = bytes_to_frames(runtime, ssi->byte_pos);
+
+       return 0;
+}
+
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_common_probe,
@@ -683,6 +789,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .start  = rsnd_ssi_start,
        .stop   = rsnd_ssi_stop,
        .irq    = rsnd_ssi_irq,
+       .pointer= rsnd_ssi_pointer,
        .pcm_new = rsnd_ssi_pcm_new,
        .hw_params = rsnd_ssi_hw_params,
 };
@@ -786,13 +893,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
 }
 
 
-/*
- *             Non SSI
- */
-static struct rsnd_mod_ops rsnd_ssi_non_ops = {
-       .name   = SSI_NAME,
-};
-
 /*
  *             ssi mod function
  */
@@ -814,7 +914,8 @@ static void rsnd_ssi_connect(struct rsnd_mod *mod,
                type = types[i];
                if (!rsnd_io_to_mod(io, type)) {
                        rsnd_dai_connect(mod, io, type);
-                       rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
+                       rsnd_rdai_channels_set(rdai, (i + 1) * 2);
+                       rsnd_rdai_ssi_lane_set(rdai, (i + 1));
                        return;
                }
        }
@@ -847,6 +948,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
        of_node_put(node);
 }
 
+static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+                                            struct rsnd_dai_stream *io,
+                                            struct device_node *remote_ep)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+       struct rsnd_ssi *ssi;
+
+       if (!mod)
+               return;
+
+       ssi  = rsnd_mod_to_ssi(mod);
+
+       if (strstr(remote_ep->full_name, "hdmi0")) {
+               ssi->flags |= RSND_SSI_HDMI0;
+               dev_dbg(dev, "%s[%d] connected to HDMI0\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+       }
+
+       if (strstr(remote_ep->full_name, "hdmi1")) {
+               ssi->flags |= RSND_SSI_HDMI1;
+               dev_dbg(dev, "%s[%d] connected to HDMI1\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       }
+}
+
+void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+                                   struct device_node *endpoint,
+                                   int dai_i)
+{
+       struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i);
+       struct device_node *remote_ep;
+
+       remote_ep = of_graph_get_remote_endpoint(endpoint);
+       if (!remote_ep)
+               return;
+
+       __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep);
+       __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture,  remote_ep);
+}
+
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
 {
        if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
@@ -952,7 +1094,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
                        goto rsnd_ssi_probe_done;
                }
 
-               ops = &rsnd_ssi_non_ops;
                if (of_property_read_bool(np, "pio-transfer"))
                        ops = &rsnd_ssi_pio_ops;
                else
index 512d238b79e2895f13a4b1b7be3e145859a65280..bed2c9c0004b8fb0c6ccb1d9215443f4846e1eba 100644 (file)
@@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
                               struct rsnd_dai_stream *io,
                               struct rsnd_priv *priv)
 {
+       int hdmi = rsnd_ssi_hdmi_port(io);
        int ret;
 
        ret = rsnd_ssiu_init(mod, io, priv);
@@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
                               rsnd_get_dalign(mod, io));
        }
 
+       if (hdmi) {
+               enum rsnd_mod_type rsnd_ssi_array[] = {
+                       RSND_MOD_SSIM1,
+                       RSND_MOD_SSIM2,
+                       RSND_MOD_SSIM3,
+               };
+               struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+               struct rsnd_mod *pos;
+               u32 val;
+               int i, shift;
+
+               i = rsnd_mod_id(ssi_mod);
+
+               /* output all same SSI as default */
+               val =   i << 16 |
+                       i << 20 |
+                       i << 24 |
+                       i << 28 |
+                       i;
+
+               for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
+                       shift   = (i * 4) + 16;
+                       val     = (val & ~(0xF << shift)) |
+                               rsnd_mod_id(pos) << shift;
+               }
+
+               switch (hdmi) {
+               case RSND_SSI_HDMI_PORT0:
+                       rsnd_mod_write(mod, HDMI0_SEL, val);
+                       break;
+               case RSND_SSI_HDMI_PORT1:
+                       rsnd_mod_write(mod, HDMI1_SEL, val);
+                       break;
+               }
+       }
+
        return 0;
 }
 
index 180bfbfe833da3c53a32a1845392d2af554e3929..921622a019448394687655034f52befb4abfd6ec 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <linux/dmi.h>
 #include <sound/core.h>
 #include <sound/jack.h>
@@ -3992,11 +3993,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                prefix = "";
 
        /*
-        * check "[prefix]format = xxx"
+        * check "dai-format = xxx"
+        * or    "[prefix]format = xxx"
         * SND_SOC_DAIFMT_FORMAT_MASK area
         */
-       snprintf(prop, sizeof(prop), "%sformat", prefix);
-       ret = of_property_read_string(np, prop, &str);
+       ret = of_property_read_string(np, "dai-format", &str);
+       if (ret < 0) {
+               snprintf(prop, sizeof(prop), "%sformat", prefix);
+               ret = of_property_read_string(np, prop, &str);
+       }
        if (ret == 0) {
                for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
                        if (strcmp(str, of_fmt_table[i].name) == 0) {
@@ -4076,6 +4081,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
 
+int snd_soc_get_dai_id(struct device_node *ep)
+{
+       struct snd_soc_component *pos;
+       struct device_node *node;
+       int ret;
+
+       node = of_graph_get_port_parent(ep);
+
+       /*
+        * For example HDMI case, HDMI has video/sound port,
+        * but ALSA SoC needs sound port number only.
+        * Thus counting HDMI DT port/endpoint doesn't work.
+        * Then, it should have .of_xlate_dai_id
+        */
+       ret = -ENOTSUPP;
+       mutex_lock(&client_mutex);
+       list_for_each_entry(pos, &component_list, list) {
+               struct device_node *component_of_node = pos->dev->of_node;
+
+               if (!component_of_node && pos->dev->parent)
+                       component_of_node = pos->dev->parent->of_node;
+
+               if (component_of_node != node)
+                       continue;
+
+               if (pos->driver->of_xlate_dai_id)
+                       ret = pos->driver->of_xlate_dai_id(pos, ep);
+
+               break;
+       }
+       mutex_unlock(&client_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
+
 int snd_soc_get_dai_name(struct of_phandle_args *args,
                                const char **dai_name)
 {