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