mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-28 04:17:47 +00:00
Merge branch 'topic/of-graph' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-rcar
This commit is contained in:
commit
5ea929467d
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";
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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,19 +32,11 @@ 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
|
||||
|
||||
@ -59,11 +45,10 @@ sound {
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -60,6 +60,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,11 @@ 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.
|
||||
|
@ -1,7 +1,9 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
310
sound/soc/generic/audio-graph-card.c
Normal file
310
sound/soc/generic/audio-graph-card.c
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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,
|
||||
cpu_dai->sysclk);
|
||||
dev_dbg(dev, "\tcodec : %s / %d\n",
|
||||
dai_link->codec_dai_name,
|
||||
codec_dai->sysclk);
|
||||
|
||||
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>");
|
@ -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
|
||||
@ -81,15 +81,21 @@ 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;
|
||||
@ -165,6 +171,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)
|
||||
{
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
@ -3960,11 +3961,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
|
||||
prefix = "";
|
||||
|
||||
/*
|
||||
* check "[prefix]format = xxx"
|
||||
* check "dai-format = xxx"
|
||||
* or "[prefix]format = xxx"
|
||||
* SND_SOC_DAIFMT_FORMAT_MASK area
|
||||
*/
|
||||
snprintf(prop, sizeof(prop), "%sformat", prefix);
|
||||
ret = of_property_read_string(np, prop, &str);
|
||||
ret = of_property_read_string(np, "dai-format", &str);
|
||||
if (ret < 0) {
|
||||
snprintf(prop, sizeof(prop), "%sformat", prefix);
|
||||
ret = of_property_read_string(np, prop, &str);
|
||||
}
|
||||
if (ret == 0) {
|
||||
for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
|
||||
if (strcmp(str, of_fmt_table[i].name) == 0) {
|
||||
@ -4044,6 +4049,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
|
||||
|
||||
int snd_soc_get_dai_id(struct device_node *ep)
|
||||
{
|
||||
struct snd_soc_component *pos;
|
||||
struct device_node *node;
|
||||
int ret;
|
||||
|
||||
node = of_graph_get_port_parent(ep);
|
||||
|
||||
/*
|
||||
* For example HDMI case, HDMI has video/sound port,
|
||||
* but ALSA SoC needs sound port number only.
|
||||
* Thus counting HDMI DT port/endpoint doesn't work.
|
||||
* Then, it should have .of_xlate_dai_id
|
||||
*/
|
||||
ret = -ENOTSUPP;
|
||||
mutex_lock(&client_mutex);
|
||||
list_for_each_entry(pos, &component_list, list) {
|
||||
struct device_node *component_of_node = pos->dev->of_node;
|
||||
|
||||
if (!component_of_node && pos->dev->parent)
|
||||
component_of_node = pos->dev->parent->of_node;
|
||||
|
||||
if (component_of_node != node)
|
||||
continue;
|
||||
|
||||
if (pos->driver->of_xlate_dai_id)
|
||||
ret = pos->driver->of_xlate_dai_id(pos, ep);
|
||||
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&client_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
|
||||
|
||||
int snd_soc_get_dai_name(struct of_phandle_args *args,
|
||||
const char **dai_name)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user