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:
Mark Brown 2017-05-29 14:27:19 +01:00
commit 5ea929467d
11 changed files with 676 additions and 61 deletions

View 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";
...
};
};
};
};

View File

@ -1,35 +1,29 @@
ASoC simple SCU Sound Card ASoC Simple SCU Sound Card
Simple-Card specifies audio DAI connections of SoC <-> codec. Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
For example, you can use this driver if you want to exchange sampling rate convert,
Mixing, etc...
Required properties: Required properties:
- compatible : "simple-scu-audio-card" - compatible : "simple-scu-audio-card"
"renesas,rsrc-card" "renesas,rsrc-card"
Optional properties: Optional properties:
- simple-audio-card,name : User specified audio sound card name, one string - simple-audio-card,name : see simple-audio-card.txt
property. - simple-audio-card,cpu : see simple-audio-card.txt
- simple-audio-card,cpu : CPU sub-node - simple-audio-card,codec : see simple-audio-card.txt
- simple-audio-card,codec : CODEC sub-node
Optional subnode properties: Optional subnode properties:
- simple-audio-card,format : CPU/CODEC common audio format. - simple-audio-card,format : see simple-audio-card.txt
"i2s", "right_j", "left_j" , "dsp_a" - simple-audio-card,frame-master : see simple-audio-card.txt
"dsp_b", "ac97", "pdm", "msb", "lsb" - simple-audio-card,bitclock-master : see simple-audio-card.txt
- simple-audio-card,frame-master : Indicates dai-link frame master. - simple-audio-card,bitclock-inversion : see simple-audio-card.txt
phandle to a cpu or codec subnode. - simple-audio-card,frame-inversion : see simple-audio-card.txt
- simple-audio-card,bitclock-master : Indicates dai-link bit clock master.
phandle to a cpu or codec subnode.
- simple-audio-card,bitclock-inversion : bool property. Add this if the
dai-link uses bit clock inversion.
- simple-audio-card,frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- simple-audio-card,convert-rate : platform specified sampling rate convert - simple-audio-card,convert-rate : platform specified sampling rate convert
- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) - simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch)
- simple-audio-card,prefix : see audio-routing - simple-audio-card,prefix : see routing
- simple-audio-card,routing : A list of the connections between audio components. - simple-audio-card,routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink, Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources. the second being the connection's source. Valid names for sources.
@ -38,19 +32,11 @@ Optional subnode properties:
Required CPU/CODEC subnodes properties: Required CPU/CODEC subnodes properties:
- sound-dai : phandle and port of CPU/CODEC - sound-dai : see simple-audio-card.txt
Optional CPU/CODEC subnodes properties: Optional CPU/CODEC subnodes properties:
- clocks / system-clock-frequency : specify subnode's clock if needed. - clocks / system-clock-frequency : see simple-audio-card.txt
it can be specified via "clocks" if system has
clock node (= common clock), or "system-clock-frequency"
(if system doens't support common clock)
If a clock is specified, it is
enabled with clk_prepare_enable()
in dai startup() and disabled with
clk_disable_unprepare() in dai
shutdown().
Example 1. Sampling Rate Covert Example 1. Sampling Rate Covert
@ -59,11 +45,10 @@ sound {
simple-audio-card,name = "rsnd-ak4643"; simple-audio-card,name = "rsnd-ak4643";
simple-audio-card,format = "left_j"; simple-audio-card,format = "left_j";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>; simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>; simple-audio-card,frame-master = <&sndcodec>;
simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */ simple-audio-card,convert-rate = <48000>;
simple-audio-card,prefix = "ak4642"; simple-audio-card,prefix = "ak4642";
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
@ -79,20 +64,18 @@ sound {
}; };
}; };
Example 2. 2 CPU 1 Codec Example 2. 2 CPU 1 Codec (Mixing)
sound { sound {
compatible = "renesas,rsrc-card"; compatible = "simple-scu-audio-card";
card-name = "rsnd-ak4643"; simple-audio-card,name = "rsnd-ak4643";
format = "left_j"; simple-audio-card,format = "left_j";
bitclock-master = <&dpcmcpu>; simple-audio-card,bitclock-master = <&dpcmcpu>;
frame-master = <&dpcmcpu>; simple-audio-card,frame-master = <&dpcmcpu>;
convert-rate = <48000>; /* see audio_clk_a */ simple-audio-card,prefix = "ak4642";
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
audio-prefix = "ak4642";
audio-routing = "ak4642 Playback", "DAI0 Playback",
"ak4642 Playback", "DAI1 Playback"; "ak4642 Playback", "DAI1 Playback";
dpcmcpu: cpu@0 { dpcmcpu: cpu@0 {

View File

@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_phandle_iterator_init);
int of_phandle_iterator_next(struct of_phandle_iterator *it) int of_phandle_iterator_next(struct of_phandle_iterator *it)
{ {
@ -1670,6 +1671,7 @@ err:
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL_GPL(of_phandle_iterator_next);
int of_phandle_iterator_args(struct of_phandle_iterator *it, int of_phandle_iterator_args(struct of_phandle_iterator *it,
uint32_t *args, uint32_t *args,
@ -2484,6 +2486,41 @@ struct device_node *of_graph_get_endpoint_by_regs(
} }
EXPORT_SYMBOL(of_graph_get_endpoint_by_regs); EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
/**
* of_graph_get_remote_endpoint() - get remote endpoint node
* @node: pointer to a local endpoint device_node
*
* Return: Remote endpoint node associated with remote endpoint node linked
* to @node. Use of_node_put() on it when done.
*/
struct device_node *of_graph_get_remote_endpoint(const struct device_node *node)
{
/* Get remote endpoint node. */
return of_parse_phandle(node, "remote-endpoint", 0);
}
EXPORT_SYMBOL(of_graph_get_remote_endpoint);
/**
* of_graph_get_port_parent() - get port's parent node
* @node: pointer to a local endpoint device_node
*
* Return: device node associated with endpoint node linked
* to @node. Use of_node_put() on it when done.
*/
struct device_node *of_graph_get_port_parent(struct device_node *node)
{
unsigned int depth;
/* Walk 3 levels up only if there is 'ports' node. */
for (depth = 3; depth && node; depth--) {
node = of_get_next_parent(node);
if (depth == 2 && of_node_cmp(node->name, "ports"))
break;
}
return node;
}
EXPORT_SYMBOL(of_graph_get_port_parent);
/** /**
* of_graph_get_remote_port_parent() - get remote port's parent node * of_graph_get_remote_port_parent() - get remote port's parent node
* @node: pointer to a local endpoint device_node * @node: pointer to a local endpoint device_node
@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node) const struct device_node *node)
{ {
struct device_node *np; struct device_node *np;
unsigned int depth;
/* Get remote endpoint node. */ /* Get remote endpoint node. */
np = of_parse_phandle(node, "remote-endpoint", 0); np = of_graph_get_remote_endpoint(node);
/* Walk 3 levels up only if there is 'ports' node. */ return of_graph_get_port_parent(np);
for (depth = 3; depth && np; depth--) {
np = of_get_next_parent(np);
if (depth == 2 && of_node_cmp(np->name, "ports"))
break;
}
return np;
} }
EXPORT_SYMBOL(of_graph_get_remote_port_parent); EXPORT_SYMBOL(of_graph_get_remote_port_parent);
@ -2522,13 +2552,25 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
struct device_node *np; struct device_node *np;
/* Get remote endpoint node. */ /* Get remote endpoint node. */
np = of_parse_phandle(node, "remote-endpoint", 0); np = of_graph_get_remote_endpoint(node);
if (!np) if (!np)
return NULL; return NULL;
return of_get_next_parent(np); return of_get_next_parent(np);
} }
EXPORT_SYMBOL(of_graph_get_remote_port); EXPORT_SYMBOL(of_graph_get_remote_port);
int of_graph_get_endpoint_count(const struct device_node *np)
{
struct device_node *endpoint;
int num = 0;
for_each_endpoint_of_node(np, endpoint)
num++;
return num;
}
EXPORT_SYMBOL(of_graph_get_endpoint_count);
/** /**
* of_graph_get_remote_node() - get remote parent device_node for given port/endpoint * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
* @node: pointer to parent device_node containing graph port/endpoint * @node: pointer to parent device_node containing graph port/endpoint

View File

@ -43,11 +43,15 @@ struct of_endpoint {
#ifdef CONFIG_OF #ifdef CONFIG_OF
int of_graph_parse_endpoint(const struct device_node *node, int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint); struct of_endpoint *endpoint);
int of_graph_get_endpoint_count(const struct device_node *np);
struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *previous); struct device_node *previous);
struct device_node *of_graph_get_endpoint_by_regs( struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg); const struct device_node *parent, int port_reg, int reg);
struct device_node *of_graph_get_remote_endpoint(
const struct device_node *node);
struct device_node *of_graph_get_port_parent(struct device_node *node);
struct device_node *of_graph_get_remote_port_parent( struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node); const struct device_node *node);
struct device_node *of_graph_get_remote_port(const struct device_node *node); struct device_node *of_graph_get_remote_port(const struct device_node *node);
@ -61,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
return -ENOSYS; return -ENOSYS;
} }
static inline int of_graph_get_endpoint_count(const struct device_node *np)
{
return 0;
}
static inline struct device_node *of_graph_get_port_by_id( static inline struct device_node *of_graph_get_port_by_id(
struct device_node *node, u32 id) struct device_node *node, u32 id)
{ {
@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs(
return NULL; return NULL;
} }
static inline struct device_node *of_graph_get_remote_endpoint(
const struct device_node *node)
{
return NULL;
}
static inline struct device_node *of_graph_get_port_parent(
struct device_node *node)
{
return NULL;
}
static inline struct device_node *of_graph_get_remote_port_parent( static inline struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node) const struct device_node *node)
{ {

View File

@ -60,6 +60,16 @@ int asoc_simple_card_parse_dai(struct device_node *node,
const char *cells_name, const char *cells_name,
int *is_single_links); int *is_single_links);
#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \
&dai_link->cpu_dai_name)
#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \
&dai_link->codec_dai_name)
int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **endpoint_np,
const char **dai_name);
int asoc_simple_card_init_dai(struct snd_soc_dai *dai, int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai); struct asoc_simple_dai *simple_dai);

View File

@ -803,6 +803,8 @@ struct snd_soc_component_driver {
int (*of_xlate_dai_name)(struct snd_soc_component *component, int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args, struct of_phandle_args *args,
const char **dai_name); const char **dai_name);
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
int subseq); int subseq);
int (*stream_event)(struct snd_soc_component *, int event); int (*stream_event)(struct snd_soc_component *, int event);
@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
const char *prefix, const char *prefix,
struct device_node **bitclkmaster, struct device_node **bitclkmaster,
struct device_node **framemaster); struct device_node **framemaster);
int snd_soc_get_dai_id(struct device_node *ep);
int snd_soc_get_dai_name(struct of_phandle_args *args, int snd_soc_get_dai_name(struct of_phandle_args *args,
const char **dai_name); const char **dai_name);
int snd_soc_of_get_dai_name(struct device_node *of_node, int snd_soc_of_get_dai_name(struct device_node *of_node,

View File

@ -14,3 +14,11 @@ config SND_SIMPLE_SCU_CARD
help help
This option enables generic simple SCU sound card support. This option enables generic simple SCU sound card support.
It supports DPCM of multi CPU single Codec system. It supports DPCM of multi CPU single Codec system.
config SND_AUDIO_GRAPH_CARD
tristate "ASoC Audio Graph sound card support"
depends on OF
select SND_SIMPLE_CARD_UTILS
help
This option enables generic simple simple sound card support
with OF-graph DT bindings.

View File

@ -1,7 +1,9 @@
snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o snd-soc-simple-card-objs := simple-card.o
snd-soc-simple-scu-card-objs := simple-scu-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o
snd-soc-audio-graph-card-objs := audio-graph-card.o
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o

View 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>");

View File

@ -10,6 +10,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_graph.h>
#include <sound/simple_card_utils.h> #include <sound/simple_card_utils.h>
int asoc_simple_card_parse_daifmt(struct device *dev, int asoc_simple_card_parse_daifmt(struct device *dev,
@ -20,14 +21,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
{ {
struct device_node *bitclkmaster = NULL; struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL; struct device_node *framemaster = NULL;
int prefix_len = prefix ? strlen(prefix) : 0;
unsigned int daifmt; unsigned int daifmt;
daifmt = snd_soc_of_parse_daifmt(node, prefix, daifmt = snd_soc_of_parse_daifmt(node, prefix,
&bitclkmaster, &framemaster); &bitclkmaster, &framemaster);
daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
if (prefix_len && !bitclkmaster && !framemaster) { if (!bitclkmaster && !framemaster) {
/* /*
* No dai-link level and master setting was not found from * No dai-link level and master setting was not found from
* sound node level, revert back to legacy DT parsing and * sound node level, revert back to legacy DT parsing and
@ -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, int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
char *prefix) char *prefix)
{ {
char prop[128];
int ret; int ret;
snprintf(prop, sizeof(prop), "%sname", prefix); if (!prefix)
prefix = "";
/* Parse the card name from DT */ /* Parse the card name from DT */
ret = snd_soc_of_parse_card_name(card, prop); ret = snd_soc_of_parse_card_name(card, "label");
if (ret < 0) if (ret < 0) {
return ret; char prop[128];
snprintf(prop, sizeof(prop), "%sname", prefix);
ret = snd_soc_of_parse_card_name(card, prop);
if (ret < 0)
return ret;
}
if (!card->name && card->dai_link) if (!card->name && card->dai_link)
card->name = card->dai_link->name; card->name = card->dai_link->name;
@ -165,6 +171,71 @@ int asoc_simple_card_parse_dai(struct device_node *node,
} }
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
static int asoc_simple_card_get_dai_id(struct device_node *ep)
{
struct device_node *node;
struct device_node *endpoint;
int i, id;
int ret;
ret = snd_soc_get_dai_id(ep);
if (ret != -ENOTSUPP)
return ret;
node = of_graph_get_port_parent(ep);
/*
* Non HDMI sound case, counting port/endpoint on its DT
* is enough. Let's count it.
*/
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
i++;
}
if (id < 0)
return -ENODEV;
return id;
}
int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **dai_of_node,
const char **dai_name)
{
struct device_node *node;
struct of_phandle_args args;
int ret;
if (!ep)
return 0;
if (!dai_name)
return 0;
/*
* of_graph_get_port_parent() will call
* of_node_put(). So, call of_node_get() here
*/
of_node_get(ep);
node = of_graph_get_port_parent(ep);
/* Get dai->name */
args.np = node;
args.args[0] = asoc_simple_card_get_dai_id(ep);
args.args_count = (of_graph_get_endpoint_count(node) > 1);
ret = snd_soc_get_dai_name(&args, dai_name);
if (ret < 0)
return ret;
*dai_of_node = node;
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
int asoc_simple_card_init_dai(struct snd_soc_dai *dai, int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai) struct asoc_simple_dai *simple_dai)
{ {

View File

@ -34,6 +34,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/jack.h> #include <sound/jack.h>
@ -3960,11 +3961,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
prefix = ""; prefix = "";
/* /*
* check "[prefix]format = xxx" * check "dai-format = xxx"
* or "[prefix]format = xxx"
* SND_SOC_DAIFMT_FORMAT_MASK area * SND_SOC_DAIFMT_FORMAT_MASK area
*/ */
snprintf(prop, sizeof(prop), "%sformat", prefix); ret = of_property_read_string(np, "dai-format", &str);
ret = of_property_read_string(np, prop, &str); if (ret < 0) {
snprintf(prop, sizeof(prop), "%sformat", prefix);
ret = of_property_read_string(np, prop, &str);
}
if (ret == 0) { if (ret == 0) {
for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
if (strcmp(str, of_fmt_table[i].name) == 0) { if (strcmp(str, of_fmt_table[i].name) == 0) {
@ -4044,6 +4049,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
} }
EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
int snd_soc_get_dai_id(struct device_node *ep)
{
struct snd_soc_component *pos;
struct device_node *node;
int ret;
node = of_graph_get_port_parent(ep);
/*
* For example HDMI case, HDMI has video/sound port,
* but ALSA SoC needs sound port number only.
* Thus counting HDMI DT port/endpoint doesn't work.
* Then, it should have .of_xlate_dai_id
*/
ret = -ENOTSUPP;
mutex_lock(&client_mutex);
list_for_each_entry(pos, &component_list, list) {
struct device_node *component_of_node = pos->dev->of_node;
if (!component_of_node && pos->dev->parent)
component_of_node = pos->dev->parent->of_node;
if (component_of_node != node)
continue;
if (pos->driver->of_xlate_dai_id)
ret = pos->driver->of_xlate_dai_id(pos, ep);
break;
}
mutex_unlock(&client_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
int snd_soc_get_dai_name(struct of_phandle_args *args, int snd_soc_get_dai_name(struct of_phandle_args *args,
const char **dai_name) const char **dai_name)
{ {