18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// ASoC simple sound card support 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2012 Renesas Solutions Corp. 68c2ecf20Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <sound/simple_card.h> 168c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define DPCM_SELECTABLE 1 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DAI "sound-dai" 228c2ecf20Sopenharmony_ci#define CELL "#sound-dai-cells" 238c2ecf20Sopenharmony_ci#define PREFIX "simple-audio-card," 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct snd_soc_ops simple_ops = { 268c2ecf20Sopenharmony_ci .startup = asoc_simple_startup, 278c2ecf20Sopenharmony_ci .shutdown = asoc_simple_shutdown, 288c2ecf20Sopenharmony_ci .hw_params = asoc_simple_hw_params, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int asoc_simple_parse_dai(struct device_node *node, 328c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *dlc, 338c2ecf20Sopenharmony_ci int *is_single_link) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct of_phandle_args args; 368c2ecf20Sopenharmony_ci int ret; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (!node) 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * Get node via "sound-dai = <&phandle port>" 438c2ecf20Sopenharmony_ci * it will be used as xxx_of_node on soc_bind_dai_link() 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); 468c2ecf20Sopenharmony_ci if (ret) 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * FIXME 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * Here, dlc->dai_name is pointer to CPU/Codec DAI name. 538c2ecf20Sopenharmony_ci * If user unbinded CPU or Codec driver, but not for Sound Card, 548c2ecf20Sopenharmony_ci * dlc->dai_name is keeping unbinded CPU or Codec 558c2ecf20Sopenharmony_ci * driver's pointer. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * If user re-bind CPU or Codec driver again, ALSA SoC will try 588c2ecf20Sopenharmony_ci * to rebind Card via snd_soc_try_rebind_card(), but because of 598c2ecf20Sopenharmony_ci * above reason, it might can't bind Sound Card. 608c2ecf20Sopenharmony_ci * Because Sound Card is pointing to released dai_name pointer. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * To avoid this rebind Card issue, 638c2ecf20Sopenharmony_ci * 1) It needs to alloc memory to keep dai_name eventhough 648c2ecf20Sopenharmony_ci * CPU or Codec driver was unbinded, or 658c2ecf20Sopenharmony_ci * 2) user need to rebind Sound Card everytime 668c2ecf20Sopenharmony_ci * if he unbinded CPU or Codec. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); 698c2ecf20Sopenharmony_ci if (ret < 0) 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci dlc->of_node = args.np; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (is_single_link) 758c2ecf20Sopenharmony_ci *is_single_link = !args.args_count; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void simple_parse_convert(struct device *dev, 818c2ecf20Sopenharmony_ci struct device_node *np, 828c2ecf20Sopenharmony_ci struct asoc_simple_data *adata) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 858c2ecf20Sopenharmony_ci struct device_node *node = of_get_parent(np); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci asoc_simple_parse_convert(dev, top, PREFIX, adata); 888c2ecf20Sopenharmony_ci asoc_simple_parse_convert(dev, node, PREFIX, adata); 898c2ecf20Sopenharmony_ci asoc_simple_parse_convert(dev, node, NULL, adata); 908c2ecf20Sopenharmony_ci asoc_simple_parse_convert(dev, np, NULL, adata); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci of_node_put(node); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void simple_parse_mclk_fs(struct device_node *top, 968c2ecf20Sopenharmony_ci struct device_node *cpu, 978c2ecf20Sopenharmony_ci struct device_node *codec, 988c2ecf20Sopenharmony_ci struct simple_dai_props *props, 998c2ecf20Sopenharmony_ci char *prefix) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct device_node *node = of_get_parent(cpu); 1028c2ecf20Sopenharmony_ci char prop[128]; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX); 1058c2ecf20Sopenharmony_ci of_property_read_u32(top, prop, &props->mclk_fs); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%smclk-fs", prefix); 1088c2ecf20Sopenharmony_ci of_property_read_u32(node, prop, &props->mclk_fs); 1098c2ecf20Sopenharmony_ci of_property_read_u32(cpu, prop, &props->mclk_fs); 1108c2ecf20Sopenharmony_ci of_property_read_u32(codec, prop, &props->mclk_fs); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci of_node_put(node); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, 1168c2ecf20Sopenharmony_ci struct device_node *np, 1178c2ecf20Sopenharmony_ci struct device_node *codec, 1188c2ecf20Sopenharmony_ci struct link_info *li, 1198c2ecf20Sopenharmony_ci bool is_top) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 1228c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 1238c2ecf20Sopenharmony_ci struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 1248c2ecf20Sopenharmony_ci struct asoc_simple_dai *dai; 1258c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *cpus = dai_link->cpus; 1268c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *codecs = dai_link->codecs; 1278c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 1288c2ecf20Sopenharmony_ci struct device_node *node = of_get_parent(np); 1298c2ecf20Sopenharmony_ci char *prefix = ""; 1308c2ecf20Sopenharmony_ci int ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * |CPU |Codec : turn 1348c2ecf20Sopenharmony_ci * CPU |Pass |return 1358c2ecf20Sopenharmony_ci * Codec |return|Pass 1368c2ecf20Sopenharmony_ci * np 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci if (li->cpu == (np == codec)) 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dev_dbg(dev, "link_of DPCM (%pOF)\n", np); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci li->link++; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* For single DAI link & old style of DT node */ 1468c2ecf20Sopenharmony_ci if (is_top) 1478c2ecf20Sopenharmony_ci prefix = PREFIX; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (li->cpu) { 1508c2ecf20Sopenharmony_ci int is_single_links = 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Codec is dummy */ 1538c2ecf20Sopenharmony_ci codecs->of_node = NULL; 1548c2ecf20Sopenharmony_ci codecs->dai_name = "snd-soc-dummy-dai"; 1558c2ecf20Sopenharmony_ci codecs->name = "snd-soc-dummy"; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* FE settings */ 1588c2ecf20Sopenharmony_ci dai_link->dynamic = 1; 1598c2ecf20Sopenharmony_ci dai_link->dpcm_merged_format = 1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci dai = 1628c2ecf20Sopenharmony_ci dai_props->cpu_dai = &priv->dais[li->dais++]; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = asoc_simple_parse_cpu(np, dai_link, &is_single_links); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci goto out_put_node; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = asoc_simple_parse_clk_cpu(dev, np, dai_link, dai); 1698c2ecf20Sopenharmony_ci if (ret < 0) 1708c2ecf20Sopenharmony_ci goto out_put_node; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = asoc_simple_set_dailink_name(dev, dai_link, 1738c2ecf20Sopenharmony_ci "fe.%s", 1748c2ecf20Sopenharmony_ci cpus->dai_name); 1758c2ecf20Sopenharmony_ci if (ret < 0) 1768c2ecf20Sopenharmony_ci goto out_put_node; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci asoc_simple_canonicalize_cpu(dai_link, is_single_links); 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci struct snd_soc_codec_conf *cconf; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* CPU is dummy */ 1838c2ecf20Sopenharmony_ci cpus->of_node = NULL; 1848c2ecf20Sopenharmony_ci cpus->dai_name = "snd-soc-dummy-dai"; 1858c2ecf20Sopenharmony_ci cpus->name = "snd-soc-dummy"; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* BE settings */ 1888c2ecf20Sopenharmony_ci dai_link->no_pcm = 1; 1898c2ecf20Sopenharmony_ci dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dai = 1928c2ecf20Sopenharmony_ci dai_props->codec_dai = &priv->dais[li->dais++]; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci cconf = 1958c2ecf20Sopenharmony_ci dai_props->codec_conf = &priv->codec_conf[li->conf++]; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = asoc_simple_parse_codec(np, dai_link); 1988c2ecf20Sopenharmony_ci if (ret < 0) 1998c2ecf20Sopenharmony_ci goto out_put_node; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = asoc_simple_parse_clk_codec(dev, np, dai_link, dai); 2028c2ecf20Sopenharmony_ci if (ret < 0) 2038c2ecf20Sopenharmony_ci goto out_put_node; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = asoc_simple_set_dailink_name(dev, dai_link, 2068c2ecf20Sopenharmony_ci "be.%s", 2078c2ecf20Sopenharmony_ci codecs->dai_name); 2088c2ecf20Sopenharmony_ci if (ret < 0) 2098c2ecf20Sopenharmony_ci goto out_put_node; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* check "prefix" from top node */ 2128c2ecf20Sopenharmony_ci snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, 2138c2ecf20Sopenharmony_ci PREFIX "prefix"); 2148c2ecf20Sopenharmony_ci snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, 2158c2ecf20Sopenharmony_ci "prefix"); 2168c2ecf20Sopenharmony_ci snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node, 2178c2ecf20Sopenharmony_ci "prefix"); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci simple_parse_convert(dev, np, &dai_props->adata); 2218c2ecf20Sopenharmony_ci simple_parse_mclk_fs(top, np, codec, dai_props, prefix); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci asoc_simple_canonicalize_platform(dai_link); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = asoc_simple_parse_tdm(np, dai); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci goto out_put_node; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = asoc_simple_parse_daifmt(dev, node, codec, 2308c2ecf20Sopenharmony_ci prefix, &dai_link->dai_fmt); 2318c2ecf20Sopenharmony_ci if (ret < 0) 2328c2ecf20Sopenharmony_ci goto out_put_node; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci snd_soc_dai_link_set_capabilities(dai_link); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci dai_link->ops = &simple_ops; 2378c2ecf20Sopenharmony_ci dai_link->init = asoc_simple_dai_init; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciout_put_node: 2408c2ecf20Sopenharmony_ci of_node_put(node); 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int simple_dai_link_of(struct asoc_simple_priv *priv, 2458c2ecf20Sopenharmony_ci struct device_node *np, 2468c2ecf20Sopenharmony_ci struct device_node *codec, 2478c2ecf20Sopenharmony_ci struct link_info *li, 2488c2ecf20Sopenharmony_ci bool is_top) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 2518c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 2528c2ecf20Sopenharmony_ci struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 2538c2ecf20Sopenharmony_ci struct asoc_simple_dai *cpu_dai; 2548c2ecf20Sopenharmony_ci struct asoc_simple_dai *codec_dai; 2558c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 2568c2ecf20Sopenharmony_ci struct device_node *cpu = NULL; 2578c2ecf20Sopenharmony_ci struct device_node *node = NULL; 2588c2ecf20Sopenharmony_ci struct device_node *plat = NULL; 2598c2ecf20Sopenharmony_ci char prop[128]; 2608c2ecf20Sopenharmony_ci char *prefix = ""; 2618c2ecf20Sopenharmony_ci int ret, single_cpu = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * |CPU |Codec : turn 2658c2ecf20Sopenharmony_ci * CPU |Pass |return 2668c2ecf20Sopenharmony_ci * Codec |return|return 2678c2ecf20Sopenharmony_ci * np 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (!li->cpu || np == codec) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci cpu = np; 2738c2ecf20Sopenharmony_ci node = of_get_parent(np); 2748c2ecf20Sopenharmony_ci li->link++; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dev_dbg(dev, "link_of (%pOF)\n", node); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* For single DAI link & old style of DT node */ 2798c2ecf20Sopenharmony_ci if (is_top) 2808c2ecf20Sopenharmony_ci prefix = PREFIX; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%splat", prefix); 2838c2ecf20Sopenharmony_ci plat = of_get_child_by_name(node, prop); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci cpu_dai = 2868c2ecf20Sopenharmony_ci dai_props->cpu_dai = &priv->dais[li->dais++]; 2878c2ecf20Sopenharmony_ci codec_dai = 2888c2ecf20Sopenharmony_ci dai_props->codec_dai = &priv->dais[li->dais++]; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ret = asoc_simple_parse_daifmt(dev, node, codec, 2918c2ecf20Sopenharmony_ci prefix, &dai_link->dai_fmt); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci goto dai_link_of_err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu); 2988c2ecf20Sopenharmony_ci if (ret < 0) 2998c2ecf20Sopenharmony_ci goto dai_link_of_err; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = asoc_simple_parse_codec(codec, dai_link); 3028c2ecf20Sopenharmony_ci if (ret < 0) 3038c2ecf20Sopenharmony_ci goto dai_link_of_err; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = asoc_simple_parse_platform(plat, dai_link); 3068c2ecf20Sopenharmony_ci if (ret < 0) 3078c2ecf20Sopenharmony_ci goto dai_link_of_err; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = asoc_simple_parse_tdm(cpu, cpu_dai); 3108c2ecf20Sopenharmony_ci if (ret < 0) 3118c2ecf20Sopenharmony_ci goto dai_link_of_err; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = asoc_simple_parse_tdm(codec, codec_dai); 3148c2ecf20Sopenharmony_ci if (ret < 0) 3158c2ecf20Sopenharmony_ci goto dai_link_of_err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 3188c2ecf20Sopenharmony_ci if (ret < 0) 3198c2ecf20Sopenharmony_ci goto dai_link_of_err; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); 3228c2ecf20Sopenharmony_ci if (ret < 0) 3238c2ecf20Sopenharmony_ci goto dai_link_of_err; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = asoc_simple_set_dailink_name(dev, dai_link, 3268c2ecf20Sopenharmony_ci "%s-%s", 3278c2ecf20Sopenharmony_ci dai_link->cpus->dai_name, 3288c2ecf20Sopenharmony_ci dai_link->codecs->dai_name); 3298c2ecf20Sopenharmony_ci if (ret < 0) 3308c2ecf20Sopenharmony_ci goto dai_link_of_err; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dai_link->ops = &simple_ops; 3338c2ecf20Sopenharmony_ci dai_link->init = asoc_simple_dai_init; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci asoc_simple_canonicalize_cpu(dai_link, single_cpu); 3368c2ecf20Sopenharmony_ci asoc_simple_canonicalize_platform(dai_link); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cidai_link_of_err: 3398c2ecf20Sopenharmony_ci of_node_put(plat); 3408c2ecf20Sopenharmony_ci of_node_put(node); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int simple_for_each_link(struct asoc_simple_priv *priv, 3468c2ecf20Sopenharmony_ci struct link_info *li, 3478c2ecf20Sopenharmony_ci int (*func_noml)(struct asoc_simple_priv *priv, 3488c2ecf20Sopenharmony_ci struct device_node *np, 3498c2ecf20Sopenharmony_ci struct device_node *codec, 3508c2ecf20Sopenharmony_ci struct link_info *li, bool is_top), 3518c2ecf20Sopenharmony_ci int (*func_dpcm)(struct asoc_simple_priv *priv, 3528c2ecf20Sopenharmony_ci struct device_node *np, 3538c2ecf20Sopenharmony_ci struct device_node *codec, 3548c2ecf20Sopenharmony_ci struct link_info *li, bool is_top)) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 3578c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 3588c2ecf20Sopenharmony_ci struct device_node *node; 3598c2ecf20Sopenharmony_ci uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); 3608c2ecf20Sopenharmony_ci bool is_top = 0; 3618c2ecf20Sopenharmony_ci int ret = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Check if it has dai-link */ 3648c2ecf20Sopenharmony_ci node = of_get_child_by_name(top, PREFIX "dai-link"); 3658c2ecf20Sopenharmony_ci if (!node) { 3668c2ecf20Sopenharmony_ci node = of_node_get(top); 3678c2ecf20Sopenharmony_ci is_top = 1; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* loop for all dai-link */ 3718c2ecf20Sopenharmony_ci do { 3728c2ecf20Sopenharmony_ci struct asoc_simple_data adata; 3738c2ecf20Sopenharmony_ci struct device_node *codec; 3748c2ecf20Sopenharmony_ci struct device_node *plat; 3758c2ecf20Sopenharmony_ci struct device_node *np; 3768c2ecf20Sopenharmony_ci int num = of_get_child_count(node); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* get codec */ 3798c2ecf20Sopenharmony_ci codec = of_get_child_by_name(node, is_top ? 3808c2ecf20Sopenharmony_ci PREFIX "codec" : "codec"); 3818c2ecf20Sopenharmony_ci if (!codec) { 3828c2ecf20Sopenharmony_ci ret = -ENODEV; 3838c2ecf20Sopenharmony_ci goto error; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci /* get platform */ 3868c2ecf20Sopenharmony_ci plat = of_get_child_by_name(node, is_top ? 3878c2ecf20Sopenharmony_ci PREFIX "plat" : "plat"); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* get convert-xxx property */ 3908c2ecf20Sopenharmony_ci memset(&adata, 0, sizeof(adata)); 3918c2ecf20Sopenharmony_ci for_each_child_of_node(node, np) 3928c2ecf20Sopenharmony_ci simple_parse_convert(dev, np, &adata); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* loop for all CPU/Codec node */ 3958c2ecf20Sopenharmony_ci for_each_child_of_node(node, np) { 3968c2ecf20Sopenharmony_ci if (plat == np) 3978c2ecf20Sopenharmony_ci continue; 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * It is DPCM 4008c2ecf20Sopenharmony_ci * if it has many CPUs, 4018c2ecf20Sopenharmony_ci * or has convert-xxx property 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci if (dpcm_selectable && 4048c2ecf20Sopenharmony_ci (num > 2 || 4058c2ecf20Sopenharmony_ci adata.convert_rate || adata.convert_channels)) 4068c2ecf20Sopenharmony_ci ret = func_dpcm(priv, np, codec, li, is_top); 4078c2ecf20Sopenharmony_ci /* else normal sound */ 4088c2ecf20Sopenharmony_ci else 4098c2ecf20Sopenharmony_ci ret = func_noml(priv, np, codec, li, is_top); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (ret < 0) { 4128c2ecf20Sopenharmony_ci of_node_put(codec); 4138c2ecf20Sopenharmony_ci of_node_put(plat); 4148c2ecf20Sopenharmony_ci of_node_put(np); 4158c2ecf20Sopenharmony_ci goto error; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci of_node_put(codec); 4208c2ecf20Sopenharmony_ci node = of_get_next_child(top, node); 4218c2ecf20Sopenharmony_ci } while (!is_top && node); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci error: 4248c2ecf20Sopenharmony_ci of_node_put(node); 4258c2ecf20Sopenharmony_ci return ret; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int simple_parse_of(struct asoc_simple_priv *priv) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 4318c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 4328c2ecf20Sopenharmony_ci struct snd_soc_card *card = simple_priv_to_card(priv); 4338c2ecf20Sopenharmony_ci struct link_info li; 4348c2ecf20Sopenharmony_ci int ret; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!top) 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = asoc_simple_parse_widgets(card, PREFIX); 4408c2ecf20Sopenharmony_ci if (ret < 0) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = asoc_simple_parse_routing(card, PREFIX); 4448c2ecf20Sopenharmony_ci if (ret < 0) 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = asoc_simple_parse_pin_switches(card, PREFIX); 4488c2ecf20Sopenharmony_ci if (ret < 0) 4498c2ecf20Sopenharmony_ci return ret; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Single/Muti DAI link(s) & New style of DT node */ 4528c2ecf20Sopenharmony_ci memset(&li, 0, sizeof(li)); 4538c2ecf20Sopenharmony_ci for (li.cpu = 1; li.cpu >= 0; li.cpu--) { 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * Detect all CPU first, and Detect all Codec 2nd. 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * In Normal sound case, all DAIs are detected 4588c2ecf20Sopenharmony_ci * as "CPU-Codec". 4598c2ecf20Sopenharmony_ci * 4608c2ecf20Sopenharmony_ci * In DPCM sound case, 4618c2ecf20Sopenharmony_ci * all CPUs are detected as "CPU-dummy", and 4628c2ecf20Sopenharmony_ci * all Codecs are detected as "dummy-Codec". 4638c2ecf20Sopenharmony_ci * To avoid random sub-device numbering, 4648c2ecf20Sopenharmony_ci * detect "dummy-Codec" in last; 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci ret = simple_for_each_link(priv, &li, 4678c2ecf20Sopenharmony_ci simple_dai_link_of, 4688c2ecf20Sopenharmony_ci simple_dai_link_of_dpcm); 4698c2ecf20Sopenharmony_ci if (ret < 0) 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = asoc_simple_parse_card_name(card, PREFIX); 4748c2ecf20Sopenharmony_ci if (ret < 0) 4758c2ecf20Sopenharmony_ci return ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int simple_count_noml(struct asoc_simple_priv *priv, 4838c2ecf20Sopenharmony_ci struct device_node *np, 4848c2ecf20Sopenharmony_ci struct device_node *codec, 4858c2ecf20Sopenharmony_ci struct link_info *li, bool is_top) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci li->dais++; /* CPU or Codec */ 4888c2ecf20Sopenharmony_ci if (np != codec) 4898c2ecf20Sopenharmony_ci li->link++; /* CPU-Codec */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int simple_count_dpcm(struct asoc_simple_priv *priv, 4958c2ecf20Sopenharmony_ci struct device_node *np, 4968c2ecf20Sopenharmony_ci struct device_node *codec, 4978c2ecf20Sopenharmony_ci struct link_info *li, bool is_top) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci li->dais++; /* CPU or Codec */ 5008c2ecf20Sopenharmony_ci li->link++; /* CPU-dummy or dummy-Codec */ 5018c2ecf20Sopenharmony_ci if (np == codec) 5028c2ecf20Sopenharmony_ci li->conf++; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void simple_get_dais_count(struct asoc_simple_priv *priv, 5088c2ecf20Sopenharmony_ci struct link_info *li) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 5118c2ecf20Sopenharmony_ci struct device_node *top = dev->of_node; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * link_num : number of links. 5158c2ecf20Sopenharmony_ci * CPU-Codec / CPU-dummy / dummy-Codec 5168c2ecf20Sopenharmony_ci * dais_num : number of DAIs 5178c2ecf20Sopenharmony_ci * ccnf_num : number of codec_conf 5188c2ecf20Sopenharmony_ci * same number for "dummy-Codec" 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * ex1) 5218c2ecf20Sopenharmony_ci * CPU0 --- Codec0 link : 5 5228c2ecf20Sopenharmony_ci * CPU1 --- Codec1 dais : 7 5238c2ecf20Sopenharmony_ci * CPU2 -/ ccnf : 1 5248c2ecf20Sopenharmony_ci * CPU3 --- Codec2 5258c2ecf20Sopenharmony_ci * 5268c2ecf20Sopenharmony_ci * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec 5278c2ecf20Sopenharmony_ci * => 7 DAIs = 4xCPU + 3xCodec 5288c2ecf20Sopenharmony_ci * => 1 ccnf = 1xdummy-Codec 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * ex2) 5318c2ecf20Sopenharmony_ci * CPU0 --- Codec0 link : 5 5328c2ecf20Sopenharmony_ci * CPU1 --- Codec1 dais : 6 5338c2ecf20Sopenharmony_ci * CPU2 -/ ccnf : 1 5348c2ecf20Sopenharmony_ci * CPU3 -/ 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec 5378c2ecf20Sopenharmony_ci * => 6 DAIs = 4xCPU + 2xCodec 5388c2ecf20Sopenharmony_ci * => 1 ccnf = 1xdummy-Codec 5398c2ecf20Sopenharmony_ci * 5408c2ecf20Sopenharmony_ci * ex3) 5418c2ecf20Sopenharmony_ci * CPU0 --- Codec0 link : 6 5428c2ecf20Sopenharmony_ci * CPU1 -/ dais : 6 5438c2ecf20Sopenharmony_ci * CPU2 --- Codec1 ccnf : 2 5448c2ecf20Sopenharmony_ci * CPU3 -/ 5458c2ecf20Sopenharmony_ci * 5468c2ecf20Sopenharmony_ci * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec 5478c2ecf20Sopenharmony_ci * => 6 DAIs = 4xCPU + 2xCodec 5488c2ecf20Sopenharmony_ci * => 2 ccnf = 2xdummy-Codec 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * ex4) 5518c2ecf20Sopenharmony_ci * CPU0 --- Codec0 (convert-rate) link : 3 5528c2ecf20Sopenharmony_ci * CPU1 --- Codec1 dais : 4 5538c2ecf20Sopenharmony_ci * ccnf : 1 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec 5568c2ecf20Sopenharmony_ci * => 4 DAIs = 2xCPU + 2xCodec 5578c2ecf20Sopenharmony_ci * => 1 ccnf = 1xdummy-Codec 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (!top) { 5608c2ecf20Sopenharmony_ci li->link = 1; 5618c2ecf20Sopenharmony_ci li->dais = 2; 5628c2ecf20Sopenharmony_ci li->conf = 0; 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci simple_for_each_link(priv, li, 5678c2ecf20Sopenharmony_ci simple_count_noml, 5688c2ecf20Sopenharmony_ci simple_count_dpcm); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci dev_dbg(dev, "link %d, dais %d, ccnf %d\n", 5718c2ecf20Sopenharmony_ci li->link, li->dais, li->conf); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int simple_soc_probe(struct snd_soc_card *card) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); 5778c2ecf20Sopenharmony_ci int ret; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ret = asoc_simple_init_hp(card, &priv->hp_jack, PREFIX); 5808c2ecf20Sopenharmony_ci if (ret < 0) 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = asoc_simple_init_mic(card, &priv->mic_jack, PREFIX); 5848c2ecf20Sopenharmony_ci if (ret < 0) 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int asoc_simple_probe(struct platform_device *pdev) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct asoc_simple_priv *priv; 5938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5948c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 5958c2ecf20Sopenharmony_ci struct snd_soc_card *card; 5968c2ecf20Sopenharmony_ci struct link_info li; 5978c2ecf20Sopenharmony_ci int ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Allocate the private data and the DAI link array */ 6008c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!priv) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci card = simple_priv_to_card(priv); 6058c2ecf20Sopenharmony_ci card->owner = THIS_MODULE; 6068c2ecf20Sopenharmony_ci card->dev = dev; 6078c2ecf20Sopenharmony_ci card->probe = simple_soc_probe; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci memset(&li, 0, sizeof(li)); 6108c2ecf20Sopenharmony_ci simple_get_dais_count(priv, &li); 6118c2ecf20Sopenharmony_ci if (!li.link || !li.dais) 6128c2ecf20Sopenharmony_ci return -EINVAL; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ret = asoc_simple_init_priv(priv, &li); 6158c2ecf20Sopenharmony_ci if (ret < 0) 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (np && of_device_is_available(np)) { 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ret = simple_parse_of(priv); 6218c2ecf20Sopenharmony_ci if (ret < 0) { 6228c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 6238c2ecf20Sopenharmony_ci dev_err(dev, "parse error %d\n", ret); 6248c2ecf20Sopenharmony_ci goto err; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci } else { 6288c2ecf20Sopenharmony_ci struct asoc_simple_card_info *cinfo; 6298c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *cpus; 6308c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *codecs; 6318c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *platform; 6328c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link = priv->dai_link; 6338c2ecf20Sopenharmony_ci struct simple_dai_props *dai_props = priv->dai_props; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci int dai_idx = 0; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ret = -EINVAL; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci cinfo = dev->platform_data; 6408c2ecf20Sopenharmony_ci if (!cinfo) { 6418c2ecf20Sopenharmony_ci dev_err(dev, "no info for asoc-simple-card\n"); 6428c2ecf20Sopenharmony_ci goto err; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!cinfo->name || 6468c2ecf20Sopenharmony_ci !cinfo->codec_dai.name || 6478c2ecf20Sopenharmony_ci !cinfo->codec || 6488c2ecf20Sopenharmony_ci !cinfo->platform || 6498c2ecf20Sopenharmony_ci !cinfo->cpu_dai.name) { 6508c2ecf20Sopenharmony_ci dev_err(dev, "insufficient asoc_simple_card_info settings\n"); 6518c2ecf20Sopenharmony_ci goto err; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci dai_props->cpu_dai = &priv->dais[dai_idx++]; 6558c2ecf20Sopenharmony_ci dai_props->codec_dai = &priv->dais[dai_idx++]; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci cpus = dai_link->cpus; 6588c2ecf20Sopenharmony_ci cpus->dai_name = cinfo->cpu_dai.name; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci codecs = dai_link->codecs; 6618c2ecf20Sopenharmony_ci codecs->name = cinfo->codec; 6628c2ecf20Sopenharmony_ci codecs->dai_name = cinfo->codec_dai.name; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci platform = dai_link->platforms; 6658c2ecf20Sopenharmony_ci platform->name = cinfo->platform; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci card->name = (cinfo->card) ? cinfo->card : cinfo->name; 6688c2ecf20Sopenharmony_ci dai_link->name = cinfo->name; 6698c2ecf20Sopenharmony_ci dai_link->stream_name = cinfo->name; 6708c2ecf20Sopenharmony_ci dai_link->dai_fmt = cinfo->daifmt; 6718c2ecf20Sopenharmony_ci dai_link->init = asoc_simple_dai_init; 6728c2ecf20Sopenharmony_ci memcpy(dai_props->cpu_dai, &cinfo->cpu_dai, 6738c2ecf20Sopenharmony_ci sizeof(*dai_props->cpu_dai)); 6748c2ecf20Sopenharmony_ci memcpy(dai_props->codec_dai, &cinfo->codec_dai, 6758c2ecf20Sopenharmony_ci sizeof(*dai_props->codec_dai)); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci snd_soc_card_set_drvdata(card, priv); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci asoc_simple_debug_info(priv); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(dev, card); 6838c2ecf20Sopenharmony_ci if (ret < 0) 6848c2ecf20Sopenharmony_ci goto err; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_cierr: 6888c2ecf20Sopenharmony_ci asoc_simple_clean_reference(card); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int asoc_simple_remove(struct platform_device *pdev) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return asoc_simple_clean_reference(card); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct of_device_id simple_of_match[] = { 7018c2ecf20Sopenharmony_ci { .compatible = "simple-audio-card", }, 7028c2ecf20Sopenharmony_ci { .compatible = "simple-scu-audio-card", 7038c2ecf20Sopenharmony_ci .data = (void *)DPCM_SELECTABLE }, 7048c2ecf20Sopenharmony_ci {}, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, simple_of_match); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic struct platform_driver asoc_simple_card = { 7098c2ecf20Sopenharmony_ci .driver = { 7108c2ecf20Sopenharmony_ci .name = "asoc-simple-card", 7118c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 7128c2ecf20Sopenharmony_ci .of_match_table = simple_of_match, 7138c2ecf20Sopenharmony_ci }, 7148c2ecf20Sopenharmony_ci .probe = asoc_simple_probe, 7158c2ecf20Sopenharmony_ci .remove = asoc_simple_remove, 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cimodule_platform_driver(asoc_simple_card); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:asoc-simple-card"); 7218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC Simple Sound Card"); 7238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 724