Merge series "ASoC: Add Audio Graph Card2 support" from Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>:

Hi Mark

We already have Audio-Graph-Card which is Of-Graph base general sound
card driver. Basically it supports basic CPU-Codec connection, and is
also supporting DPCM connection. Because it was forcibly expanded to
DPCM, DT parsing is very limited and very difficult to add new features
on it, for example Multi-CPU/Codec support, Codec2Codec support, etc.

This patch adds more flexible new Audio-Graph-Card2 driver for it.
Audio-Graph-Card and Audio-Graph-Card2 are similar, but don't have
full compatibility.
The reason why I need Audio-Graph-Card2 instead of updating Audio-Graph-Card
is that it is very difficult to keep compatibility.

Audio-Graph-Card2 supports Normal/DPCM/Codec2Codec Connection wich
Single/Multi DAIs. And it is possible to Customizing.

This patch-set adds Audio-Graph-Card2 driver and its custom driver
sample, and DT settings sample which can be used for testing.

To enable testing/debuging, this patch-set also adds Test-Component
driver. We already have Dummy Component and/or Dummy DAI on soc-utils,
but 1) we can't use it from DT, 2) it do nothing.
Added new Test-Component can be used from DT, and it can indicate called
function name. We can use it to trace callback order, understanding
ALSA SoC behavior, etc, etc...
Sample DT settings of Audio Graph Card2 is using Test-Component as CPU/Codec DAI.

You can easily try to use/test it if you added below line to your DT file.
Your .config needs to have below CONFIGs to use/test it.
It will probe sample Sound Card which has Normal/DPCM/Multi/Codec2Codec
connections.

	#include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi"

	CONFIG_SND_AUDIO_GRAPH_CARD2
	CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
	CONFIG_SND_TEST_COMPONENT

Because Audio Graph Card2 is still under experimental stage, it will
indicate such warning when probing, and the DT might be updated/exchanged.

It can use Codec2Codec, but it will start automatically when probed,
and can't stop it so far. It should be updated.

Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com
Link: https://lore.kernel.org/r/871r8u4s6q.wl-kuninori.morimoto.gx@renesas.com
Link: https://lore.kernel.org/r/87a6mhwyqn.wl-kuninori.morimoto.gx@renesas.com
Link: https://lore.kernel.org/r/87tuitusy4.wl-kuninori.morimoto.gx@renesas.com
Link: https://lore.kernel.org/r/87a6jn56x0.wl-kuninori.morimoto.gx@renesas.com

v1 -> v2
	- don't use "port" base for_each loop

v2 -> v3
	- Rename audio-graph-card2 to rich-graph-card
	- Rename DSP to DPCM not to confuse
	- Normal/DPCM/Codec2Codec can use Single/Multi DAIs.
	- use dpcm/multi/codec2codec node instead of using extra compatible
	- Sample DTSI patch is separated to Single/Multi.

v3 -> v4
	- Rename rich-graph-card to audio-graph-card2
	- fixup custom sample driver's connection bug
	- test-component compatible uses "verbose" instead of "vv"

v4 -> v5
	- tidyup git-log comment at
	- tidyup Custom Sample comment

Kuninori Morimoto (16):
  ASoC: test-component: add Test Component YAML bindings
  ASoC: test-component: add Test Component for Sound debug/test
  ASoC: simple-card-utils: add asoc_graph_is_ports0()
  ASoC: simple-card-utils: add codec2codec support
  ASoC: add Audio Graph Card2 driver
  ASoC: audio-graph-card2: add Multi CPU/Codec support
  ASoC: audio-graph-card2: add DPCM support
  ASoC: audio-graph-card2: add Codec2Codec support
  ASoC: add Audio Graph Card2 Yaml Document
  ASoC: add Audio Graph Card2 Custom Sample
  ASoC: audio-graph-card2-custom-sample.dtsi: add Sample DT for Normal (Single)
  ASoC: audio-graph-card2-custom-sample.dtsi: add Sample DT for Normal (Nulti)
  ASoC: audio-graph-card2-custom-sample.dtsi: add DPCM sample (Single)
  ASoC: audio-graph-card2-custom-sample.dtsi: add DPCM sample (Multi)
  ASoC: audio-graph-card2-custom-sample.dtsi: add Codec2Codec sample (Single)
  ASoC: audio-graph-card2-custom-sample.dtsi: add Codec2Codec sample (Multi)

 .../bindings/sound/audio-graph-card2.yaml     |   57 +
 .../bindings/sound/test-component.yaml        |   33 +
 include/sound/graph_card.h                    |   21 +
 include/sound/simple_card_utils.h             |    4 +
 sound/soc/generic/Kconfig                     |   20 +
 sound/soc/generic/Makefile                    |    6 +
 .../generic/audio-graph-card2-custom-sample.c |  183 +++
 .../audio-graph-card2-custom-sample.dtsi      |  227 +++
 sound/soc/generic/audio-graph-card2.c         | 1281 +++++++++++++++++
 sound/soc/generic/simple-card-utils.c         |   46 +-
 sound/soc/generic/test-component.c            |  659 +++++++++
 11 files changed, 2536 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card2.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/test-component.yaml
 create mode 100644 sound/soc/generic/audio-graph-card2-custom-sample.c
 create mode 100644 sound/soc/generic/audio-graph-card2-custom-sample.dtsi
 create mode 100644 sound/soc/generic/audio-graph-card2.c
 create mode 100644 sound/soc/generic/test-component.c

--
2.25.1
This commit is contained in:
Mark Brown 2021-10-15 20:40:27 +01:00
commit 9a61277af7
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
11 changed files with 2536 additions and 1 deletions

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/audio-graph-card2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Audio Graph Card2 Device Tree Bindings
maintainers:
- Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
properties:
compatible:
enum:
- audio-graph-card2
links:
$ref: /schemas/types.yaml#/definitions/phandle-array
label:
maxItems: 1
routing:
description: |
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.
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
multi:
description: Multi-CPU/Codec node
dpcm:
description: DPCM node
codec2codec:
description: Codec to Codec node
required:
- compatible
- links
additionalProperties: false
examples:
- |
sound {
compatible = "audio-graph-card2";
links = <&cpu_port>;
};
cpu {
compatible = "cpu-driver";
cpu_port: port { cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; };
};
codec {
compatible = "codec-driver";
port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
};

View File

@ -0,0 +1,33 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/test-component.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Test Component Device Tree Bindings
maintainers:
- Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
properties:
compatible:
enum:
- test-cpu
- test-cpu-verbose
- test-cpu-verbose-dai
- test-cpu-verbose-component
- test-codec
- test-codec-verbose
- test-codec-verbose-dai
- test-codec-verbose-component
required:
- compatible
additionalProperties: true
examples:
- |
test_cpu {
compatible = "test-cpu";
};

View File

@ -9,6 +9,27 @@
#include <sound/simple_card_utils.h>
typedef int (*GRAPH2_CUSTOM)(struct asoc_simple_priv *priv,
struct device_node *lnk,
struct link_info *li);
struct graph2_custom_hooks {
int (*hook_pre)(struct asoc_simple_priv *priv);
int (*hook_post)(struct asoc_simple_priv *priv);
GRAPH2_CUSTOM custom_normal;
GRAPH2_CUSTOM custom_dpcm;
GRAPH2_CUSTOM custom_c2c;
};
int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);
int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
struct graph2_custom_hooks *hooks);
int audio_graph2_link_normal(struct asoc_simple_priv *priv,
struct device_node *lnk, struct link_info *li);
int audio_graph2_link_dpcm(struct asoc_simple_priv *priv,
struct device_node *lnk, struct link_info *li);
int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
struct device_node *lnk, struct link_info *li);
#endif /* __GRAPH_CARD_H */

View File

@ -42,6 +42,7 @@ struct prop_nums {
int cpus;
int codecs;
int platforms;
int c2c;
};
struct asoc_simple_priv {
@ -54,6 +55,7 @@ struct asoc_simple_priv {
struct snd_soc_dai_link_component *platforms;
struct asoc_simple_data adata;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_pcm_stream *c2c_conf;
struct prop_nums num;
unsigned int mclk_fs;
} *dai_props;
@ -64,6 +66,7 @@ struct asoc_simple_priv {
struct snd_soc_dai_link_component *dlcs;
struct snd_soc_dai_link_component dummy;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_pcm_stream *c2c_conf;
struct gpio_desc *pa_gpio;
const struct snd_soc_ops *ops;
unsigned int dpcm_selectable:1;
@ -180,6 +183,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
int asoc_simple_remove(struct platform_device *pdev);
int asoc_graph_card_probe(struct snd_soc_card *card);
int asoc_graph_is_ports0(struct device_node *port);
#ifdef DEBUG
static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv,

View File

@ -17,3 +17,23 @@ config SND_AUDIO_GRAPH_CARD
This option enables generic simple sound card support
with OF-graph DT bindings.
It also support DPCM of multi CPU single Codec ststem.
config SND_AUDIO_GRAPH_CARD2
tristate "ASoC Audio Graph sound card2 support"
depends on OF
select SND_SIMPLE_CARD_UTILS
help
This option enables generic simple sound card2 support
with OF-graph DT bindings.
config SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
tristate "ASoC Audio Graph Card2 base custom sample support"
depends on SND_AUDIO_GRAPH_CARD2
help
This option enables Audio Graph Card2 base custom sample
config SND_TEST_COMPONENT
tristate "ASoC Test component sound support"
depends on OF
help
This option enables test component sound driver support.

View File

@ -2,7 +2,13 @@
snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o
snd-soc-audio-graph-card-objs := audio-graph-card.o
snd-soc-audio-graph-card2-objs := audio-graph-card2.o
snd-soc-audio-graph-card2-custom-sample-objs := audio-graph-card2-custom-sample.o
snd-soc-test-component-objs := test-component.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_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE) += snd-soc-audio-graph-card2-custom-sample.o
obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o

View File

@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0
//
// audio-graph-card2-custom-sample.c
//
// Copyright (C) 2020 Renesas Electronics Corp.
// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <sound/graph_card.h>
/*
* Custom driver can have own priv
* which includes asoc_simple_priv.
*/
struct custom_priv {
struct asoc_simple_priv simple_priv;
/* custom driver's own params */
int custom_params;
};
/* You can get custom_priv from simple_priv */
#define simple_to_custom(simple) container_of((simple), struct custom_priv, simple_priv)
static int custom_card_probe(struct snd_soc_card *card)
{
struct asoc_simple_priv *simple_priv = snd_soc_card_get_drvdata(card);
struct custom_priv *custom_priv = simple_to_custom(simple_priv);
struct device *dev = simple_priv_to_dev(simple_priv);
dev_info(dev, "custom probe\n");
custom_priv->custom_params = 1;
/* you can use generic probe function */
return asoc_graph_card_probe(card);
}
static int custom_hook_pre(struct asoc_simple_priv *priv)
{
struct device *dev = simple_priv_to_dev(priv);
/* You can custom before parsing */
dev_info(dev, "hook : %s\n", __func__);
return 0;
}
static int custom_hook_post(struct asoc_simple_priv *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct snd_soc_card *card;
/* You can custom after parsing */
dev_info(dev, "hook : %s\n", __func__);
/* overwrite .probe sample */
card = simple_priv_to_card(priv);
card->probe = custom_card_probe;
return 0;
}
static int custom_normal(struct asoc_simple_priv *priv,
struct device_node *lnk,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
/*
* You can custom Normal parsing
* before/affter audio_graph2_link_normal()
*/
dev_info(dev, "hook : %s\n", __func__);
return audio_graph2_link_normal(priv, lnk, li);
}
static int custom_dpcm(struct asoc_simple_priv *priv,
struct device_node *lnk,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
/*
* You can custom DPCM parsing
* before/affter audio_graph2_link_dpcm()
*/
dev_info(dev, "hook : %s\n", __func__);
return audio_graph2_link_dpcm(priv, lnk, li);
}
static int custom_c2c(struct asoc_simple_priv *priv,
struct device_node *lnk,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
/*
* You can custom Codec2Codec parsing
* before/affter audio_graph2_link_c2c()
*/
dev_info(dev, "hook : %s\n", __func__);
return audio_graph2_link_c2c(priv, lnk, li);
}
/*
* audio-graph-card2 has many hooks for your customizing.
*/
static struct graph2_custom_hooks custom_hooks = {
.hook_pre = custom_hook_pre,
.hook_post = custom_hook_post,
.custom_normal = custom_normal,
.custom_dpcm = custom_dpcm,
.custom_c2c = custom_c2c,
};
static int custom_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct device *dev = simple_priv_to_dev(priv);
dev_info(dev, "custom startup\n");
return asoc_simple_startup(substream);
}
/* You can use custom ops */
static const struct snd_soc_ops custom_ops = {
.startup = custom_startup,
.shutdown = asoc_simple_shutdown,
.hw_params = asoc_simple_hw_params,
};
static int custom_probe(struct platform_device *pdev)
{
struct custom_priv *custom_priv;
struct asoc_simple_priv *simple_priv;
struct device *dev = &pdev->dev;
int ret;
custom_priv = devm_kzalloc(dev, sizeof(*custom_priv), GFP_KERNEL);
if (!custom_priv)
return -ENOMEM;
simple_priv = &custom_priv->simple_priv;
simple_priv->ops = &custom_ops; /* customize dai_link ops */
/* use audio-graph-card2 parsing with own custom hooks */
ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks);
if (ret < 0)
return ret;
/* customize more if needed */
return 0;
}
static const struct of_device_id custom_of_match[] = {
{ .compatible = "audio-graph-card2-custom-sample", },
{},
};
MODULE_DEVICE_TABLE(of, custom_of_match);
static struct platform_driver custom_card = {
.driver = {
.name = "audio-graph-card2-custom-sample",
.of_match_table = custom_of_match,
},
.probe = custom_probe,
.remove = asoc_simple_remove,
};
module_platform_driver(custom_card);
MODULE_ALIAS("platform:asoc-audio-graph-card2-custom-sample");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ASoC Audio Graph Card2 Custom Sample");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

View File

@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0
/*
* audio-graph-card2-custom-sample.dtsi
*
* Copyright (C) 2020 Renesas Electronics Corp.
* Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This sample indicates how to use audio-graph-card2 and its
* custom driver. "audio-graph-card2-custom-sample" is the custome driver
* which is using audio-graph-card2.
*
* You can easily use this sample by adding below line on your DT file,
* and add new CONFIG to your .config.
*
* #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi"
*
* CONFIG_SND_AUDIO_GRAPH_CARD2
* CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
* CONFIG_SND_TEST_COMPONENT
*/
/ {
/*
* @ : used at links
*
* [Normal]
* cpu0 <-@-----------------> codec0
*
* [Multi-CPU/Codec]
* +-+ +-+
* cpu1 <--| |<-@--------->| |-> codec1
* cpu2 <--| | | |-> codec2
* +-+ +-+
*
* [DPCM]
* FE BE
* ****
* cpu3 <-@--* *--@-> codec3
* cpu4 <-@--* *
* ****
*
* [DPCM-Multi]
*
* --NOTE--
* Multi-FE is not supported by ASoC.
*
* FE BE
* **** +-+
* cpu5 <-@--* *--@-> | | -> codec4
* cpu6 <-@--* * | | -> codec5
* **** +-+
*
* [Codec2Codec]
* +-@-> codec6
* |
* +---> codec7
*
* [Codec2Codec-Multi]
*
* --NOTE--
* Multi connect N:M is not supported by ASoC.
*
* +-+
* +-@->| |-> codec8
* | | |-> codec9
* | +-+
* | +-+
* +--->| |-> codec10
* | |-> codec11
* +-+
*/
audio-graph-card2-custom-sample {
/*
* You can use audio-graph-card2 directly by using
*
* compatible = "audio-graph-card2";
*/
compatible = "audio-graph-card2-custom-sample";
/* for [DPCM] */
/* BE FE */
routing = "TC DAI3 Playback", "DAI3 Playback",
"TC DAI3 Playback", "DAI4 Playback",
"DAI3 Capture", "TC DAI3 Capture",
"DAI4 Capture", "TC DAI3 Capture",
/* for [DPCM-Multi] */
/* BE FE */
"TC DAI4 Playback", "DAI5 Playback",
"TC DAI5 Playback", "DAI5 Playback",
"TC DAI4 Playback", "DAI6 Playback",
"TC DAI5 Playback", "DAI6 Playback",
"DAI5 Capture", "TC DAI4 Capture",
"DAI5 Capture", "TC DAI5 Capture",
"DAI6 Capture", "TC DAI4 Capture",
"DAI6 Capture", "TC DAI5 Capture",
/* for [Codec2Codec] */
"TC OUT", "TC DAI7 Playback",
"TC DAI6 Capture", "TC IN",
/* for [Codec2Codec-Multi] */
"TC OUT", "TC DAI10 Playback",
"TC DAI8 Capture", "TC IN",
"TC OUT", "TC DAI11 Playback",
"TC DAI9 Capture", "TC IN";
links = <&cpu0 /* normal: cpu side only */
&mcpu0 /* multi: cpu side only */
&fe00 &fe01 &be0 /* dpcm: both FE / BE */
&fe10 &fe11 &be1 /* dpcm-m: both FE / BE */
&c2c /* c2c: cpu side only */
&c2c_m /* c2c: cpu side only */
>;
multi {
ports@0 {
mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
};
ports@1 {
port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
};
ports@2 {
port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
};
ports@3 {
port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
};
ports@4 {
port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
port@2 { mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; };
};
};
dpcm {
/* FE */
ports@0 {
fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
};
/* BE */
ports@1 {
be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
};
};
codec2codec {
ports@0 {
rate = <48000>;
c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; };
port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
};
ports@1 {
rate = <48000>;
c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; };
port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; };
};
};
};
test_cpu {
/*
* update compatible to indicate more detail behaviour
* if you want. see test-compatible for more detail.
*
* ex)
* - compatible = "test-cpu";
* + compatible = "test-cpu-verbose";
*/
compatible = "test-cpu";
ports {
bitclock-master;
frame-master;
cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; };
port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; };
port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; };
};
};
test_codec {
/*
* update compatible to indicate more detail behaviour
* if you want. see test-compatible for more detail.
*
* ex)
* - compatible = "test-codec";
* + compatible = "test-codec-verbose";
*/
compatible = "test-codec";
ports {
/*
* prefix can be added to *component*,
* see audio-graph-card2::routing
*/
prefix = "TC";
port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; };
port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; };
port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; };
port@6 { bitclock-master;
frame-master;
codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
port@8 { bitclock-master;
frame-master;
codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; };
port@9 { codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; };
port@10 { codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; };
port@11 { codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; };
};
};
};

File diff suppressed because it is too large Load Diff

View File

@ -619,7 +619,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
struct asoc_simple_dai *dais;
struct snd_soc_dai_link_component *dlcs;
struct snd_soc_codec_conf *cconf = NULL;
int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
struct snd_soc_pcm_stream *c2c_conf = NULL;
int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0;
dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
@ -638,6 +639,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
if (!li->num[i].cpus)
cnf_num += li->num[i].codecs;
c2c_num += li->num[i].c2c;
}
dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
@ -651,6 +654,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
return -ENOMEM;
}
if (c2c_num) {
c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL);
if (!c2c_conf)
return -ENOMEM;
}
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
li->link, dai_num, cnf_num);
@ -664,6 +673,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
priv->dais = dais;
priv->dlcs = dlcs;
priv->codec_conf = cconf;
priv->c2c_conf = c2c_conf;
card->dai_link = priv->dai_link;
card->num_links = li->link;
@ -681,6 +691,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dlcs += li->num[i].cpus;
dais += li->num[i].cpus;
if (li->num[i].c2c) {
/* Codec2Codec */
dai_props[i].c2c_conf = c2c_conf;
c2c_conf += li->num[i].c2c;
}
} else {
/* DPCM Be's CPU = dummy */
dai_props[i].cpus =
@ -759,6 +775,34 @@ int asoc_graph_card_probe(struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
int asoc_graph_is_ports0(struct device_node *np)
{
struct device_node *port, *ports, *ports0, *top;
int ret;
/* np is "endpoint" or "port" */
if (of_node_name_eq(np, "endpoint")) {
port = of_get_parent(np);
} else {
port = np;
of_node_get(port);
}
ports = of_get_parent(port);
top = of_get_parent(ports);
ports0 = of_get_child_by_name(top, "ports");
ret = ports0 == ports;
of_node_put(port);
of_node_put(ports);
of_node_put(ports0);
of_node_put(top);
return ret;
}
EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");

View File

@ -0,0 +1,659 @@
// SPDX-License-Identifier: GPL-2.0
//
// test-component.c -- Test Audio Component driver
//
// Copyright (C) 2020 Renesas Electronics Corporation
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#define TEST_NAME_LEN 32
struct test_dai_name {
char name[TEST_NAME_LEN];
char name_playback[TEST_NAME_LEN];
char name_capture[TEST_NAME_LEN];
};
struct test_priv {
struct device *dev;
struct snd_pcm_substream *substream;
struct delayed_work dwork;
struct snd_soc_component_driver *component_driver;
struct snd_soc_dai_driver *dai_driver;
struct test_dai_name *name;
};
struct test_adata {
u32 is_cpu:1;
u32 cmp_v:1;
u32 dai_v:1;
};
#define mile_stone(d) dev_info((d)->dev, "%s() : %s", __func__, (d)->driver->name)
#define mile_stone_x(dev) dev_info(dev, "%s()", __func__)
static int test_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
mile_stone(dai);
return 0;
}
static int test_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
mile_stone(dai);
return 0;
}
static int test_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
{
mile_stone(dai);
return 0;
}
static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
unsigned int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
unsigned int clock = fmt & SND_SOC_DAIFMT_CLOCK_MASK;
unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
unsigned int master = fmt & SND_SOC_DAIFMT_MASTER_MASK;
char *str;
dev_info(dai->dev, "name : %s", dai->name);
str = "unknown";
switch (format) {
case SND_SOC_DAIFMT_I2S:
str = "i2s";
break;
case SND_SOC_DAIFMT_RIGHT_J:
str = "right_j";
break;
case SND_SOC_DAIFMT_LEFT_J:
str = "left_j";
break;
case SND_SOC_DAIFMT_DSP_A:
str = "dsp_a";
break;
case SND_SOC_DAIFMT_DSP_B:
str = "dsp_b";
break;
case SND_SOC_DAIFMT_AC97:
str = "ac97";
break;
case SND_SOC_DAIFMT_PDM:
str = "pdm";
break;
}
dev_info(dai->dev, "format : %s", str);
if (clock == SND_SOC_DAIFMT_CONT)
str = "continuous";
else
str = "gated";
dev_info(dai->dev, "clock : %s", str);
str = "unknown";
switch (master) {
case SND_SOC_DAIFMT_CBP_CFP:
str = "clk provider, frame provider";
break;
case SND_SOC_DAIFMT_CBC_CFP:
str = "clk consumer, frame provider";
break;
case SND_SOC_DAIFMT_CBP_CFC:
str = "clk provider, frame consumer";
break;
case SND_SOC_DAIFMT_CBC_CFC:
str = "clk consumer, frame consumer";
break;
}
dev_info(dai->dev, "clock : codec is %s", str);
str = "unknown";
switch (inv) {
case SND_SOC_DAIFMT_NB_NF:
str = "normal bit, normal frame";
break;
case SND_SOC_DAIFMT_NB_IF:
str = "normal bit, invert frame";
break;
case SND_SOC_DAIFMT_IB_NF:
str = "invert bit, normal frame";
break;
case SND_SOC_DAIFMT_IB_IF:
str = "invert bit, invert frame";
break;
}
dev_info(dai->dev, "signal : %s", str);
return 0;
}
static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
mile_stone(dai);
return 0;
}
static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
mile_stone(dai);
return 0;
}
static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
mile_stone(dai);
}
static int test_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
mile_stone(dai);
return 0;
}
static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
mile_stone(dai);
return 0;
}
static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
mile_stone(dai);
return 0;
}
static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
mile_stone(dai);
return 0;
}
static u64 test_dai_formats =
/*
* Select below from Sound Card, not auto
* SND_SOC_POSSIBLE_DAIFMT_CBP_CFP
* SND_SOC_POSSIBLE_DAIFMT_CBC_CFP
* SND_SOC_POSSIBLE_DAIFMT_CBP_CFC
* SND_SOC_POSSIBLE_DAIFMT_CBC_CFC
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B |
SND_SOC_POSSIBLE_DAIFMT_AC97 |
SND_SOC_POSSIBLE_DAIFMT_PDM |
SND_SOC_POSSIBLE_DAIFMT_NB_NF |
SND_SOC_POSSIBLE_DAIFMT_NB_IF |
SND_SOC_POSSIBLE_DAIFMT_IB_NF |
SND_SOC_POSSIBLE_DAIFMT_IB_IF;
static const struct snd_soc_dai_ops test_ops = {
.set_fmt = test_dai_set_fmt,
.startup = test_dai_startup,
.shutdown = test_dai_shutdown,
.auto_selectable_formats = &test_dai_formats,
.num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops test_verbose_ops = {
.set_sysclk = test_dai_set_sysclk,
.set_pll = test_dai_set_pll,
.set_clkdiv = test_dai_set_clkdiv,
.set_fmt = test_dai_set_fmt,
.mute_stream = test_dai_mute_stream,
.startup = test_dai_startup,
.shutdown = test_dai_shutdown,
.hw_params = test_dai_hw_params,
.hw_free = test_dai_hw_free,
.trigger = test_dai_trigger,
.bespoke_trigger = test_dai_bespoke_trigger,
.auto_selectable_formats = &test_dai_formats,
.num_auto_selectable_formats = 1,
};
#define STUB_RATES SNDRV_PCM_RATE_8000_384000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_U24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_U32_LE)
static int test_component_probe(struct snd_soc_component *component)
{
mile_stone(component);
return 0;
}
static void test_component_remove(struct snd_soc_component *component)
{
mile_stone(component);
}
static int test_component_suspend(struct snd_soc_component *component)
{
mile_stone(component);
return 0;
}
static int test_component_resume(struct snd_soc_component *component)
{
mile_stone(component);
return 0;
}
#define PREALLOC_BUFFER (32 * 1024)
static int test_component_pcm_construct(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
mile_stone(component);
snd_pcm_set_managed_buffer_all(
rtd->pcm,
SNDRV_DMA_TYPE_DEV,
rtd->card->snd_card->dev,
PREALLOC_BUFFER, PREALLOC_BUFFER);
return 0;
}
static void test_component_pcm_destruct(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
mile_stone(component);
}
static int test_component_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir)
{
mile_stone(component);
return 0;
}
static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
mile_stone(component);
return 0;
}
static int test_component_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
mile_stone(component);
return 0;
}
static void test_component_seq_notifier(struct snd_soc_component *component,
enum snd_soc_dapm_type type, int subseq)
{
mile_stone(component);
}
static int test_component_stream_event(struct snd_soc_component *component, int event)
{
mile_stone(component);
return 0;
}
static int test_component_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
mile_stone(component);
return 0;
}
static const struct snd_pcm_hardware test_component_hardware = {
/* Random values to keep userspace happy when checking constraints */
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 32 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 128,
.fifo_size = 256,
};
static int test_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
mile_stone(component);
/* BE's dont need dummy params */
if (!rtd->dai_link->no_pcm)
snd_soc_set_runtime_hwparams(substream, &test_component_hardware);
return 0;
}
static int test_component_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
mile_stone(component);
return 0;
}
static int test_component_ioctl(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
mile_stone(component);
return 0;
}
static int test_component_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
mile_stone(component);
return 0;
}
static int test_component_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
mile_stone(component);
return 0;
}
static int test_component_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
mile_stone(component);
return 0;
}
static void test_component_timer_stop(struct test_priv *priv)
{
cancel_delayed_work(&priv->dwork);
}
static void test_component_timer_start(struct test_priv *priv)
{
schedule_delayed_work(&priv->dwork, msecs_to_jiffies(10));
}
static void test_component_dwork(struct work_struct *work)
{
struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
if (priv->substream)
snd_pcm_period_elapsed(priv->substream);
test_component_timer_start(priv);
}
static int test_component_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct test_priv *priv = dev_get_drvdata(component->dev);
mile_stone(component);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
test_component_timer_start(priv);
priv->substream = substream; /* set substream later */
break;
case SNDRV_PCM_TRIGGER_STOP:
priv->substream = NULL;
test_component_timer_stop(priv);
}
return 0;
}
static int test_component_sync_stop(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
mile_stone(component);
return 0;
}
static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
static int pointer;
if (!runtime)
return 0;
pointer += 10;
if (pointer > PREALLOC_BUFFER)
pointer = 0;
/* mile_stone(component); */
return bytes_to_frames(runtime, pointer);
}
static int test_component_get_time_info(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct timespec64 *system_ts,
struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{
mile_stone(component);
return 0;
}
static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
mile_stone_x(rtd->dev);
return 0;
}
/* CPU */
static const struct test_adata test_cpu = { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
static const struct test_adata test_cpu_vv = { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
static const struct test_adata test_cpu_nv = { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
static const struct test_adata test_cpu_vn = { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
/* Codec */
static const struct test_adata test_codec = { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
static const struct test_adata test_codec_vv = { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
static const struct test_adata test_codec_nv = { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
static const struct test_adata test_codec_vn = { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
static const struct of_device_id test_of_match[] = {
{ .compatible = "test-cpu", .data = (void *)&test_cpu, },
{ .compatible = "test-cpu-verbose", .data = (void *)&test_cpu_vv, },
{ .compatible = "test-cpu-verbose-dai", .data = (void *)&test_cpu_nv, },
{ .compatible = "test-cpu-verbose-component", .data = (void *)&test_cpu_vn, },
{ .compatible = "test-codec", .data = (void *)&test_codec, },
{ .compatible = "test-codec-verbose", .data = (void *)&test_codec_vv, },
{ .compatible = "test-codec-verbose-dai", .data = (void *)&test_codec_nv, },
{ .compatible = "test-codec-verbose-component", .data = (void *)&test_codec_vn, },
{},
};
MODULE_DEVICE_TABLE(of, test_of_match);
static const struct snd_soc_dapm_widget widgets[] = {
/*
* FIXME
*
* Just IN/OUT is OK for now,
* but need to be updated ?
*/
SND_SOC_DAPM_INPUT("IN"),
SND_SOC_DAPM_OUTPUT("OUT"),
};
static int test_driver_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct device_node *ep;
const struct of_device_id *of_id = of_match_device(test_of_match, &pdev->dev);
const struct test_adata *adata = of_id->data;
struct snd_soc_component_driver *cdriv;
struct snd_soc_dai_driver *ddriv;
struct test_dai_name *dname;
struct test_priv *priv;
int num, ret, i;
num = of_graph_get_endpoint_count(node);
if (!num) {
dev_err(dev, "no port exits\n");
return -EINVAL;
}
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL);
ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL);
dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL);
if (!priv || !cdriv || !ddriv || !dname)
return -EINVAL;
priv->dev = dev;
priv->component_driver = cdriv;
priv->dai_driver = ddriv;
priv->name = dname;
INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
dev_set_drvdata(dev, priv);
if (adata->is_cpu) {
cdriv->name = "test_cpu";
cdriv->pcm_construct = test_component_pcm_construct;
cdriv->pointer = test_component_pointer;
cdriv->trigger = test_component_trigger;
} else {
cdriv->name = "test_codec";
cdriv->idle_bias_on = 1;
cdriv->endianness = 1;
cdriv->non_legacy_dai_naming = 1;
}
cdriv->open = test_component_open;
cdriv->dapm_widgets = widgets;
cdriv->num_dapm_widgets = ARRAY_SIZE(widgets);
if (adata->cmp_v) {
cdriv->probe = test_component_probe;
cdriv->remove = test_component_remove;
cdriv->suspend = test_component_suspend;
cdriv->resume = test_component_resume;
cdriv->set_sysclk = test_component_set_sysclk;
cdriv->set_pll = test_component_set_pll;
cdriv->set_jack = test_component_set_jack;
cdriv->seq_notifier = test_component_seq_notifier;
cdriv->stream_event = test_component_stream_event;
cdriv->set_bias_level = test_component_set_bias_level;
cdriv->close = test_component_close;
cdriv->ioctl = test_component_ioctl;
cdriv->hw_params = test_component_hw_params;
cdriv->hw_free = test_component_hw_free;
cdriv->prepare = test_component_prepare;
cdriv->sync_stop = test_component_sync_stop;
cdriv->get_time_info = test_component_get_time_info;
cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup;
if (adata->is_cpu)
cdriv->pcm_destruct = test_component_pcm_destruct;
}
i = 0;
for_each_endpoint_of_node(node, ep) {
snprintf(dname[i].name, TEST_NAME_LEN, "%s.%d", node->name, i);
ddriv[i].name = dname[i].name;
snprintf(dname[i].name_playback, TEST_NAME_LEN, "DAI%d Playback", i);
ddriv[i].playback.stream_name = dname[i].name_playback;
ddriv[i].playback.channels_min = 1;
ddriv[i].playback.channels_max = 384;
ddriv[i].playback.rates = STUB_RATES;
ddriv[i].playback.formats = STUB_FORMATS;
snprintf(dname[i].name_capture, TEST_NAME_LEN, "DAI%d Capture", i);
ddriv[i].capture.stream_name = dname[i].name_capture;
ddriv[i].capture.channels_min = 1;
ddriv[i].capture.channels_max = 384;
ddriv[i].capture.rates = STUB_RATES;
ddriv[i].capture.formats = STUB_FORMATS;
if (adata->dai_v)
ddriv[i].ops = &test_verbose_ops;
else
ddriv[i].ops = &test_ops;
i++;
}
ret = devm_snd_soc_register_component(dev, cdriv, ddriv, num);
if (ret < 0)
return ret;
mile_stone_x(dev);
return 0;
}
static int test_driver_remove(struct platform_device *pdev)
{
mile_stone_x(&pdev->dev);
return 0;
}
static struct platform_driver test_driver = {
.driver = {
.name = "test-component",
.of_match_table = test_of_match,
},
.probe = test_driver_probe,
.remove = test_driver_remove,
};
module_platform_driver(test_driver);
MODULE_ALIAS("platform:asoc-test-component");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ASoC Test Component");
MODULE_LICENSE("GPL v2");