diff options
26 files changed, 1546 insertions, 215 deletions
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt new file mode 100644 index 000000000000..bac4b1b1060f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-card.txt @@ -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 index 000000000000..b63c5594bbb3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt @@ -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"; + ... + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 15a7316e4c91..7246bb268bf9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -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 diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index d6fe47ed09af..327d229a51b2 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -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 { diff --git a/drivers/of/base.c b/drivers/of/base.c index 28d5f53bc631..cb1c49ae3b88 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -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, @@ -2485,6 +2487,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 diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index abdb02eaef06..3e058f05ab04 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -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) { diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index af58d2362975..108cae459ed0 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -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); diff --git a/include/sound/soc.h b/include/sound/soc.h index 5170fd81e1fd..9c94b97c17f8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -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, diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index d023959b8cd6..c954be0a0f96 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -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. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3023ba..9e000523a3b4 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -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 index 000000000000..0180b286bee3 --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c @@ -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 index 000000000000..0066102f5bc4 --- /dev/null +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -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>"); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 343b291fc372..d9d8b8a58348 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -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) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 2c9dedab5184..e86c6e16146b 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -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); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index dcbcab230d1b..5faf5d6c48a2 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -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); diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 147ebecfed94..1aa5cd77ca24 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -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 diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d3b0dc145a56..4a72fd74ddc2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -480,6 +480,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 +558,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 +582,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 +598,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); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index d879c010cf03..9a136d86e2a9 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -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; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 8c1f4e2e0c4f..7a8c08933503 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -766,11 +766,16 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; /* * 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, @@ -820,32 +825,131 @@ 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 = 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; - 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 +959,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 +1019,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 = { diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 241cb3b08a07..05e538f4c8d5 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -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; @@ -292,7 +293,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 +350,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, }; /* diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 4b0980728e13..ee00e3516911 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -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), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 323af41ecfcb..bd8def0bc212 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -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 @@ -420,7 +430,6 @@ 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; @@ -477,7 +486,6 @@ 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 @@ -648,6 +656,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); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 76a477a3ccb5..8dbe9ebcbff1 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,10 +12,6 @@ #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); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91e5c07911b4..59ca6e3f46bc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -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 @@ -81,6 +82,8 @@ struct rsnd_ssi { /* 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 +102,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); @@ -675,6 +692,17 @@ 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 snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *pointer = bytes_to_frames(runtime, io->byte_pos); + + return 0; +} + static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_common_probe, @@ -683,6 +711,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, }; @@ -787,13 +816,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 */ static void rsnd_ssi_connect(struct rsnd_mod *mod, @@ -847,6 +869,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 +1015,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 diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 512d238b79e2..bed2c9c0004b 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -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; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index aae099c0e502..9ba183781017 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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> @@ -3960,11 +3961,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) { @@ -4044,6 +4049,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) { |