// SPDX-License-Identifier: GPL-2.0 // // ASoC Audio Graph Card2 support // // Copyright (C) 2020 Renesas Electronics Corp. // Copyright (C) 2020 Kuninori Morimoto <[email protected]> // // based on ${LINUX}/sound/soc/generic/audio-graph-card.c #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/string.h> #include <sound/graph_card.h> /************************************ daifmt ************************************ ports { format = "left_j"; port@0 { bitclock-master; sample0: endpoint@0 { frame-master; }; sample1: endpoint@1 { format = "i2s"; }; }; ... }; You can set daifmt at ports/port/endpoint. It uses *latest* format, and *share* master settings. In above case, sample0: left_j, bitclock-master, frame-master sample1: i2s, bitclock-master If there was no settings, *Codec* will be bitclock/frame provider as default. see graph_parse_daifmt(). "format" property is no longer needed on DT if both CPU/Codec drivers are supporting snd_soc_dai_ops :: .auto_selectable_formats. see snd_soc_runtime_get_dai_fmt() sample driver linux/sound/soc/sh/rcar/core.c linux/sound/soc/codecs/ak4613.c linux/sound/soc/codecs/pcm3168a.c linux/sound/soc/soc-utils.c linux/sound/soc/generic/test-component.c ************************************ Normal Audio-Graph ************************************ CPU <---> Codec sound { compatible = "audio-graph-card2"; links = <&cpu>; }; CPU { cpu: port { bitclock-master; frame-master; cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; }; }; Codec { port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; }; }; ************************************ Multi-CPU/Codec ************************************ It has link connection part (= X,x) and list part (= A,B,a,b). "links" is connection part of CPU side (= @). +----+ +---+ CPU1 --|A X| <-@----> |x a|-- Codec1 CPU2 --|B | | b|-- Codec2 +----+ +---+ sound { compatible = "audio-graph-card2"; (@) links = <&mcpu>; multi { ports@0 { (@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element }; ports@1 { port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; }; }; Codec { ports { port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; }; }; ************************************ DPCM ************************************ DSP ************ PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers PCM2 <--> * fe2 be2 * <--> DAI2: MODEM PCM3 <--> * fe3 be3 * <--> DAI3: BT * be4 * <--> DAI4: DMIC * be5 * <--> DAI5: FM ************ sound { compatible = "audio-graph-card2"; // indicate routing routing = "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback"; // indicate all Front-End, Back-End links = <&fe0, &fe1, ..., &be0, &be1, ...>; dpcm { // Front-End ports@0 { fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; ... }; // Back-End ports@1 { be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; ... }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; ... }; }; Codec { ports { port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; ... }; }; ************************************ Codec to Codec ************************************ +--+ | |<-- Codec0 <- IN | |--> Codec1 -> OUT +--+ sound { compatible = "audio-graph-card2"; routing = "OUT" ,"DAI1 Playback", "DAI0 Capture", "IN"; links = <&c2c>; codec2codec { ports { rate = <48000>; c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; }; }; Codec { ports { port@0 { bitclock-master; frame-master; codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; }; }; */ enum graph_type { … }; #define GRAPH_NODENAME_MULTI … #define GRAPH_NODENAME_DPCM … #define GRAPH_NODENAME_C2C … #define port_to_endpoint(port) … #define ep_to_port(ep) … static struct device_node *port_to_ports(struct device_node *port) { … } static enum graph_type __graph_get_type(struct device_node *lnk) { … } static enum graph_type graph_get_type(struct simple_util_priv *priv, struct device_node *lnk) { … } static int graph_lnk_is_multi(struct device_node *lnk) { … } static struct device_node *graph_get_next_multi_ep(struct device_node **port) { … } static const struct snd_soc_ops graph_ops = …; static void graph_parse_convert(struct device_node *ep, struct simple_dai_props *props) { … } static int __graph_parse_node(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *ep, struct link_info *li, int is_cpu, int idx) { … } static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, int *nm_idx, int cpu_idx, struct device_node *mcpu_port) { … } static int graph_parse_node_multi(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *port, struct link_info *li, int is_cpu) { … } static int graph_parse_node_single(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *port, struct link_info *li, int is_cpu) { … } static int graph_parse_node(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *port, struct link_info *li, int is_cpu) { … } static void graph_parse_daifmt(struct device_node *node, unsigned int *daifmt, unsigned int *bit_frame) { … } static void graph_link_init(struct simple_util_priv *priv, struct device_node *lnk, struct device_node *port_cpu, struct device_node *port_codec, struct link_info *li, int is_cpu_node) { … } int audio_graph2_link_normal(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } EXPORT_SYMBOL_GPL(…); int audio_graph2_link_dpcm(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } EXPORT_SYMBOL_GPL(…); int audio_graph2_link_c2c(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } EXPORT_SYMBOL_GPL(…); static int graph_link(struct simple_util_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, struct device_node *lnk, struct link_info *li) { … } static int graph_counter(struct device_node *lnk) { … } static int graph_count_normal(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } static int graph_count_dpcm(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } static int graph_count_c2c(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { … } static int graph_count(struct simple_util_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, struct device_node *lnk, struct link_info *li) { … } static int graph_for_each_link(struct simple_util_priv *priv, struct graph2_custom_hooks *hooks, struct link_info *li, int (*func)(struct simple_util_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, struct device_node *lnk, struct link_info *li)) { … } int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, struct graph2_custom_hooks *hooks) { … } EXPORT_SYMBOL_GPL(…); static int graph_probe(struct platform_device *pdev) { … } static const struct of_device_id graph_of_match[] = …; MODULE_DEVICE_TABLE(of, graph_of_match); static struct platform_driver graph_card = …; module_platform_driver(…) …; MODULE_ALIAS(…) …; MODULE_LICENSE(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …;