Commit 85ee1f42 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next

parents 51fa6a8f 82885913
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";
...
};
};
};
};
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";
...
};
};
};
};
...@@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes ...@@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
** Asynchronous mode ** 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) example)
sound { sound {
compatible = "renesas,rsrc-card"; compatible = "simple-scu-audio-card";
... ...
/* /*
* SRC Asynchronous mode setting * SRC Asynchronous mode setting
...@@ -97,12 +97,12 @@ example) ...@@ -97,12 +97,12 @@ example)
* Inputed 48kHz data will be converted to * Inputed 48kHz data will be converted to
* system specified Hz * system specified Hz
*/ */
convert-rate = <48000>; simple-audio-card,convert-rate = <48000>;
... ...
cpu { simple-audio-card,cpu {
sound-dai = <&rcar_sound>; sound-dai = <&rcar_sound>;
}; };
codec { simple-audio-card,codec {
... ...
}; };
}; };
...@@ -141,23 +141,23 @@ For more detail information, see below ...@@ -141,23 +141,23 @@ For more detail information, see below
${LINUX}/sound/soc/sh/rcar/ctu.c ${LINUX}/sound/soc/sh/rcar/ctu.c
- comment of header - 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) example)
sound { sound {
compatible = "renesas,rsrc-card"; compatible = "simple-scu-audio-card";
... ...
/* /*
* CTU setting * CTU setting
* All input data will be converted to 2ch * All input data will be converted to 2ch
* as output data * as output data
*/ */
convert-channels = <2>; simple-audio-card,convert-channels = <2>;
... ...
cpu { simple-audio-card,cpu {
sound-dai = <&rcar_sound>; sound-dai = <&rcar_sound>;
}; };
codec { simple-audio-card,codec {
... ...
}; };
}; };
...@@ -190,22 +190,22 @@ and these sounds will be merged by MIX. ...@@ -190,22 +190,22 @@ and these sounds will be merged by MIX.
aplay -D plughw:0,0 xxxx.wav & aplay -D plughw:0,0 xxxx.wav &
aplay -D plughw:0,1 yyyy.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) Ex)
[MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
| |
[MEM] -> [SRC2] -> [CTU03] -+ [MEM] -> [SRC2] -> [CTU03] -+
sound { sound {
compatible = "renesas,rsrc-card"; compatible = "simple-scu-audio-card";
... ...
cpu@0 { simple-audio-card,cpu@0 {
sound-dai = <&rcar_sound 0>; sound-dai = <&rcar_sound 0>;
}; };
cpu@1 { simple-audio-card,cpu@1 {
sound-dai = <&rcar_sound 1>; sound-dai = <&rcar_sound 1>;
}; };
codec { simple-audio-card,codec {
... ...
}; };
}; };
...@@ -368,6 +368,10 @@ Required properties: ...@@ -368,6 +368,10 @@ Required properties:
see below for detail. see below for detail.
- #sound-dai-cells : it must be 0 if your system is using single DAI - #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 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: Optional properties:
- #clock-cells : it must be 0 if your system has audio_clkout - #clock-cells : it must be 0 if your system has audio_clkout
...@@ -375,6 +379,9 @@ Optional properties: ...@@ -375,6 +379,9 @@ Optional properties:
- clock-frequency : for all audio_clkout0/1/2/3 - clock-frequency : for all audio_clkout0/1/2/3
- clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn - clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn
is asynchronizes with lr-clock. is asynchronizes with lr-clock.
- resets : References to SSI resets.
- reset-names : List of valid reset names.
"ssi-all", "ssi.X"
SSI subnode properties: SSI subnode properties:
- interrupts : Should contain SSI interrupt for PIO transfer - interrupts : Should contain SSI interrupt for PIO transfer
......
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: Required properties:
- compatible : "simple-scu-audio-card" - compatible : "simple-scu-audio-card"
"renesas,rsrc-card" "renesas,rsrc-card"
Optional properties: Optional properties:
- simple-audio-card,name : User specified audio sound card name, one string - simple-audio-card,name : see simple-audio-card.txt
property. - simple-audio-card,cpu : see simple-audio-card.txt
- simple-audio-card,cpu : CPU sub-node - simple-audio-card,codec : see simple-audio-card.txt
- simple-audio-card,codec : CODEC sub-node
Optional subnode properties: Optional subnode properties:
- simple-audio-card,format : CPU/CODEC common audio format. - simple-audio-card,format : see simple-audio-card.txt
"i2s", "right_j", "left_j" , "dsp_a" - simple-audio-card,frame-master : see simple-audio-card.txt
"dsp_b", "ac97", "pdm", "msb", "lsb" - simple-audio-card,bitclock-master : see simple-audio-card.txt
- simple-audio-card,frame-master : Indicates dai-link frame master. - simple-audio-card,bitclock-inversion : see simple-audio-card.txt
phandle to a cpu or codec subnode. - simple-audio-card,frame-inversion : see simple-audio-card.txt
- 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,convert-rate : platform specified sampling rate convert - 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,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. - 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, 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. the second being the connection's source. Valid names for sources.
...@@ -38,32 +32,23 @@ Optional subnode properties: ...@@ -38,32 +32,23 @@ Optional subnode properties:
Required CPU/CODEC subnodes 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: Optional CPU/CODEC subnodes properties:
- clocks / system-clock-frequency : specify subnode's clock if needed. - clocks / system-clock-frequency : see simple-audio-card.txt
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().
Example 1. Sampling Rate Covert Example 1. Sampling Rate Conversion
sound { sound {
compatible = "simple-scu-audio-card"; compatible = "simple-scu-audio-card";
simple-audio-card,name = "rsnd-ak4643"; simple-audio-card,name = "rsnd-ak4643";
simple-audio-card,format = "left_j"; simple-audio-card,format = "left_j";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>; simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-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,prefix = "ak4642";
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
...@@ -79,20 +64,18 @@ sound { ...@@ -79,20 +64,18 @@ sound {
}; };
}; };
Example 2. 2 CPU 1 Codec Example 2. 2 CPU 1 Codec (Mixing)
sound { sound {
compatible = "renesas,rsrc-card"; compatible = "simple-scu-audio-card";
card-name = "rsnd-ak4643";
format = "left_j";
bitclock-master = <&dpcmcpu>;
frame-master = <&dpcmcpu>;
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"; simple-audio-card,prefix = "ak4642";
audio-routing = "ak4642 Playback", "DAI0 Playback", simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
"ak4642 Playback", "DAI1 Playback"; "ak4642 Playback", "DAI1 Playback";
dpcmcpu: cpu@0 { dpcmcpu: cpu@0 {
......
...@@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it, ...@@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_phandle_iterator_init);
int of_phandle_iterator_next(struct of_phandle_iterator *it) int of_phandle_iterator_next(struct of_phandle_iterator *it)
{ {
...@@ -1670,6 +1671,7 @@ int of_phandle_iterator_next(struct of_phandle_iterator *it) ...@@ -1670,6 +1671,7 @@ int of_phandle_iterator_next(struct of_phandle_iterator *it)
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL_GPL(of_phandle_iterator_next);
int of_phandle_iterator_args(struct of_phandle_iterator *it, int of_phandle_iterator_args(struct of_phandle_iterator *it,
uint32_t *args, uint32_t *args,
...@@ -2484,6 +2486,41 @@ struct device_node *of_graph_get_endpoint_by_regs( ...@@ -2484,6 +2486,41 @@ struct device_node *of_graph_get_endpoint_by_regs(
} }
EXPORT_SYMBOL(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 * of_graph_get_remote_port_parent() - get remote port's parent node
* @node: pointer to a local endpoint device_node * @node: pointer to a local endpoint device_node
...@@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent( ...@@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node) const struct device_node *node)
{ {
struct device_node *np; struct device_node *np;
unsigned int depth;
/* Get remote endpoint node. */ /* 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. */ return of_graph_get_port_parent(np);
for (depth = 3; depth && np; depth--) {
np = of_get_next_parent(np);
if (depth == 2 && of_node_cmp(np->name, "ports"))
break;
}
return np;
} }
EXPORT_SYMBOL(of_graph_get_remote_port_parent); 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) ...@@ -2522,13 +2552,25 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
struct device_node *np; struct device_node *np;
/* Get remote endpoint node. */ /* Get remote endpoint node. */
np = of_parse_phandle(node, "remote-endpoint", 0); np = of_graph_get_remote_endpoint(node);
if (!np) if (!np)
return NULL; return NULL;
return of_get_next_parent(np); return of_get_next_parent(np);
} }
EXPORT_SYMBOL(of_graph_get_remote_port); 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 * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
* @node: pointer to parent device_node containing graph port/endpoint * @node: pointer to parent device_node containing graph port/endpoint
......
...@@ -43,11 +43,15 @@ struct of_endpoint { ...@@ -43,11 +43,15 @@ struct of_endpoint {
#ifdef CONFIG_OF #ifdef CONFIG_OF
int of_graph_parse_endpoint(const struct device_node *node, int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint); 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_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 *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *previous); struct device_node *previous);
struct device_node *of_graph_get_endpoint_by_regs( struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg); 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( struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node); const struct device_node *node);
struct device_node *of_graph_get_remote_port(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, ...@@ -61,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
return -ENOSYS; 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( static inline struct device_node *of_graph_get_port_by_id(
struct device_node *node, u32 id) struct device_node *node, u32 id)
{ {
...@@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs( ...@@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs(
return NULL; 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( static inline struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node) const struct device_node *node)
{ {
......
...@@ -35,13 +35,16 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, ...@@ -35,13 +35,16 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
char *prefix); char *prefix);
#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \ #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) \ #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, int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *node, struct device_node *node,
struct device_node *dai_of_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, \ #define asoc_simple_card_parse_cpu(node, dai_link, \
list_name, cells_name, is_single_link) \ list_name, cells_name, is_single_link) \
...@@ -60,6 +63,16 @@ int asoc_simple_card_parse_dai(struct device_node *node, ...@@ -60,6 +63,16 @@ int asoc_simple_card_parse_dai(struct device_node *node,
const char *cells_name, const char *cells_name,
int *is_single_links); 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, int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai); struct asoc_simple_dai *simple_dai);
......
...@@ -803,6 +803,8 @@ struct snd_soc_component_driver { ...@@ -803,6 +803,8 @@ struct snd_soc_component_driver {
int (*of_xlate_dai_name)(struct snd_soc_component *component, int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args, struct of_phandle_args *args,
const char **dai_name); 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, void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
int subseq); int subseq);
int (*stream_event)(struct snd_soc_component *, int event); int (*stream_event)(struct snd_soc_component *, int event);
...@@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, ...@@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
const char *prefix, const char *prefix,
struct device_node **bitclkmaster, struct device_node **bitclkmaster,
struct device_node **framemaster); 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, int snd_soc_get_dai_name(struct of_phandle_args *args,
const char **dai_name); const char **dai_name);
int snd_soc_of_get_dai_name(struct device_node *of_node, int snd_soc_of_get_dai_name(struct device_node *of_node,
......
...@@ -14,3 +14,20 @@ config SND_SIMPLE_SCU_CARD ...@@ -14,3 +14,20 @@ config SND_SIMPLE_SCU_CARD
help help
This option enables generic simple SCU sound card support. This option enables generic simple SCU sound card support.
It supports DPCM of multi CPU single Codec system. 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.
snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o snd-soc-simple-card-objs := simple-card.o
snd-soc-simple-scu-card-objs := simple-scu-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_UTILS) += snd-soc-simple-card-utils.o
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.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_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
/*
* 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>");
/*
* 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>");
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_graph.h>
#include <sound/simple_card_utils.h> #include <sound/simple_card_utils.h>
int asoc_simple_card_parse_daifmt(struct device *dev, int asoc_simple_card_parse_daifmt(struct device *dev,
...@@ -20,14 +21,13 @@ 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 *bitclkmaster = NULL;
struct device_node *framemaster = NULL; struct device_node *framemaster = NULL;
int prefix_len = prefix ? strlen(prefix) : 0;
unsigned int daifmt; unsigned int daifmt;
daifmt = snd_soc_of_parse_daifmt(node, prefix, daifmt = snd_soc_of_parse_daifmt(node, prefix,
&bitclkmaster, &framemaster); &bitclkmaster, &framemaster);
daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 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 * No dai-link level and master setting was not found from
* sound node level, revert back to legacy DT parsing and * sound node level, revert back to legacy DT parsing and
...@@ -51,6 +51,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev, ...@@ -51,6 +51,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
*retfmt = daifmt; *retfmt = daifmt;
dev_dbg(dev, "format : %04x\n", daifmt);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
...@@ -72,6 +74,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev, ...@@ -72,6 +74,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
dai_link->name = name; dai_link->name = name;
dai_link->stream_name = name; dai_link->stream_name = name;
dev_dbg(dev, "name : %s\n", name);
} }
return ret; return ret;
...@@ -81,19 +85,27 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name); ...@@ -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, int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
char *prefix) char *prefix)
{ {
char prop[128];
int ret; int ret;
snprintf(prop, sizeof(prop), "%sname", prefix); if (!prefix)
prefix = "";
/* Parse the card name from DT */ /* Parse the card name from DT */
ret = snd_soc_of_parse_card_name(card, prop); ret = snd_soc_of_parse_card_name(card, "label");
if (ret < 0) if (ret < 0) {
return ret; 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) if (!card->name && card->dai_link)
card->name = card->dai_link->name; card->name = card->dai_link->name;
dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
...@@ -101,7 +113,8 @@ 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, int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *node, struct device_node *node,
struct device_node *dai_of_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; struct clk *clk;
u32 val; u32 val;
...@@ -124,6 +137,8 @@ int asoc_simple_card_parse_clk(struct device *dev, ...@@ -124,6 +137,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
simple_dai->sysclk = clk_get_rate(clk); simple_dai->sysclk = clk_get_rate(clk);
} }
dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
...@@ -165,6 +180,71 @@ int asoc_simple_card_parse_dai(struct device_node *node, ...@@ -165,6 +180,71 @@ int asoc_simple_card_parse_dai(struct device_node *node,
} }
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); 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, int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai) struct asoc_simple_dai *simple_dai)
{ {
......
...@@ -301,15 +301,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, ...@@ -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->ops = &asoc_simple_card_ops;
dai_link->init = asoc_simple_card_dai_init; 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); asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
dai_link_of_err: dai_link_of_err:
...@@ -497,8 +488,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) ...@@ -497,8 +488,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv); snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card); ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0) if (ret < 0)
return ret; goto err;
return 0;
err: err:
asoc_simple_card_clean_reference(card); asoc_simple_card_clean_reference(card);
......
...@@ -189,21 +189,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, ...@@ -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->ops = &asoc_simple_card_ops;
dai_link->init = asoc_simple_card_dai_init; 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; return 0;
} }
static int asoc_simple_card_parse_of(struct device_node *node, static int asoc_simple_card_parse_of(struct simple_card_data *priv)
struct simple_card_data *priv)
{ {
struct device *dev = simple_priv_to_dev(priv); struct device *dev = simple_priv_to_dev(priv);
struct device_node *np; struct device_node *np;
struct snd_soc_card *card = simple_priv_to_card(priv); struct snd_soc_card *card = simple_priv_to_card(priv);
struct device_node *node = dev->of_node;
unsigned int daifmt = 0; unsigned int daifmt = 0;
bool is_fe; bool is_fe;
int ret, i; int ret, i;
...@@ -246,8 +241,6 @@ static int asoc_simple_card_parse_of(struct device_node *node, ...@@ -246,8 +241,6 @@ static int asoc_simple_card_parse_of(struct device_node *node,
if (ret < 0) if (ret < 0)
return ret; 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_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
...@@ -288,7 +281,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) ...@@ -288,7 +281,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
card->codec_conf = &priv->codec_conf; card->codec_conf = &priv->codec_conf;
card->num_configs = 1; card->num_configs = 1;
ret = asoc_simple_card_parse_of(np, priv); ret = asoc_simple_card_parse_of(priv);
if (ret < 0) { if (ret < 0) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret); dev_err(dev, "parse error %d\n", ret);
...@@ -298,8 +291,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) ...@@ -298,8 +291,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv); snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card); ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0) if (ret < 0)
return ret; goto err;
return 0;
err: err:
asoc_simple_card_clean_reference(card); asoc_simple_card_clean_reference(card);
......
...@@ -38,7 +38,7 @@ config SND_SOC_RCAR ...@@ -38,7 +38,7 @@ config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support" tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on COMMON_CLK depends on COMMON_CLK
depends on OF || COMPILE_TEST depends on OF || COMPILE_TEST
select SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS
select REGMAP_MMIO select REGMAP_MMIO
help help
This option enables R-Car SRU/SCU/SSIU/SSI sound support This option enables R-Car SRU/SCU/SSIU/SSI sound support
......
...@@ -308,23 +308,12 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) ...@@ -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) int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
{
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 rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct clk *clk; struct clk *clk;
int i; int i;
u32 data;
u32 ckr = 0;
int sel_table[] = { int sel_table[] = {
[CLKA] = 0x1, [CLKA] = 0x1,
[CLKB] = 0x2, [CLKB] = 0x2,
...@@ -338,30 +327,42 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) ...@@ -338,30 +327,42 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
* find suitable clock from * find suitable clock from
* AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
*/ */
data = 0;
for_each_rsnd_clk(clk, adg, i) { for_each_rsnd_clk(clk, adg, i) {
if (rate == clk_get_rate(clk)) { if (rate == clk_get_rate(clk))
data = sel_table[i]; return sel_table[i];
goto found_clock;
}
} }
/* /*
* find divided clock from BRGA/BRGB * find divided clock from BRGA/BRGB
*/ */
if (rate == adg->rbga_rate_for_441khz) { if (rate == adg->rbga_rate_for_441khz)
data = 0x10; return 0x10;
goto found_clock;
}
if (rate == adg->rbgb_rate_for_48khz) { if (rate == adg->rbgb_rate_for_48khz)
data = 0x20; return 0x20;
goto found_clock;
}
return -EIO; 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); rsnd_adg_set_ssi_clk(ssi_mod, data);
...@@ -480,6 +481,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, ...@@ -480,6 +481,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
if (req_rate[0] % 48000 == 0) if (req_rate[0] % 48000 == 0)
adg->flags = AUDIO_OUT_48; 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 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
* have 44.1kHz or 48kHz base clocks for now. * have 44.1kHz or 48kHz base clocks for now.
...@@ -555,7 +559,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, ...@@ -555,7 +559,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
clk = clk_register_fixed_rate(dev, clkout_name[i], clk = clk_register_fixed_rate(dev, clkout_name[i],
parent_clk_name, 0, parent_clk_name, 0,
req_rate[0]); req_rate[0]);
adg->clkout[i] = ERR_PTR(-ENOENT);
if (!IS_ERR(clk)) if (!IS_ERR(clk))
adg->clkout[i] = clk; adg->clkout[i] = clk;
} }
...@@ -580,7 +583,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) ...@@ -580,7 +583,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
{ {
struct rsnd_adg *adg; struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
int ret; int ret;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
...@@ -597,9 +599,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) ...@@ -597,9 +599,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg); rsnd_adg_get_clkout(priv, adg);
if (of_get_property(np, "clkout-lr-asynchronous", NULL))
adg->flags = LRCLK_ASYNC;
priv->adg = adg; priv->adg = adg;
rsnd_adg_clk_enable(priv); rsnd_adg_clk_enable(priv);
......
...@@ -31,7 +31,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, ...@@ -31,7 +31,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
u32 data; u32 data;
u32 path[] = { static const u32 path[] = {
[1] = 1 << 0, [1] = 1 << 0,
[5] = 1 << 8, [5] = 1 << 8,
[6] = 1 << 12, [6] = 1 << 12,
...@@ -71,7 +71,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, ...@@ -71,7 +71,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
} else { } else {
struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io);
u8 cmd_case[] = { static const u8 cmd_case[] = {
[0] = 0x3, [0] = 0x3,
[1] = 0x3, [1] = 0x3,
[2] = 0x4, [2] = 0x4,
...@@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, ...@@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
[9] = 0x2, [9] = 0x2,
}; };
if (unlikely(!src))
return -EIO;
data = path[rsnd_mod_id(src)] | data = path[rsnd_mod_id(src)] |
cmd_case[rsnd_mod_id(src)] << 16; cmd_case[rsnd_mod_id(src)] << 16;
} }
......
...@@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) ...@@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
return !!io->substream; 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) int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
{ {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(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) ...@@ -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) 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) ? int chan = rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(io); rsnd_runtime_channel_original(io);
/* Use Multi SSI */ /* Use Multi SSI */
if (rsnd_runtime_is_ssi_multi(io)) 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 */ /* TDM Extend Mode needs 8ch */
if (chan == 6) if (chan == 6)
...@@ -265,12 +245,13 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) ...@@ -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 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) ? int chan = rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(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) 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) ...@@ -310,6 +291,24 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
u32 val = 0x76543210; u32 val = 0x76543210;
u32 mask = ~0; 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)) { if (rsnd_io_is_play(io)) {
struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io);
...@@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status, ...@@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status,
#define rsnd_dai_call(fn, io, param...) \ #define rsnd_dai_call(fn, io, param...) \
({ \ ({ \
struct rsnd_priv *priv = rsnd_io_to_priv(io); \ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \
struct device *dev = rsnd_priv_to_dev(priv); \
struct rsnd_mod *mod; \ struct rsnd_mod *mod; \
int is_play = rsnd_io_is_play(io); \ int is_play = rsnd_io_is_play(io); \
int ret = 0, i; \ int ret = 0, i; \
...@@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod, ...@@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod,
io->mod[type] = NULL; 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) struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
{ {
if ((id < 0) || (id >= rsnd_rdai_nr(priv))) 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) ...@@ -551,40 +567,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
/* /*
* rsnd_soc_dai functions * 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) void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
{ {
struct snd_pcm_substream *substream = io->substream; struct snd_pcm_substream *substream = io->substream;
...@@ -602,15 +584,7 @@ void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) ...@@ -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, static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime;
io->substream = substream; 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) 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, ...@@ -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); struct device *dev = rsnd_priv_to_dev(priv);
switch (slots) { switch (slots) {
case 2:
case 6: case 6:
case 8:
case 16:
/* TDM Extend Mode */ /* TDM Extend Mode */
rsnd_set_slot(rdai, slots, 1); rsnd_rdai_channels_set(rdai, slots);
rsnd_rdai_ssi_lane_set(rdai, 1);
break; break;
default: default:
dev_err(dev, "unsupported TDM slots (%d)\n", slots); 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, ...@@ -761,22 +739,177 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0; 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, static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct rsnd_dai *rdai = rsnd_dai_to_rdai(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); 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 * 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, static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct rsnd_dai *rdai = rsnd_dai_to_rdai(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); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
/* /*
...@@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, ...@@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
of_node_put(node); 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_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 device_node *playback, *capture;
struct rsnd_dai_stream *io_playback; struct rsnd_dai_stream *io_playback;
struct rsnd_dai_stream *io_capture; struct rsnd_dai_stream *io_capture;
struct snd_soc_dai_driver *rdrv, *drv; struct snd_soc_dai_driver *drv;
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
int nr, dai_i, io_i; int io_i;
int ret;
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); rsnd_parse_connect_ssi(rdai, playback, capture);
nr = of_get_child_count(dai_node); rsnd_parse_connect_src(rdai, playback, capture);
if (!nr) { rsnd_parse_connect_ctu(rdai, playback, capture);
ret = -EINVAL; rsnd_parse_connect_mix(rdai, playback, capture);
goto rsnd_dai_probe_done; 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); rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
if (!rdrv || !rdai) { if (!rdrv || !rdai)
ret = -ENOMEM; return -ENOMEM;
goto rsnd_dai_probe_done;
}
priv->rdai_nr = nr; priv->rdai_nr = nr;
priv->daidrv = rdrv; priv->daidrv = rdrv;
...@@ -855,68 +1088,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) ...@@ -855,68 +1088,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
* parse all dai * parse all dai
*/ */
dai_i = 0; dai_i = 0;
for_each_child_of_node(dai_node, dai_np) { if (is_graph) {
rdai = rsnd_rdai_get(priv, dai_i); for_each_endpoint_of_node(dai_node, dai_np) {
drv = rdrv + dai_i; __rsnd_dai_probe(priv, dai_np, dai_i, is_graph);
io_playback = &rdai->playback; rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i);
io_capture = &rdai->capture; dai_i++;
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);
} }
} else {
dai_i++; for_each_child_of_node(dai_node, dai_np)
__rsnd_dai_probe(priv, dai_np, dai_i++, is_graph);
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" : " -- ");
} }
ret = 0; return 0;
rsnd_dai_probe_done:
of_node_put(dai_node);
return ret;
} }
/* /*
...@@ -965,12 +1148,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, ...@@ -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) 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 snd_soc_dai *dai = rsnd_substream_to_dai(substream);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); 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 = { static struct snd_pcm_ops rsnd_pcm_ops = {
...@@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, ...@@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
int i, change = 0; int i, change = 0;
if (!cfg->accept(cfg->io))
return 0;
for (i = 0; i < cfg->size; i++) { for (i = 0; i < cfg->size; i++) {
if (cfg->texts) { if (cfg->texts) {
change |= (uc->value.enumerated.item[i] != cfg->val[i]); change |= (uc->value.enumerated.item[i] != cfg->val[i]);
...@@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, ...@@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
return change; 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) struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
{ {
cfg->cfg.val = cfg->val; cfg->cfg.val = cfg->val;
...@@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, ...@@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd, struct snd_soc_pcm_runtime *rtd,
const unsigned char *name, const unsigned char *name,
int (*accept)(struct rsnd_dai_stream *io),
void (*update)(struct rsnd_dai_stream *io, void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod), struct rsnd_mod *mod),
struct rsnd_kctrl_cfg *cfg, struct rsnd_kctrl_cfg *cfg,
...@@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, ...@@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
cfg->texts = texts; cfg->texts = texts;
cfg->max = max; cfg->max = max;
cfg->size = size; cfg->size = size;
cfg->accept = accept;
cfg->update = update; cfg->update = update;
cfg->card = card; cfg->card = card;
cfg->kctrl = kctrl; cfg->kctrl = kctrl;
...@@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev) ...@@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev)
return 0; return 0;
} }
static struct dev_pm_ops rsnd_pm_ops = { static const struct dev_pm_ops rsnd_pm_ops = {
.suspend = rsnd_suspend, .suspend = rsnd_suspend,
.resume = rsnd_resume, .resume = rsnd_resume,
}; };
......
...@@ -279,12 +279,14 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ...@@ -279,12 +279,14 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
/* CTU Pass */ /* CTU Pass */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
rsnd_kctrl_accept_anytime,
NULL, NULL,
&ctu->pass, RSND_MAX_CHANNELS, &ctu->pass, RSND_MAX_CHANNELS,
0xC); 0xC);
/* ROW0 */ /* ROW0 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
rsnd_kctrl_accept_anytime,
NULL, NULL,
&ctu->sv0, RSND_MAX_CHANNELS, &ctu->sv0, RSND_MAX_CHANNELS,
0x00FFFFFF); 0x00FFFFFF);
...@@ -293,6 +295,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ...@@ -293,6 +295,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
/* ROW1 */ /* ROW1 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
rsnd_kctrl_accept_anytime,
NULL, NULL,
&ctu->sv1, RSND_MAX_CHANNELS, &ctu->sv1, RSND_MAX_CHANNELS,
0x00FFFFFF); 0x00FFFFFF);
...@@ -301,6 +304,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ...@@ -301,6 +304,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
/* ROW2 */ /* ROW2 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
rsnd_kctrl_accept_anytime,
NULL, NULL,
&ctu->sv2, RSND_MAX_CHANNELS, &ctu->sv2, RSND_MAX_CHANNELS,
0x00FFFFFF); 0x00FFFFFF);
...@@ -309,6 +313,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ...@@ -309,6 +313,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
/* ROW3 */ /* ROW3 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
rsnd_kctrl_accept_anytime,
NULL, NULL,
&ctu->sv3, RSND_MAX_CHANNELS, &ctu->sv3, RSND_MAX_CHANNELS,
0x00FFFFFF); 0x00FFFFFF);
...@@ -317,6 +322,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ...@@ -317,6 +322,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
/* Reset */ /* Reset */
ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
rsnd_kctrl_accept_anytime,
rsnd_ctu_value_reset, rsnd_ctu_value_reset,
&ctu->reset, 1); &ctu->reset, 1);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
struct rsnd_dmaen { struct rsnd_dmaen {
struct dma_chan *chan; struct dma_chan *chan;
dma_cookie_t cookie;
dma_addr_t dma_buf; dma_addr_t dma_buf;
unsigned int dma_len; unsigned int dma_len;
unsigned int dma_period; unsigned int dma_period;
...@@ -103,10 +104,6 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, ...@@ -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. * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
* But, Audio-DMAC-peri-peri doesn't have interrupt, * But, Audio-DMAC-peri-peri doesn't have interrupt,
* and this driver is assuming that here. * 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); spin_lock_irqsave(&priv->lock, flags);
...@@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, ...@@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
*/ */
rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2); rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); elapsed = true;
dmaen->dma_cnt++; dmaen->dma_cnt++;
} }
...@@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, ...@@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
rsnd_dmaen_sync(dmaen, io, 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"); dev_err(dev, "dmaengine_submit() fail\n");
return -EIO; return -EIO;
} }
...@@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, ...@@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
return 0; 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 = { static struct rsnd_mod_ops rsnd_dmaen_ops = {
.name = "audmac", .name = "audmac",
.nolock_start = rsnd_dmaen_nolock_start, .nolock_start = rsnd_dmaen_nolock_start,
.nolock_stop = rsnd_dmaen_nolock_stop, .nolock_stop = rsnd_dmaen_nolock_stop,
.start = rsnd_dmaen_start, .start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop, .stop = rsnd_dmaen_stop,
.pointer= rsnd_dmaen_pointer,
}; };
/* /*
......
...@@ -249,16 +249,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -249,16 +249,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
struct snd_soc_pcm_runtime *rtd) struct snd_soc_pcm_runtime *rtd)
{ {
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); 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 is_play = rsnd_io_is_play(io);
int slots = rsnd_get_slot(io); int channels = rsnd_rdai_channels_get(rdai);
int ret; int ret;
/* Volume */ /* Volume */
ret = rsnd_kctrl_new_m(mod, io, rtd, ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ? is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume", "DVC Out Playback Volume" : "DVC In Capture Volume",
rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->volume, slots, &dvc->volume, channels,
0x00800000 - 1); 0x00800000 - 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -267,8 +269,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -267,8 +269,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_m(mod, io, rtd, ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ? is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch", "DVC Out Mute Switch" : "DVC In Mute Switch",
rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->mute, slots, &dvc->mute, channels,
1); 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -277,6 +280,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -277,6 +280,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_s(mod, io, rtd, ret = rsnd_kctrl_new_s(mod, io, rtd,
is_play ? is_play ?
"DVC Out Ramp Switch" : "DVC In Ramp Switch", "DVC Out Ramp Switch" : "DVC In Ramp Switch",
rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->ren, 1); &dvc->ren, 1);
if (ret < 0) if (ret < 0)
...@@ -285,6 +289,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -285,6 +289,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_e(mod, io, rtd, ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ? is_play ?
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->rup, &dvc->rup,
dvc_ramp_rate); dvc_ramp_rate);
...@@ -294,6 +299,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -294,6 +299,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_e(mod, io, rtd, ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ? is_play ?
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
rsnd_kctrl_accept_anytime,
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->rdown, &dvc->rdown,
dvc_ramp_rate); dvc_ramp_rate);
......
...@@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) ...@@ -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_STATUS5, 0x884),
RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), 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 */ /* FIXME: it needs SSI_MODE2/3 in the future */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/sh_dma.h> #include <linux/sh_dma.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
...@@ -170,6 +171,8 @@ enum rsnd_reg { ...@@ -170,6 +171,8 @@ enum rsnd_reg {
RSND_REG_SSI_SYS_STATUS5, RSND_REG_SSI_SYS_STATUS5,
RSND_REG_SSI_SYS_STATUS6, RSND_REG_SSI_SYS_STATUS6,
RSND_REG_SSI_SYS_STATUS7, RSND_REG_SSI_SYS_STATUS7,
RSND_REG_HDMI0_SEL,
RSND_REG_HDMI1_SEL,
/* SSI */ /* SSI */
RSND_REG_SSICR, RSND_REG_SSICR,
...@@ -268,6 +271,9 @@ struct rsnd_mod_ops { ...@@ -268,6 +271,9 @@ struct rsnd_mod_ops {
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params); 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, int (*fallback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv); struct rsnd_priv *priv);
...@@ -305,6 +311,7 @@ struct rsnd_mod { ...@@ -305,6 +311,7 @@ struct rsnd_mod {
* H 0: pcm_new * H 0: pcm_new
* H 0: fallback * H 0: fallback
* H 0: hw_params * H 0: hw_params
* H 0: pointer
*/ */
#define __rsnd_mod_shift_nolock_start 0 #define __rsnd_mod_shift_nolock_start 0
#define __rsnd_mod_shift_nolock_stop 0 #define __rsnd_mod_shift_nolock_stop 0
...@@ -318,6 +325,7 @@ struct rsnd_mod { ...@@ -318,6 +325,7 @@ struct rsnd_mod {
#define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 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_probe 0
#define __rsnd_mod_add_remove 0 #define __rsnd_mod_add_remove 0
...@@ -331,6 +339,7 @@ struct rsnd_mod { ...@@ -331,6 +339,7 @@ struct rsnd_mod {
#define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_add_pointer 0
#define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_probe 0
#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_remove 0
...@@ -342,6 +351,7 @@ struct rsnd_mod { ...@@ -342,6 +351,7 @@ struct rsnd_mod {
#define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 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_start 0
#define __rsnd_mod_call_nolock_stop 1 #define __rsnd_mod_call_nolock_stop 1
...@@ -389,11 +399,6 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, ...@@ -389,11 +399,6 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct device_node *playback, struct device_node *playback,
struct device_node *capture); 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_original(struct rsnd_dai_stream *io);
int rsnd_runtime_channel_after_ctu(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); int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
...@@ -420,13 +425,8 @@ struct rsnd_dai_stream { ...@@ -420,13 +425,8 @@ struct rsnd_dai_stream {
char name[RSND_DAI_NAME_SIZE]; char name[RSND_DAI_NAME_SIZE];
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
u32 parent_ssi_status; 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(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) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
...@@ -449,9 +449,10 @@ struct rsnd_dai { ...@@ -449,9 +449,10 @@ struct rsnd_dai {
struct rsnd_dai_stream playback; struct rsnd_dai_stream playback;
struct rsnd_dai_stream capture; struct rsnd_dai_stream capture;
struct rsnd_priv *priv; struct rsnd_priv *priv;
struct snd_pcm_hw_constraint_list constraint;
int slots; int max_channels; /* 2ch - 16ch */
int slots_num; int ssi_lane; /* 1lane - 4lane */
unsigned int clk_master:1; unsigned int clk_master:1;
unsigned int bit_clk_inv:1; unsigned int bit_clk_inv:1;
...@@ -471,13 +472,24 @@ struct rsnd_dai { ...@@ -471,13 +472,24 @@ struct rsnd_dai {
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); 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); 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, int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
enum rsnd_mod_type type); enum rsnd_mod_type type);
#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI)
/* /*
* R-Car Gen1/Gen2 * R-Car Gen1/Gen2
...@@ -491,6 +503,7 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); ...@@ -491,6 +503,7 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
/* /*
* R-Car ADG * 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_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct rsnd_priv *priv); int rsnd_adg_probe(struct rsnd_priv *priv);
...@@ -596,6 +609,7 @@ struct rsnd_kctrl_cfg { ...@@ -596,6 +609,7 @@ struct rsnd_kctrl_cfg {
unsigned int size; unsigned int size;
u32 *val; u32 *val;
const char * const *texts; const char * const *texts;
int (*accept)(struct rsnd_dai_stream *io);
void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
struct rsnd_dai_stream *io; struct rsnd_dai_stream *io;
struct snd_card *card; struct snd_card *card;
...@@ -613,12 +627,15 @@ struct rsnd_kctrl_cfg_s { ...@@ -613,12 +627,15 @@ struct rsnd_kctrl_cfg_s {
u32 val; 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_m(struct rsnd_kctrl_cfg_m *cfg);
struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
int rsnd_kctrl_new(struct rsnd_mod *mod, int rsnd_kctrl_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd, struct snd_soc_pcm_runtime *rtd,
const unsigned char *name, const unsigned char *name,
int (*accept)(struct rsnd_dai_stream *io),
void (*update)(struct rsnd_dai_stream *io, void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod), struct rsnd_mod *mod),
struct rsnd_kctrl_cfg *cfg, struct rsnd_kctrl_cfg *cfg,
...@@ -626,16 +643,16 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, ...@@ -626,16 +643,16 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
int size, int size,
u32 max); u32 max);
#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \ #define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \
rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \
NULL, size, max) NULL, size, max)
#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max) \ #define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \
rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
NULL, 1, max) NULL, 1, max)
#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts) \ #define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts) \
rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
texts, 1, ARRAY_SIZE(texts)) texts, 1, ARRAY_SIZE(texts))
/* /*
...@@ -648,6 +665,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); ...@@ -648,6 +665,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves_runtime(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) \ #define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
...@@ -656,6 +680,8 @@ 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, void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
struct device_node *playback, struct device_node *playback,
struct device_node *capture); struct device_node *capture);
unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
int param1, int param2, int *idx);
/* /*
* R-Car SSIU * R-Car SSIU
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
#define SRC_NAME "src" #define SRC_NAME "src"
/* SRCx_STATUS */
#define OUF_SRCO ((1 << 12) | (1 << 13))
#define OUF_SRCI ((1 << 9) | (1 << 8))
/* SCU_SYSTEM_STATUS0/1 */ /* SCU_SYSTEM_STATUS0/1 */
#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
...@@ -55,20 +51,6 @@ struct rsnd_src { ...@@ -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) static void rsnd_src_activation(struct rsnd_mod *mod)
{ {
rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 0);
...@@ -515,6 +497,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, ...@@ -515,6 +497,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
rsnd_io_is_play(io) ? rsnd_io_is_play(io) ?
"SRC Out Rate Switch" : "SRC Out Rate Switch" :
"SRC In Rate Switch", "SRC In Rate Switch",
rsnd_kctrl_accept_anytime,
rsnd_src_set_convert_rate, rsnd_src_set_convert_rate,
&src->sen, 1); &src->sen, 1);
if (ret < 0) if (ret < 0)
...@@ -524,6 +507,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, ...@@ -524,6 +507,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
rsnd_io_is_play(io) ? rsnd_io_is_play(io) ?
"SRC Out Rate" : "SRC Out Rate" :
"SRC In Rate", "SRC In Rate",
rsnd_kctrl_accept_runtime,
rsnd_src_set_convert_rate, rsnd_src_set_convert_rate,
&src->sync, 192000); &src->sync, 192000);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <sound/simple_card_utils.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "rsnd.h" #include "rsnd.h"
#define RSND_SSI_NAME_SIZE 16 #define RSND_SSI_NAME_SIZE 16
...@@ -76,11 +77,18 @@ struct rsnd_ssi { ...@@ -76,11 +77,18 @@ struct rsnd_ssi {
int rate; int rate;
int irq; int irq;
unsigned int usrcnt; unsigned int usrcnt;
int byte_pos;
int period_pos;
int byte_per_period;
int next_period_byte;
}; };
/* flags */ /* flags */
#define RSND_SSI_CLK_PIN_SHARE (1 << 0) #define RSND_SSI_CLK_PIN_SHARE (1 << 0)
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ #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) \ #define for_each_rsnd_ssi(pos, priv, i) \
for (i = 0; \ for (i = 0; \
...@@ -99,6 +107,20 @@ struct rsnd_ssi { ...@@ -99,6 +107,20 @@ struct rsnd_ssi {
#define rsnd_ssi_is_run_mods(mod, io) \ #define rsnd_ssi_is_run_mods(mod, io) \
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) (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) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{ {
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(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) ...@@ -186,6 +208,46 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
return 0; 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, static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io)
{ {
...@@ -195,10 +257,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, ...@@ -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_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
int chan = rsnd_runtime_channel_for_ssi(io); int chan = rsnd_runtime_channel_for_ssi(io);
int j, ret; int idx, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
unsigned int main_rate; unsigned int main_rate;
unsigned int rate = rsnd_io_is_play(io) ? unsigned int rate = rsnd_io_is_play(io) ?
rsnd_src_get_out_rate(priv, io) : rsnd_src_get_out_rate(priv, io) :
...@@ -222,45 +281,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, ...@@ -222,45 +281,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
return 0; return 0;
} }
/* main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx);
* Find best clock, and try to start ADG if (!main_rate) {
*/ dev_err(dev, "unsupported clock rate\n");
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { return -EIO;
}
/*
* 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;
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", ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx);
rsnd_mod_name(mod), ssi->wsr = CONT;
rsnd_mod_id(mod), rate); 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 0;
return -EIO;
} }
static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, 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) ...@@ -357,6 +396,59 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
ssi->cr_mode); /* without EN */ 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 * SSI mod common functions
*/ */
...@@ -370,6 +462,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, ...@@ -370,6 +462,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
if (!rsnd_ssi_is_run_mods(mod, io)) if (!rsnd_ssi_is_run_mods(mod, io))
return 0; return 0;
rsnd_ssi_pointer_init(mod, io);
ssi->usrcnt++; ssi->usrcnt++;
rsnd_mod_power_on(mod); rsnd_mod_power_on(mod);
...@@ -549,7 +643,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, ...@@ -549,7 +643,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!is_dma && (status & DIRQ)) { if (!is_dma && (status & DIRQ)) {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 *buf = (u32 *)(runtime->dma_area + u32 *buf = (u32 *)(runtime->dma_area +
rsnd_dai_pointer_offset(io, 0)); rsnd_ssi_pointer_offset(mod, io, 0));
int shift = 0; int shift = 0;
switch (runtime->sample_bits) { switch (runtime->sample_bits) {
...@@ -568,7 +662,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, ...@@ -568,7 +662,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
else else
*buf = (rsnd_mod_read(mod, SSIRDR) >> shift); *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 */ /* DMA only */
...@@ -675,6 +769,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, ...@@ -675,6 +769,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
return ret; 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 = { static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = SSI_NAME, .name = SSI_NAME,
.probe = rsnd_ssi_common_probe, .probe = rsnd_ssi_common_probe,
...@@ -683,6 +789,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { ...@@ -683,6 +789,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.start = rsnd_ssi_start, .start = rsnd_ssi_start,
.stop = rsnd_ssi_stop, .stop = rsnd_ssi_stop,
.irq = rsnd_ssi_irq, .irq = rsnd_ssi_irq,
.pointer= rsnd_ssi_pointer,
.pcm_new = rsnd_ssi_pcm_new, .pcm_new = rsnd_ssi_pcm_new,
.hw_params = rsnd_ssi_hw_params, .hw_params = rsnd_ssi_hw_params,
}; };
...@@ -786,13 +893,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) ...@@ -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 * ssi mod function
*/ */
...@@ -814,7 +914,8 @@ static void rsnd_ssi_connect(struct rsnd_mod *mod, ...@@ -814,7 +914,8 @@ static void rsnd_ssi_connect(struct rsnd_mod *mod,
type = types[i]; type = types[i];
if (!rsnd_io_to_mod(io, type)) { if (!rsnd_io_to_mod(io, type)) {
rsnd_dai_connect(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; return;
} }
} }
...@@ -847,6 +948,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, ...@@ -847,6 +948,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
of_node_put(node); 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) struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
{ {
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
...@@ -952,7 +1094,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ...@@ -952,7 +1094,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
goto rsnd_ssi_probe_done; goto rsnd_ssi_probe_done;
} }
ops = &rsnd_ssi_non_ops;
if (of_property_read_bool(np, "pio-transfer")) if (of_property_read_bool(np, "pio-transfer"))
ops = &rsnd_ssi_pio_ops; ops = &rsnd_ssi_pio_ops;
else else
......
...@@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, ...@@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
int hdmi = rsnd_ssi_hdmi_port(io);
int ret; int ret;
ret = rsnd_ssiu_init(mod, io, priv); ret = rsnd_ssiu_init(mod, io, priv);
...@@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, ...@@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
rsnd_get_dalign(mod, io)); 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; return 0;
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/jack.h> #include <sound/jack.h>
...@@ -3992,11 +3993,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, ...@@ -3992,11 +3993,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
prefix = ""; prefix = "";
/* /*
* check "[prefix]format = xxx" * check "dai-format = xxx"
* or "[prefix]format = xxx"
* SND_SOC_DAIFMT_FORMAT_MASK area * SND_SOC_DAIFMT_FORMAT_MASK area
*/ */
snprintf(prop, sizeof(prop), "%sformat", prefix); ret = of_property_read_string(np, "dai-format", &str);
ret = of_property_read_string(np, prop, &str); if (ret < 0) {
snprintf(prop, sizeof(prop), "%sformat", prefix);
ret = of_property_read_string(np, prop, &str);
}
if (ret == 0) { if (ret == 0) {
for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
if (strcmp(str, of_fmt_table[i].name) == 0) { if (strcmp(str, of_fmt_table[i].name) == 0) {
...@@ -4076,6 +4081,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, ...@@ -4076,6 +4081,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
} }
EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 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, int snd_soc_get_dai_name(struct of_phandle_args *args,
const char **dai_name) const char **dai_name)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment