162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// ASoC Audio Graph Card2 support 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2020 Renesas Electronics Corp. 662306a36Sopenharmony_ci// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// based on ${LINUX}/sound/soc/generic/audio-graph-card.c 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/gpio.h> 1262306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_device.h> 1662306a36Sopenharmony_ci#include <linux/of_gpio.h> 1762306a36Sopenharmony_ci#include <linux/of_graph.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <sound/graph_card.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/************************************ 2362306a36Sopenharmony_ci daifmt 2462306a36Sopenharmony_ci ************************************ 2562306a36Sopenharmony_ci ports { 2662306a36Sopenharmony_ci format = "left_j"; 2762306a36Sopenharmony_ci port@0 { 2862306a36Sopenharmony_ci bitclock-master; 2962306a36Sopenharmony_ci sample0: endpoint@0 { 3062306a36Sopenharmony_ci frame-master; 3162306a36Sopenharmony_ci }; 3262306a36Sopenharmony_ci sample1: endpoint@1 { 3362306a36Sopenharmony_ci format = "i2s"; 3462306a36Sopenharmony_ci }; 3562306a36Sopenharmony_ci }; 3662306a36Sopenharmony_ci ... 3762306a36Sopenharmony_ci }; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci You can set daifmt at ports/port/endpoint. 4062306a36Sopenharmony_ci It uses *latest* format, and *share* master settings. 4162306a36Sopenharmony_ci In above case, 4262306a36Sopenharmony_ci sample0: left_j, bitclock-master, frame-master 4362306a36Sopenharmony_ci sample1: i2s, bitclock-master 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci If there was no settings, *Codec* will be 4662306a36Sopenharmony_ci bitclock/frame provider as default. 4762306a36Sopenharmony_ci see 4862306a36Sopenharmony_ci graph_parse_daifmt(). 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci "format" property is no longer needed on DT if both CPU/Codec drivers are 5162306a36Sopenharmony_ci supporting snd_soc_dai_ops :: .auto_selectable_formats. 5262306a36Sopenharmony_ci see 5362306a36Sopenharmony_ci snd_soc_runtime_get_dai_fmt() 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci sample driver 5662306a36Sopenharmony_ci linux/sound/soc/sh/rcar/core.c 5762306a36Sopenharmony_ci linux/sound/soc/codecs/ak4613.c 5862306a36Sopenharmony_ci linux/sound/soc/codecs/pcm3168a.c 5962306a36Sopenharmony_ci linux/sound/soc/soc-utils.c 6062306a36Sopenharmony_ci linux/sound/soc/generic/test-component.c 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ************************************ 6362306a36Sopenharmony_ci Normal Audio-Graph 6462306a36Sopenharmony_ci ************************************ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci CPU <---> Codec 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci sound { 6962306a36Sopenharmony_ci compatible = "audio-graph-card2"; 7062306a36Sopenharmony_ci links = <&cpu>; 7162306a36Sopenharmony_ci }; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci CPU { 7462306a36Sopenharmony_ci cpu: port { 7562306a36Sopenharmony_ci bitclock-master; 7662306a36Sopenharmony_ci frame-master; 7762306a36Sopenharmony_ci cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; }; 7862306a36Sopenharmony_ci }; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci Codec { 8162306a36Sopenharmony_ci port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; }; 8262306a36Sopenharmony_ci }; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ************************************ 8562306a36Sopenharmony_ci Multi-CPU/Codec 8662306a36Sopenharmony_ci ************************************ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciIt has connection part (= X) and list part (= y). 8962306a36Sopenharmony_cilinks indicates connection part of CPU side (= A). 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci +-+ (A) +-+ 9262306a36Sopenharmony_ci CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1 9362306a36Sopenharmony_ci CPU2 --(y) | | | | (y)-- Codec2 9462306a36Sopenharmony_ci +-+ +-+ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci sound { 9762306a36Sopenharmony_ci compatible = "audio-graph-card2"; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci(A) links = <&mcpu>; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci multi { 10262306a36Sopenharmony_ci ports@0 { 10362306a36Sopenharmony_ci(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; 10462306a36Sopenharmony_ci(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; 10562306a36Sopenharmony_ci(y) port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; 10662306a36Sopenharmony_ci }; 10762306a36Sopenharmony_ci ports@1 { 10862306a36Sopenharmony_ci(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; 10962306a36Sopenharmony_ci(y) port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; 11062306a36Sopenharmony_ci(y) port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; 11162306a36Sopenharmony_ci }; 11262306a36Sopenharmony_ci }; 11362306a36Sopenharmony_ci }; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci CPU { 11662306a36Sopenharmony_ci ports { 11762306a36Sopenharmony_ci bitclock-master; 11862306a36Sopenharmony_ci frame-master; 11962306a36Sopenharmony_ci port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; 12062306a36Sopenharmony_ci port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; 12162306a36Sopenharmony_ci }; 12262306a36Sopenharmony_ci }; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci Codec { 12562306a36Sopenharmony_ci ports { 12662306a36Sopenharmony_ci port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; 12762306a36Sopenharmony_ci port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; 12862306a36Sopenharmony_ci }; 12962306a36Sopenharmony_ci }; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ************************************ 13262306a36Sopenharmony_ci DPCM 13362306a36Sopenharmony_ci ************************************ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci DSP 13662306a36Sopenharmony_ci ************ 13762306a36Sopenharmony_ci PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset 13862306a36Sopenharmony_ci PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers 13962306a36Sopenharmony_ci PCM2 <--> * fe2 be2 * <--> DAI2: MODEM 14062306a36Sopenharmony_ci PCM3 <--> * fe3 be3 * <--> DAI3: BT 14162306a36Sopenharmony_ci * be4 * <--> DAI4: DMIC 14262306a36Sopenharmony_ci * be5 * <--> DAI5: FM 14362306a36Sopenharmony_ci ************ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci sound { 14662306a36Sopenharmony_ci compatible = "audio-graph-card2"; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci // indicate routing 14962306a36Sopenharmony_ci routing = "xxx Playback", "xxx Playback", 15062306a36Sopenharmony_ci "xxx Playback", "xxx Playback", 15162306a36Sopenharmony_ci "xxx Playback", "xxx Playback"; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci // indicate all Front-End, Back-End 15462306a36Sopenharmony_ci links = <&fe0, &fe1, ..., 15562306a36Sopenharmony_ci &be0, &be1, ...>; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dpcm { 15862306a36Sopenharmony_ci // Front-End 15962306a36Sopenharmony_ci ports@0 { 16062306a36Sopenharmony_ci fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; 16162306a36Sopenharmony_ci fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; 16262306a36Sopenharmony_ci ... 16362306a36Sopenharmony_ci }; 16462306a36Sopenharmony_ci // Back-End 16562306a36Sopenharmony_ci ports@1 { 16662306a36Sopenharmony_ci be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; 16762306a36Sopenharmony_ci be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; 16862306a36Sopenharmony_ci ... 16962306a36Sopenharmony_ci }; 17062306a36Sopenharmony_ci }; 17162306a36Sopenharmony_ci }; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci CPU { 17462306a36Sopenharmony_ci ports { 17562306a36Sopenharmony_ci bitclock-master; 17662306a36Sopenharmony_ci frame-master; 17762306a36Sopenharmony_ci port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; 17862306a36Sopenharmony_ci port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; 17962306a36Sopenharmony_ci ... 18062306a36Sopenharmony_ci }; 18162306a36Sopenharmony_ci }; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci Codec { 18462306a36Sopenharmony_ci ports { 18562306a36Sopenharmony_ci port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; 18662306a36Sopenharmony_ci port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; 18762306a36Sopenharmony_ci ... 18862306a36Sopenharmony_ci }; 18962306a36Sopenharmony_ci }; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ************************************ 19262306a36Sopenharmony_ci Codec to Codec 19362306a36Sopenharmony_ci ************************************ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci +--+ 19662306a36Sopenharmony_ci | |<-- Codec0 <- IN 19762306a36Sopenharmony_ci | |--> Codec1 -> OUT 19862306a36Sopenharmony_ci +--+ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci sound { 20162306a36Sopenharmony_ci compatible = "audio-graph-card2"; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci routing = "OUT" ,"DAI1 Playback", 20462306a36Sopenharmony_ci "DAI0 Capture", "IN"; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci links = <&c2c>; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci codec2codec { 20962306a36Sopenharmony_ci ports { 21062306a36Sopenharmony_ci rate = <48000>; 21162306a36Sopenharmony_ci c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; 21262306a36Sopenharmony_ci port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; 21362306a36Sopenharmony_ci }; 21462306a36Sopenharmony_ci }; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci Codec { 21762306a36Sopenharmony_ci ports { 21862306a36Sopenharmony_ci port@0 { 21962306a36Sopenharmony_ci bitclock-master; 22062306a36Sopenharmony_ci frame-master; 22162306a36Sopenharmony_ci codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; 22262306a36Sopenharmony_ci port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; 22362306a36Sopenharmony_ci }; 22462306a36Sopenharmony_ci }; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci*/ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cienum graph_type { 22962306a36Sopenharmony_ci GRAPH_NORMAL, 23062306a36Sopenharmony_ci GRAPH_DPCM, 23162306a36Sopenharmony_ci GRAPH_C2C, 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define GRAPH_NODENAME_MULTI "multi" 23762306a36Sopenharmony_ci#define GRAPH_NODENAME_DPCM "dpcm" 23862306a36Sopenharmony_ci#define GRAPH_NODENAME_C2C "codec2codec" 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic enum graph_type __graph_get_type(struct device_node *lnk) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct device_node *np, *parent_np; 24562306a36Sopenharmony_ci enum graph_type ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * target { 24962306a36Sopenharmony_ci * ports { 25062306a36Sopenharmony_ci * => lnk: port@0 { ... }; 25162306a36Sopenharmony_ci * port@1 { ... }; 25262306a36Sopenharmony_ci * }; 25362306a36Sopenharmony_ci * }; 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci np = of_get_parent(lnk); 25662306a36Sopenharmony_ci if (of_node_name_eq(np, "ports")) { 25762306a36Sopenharmony_ci parent_np = of_get_parent(np); 25862306a36Sopenharmony_ci of_node_put(np); 25962306a36Sopenharmony_ci np = parent_np; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) { 26362306a36Sopenharmony_ci ret = GRAPH_MULTI; 26462306a36Sopenharmony_ci goto out_put; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) { 26862306a36Sopenharmony_ci ret = GRAPH_DPCM; 26962306a36Sopenharmony_ci goto out_put; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (of_node_name_eq(np, GRAPH_NODENAME_C2C)) { 27362306a36Sopenharmony_ci ret = GRAPH_C2C; 27462306a36Sopenharmony_ci goto out_put; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = GRAPH_NORMAL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciout_put: 28062306a36Sopenharmony_ci of_node_put(np); 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic enum graph_type graph_get_type(struct asoc_simple_priv *priv, 28662306a36Sopenharmony_ci struct device_node *lnk) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci enum graph_type type = __graph_get_type(lnk); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* GRAPH_MULTI here means GRAPH_NORMAL */ 29162306a36Sopenharmony_ci if (type == GRAPH_MULTI) 29262306a36Sopenharmony_ci type = GRAPH_NORMAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#ifdef DEBUG 29562306a36Sopenharmony_ci { 29662306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 29762306a36Sopenharmony_ci const char *str = "Normal"; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci switch (type) { 30062306a36Sopenharmony_ci case GRAPH_DPCM: 30162306a36Sopenharmony_ci if (asoc_graph_is_ports0(lnk)) 30262306a36Sopenharmony_ci str = "DPCM Front-End"; 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci str = "DPCM Back-End"; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case GRAPH_C2C: 30762306a36Sopenharmony_ci str = "Codec2Codec"; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci default: 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dev_dbg(dev, "%pOF (%s)", lnk, str); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci return type; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int graph_lnk_is_multi(struct device_node *lnk) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci return __graph_get_type(lnk) == GRAPH_MULTI; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic struct device_node *graph_get_next_multi_ep(struct device_node **port) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct device_node *ports = of_get_parent(*port); 32762306a36Sopenharmony_ci struct device_node *ep = NULL; 32862306a36Sopenharmony_ci struct device_node *rep = NULL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * multi { 33262306a36Sopenharmony_ci * ports { 33362306a36Sopenharmony_ci * => lnk: port@0 { ... }; 33462306a36Sopenharmony_ci * port@1 { ep { ... = rep0 } }; 33562306a36Sopenharmony_ci * port@2 { ep { ... = rep1 } }; 33662306a36Sopenharmony_ci * ... 33762306a36Sopenharmony_ci * }; 33862306a36Sopenharmony_ci * }; 33962306a36Sopenharmony_ci * 34062306a36Sopenharmony_ci * xxx { 34162306a36Sopenharmony_ci * port@0 { rep0 }; 34262306a36Sopenharmony_ci * port@1 { rep1 }; 34362306a36Sopenharmony_ci * }; 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci do { 34662306a36Sopenharmony_ci *port = of_get_next_child(ports, *port); 34762306a36Sopenharmony_ci if (!*port) 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci } while (!of_node_name_eq(*port, "port")); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (*port) { 35262306a36Sopenharmony_ci ep = port_to_endpoint(*port); 35362306a36Sopenharmony_ci rep = of_graph_get_remote_endpoint(ep); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci of_node_put(ep); 35762306a36Sopenharmony_ci of_node_put(ports); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return rep; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct snd_soc_ops graph_ops = { 36362306a36Sopenharmony_ci .startup = asoc_simple_startup, 36462306a36Sopenharmony_ci .shutdown = asoc_simple_shutdown, 36562306a36Sopenharmony_ci .hw_params = asoc_simple_hw_params, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void graph_parse_convert(struct device_node *ep, 36962306a36Sopenharmony_ci struct simple_dai_props *props) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct device_node *port = of_get_parent(ep); 37262306a36Sopenharmony_ci struct device_node *ports = of_get_parent(port); 37362306a36Sopenharmony_ci struct asoc_simple_data *adata = &props->adata; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (of_node_name_eq(ports, "ports")) 37662306a36Sopenharmony_ci asoc_simple_parse_convert(ports, NULL, adata); 37762306a36Sopenharmony_ci asoc_simple_parse_convert(port, NULL, adata); 37862306a36Sopenharmony_ci asoc_simple_parse_convert(ep, NULL, adata); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci of_node_put(port); 38162306a36Sopenharmony_ci of_node_put(ports); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void graph_parse_mclk_fs(struct device_node *ep, 38562306a36Sopenharmony_ci struct simple_dai_props *props) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct device_node *port = of_get_parent(ep); 38862306a36Sopenharmony_ci struct device_node *ports = of_get_parent(port); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (of_node_name_eq(ports, "ports")) 39162306a36Sopenharmony_ci of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); 39262306a36Sopenharmony_ci of_property_read_u32(port, "mclk-fs", &props->mclk_fs); 39362306a36Sopenharmony_ci of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci of_node_put(port); 39662306a36Sopenharmony_ci of_node_put(ports); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int __graph_parse_node(struct asoc_simple_priv *priv, 40062306a36Sopenharmony_ci enum graph_type gtype, 40162306a36Sopenharmony_ci struct device_node *ep, 40262306a36Sopenharmony_ci struct link_info *li, 40362306a36Sopenharmony_ci int is_cpu, int idx) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 40662306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 40762306a36Sopenharmony_ci struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 40862306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc; 40962306a36Sopenharmony_ci struct asoc_simple_dai *dai; 41062306a36Sopenharmony_ci int ret, is_single_links = 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (is_cpu) { 41362306a36Sopenharmony_ci dlc = asoc_link_to_cpu(dai_link, idx); 41462306a36Sopenharmony_ci dai = simple_props_to_dai_cpu(dai_props, idx); 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci dlc = asoc_link_to_codec(dai_link, idx); 41762306a36Sopenharmony_ci dai = simple_props_to_dai_codec(dai_props, idx); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci graph_parse_mclk_fs(ep, dai_props); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = asoc_graph_parse_dai(dev, ep, dlc, &is_single_links); 42362306a36Sopenharmony_ci if (ret < 0) 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ret = asoc_simple_parse_tdm(ep, dai); 42762306a36Sopenharmony_ci if (ret < 0) 42862306a36Sopenharmony_ci return ret; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ret = asoc_simple_parse_tdm_width_map(dev, ep, dai); 43162306a36Sopenharmony_ci if (ret < 0) 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ret = asoc_simple_parse_clk(dev, ep, dai, dlc); 43562306a36Sopenharmony_ci if (ret < 0) 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * set DAI Name 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci if (!dai_link->name) { 44262306a36Sopenharmony_ci struct snd_soc_dai_link_component *cpus = dlc; 44362306a36Sopenharmony_ci struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); 44462306a36Sopenharmony_ci char *cpu_multi = ""; 44562306a36Sopenharmony_ci char *codec_multi = ""; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (dai_link->num_cpus > 1) 44862306a36Sopenharmony_ci cpu_multi = "_multi"; 44962306a36Sopenharmony_ci if (dai_link->num_codecs > 1) 45062306a36Sopenharmony_ci codec_multi = "_multi"; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci switch (gtype) { 45362306a36Sopenharmony_ci case GRAPH_NORMAL: 45462306a36Sopenharmony_ci /* run is_cpu only. see audio_graph2_link_normal() */ 45562306a36Sopenharmony_ci if (is_cpu) 45662306a36Sopenharmony_ci asoc_simple_set_dailink_name(dev, dai_link, "%s%s-%s%s", 45762306a36Sopenharmony_ci cpus->dai_name, cpu_multi, 45862306a36Sopenharmony_ci codecs->dai_name, codec_multi); 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case GRAPH_DPCM: 46162306a36Sopenharmony_ci if (is_cpu) 46262306a36Sopenharmony_ci asoc_simple_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s", 46362306a36Sopenharmony_ci cpus->of_node, cpus->dai_name, cpu_multi); 46462306a36Sopenharmony_ci else 46562306a36Sopenharmony_ci asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", 46662306a36Sopenharmony_ci codecs->of_node, codecs->dai_name, codec_multi); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case GRAPH_C2C: 46962306a36Sopenharmony_ci /* run is_cpu only. see audio_graph2_link_c2c() */ 47062306a36Sopenharmony_ci if (is_cpu) 47162306a36Sopenharmony_ci asoc_simple_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s", 47262306a36Sopenharmony_ci cpus->dai_name, cpu_multi, 47362306a36Sopenharmony_ci codecs->dai_name, codec_multi); 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci default: 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Check "prefix" from top node 48262306a36Sopenharmony_ci * if DPCM-BE case 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (!is_cpu && gtype == GRAPH_DPCM) { 48562306a36Sopenharmony_ci struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); 48662306a36Sopenharmony_ci struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); 48762306a36Sopenharmony_ci struct device_node *rport = of_get_parent(ep); 48862306a36Sopenharmony_ci struct device_node *rports = of_get_parent(rport); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (of_node_name_eq(rports, "ports")) 49162306a36Sopenharmony_ci snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); 49262306a36Sopenharmony_ci snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci of_node_put(rport); 49562306a36Sopenharmony_ci of_node_put(rports); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (is_cpu) { 49962306a36Sopenharmony_ci struct snd_soc_dai_link_component *cpus = dlc; 50062306a36Sopenharmony_ci struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci asoc_simple_canonicalize_cpu(cpus, is_single_links); 50362306a36Sopenharmony_ci asoc_simple_canonicalize_platform(platforms, cpus); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int graph_parse_node(struct asoc_simple_priv *priv, 51062306a36Sopenharmony_ci enum graph_type gtype, 51162306a36Sopenharmony_ci struct device_node *port, 51262306a36Sopenharmony_ci struct link_info *li, int is_cpu) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct device_node *ep; 51562306a36Sopenharmony_ci int ret = 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (graph_lnk_is_multi(port)) { 51862306a36Sopenharmony_ci int idx; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci of_node_get(port); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci for (idx = 0;; idx++) { 52362306a36Sopenharmony_ci ep = graph_get_next_multi_ep(&port); 52462306a36Sopenharmony_ci if (!ep) 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ret = __graph_parse_node(priv, gtype, ep, 52862306a36Sopenharmony_ci li, is_cpu, idx); 52962306a36Sopenharmony_ci of_node_put(ep); 53062306a36Sopenharmony_ci if (ret < 0) 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } else { 53462306a36Sopenharmony_ci /* Single CPU / Codec */ 53562306a36Sopenharmony_ci ep = port_to_endpoint(port); 53662306a36Sopenharmony_ci ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); 53762306a36Sopenharmony_ci of_node_put(ep); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic void graph_parse_daifmt(struct device_node *node, 54462306a36Sopenharmony_ci unsigned int *daifmt, unsigned int *bit_frame) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci unsigned int fmt; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * see also above "daifmt" explanation 55062306a36Sopenharmony_ci * and samples. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * ports { 55562306a36Sopenharmony_ci * (A) 55662306a36Sopenharmony_ci * port { 55762306a36Sopenharmony_ci * (B) 55862306a36Sopenharmony_ci * endpoint { 55962306a36Sopenharmony_ci * (C) 56062306a36Sopenharmony_ci * }; 56162306a36Sopenharmony_ci * }; 56262306a36Sopenharmony_ci * }; 56362306a36Sopenharmony_ci * }; 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * clock_provider: 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * It can be judged it is provider 57062306a36Sopenharmony_ci * if (A) or (B) or (C) has bitclock-master / frame-master flag. 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * use "or" 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci *bit_frame |= snd_soc_daifmt_parse_clock_provider_as_bitmap(node, NULL); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci#define update_daifmt(name) \ 57762306a36Sopenharmony_ci if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \ 57862306a36Sopenharmony_ci (fmt & SND_SOC_DAIFMT_##name##_MASK)) \ 57962306a36Sopenharmony_ci *daifmt |= fmt & SND_SOC_DAIFMT_##name##_MASK 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * format 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * This function is called by (C) -> (B) -> (A) order. 58562306a36Sopenharmony_ci * Set if applicable part was not yet set. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci fmt = snd_soc_daifmt_parse_format(node, NULL); 58862306a36Sopenharmony_ci update_daifmt(FORMAT); 58962306a36Sopenharmony_ci update_daifmt(CLOCK); 59062306a36Sopenharmony_ci update_daifmt(INV); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void graph_link_init(struct asoc_simple_priv *priv, 59462306a36Sopenharmony_ci struct device_node *port, 59562306a36Sopenharmony_ci struct link_info *li, 59662306a36Sopenharmony_ci int is_cpu_node) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 59962306a36Sopenharmony_ci struct device_node *ep; 60062306a36Sopenharmony_ci struct device_node *ports; 60162306a36Sopenharmony_ci unsigned int daifmt = 0, daiclk = 0; 60262306a36Sopenharmony_ci unsigned int bit_frame = 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (graph_lnk_is_multi(port)) { 60562306a36Sopenharmony_ci of_node_get(port); 60662306a36Sopenharmony_ci ep = graph_get_next_multi_ep(&port); 60762306a36Sopenharmony_ci port = of_get_parent(ep); 60862306a36Sopenharmony_ci } else { 60962306a36Sopenharmony_ci ep = port_to_endpoint(port); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ports = of_get_parent(port); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * ports { 61662306a36Sopenharmony_ci * (A) 61762306a36Sopenharmony_ci * port { 61862306a36Sopenharmony_ci * (B) 61962306a36Sopenharmony_ci * endpoint { 62062306a36Sopenharmony_ci * (C) 62162306a36Sopenharmony_ci * }; 62262306a36Sopenharmony_ci * }; 62362306a36Sopenharmony_ci * }; 62462306a36Sopenharmony_ci * }; 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */ 62762306a36Sopenharmony_ci graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */ 62862306a36Sopenharmony_ci if (of_node_name_eq(ports, "ports")) 62962306a36Sopenharmony_ci graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * convert bit_frame 63362306a36Sopenharmony_ci * We need to flip clock_provider if it was CPU node, 63462306a36Sopenharmony_ci * because it is Codec base. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame); 63762306a36Sopenharmony_ci if (is_cpu_node) 63862306a36Sopenharmony_ci daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci dai_link->dai_fmt = daifmt | daiclk; 64162306a36Sopenharmony_ci dai_link->init = asoc_simple_dai_init; 64262306a36Sopenharmony_ci dai_link->ops = &graph_ops; 64362306a36Sopenharmony_ci if (priv->ops) 64462306a36Sopenharmony_ci dai_link->ops = priv->ops; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciint audio_graph2_link_normal(struct asoc_simple_priv *priv, 64862306a36Sopenharmony_ci struct device_node *lnk, 64962306a36Sopenharmony_ci struct link_info *li) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct device_node *cpu_port = lnk; 65262306a36Sopenharmony_ci struct device_node *cpu_ep = port_to_endpoint(cpu_port); 65362306a36Sopenharmony_ci struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); 65462306a36Sopenharmony_ci int ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* 65762306a36Sopenharmony_ci * call Codec first. 65862306a36Sopenharmony_ci * see 65962306a36Sopenharmony_ci * __graph_parse_node() :: DAI Naming 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_NORMAL, codec_port, li, 0); 66262306a36Sopenharmony_ci if (ret < 0) 66362306a36Sopenharmony_ci goto err; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * call CPU, and set DAI Name 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_port, li, 1); 66962306a36Sopenharmony_ci if (ret < 0) 67062306a36Sopenharmony_ci goto err; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci graph_link_init(priv, cpu_port, li, 1); 67362306a36Sopenharmony_cierr: 67462306a36Sopenharmony_ci of_node_put(codec_port); 67562306a36Sopenharmony_ci of_node_put(cpu_ep); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(audio_graph2_link_normal); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciint audio_graph2_link_dpcm(struct asoc_simple_priv *priv, 68262306a36Sopenharmony_ci struct device_node *lnk, 68362306a36Sopenharmony_ci struct link_info *li) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct device_node *ep = port_to_endpoint(lnk); 68662306a36Sopenharmony_ci struct device_node *rep = of_graph_get_remote_endpoint(ep); 68762306a36Sopenharmony_ci struct device_node *rport = of_graph_get_remote_port(ep); 68862306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 68962306a36Sopenharmony_ci struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 69062306a36Sopenharmony_ci int is_cpu = asoc_graph_is_ports0(lnk); 69162306a36Sopenharmony_ci int ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (is_cpu) { 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * dpcm { 69662306a36Sopenharmony_ci * // Front-End 69762306a36Sopenharmony_ci * ports@0 { 69862306a36Sopenharmony_ci * => lnk: port@0 { ep: { ... = rep }; }; 69962306a36Sopenharmony_ci * ... 70062306a36Sopenharmony_ci * }; 70162306a36Sopenharmony_ci * // Back-End 70262306a36Sopenharmony_ci * ports@0 { 70362306a36Sopenharmony_ci * ... 70462306a36Sopenharmony_ci * }; 70562306a36Sopenharmony_ci * }; 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * CPU { 70862306a36Sopenharmony_ci * rports: ports { 70962306a36Sopenharmony_ci * rport: port@0 { rep: { ... = ep } }; 71062306a36Sopenharmony_ci * } 71162306a36Sopenharmony_ci * } 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci /* 71462306a36Sopenharmony_ci * setup CPU here, Codec is already set as dummy. 71562306a36Sopenharmony_ci * see 71662306a36Sopenharmony_ci * asoc_simple_init_priv() 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci dai_link->dynamic = 1; 71962306a36Sopenharmony_ci dai_link->dpcm_merged_format = 1; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1); 72262306a36Sopenharmony_ci if (ret) 72362306a36Sopenharmony_ci goto err; 72462306a36Sopenharmony_ci } else { 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * dpcm { 72762306a36Sopenharmony_ci * // Front-End 72862306a36Sopenharmony_ci * ports@0 { 72962306a36Sopenharmony_ci * ... 73062306a36Sopenharmony_ci * }; 73162306a36Sopenharmony_ci * // Back-End 73262306a36Sopenharmony_ci * ports@0 { 73362306a36Sopenharmony_ci * => lnk: port@0 { ep: { ... = rep; }; }; 73462306a36Sopenharmony_ci * ... 73562306a36Sopenharmony_ci * }; 73662306a36Sopenharmony_ci * }; 73762306a36Sopenharmony_ci * 73862306a36Sopenharmony_ci * Codec { 73962306a36Sopenharmony_ci * rports: ports { 74062306a36Sopenharmony_ci * rport: port@0 { rep: { ... = ep; }; }; 74162306a36Sopenharmony_ci * } 74262306a36Sopenharmony_ci * } 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ci /* 74562306a36Sopenharmony_ci * setup Codec here, CPU is already set as dummy. 74662306a36Sopenharmony_ci * see 74762306a36Sopenharmony_ci * asoc_simple_init_priv() 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* BE settings */ 75162306a36Sopenharmony_ci dai_link->no_pcm = 1; 75262306a36Sopenharmony_ci dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0); 75562306a36Sopenharmony_ci if (ret < 0) 75662306a36Sopenharmony_ci goto err; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci graph_parse_convert(ep, dai_props); /* at node of <dpcm> */ 76062306a36Sopenharmony_ci graph_parse_convert(rep, dai_props); /* at node of <CPU/Codec> */ 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci snd_soc_dai_link_set_capabilities(dai_link); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci graph_link_init(priv, rport, li, is_cpu); 76562306a36Sopenharmony_cierr: 76662306a36Sopenharmony_ci of_node_put(ep); 76762306a36Sopenharmony_ci of_node_put(rep); 76862306a36Sopenharmony_ci of_node_put(rport); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ciint audio_graph2_link_c2c(struct asoc_simple_priv *priv, 77562306a36Sopenharmony_ci struct device_node *lnk, 77662306a36Sopenharmony_ci struct link_info *li) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 77962306a36Sopenharmony_ci struct device_node *port0, *port1, *ports; 78062306a36Sopenharmony_ci struct device_node *codec0_port, *codec1_port; 78162306a36Sopenharmony_ci struct device_node *ep0, *ep1; 78262306a36Sopenharmony_ci u32 val = 0; 78362306a36Sopenharmony_ci int ret = -EINVAL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* 78662306a36Sopenharmony_ci * codec2codec { 78762306a36Sopenharmony_ci * ports { 78862306a36Sopenharmony_ci * rate = <48000>; 78962306a36Sopenharmony_ci * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; }; 79062306a36Sopenharmony_ci * port@1 { c2c1_ep: { ... = codec1_ep; }; }; 79162306a36Sopenharmony_ci * }; 79262306a36Sopenharmony_ci * }; 79362306a36Sopenharmony_ci * 79462306a36Sopenharmony_ci * Codec { 79562306a36Sopenharmony_ci * ports { 79662306a36Sopenharmony_ci * port@0 { codec0_ep: ... }; }; 79762306a36Sopenharmony_ci * port@1 { codec1_ep: ... }; }; 79862306a36Sopenharmony_ci * }; 79962306a36Sopenharmony_ci * }; 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci of_node_get(lnk); 80262306a36Sopenharmony_ci port0 = lnk; 80362306a36Sopenharmony_ci ports = of_get_parent(port0); 80462306a36Sopenharmony_ci port1 = of_get_next_child(ports, lnk); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* 80762306a36Sopenharmony_ci * Card2 can use original Codec2Codec settings if DT has. 80862306a36Sopenharmony_ci * It will use default settings if no settings on DT. 80962306a36Sopenharmony_ci * see 81062306a36Sopenharmony_ci * asoc_simple_init_for_codec2codec() 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * Add more settings here if needed 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci of_property_read_u32(ports, "rate", &val); 81562306a36Sopenharmony_ci if (val) { 81662306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 81762306a36Sopenharmony_ci struct snd_soc_pcm_stream *c2c_conf; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL); 82062306a36Sopenharmony_ci if (!c2c_conf) 82162306a36Sopenharmony_ci goto err1; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */ 82462306a36Sopenharmony_ci c2c_conf->rates = SNDRV_PCM_RATE_8000_384000; 82562306a36Sopenharmony_ci c2c_conf->rate_min = 82662306a36Sopenharmony_ci c2c_conf->rate_max = val; 82762306a36Sopenharmony_ci c2c_conf->channels_min = 82862306a36Sopenharmony_ci c2c_conf->channels_max = 2; /* update ME */ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci dai_link->c2c_params = c2c_conf; 83162306a36Sopenharmony_ci dai_link->num_c2c_params = 1; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ep0 = port_to_endpoint(port0); 83562306a36Sopenharmony_ci ep1 = port_to_endpoint(port1); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci codec0_port = of_graph_get_remote_port(ep0); 83862306a36Sopenharmony_ci codec1_port = of_graph_get_remote_port(ep1); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* 84162306a36Sopenharmony_ci * call Codec first. 84262306a36Sopenharmony_ci * see 84362306a36Sopenharmony_ci * __graph_parse_node() :: DAI Naming 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0); 84662306a36Sopenharmony_ci if (ret < 0) 84762306a36Sopenharmony_ci goto err2; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * call CPU, and set DAI Name 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci goto err2; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci graph_link_init(priv, codec0_port, li, 1); 85762306a36Sopenharmony_cierr2: 85862306a36Sopenharmony_ci of_node_put(ep0); 85962306a36Sopenharmony_ci of_node_put(ep1); 86062306a36Sopenharmony_ci of_node_put(codec0_port); 86162306a36Sopenharmony_ci of_node_put(codec1_port); 86262306a36Sopenharmony_cierr1: 86362306a36Sopenharmony_ci of_node_put(ports); 86462306a36Sopenharmony_ci of_node_put(port0); 86562306a36Sopenharmony_ci of_node_put(port1); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(audio_graph2_link_c2c); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int graph_link(struct asoc_simple_priv *priv, 87262306a36Sopenharmony_ci struct graph2_custom_hooks *hooks, 87362306a36Sopenharmony_ci enum graph_type gtype, 87462306a36Sopenharmony_ci struct device_node *lnk, 87562306a36Sopenharmony_ci struct link_info *li) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 87862306a36Sopenharmony_ci GRAPH2_CUSTOM func = NULL; 87962306a36Sopenharmony_ci int ret = -EINVAL; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci switch (gtype) { 88262306a36Sopenharmony_ci case GRAPH_NORMAL: 88362306a36Sopenharmony_ci if (hooks && hooks->custom_normal) 88462306a36Sopenharmony_ci func = hooks->custom_normal; 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci func = audio_graph2_link_normal; 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci case GRAPH_DPCM: 88962306a36Sopenharmony_ci if (hooks && hooks->custom_dpcm) 89062306a36Sopenharmony_ci func = hooks->custom_dpcm; 89162306a36Sopenharmony_ci else 89262306a36Sopenharmony_ci func = audio_graph2_link_dpcm; 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci case GRAPH_C2C: 89562306a36Sopenharmony_ci if (hooks && hooks->custom_c2c) 89662306a36Sopenharmony_ci func = hooks->custom_c2c; 89762306a36Sopenharmony_ci else 89862306a36Sopenharmony_ci func = audio_graph2_link_c2c; 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci default: 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (!func) { 90562306a36Sopenharmony_ci dev_err(dev, "non supported gtype (%d)\n", gtype); 90662306a36Sopenharmony_ci goto err; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci ret = func(priv, lnk, li); 91062306a36Sopenharmony_ci if (ret < 0) 91162306a36Sopenharmony_ci goto err; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci li->link++; 91462306a36Sopenharmony_cierr: 91562306a36Sopenharmony_ci return ret; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int graph_counter(struct device_node *lnk) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci /* 92162306a36Sopenharmony_ci * Multi CPU / Codec 92262306a36Sopenharmony_ci * 92362306a36Sopenharmony_ci * multi { 92462306a36Sopenharmony_ci * ports { 92562306a36Sopenharmony_ci * => lnk: port@0 { ... }; 92662306a36Sopenharmony_ci * port@1 { ... }; 92762306a36Sopenharmony_ci * port@2 { ... }; 92862306a36Sopenharmony_ci * ... 92962306a36Sopenharmony_ci * }; 93062306a36Sopenharmony_ci * }; 93162306a36Sopenharmony_ci * 93262306a36Sopenharmony_ci * ignore first lnk part 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci if (graph_lnk_is_multi(lnk)) 93562306a36Sopenharmony_ci return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1; 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * Single CPU / Codec 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci else 94062306a36Sopenharmony_ci return 1; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int graph_count_normal(struct asoc_simple_priv *priv, 94462306a36Sopenharmony_ci struct device_node *lnk, 94562306a36Sopenharmony_ci struct link_info *li) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct device_node *cpu_port = lnk; 94862306a36Sopenharmony_ci struct device_node *cpu_ep = port_to_endpoint(cpu_port); 94962306a36Sopenharmony_ci struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * CPU { 95362306a36Sopenharmony_ci * => lnk: port { endpoint { .. }; }; 95462306a36Sopenharmony_ci * }; 95562306a36Sopenharmony_ci */ 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * DON'T REMOVE platforms 95862306a36Sopenharmony_ci * see 95962306a36Sopenharmony_ci * simple-card.c :: simple_count_noml() 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci li->num[li->link].cpus = 96262306a36Sopenharmony_ci li->num[li->link].platforms = graph_counter(cpu_port); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci li->num[li->link].codecs = graph_counter(codec_port); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci of_node_put(cpu_ep); 96762306a36Sopenharmony_ci of_node_put(codec_port); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return 0; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int graph_count_dpcm(struct asoc_simple_priv *priv, 97362306a36Sopenharmony_ci struct device_node *lnk, 97462306a36Sopenharmony_ci struct link_info *li) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct device_node *ep = port_to_endpoint(lnk); 97762306a36Sopenharmony_ci struct device_node *rport = of_graph_get_remote_port(ep); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * dpcm { 98162306a36Sopenharmony_ci * // Front-End 98262306a36Sopenharmony_ci * ports@0 { 98362306a36Sopenharmony_ci * => lnk: port@0 { endpoint { ... }; }; 98462306a36Sopenharmony_ci * ... 98562306a36Sopenharmony_ci * }; 98662306a36Sopenharmony_ci * // Back-End 98762306a36Sopenharmony_ci * ports@1 { 98862306a36Sopenharmony_ci * => lnk: port@0 { endpoint { ... }; }; 98962306a36Sopenharmony_ci * ... 99062306a36Sopenharmony_ci * }; 99162306a36Sopenharmony_ci * }; 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (asoc_graph_is_ports0(lnk)) { 99562306a36Sopenharmony_ci /* 99662306a36Sopenharmony_ci * DON'T REMOVE platforms 99762306a36Sopenharmony_ci * see 99862306a36Sopenharmony_ci * simple-card.c :: simple_count_noml() 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci li->num[li->link].cpus = graph_counter(rport); /* FE */ 100162306a36Sopenharmony_ci li->num[li->link].platforms = graph_counter(rport); 100262306a36Sopenharmony_ci } else { 100362306a36Sopenharmony_ci li->num[li->link].codecs = graph_counter(rport); /* BE */ 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci of_node_put(ep); 100762306a36Sopenharmony_ci of_node_put(rport); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int graph_count_c2c(struct asoc_simple_priv *priv, 101362306a36Sopenharmony_ci struct device_node *lnk, 101462306a36Sopenharmony_ci struct link_info *li) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct device_node *ports = of_get_parent(lnk); 101762306a36Sopenharmony_ci struct device_node *port0 = lnk; 101862306a36Sopenharmony_ci struct device_node *port1 = of_get_next_child(ports, lnk); 101962306a36Sopenharmony_ci struct device_node *ep0 = port_to_endpoint(port0); 102062306a36Sopenharmony_ci struct device_node *ep1 = port_to_endpoint(port1); 102162306a36Sopenharmony_ci struct device_node *codec0 = of_graph_get_remote_port(ep0); 102262306a36Sopenharmony_ci struct device_node *codec1 = of_graph_get_remote_port(ep1); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci of_node_get(lnk); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* 102762306a36Sopenharmony_ci * codec2codec { 102862306a36Sopenharmony_ci * ports { 102962306a36Sopenharmony_ci * => lnk: port@0 { endpoint { ... }; }; 103062306a36Sopenharmony_ci * port@1 { endpoint { ... }; }; 103162306a36Sopenharmony_ci * }; 103262306a36Sopenharmony_ci * }; 103362306a36Sopenharmony_ci */ 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * DON'T REMOVE platforms 103662306a36Sopenharmony_ci * see 103762306a36Sopenharmony_ci * simple-card.c :: simple_count_noml() 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci li->num[li->link].cpus = 104062306a36Sopenharmony_ci li->num[li->link].platforms = graph_counter(codec0); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci li->num[li->link].codecs = graph_counter(codec1); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci of_node_put(ports); 104562306a36Sopenharmony_ci of_node_put(port1); 104662306a36Sopenharmony_ci of_node_put(ep0); 104762306a36Sopenharmony_ci of_node_put(ep1); 104862306a36Sopenharmony_ci of_node_put(codec0); 104962306a36Sopenharmony_ci of_node_put(codec1); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return 0; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int graph_count(struct asoc_simple_priv *priv, 105562306a36Sopenharmony_ci struct graph2_custom_hooks *hooks, 105662306a36Sopenharmony_ci enum graph_type gtype, 105762306a36Sopenharmony_ci struct device_node *lnk, 105862306a36Sopenharmony_ci struct link_info *li) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 106162306a36Sopenharmony_ci GRAPH2_CUSTOM func = NULL; 106262306a36Sopenharmony_ci int ret = -EINVAL; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (li->link >= SNDRV_MAX_LINKS) { 106562306a36Sopenharmony_ci dev_err(dev, "too many links\n"); 106662306a36Sopenharmony_ci return ret; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci switch (gtype) { 107062306a36Sopenharmony_ci case GRAPH_NORMAL: 107162306a36Sopenharmony_ci func = graph_count_normal; 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci case GRAPH_DPCM: 107462306a36Sopenharmony_ci func = graph_count_dpcm; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case GRAPH_C2C: 107762306a36Sopenharmony_ci func = graph_count_c2c; 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci default: 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (!func) { 108462306a36Sopenharmony_ci dev_err(dev, "non supported gtype (%d)\n", gtype); 108562306a36Sopenharmony_ci goto err; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci ret = func(priv, lnk, li); 108962306a36Sopenharmony_ci if (ret < 0) 109062306a36Sopenharmony_ci goto err; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci li->link++; 109362306a36Sopenharmony_cierr: 109462306a36Sopenharmony_ci return ret; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int graph_for_each_link(struct asoc_simple_priv *priv, 109862306a36Sopenharmony_ci struct graph2_custom_hooks *hooks, 109962306a36Sopenharmony_ci struct link_info *li, 110062306a36Sopenharmony_ci int (*func)(struct asoc_simple_priv *priv, 110162306a36Sopenharmony_ci struct graph2_custom_hooks *hooks, 110262306a36Sopenharmony_ci enum graph_type gtype, 110362306a36Sopenharmony_ci struct device_node *lnk, 110462306a36Sopenharmony_ci struct link_info *li)) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct of_phandle_iterator it; 110762306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 110862306a36Sopenharmony_ci struct device_node *node = dev->of_node; 110962306a36Sopenharmony_ci struct device_node *lnk; 111062306a36Sopenharmony_ci enum graph_type gtype; 111162306a36Sopenharmony_ci int rc, ret; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* loop for all listed CPU port */ 111462306a36Sopenharmony_ci of_for_each_phandle(&it, rc, node, "links", NULL, 0) { 111562306a36Sopenharmony_ci lnk = it.node; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci gtype = graph_get_type(priv, lnk); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci ret = func(priv, hooks, gtype, lnk, li); 112062306a36Sopenharmony_ci if (ret < 0) 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ciint audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, 112862306a36Sopenharmony_ci struct graph2_custom_hooks *hooks) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci struct snd_soc_card *card = simple_priv_to_card(priv); 113162306a36Sopenharmony_ci struct link_info *li; 113262306a36Sopenharmony_ci int ret; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); 113562306a36Sopenharmony_ci if (!li) 113662306a36Sopenharmony_ci return -ENOMEM; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci card->probe = asoc_graph_card_probe; 113962306a36Sopenharmony_ci card->owner = THIS_MODULE; 114062306a36Sopenharmony_ci card->dev = dev; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if ((hooks) && (hooks)->hook_pre) { 114362306a36Sopenharmony_ci ret = (hooks)->hook_pre(priv); 114462306a36Sopenharmony_ci if (ret < 0) 114562306a36Sopenharmony_ci goto err; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci ret = graph_for_each_link(priv, hooks, li, graph_count); 114962306a36Sopenharmony_ci if (!li->link) 115062306a36Sopenharmony_ci ret = -EINVAL; 115162306a36Sopenharmony_ci if (ret < 0) 115262306a36Sopenharmony_ci goto err; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ret = asoc_simple_init_priv(priv, li); 115562306a36Sopenharmony_ci if (ret < 0) 115662306a36Sopenharmony_ci goto err; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); 115962306a36Sopenharmony_ci if (IS_ERR(priv->pa_gpio)) { 116062306a36Sopenharmony_ci ret = PTR_ERR(priv->pa_gpio); 116162306a36Sopenharmony_ci dev_err(dev, "failed to get amplifier gpio: %d\n", ret); 116262306a36Sopenharmony_ci goto err; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci ret = asoc_simple_parse_widgets(card, NULL); 116662306a36Sopenharmony_ci if (ret < 0) 116762306a36Sopenharmony_ci goto err; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci ret = asoc_simple_parse_routing(card, NULL); 117062306a36Sopenharmony_ci if (ret < 0) 117162306a36Sopenharmony_ci goto err; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci memset(li, 0, sizeof(*li)); 117462306a36Sopenharmony_ci ret = graph_for_each_link(priv, hooks, li, graph_link); 117562306a36Sopenharmony_ci if (ret < 0) 117662306a36Sopenharmony_ci goto err; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci ret = asoc_simple_parse_card_name(card, NULL); 117962306a36Sopenharmony_ci if (ret < 0) 118062306a36Sopenharmony_ci goto err; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, priv); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if ((hooks) && (hooks)->hook_post) { 118562306a36Sopenharmony_ci ret = (hooks)->hook_post(priv); 118662306a36Sopenharmony_ci if (ret < 0) 118762306a36Sopenharmony_ci goto err; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci asoc_simple_debug_info(priv); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ret = devm_snd_soc_register_card(dev, card); 119362306a36Sopenharmony_cierr: 119462306a36Sopenharmony_ci devm_kfree(dev, li); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (ret < 0) 119762306a36Sopenharmony_ci dev_err_probe(dev, ret, "parse error\n"); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return ret; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(audio_graph2_parse_of); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic int graph_probe(struct platform_device *pdev) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct asoc_simple_priv *priv; 120662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* Allocate the private data and the DAI link array */ 120962306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 121062306a36Sopenharmony_ci if (!priv) 121162306a36Sopenharmony_ci return -ENOMEM; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return audio_graph2_parse_of(priv, dev, NULL); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic const struct of_device_id graph_of_match[] = { 121762306a36Sopenharmony_ci { .compatible = "audio-graph-card2", }, 121862306a36Sopenharmony_ci {}, 121962306a36Sopenharmony_ci}; 122062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, graph_of_match); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic struct platform_driver graph_card = { 122362306a36Sopenharmony_ci .driver = { 122462306a36Sopenharmony_ci .name = "asoc-audio-graph-card2", 122562306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 122662306a36Sopenharmony_ci .of_match_table = graph_of_match, 122762306a36Sopenharmony_ci }, 122862306a36Sopenharmony_ci .probe = graph_probe, 122962306a36Sopenharmony_ci .remove = asoc_simple_remove, 123062306a36Sopenharmony_ci}; 123162306a36Sopenharmony_cimodule_platform_driver(graph_card); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ciMODULE_ALIAS("platform:asoc-audio-graph-card2"); 123462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 123562306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC Audio Graph Card2"); 123662306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 1237