audio-graph-card.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // ASoC audio graph sound card support
  4. //
  5. // Copyright (C) 2016 Renesas Solutions Corp.
  6. // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  7. //
  8. // based on ${LINUX}/sound/soc/generic/simple-card.c
  9. #include <linux/cleanup.h>
  10. #include <linux/clk.h>
  11. #include <linux/device.h>
  12. #include <linux/gpio/consumer.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/of_graph.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/string.h>
  18. #include <sound/graph_card.h>
  19. #define DPCM_SELECTABLE 1
  20. #define ep_to_port(ep) of_get_parent(ep)
  21. static struct device_node *port_to_ports(struct device_node *port)
  22. {
  23. struct device_node *ports = of_get_parent(port);
  24. if (!of_node_name_eq(ports, "ports")) {
  25. of_node_put(ports);
  26. return NULL;
  27. }
  28. return ports;
  29. }
  30. static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
  31. struct snd_kcontrol *kcontrol,
  32. int event)
  33. {
  34. struct snd_soc_dapm_context *dapm = w->dapm;
  35. struct simple_util_priv *priv = snd_soc_card_get_drvdata(dapm->card);
  36. switch (event) {
  37. case SND_SOC_DAPM_POST_PMU:
  38. gpiod_set_value_cansleep(priv->pa_gpio, 1);
  39. break;
  40. case SND_SOC_DAPM_PRE_PMD:
  41. gpiod_set_value_cansleep(priv->pa_gpio, 0);
  42. break;
  43. default:
  44. return -EINVAL;
  45. }
  46. return 0;
  47. }
  48. static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
  49. SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
  50. 0, 0, NULL, 0, graph_outdrv_event,
  51. SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
  52. };
  53. static const struct snd_soc_ops graph_ops = {
  54. .startup = simple_util_startup,
  55. .shutdown = simple_util_shutdown,
  56. .hw_params = simple_util_hw_params,
  57. };
  58. static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
  59. {
  60. struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
  61. if (dai && (dai->component->driver->pcm_construct ||
  62. (dai->driver->ops && dai->driver->ops->pcm_new)))
  63. return true;
  64. return false;
  65. }
  66. static void graph_parse_convert(struct device *dev,
  67. struct device_node *ep,
  68. struct simple_util_data *adata)
  69. {
  70. struct device_node *top = dev->of_node;
  71. struct device_node *port = ep_to_port(ep);
  72. struct device_node *ports = port_to_ports(port);
  73. struct device_node *node = of_graph_get_port_parent(ep);
  74. simple_util_parse_convert(top, NULL, adata);
  75. simple_util_parse_convert(ports, NULL, adata);
  76. simple_util_parse_convert(port, NULL, adata);
  77. simple_util_parse_convert(ep, NULL, adata);
  78. of_node_put(port);
  79. of_node_put(ports);
  80. of_node_put(node);
  81. }
  82. static int graph_parse_node(struct simple_util_priv *priv,
  83. struct device_node *ep,
  84. struct link_info *li,
  85. int *cpu)
  86. {
  87. struct device *dev = simple_priv_to_dev(priv);
  88. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
  89. struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
  90. struct snd_soc_dai_link_component *dlc;
  91. struct simple_util_dai *dai;
  92. int ret;
  93. if (cpu) {
  94. dlc = snd_soc_link_to_cpu(dai_link, 0);
  95. dai = simple_props_to_dai_cpu(dai_props, 0);
  96. } else {
  97. dlc = snd_soc_link_to_codec(dai_link, 0);
  98. dai = simple_props_to_dai_codec(dai_props, 0);
  99. }
  100. ret = graph_util_parse_dai(dev, ep, dlc, cpu);
  101. if (ret < 0)
  102. return ret;
  103. ret = simple_util_parse_tdm(ep, dai);
  104. if (ret < 0)
  105. return ret;
  106. ret = simple_util_parse_clk(dev, ep, dai, dlc);
  107. if (ret < 0)
  108. return ret;
  109. return 0;
  110. }
  111. static int graph_link_init(struct simple_util_priv *priv,
  112. struct device_node *ep_cpu,
  113. struct device_node *ep_codec,
  114. struct link_info *li,
  115. char *name)
  116. {
  117. struct device *dev = simple_priv_to_dev(priv);
  118. struct device_node *top = dev->of_node;
  119. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
  120. struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
  121. struct device_node *port_cpu = ep_to_port(ep_cpu);
  122. struct device_node *port_codec = ep_to_port(ep_codec);
  123. struct device_node *ports_cpu = port_to_ports(port_cpu);
  124. struct device_node *ports_codec = port_to_ports(port_codec);
  125. enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
  126. enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT;
  127. bool playback_only = 0, capture_only = 0;
  128. int ret;
  129. ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec,
  130. NULL, &dai_link->dai_fmt);
  131. if (ret < 0)
  132. goto init_end;
  133. graph_util_parse_link_direction(top, &playback_only, &capture_only);
  134. graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only);
  135. graph_util_parse_link_direction(port_codec, &playback_only, &capture_only);
  136. graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only);
  137. graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only);
  138. of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
  139. of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs);
  140. of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs);
  141. of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs);
  142. of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs);
  143. of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs);
  144. of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs);
  145. graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop);
  146. graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop);
  147. graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop);
  148. graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
  149. graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);
  150. graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop);
  151. graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop);
  152. dai_link->playback_only = playback_only;
  153. dai_link->capture_only = capture_only;
  154. dai_link->trigger_start = trigger_start;
  155. dai_link->trigger_stop = trigger_stop;
  156. dai_link->init = simple_util_dai_init;
  157. dai_link->ops = &graph_ops;
  158. if (priv->ops)
  159. dai_link->ops = priv->ops;
  160. ret = simple_util_set_dailink_name(dev, dai_link, name);
  161. init_end:
  162. of_node_put(ports_cpu);
  163. of_node_put(ports_codec);
  164. of_node_put(port_cpu);
  165. of_node_put(port_codec);
  166. return ret;
  167. }
  168. static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
  169. struct device_node *cpu_ep,
  170. struct device_node *codec_ep,
  171. struct link_info *li)
  172. {
  173. struct device *dev = simple_priv_to_dev(priv);
  174. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
  175. struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
  176. struct device_node *top = dev->of_node;
  177. struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
  178. char dai_name[64];
  179. int ret;
  180. dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
  181. if (li->cpu) {
  182. struct snd_soc_card *card = simple_priv_to_card(priv);
  183. struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
  184. struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
  185. int is_single_links = 0;
  186. /* Codec is dummy */
  187. /* FE settings */
  188. dai_link->dynamic = 1;
  189. dai_link->dpcm_merged_format = 1;
  190. ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
  191. if (ret)
  192. return ret;
  193. snprintf(dai_name, sizeof(dai_name),
  194. "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
  195. /*
  196. * In BE<->BE connections it is not required to create
  197. * PCM devices at CPU end of the dai link and thus 'no_pcm'
  198. * flag needs to be set. It is useful when there are many
  199. * BE components and some of these have to be connected to
  200. * form a valid audio path.
  201. *
  202. * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
  203. * there are 'n' BE components in the path.
  204. */
  205. if (card->component_chaining && !soc_component_is_pcm(cpus)) {
  206. dai_link->no_pcm = 1;
  207. dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
  208. }
  209. simple_util_canonicalize_cpu(cpus, is_single_links);
  210. simple_util_canonicalize_platform(platforms, cpus);
  211. } else {
  212. struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
  213. struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
  214. struct device_node *port;
  215. struct device_node *ports;
  216. /* CPU is dummy */
  217. /* BE settings */
  218. dai_link->no_pcm = 1;
  219. dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
  220. ret = graph_parse_node(priv, codec_ep, li, NULL);
  221. if (ret < 0)
  222. return ret;
  223. snprintf(dai_name, sizeof(dai_name),
  224. "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
  225. /* check "prefix" from top node */
  226. port = ep_to_port(ep);
  227. ports = port_to_ports(port);
  228. snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix");
  229. snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
  230. snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, "prefix");
  231. of_node_put(ports);
  232. of_node_put(port);
  233. }
  234. graph_parse_convert(dev, ep, &dai_props->adata);
  235. ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
  236. li->link++;
  237. return ret;
  238. }
  239. static int graph_dai_link_of(struct simple_util_priv *priv,
  240. struct device_node *cpu_ep,
  241. struct device_node *codec_ep,
  242. struct link_info *li)
  243. {
  244. struct device *dev = simple_priv_to_dev(priv);
  245. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
  246. struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
  247. struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
  248. struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
  249. char dai_name[64];
  250. int ret, is_single_links = 0;
  251. dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
  252. ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
  253. if (ret < 0)
  254. return ret;
  255. ret = graph_parse_node(priv, codec_ep, li, NULL);
  256. if (ret < 0)
  257. return ret;
  258. snprintf(dai_name, sizeof(dai_name),
  259. "%s-%s", cpus->dai_name, codecs->dai_name);
  260. simple_util_canonicalize_cpu(cpus, is_single_links);
  261. simple_util_canonicalize_platform(platforms, cpus);
  262. ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
  263. if (ret < 0)
  264. return ret;
  265. li->link++;
  266. return 0;
  267. }
  268. static inline bool parse_as_dpcm_link(struct simple_util_priv *priv,
  269. struct device_node *codec_port,
  270. struct simple_util_data *adata)
  271. {
  272. if (priv->force_dpcm)
  273. return true;
  274. if (!priv->dpcm_selectable)
  275. return false;
  276. /*
  277. * It is DPCM
  278. * if Codec port has many endpoints,
  279. * or has convert-xxx property
  280. */
  281. if ((of_get_child_count(codec_port) > 1) ||
  282. simple_util_is_convert_required(adata))
  283. return true;
  284. return false;
  285. }
  286. static int __graph_for_each_link(struct simple_util_priv *priv,
  287. struct link_info *li,
  288. int (*func_noml)(struct simple_util_priv *priv,
  289. struct device_node *cpu_ep,
  290. struct device_node *codec_ep,
  291. struct link_info *li),
  292. int (*func_dpcm)(struct simple_util_priv *priv,
  293. struct device_node *cpu_ep,
  294. struct device_node *codec_ep,
  295. struct link_info *li))
  296. {
  297. struct of_phandle_iterator it;
  298. struct device *dev = simple_priv_to_dev(priv);
  299. struct device_node *node = dev->of_node;
  300. struct device_node *cpu_port;
  301. struct device_node *codec_ep;
  302. struct device_node *codec_port;
  303. struct device_node *codec_port_old = NULL;
  304. struct simple_util_data adata;
  305. int rc, ret = 0;
  306. /* loop for all listed CPU port */
  307. of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
  308. cpu_port = it.node;
  309. /* loop for all CPU endpoint */
  310. for_each_child_of_node_scoped(cpu_port, cpu_ep) {
  311. /* get codec */
  312. codec_ep = of_graph_get_remote_endpoint(cpu_ep);
  313. codec_port = ep_to_port(codec_ep);
  314. /* get convert-xxx property */
  315. memset(&adata, 0, sizeof(adata));
  316. graph_parse_convert(dev, codec_ep, &adata);
  317. graph_parse_convert(dev, cpu_ep, &adata);
  318. /* check if link requires DPCM parsing */
  319. if (parse_as_dpcm_link(priv, codec_port, &adata)) {
  320. /*
  321. * Codec endpoint can be NULL for pluggable audio HW.
  322. * Platform DT can populate the Codec endpoint depending on the
  323. * plugged HW.
  324. */
  325. /* Do it all CPU endpoint, and 1st Codec endpoint */
  326. if (li->cpu ||
  327. ((codec_port_old != codec_port) && codec_ep))
  328. ret = func_dpcm(priv, cpu_ep, codec_ep, li);
  329. /* else normal sound */
  330. } else {
  331. if (li->cpu)
  332. ret = func_noml(priv, cpu_ep, codec_ep, li);
  333. }
  334. of_node_put(codec_ep);
  335. of_node_put(codec_port);
  336. if (ret < 0)
  337. return ret;
  338. codec_port_old = codec_port;
  339. }
  340. }
  341. return 0;
  342. }
  343. static int graph_for_each_link(struct simple_util_priv *priv,
  344. struct link_info *li,
  345. int (*func_noml)(struct simple_util_priv *priv,
  346. struct device_node *cpu_ep,
  347. struct device_node *codec_ep,
  348. struct link_info *li),
  349. int (*func_dpcm)(struct simple_util_priv *priv,
  350. struct device_node *cpu_ep,
  351. struct device_node *codec_ep,
  352. struct link_info *li))
  353. {
  354. int ret;
  355. /*
  356. * Detect all CPU first, and Detect all Codec 2nd.
  357. *
  358. * In Normal sound case, all DAIs are detected
  359. * as "CPU-Codec".
  360. *
  361. * In DPCM sound case,
  362. * all CPUs are detected as "CPU-dummy", and
  363. * all Codecs are detected as "dummy-Codec".
  364. * To avoid random sub-device numbering,
  365. * detect "dummy-Codec" in last;
  366. */
  367. for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
  368. ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
  369. if (ret < 0)
  370. break;
  371. }
  372. return ret;
  373. }
  374. static int graph_count_noml(struct simple_util_priv *priv,
  375. struct device_node *cpu_ep,
  376. struct device_node *codec_ep,
  377. struct link_info *li)
  378. {
  379. struct device *dev = simple_priv_to_dev(priv);
  380. if (li->link >= SNDRV_MAX_LINKS) {
  381. dev_err(dev, "too many links\n");
  382. return -EINVAL;
  383. }
  384. /*
  385. * DON'T REMOVE platforms
  386. * see
  387. * simple-card.c :: simple_count_noml()
  388. */
  389. li->num[li->link].cpus = 1;
  390. li->num[li->link].platforms = 1;
  391. li->num[li->link].codecs = 1;
  392. li->link += 1; /* 1xCPU-Codec */
  393. dev_dbg(dev, "Count As Normal\n");
  394. return 0;
  395. }
  396. static int graph_count_dpcm(struct simple_util_priv *priv,
  397. struct device_node *cpu_ep,
  398. struct device_node *codec_ep,
  399. struct link_info *li)
  400. {
  401. struct device *dev = simple_priv_to_dev(priv);
  402. if (li->link >= SNDRV_MAX_LINKS) {
  403. dev_err(dev, "too many links\n");
  404. return -EINVAL;
  405. }
  406. if (li->cpu) {
  407. /*
  408. * DON'T REMOVE platforms
  409. * see
  410. * simple-card.c :: simple_count_noml()
  411. */
  412. li->num[li->link].cpus = 1;
  413. li->num[li->link].platforms = 1;
  414. li->link++; /* 1xCPU-dummy */
  415. } else {
  416. li->num[li->link].codecs = 1;
  417. li->link++; /* 1xdummy-Codec */
  418. }
  419. dev_dbg(dev, "Count As DPCM\n");
  420. return 0;
  421. }
  422. static int graph_get_dais_count(struct simple_util_priv *priv,
  423. struct link_info *li)
  424. {
  425. /*
  426. * link_num : number of links.
  427. * CPU-Codec / CPU-dummy / dummy-Codec
  428. * dais_num : number of DAIs
  429. * ccnf_num : number of codec_conf
  430. * same number for "dummy-Codec"
  431. *
  432. * ex1)
  433. * CPU0 --- Codec0 link : 5
  434. * CPU1 --- Codec1 dais : 7
  435. * CPU2 -/ ccnf : 1
  436. * CPU3 --- Codec2
  437. *
  438. * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
  439. * => 7 DAIs = 4xCPU + 3xCodec
  440. * => 1 ccnf = 1xdummy-Codec
  441. *
  442. * ex2)
  443. * CPU0 --- Codec0 link : 5
  444. * CPU1 --- Codec1 dais : 6
  445. * CPU2 -/ ccnf : 1
  446. * CPU3 -/
  447. *
  448. * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
  449. * => 6 DAIs = 4xCPU + 2xCodec
  450. * => 1 ccnf = 1xdummy-Codec
  451. *
  452. * ex3)
  453. * CPU0 --- Codec0 link : 6
  454. * CPU1 -/ dais : 6
  455. * CPU2 --- Codec1 ccnf : 2
  456. * CPU3 -/
  457. *
  458. * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
  459. * => 6 DAIs = 4xCPU + 2xCodec
  460. * => 2 ccnf = 2xdummy-Codec
  461. *
  462. * ex4)
  463. * CPU0 --- Codec0 (convert-rate) link : 3
  464. * CPU1 --- Codec1 dais : 4
  465. * ccnf : 1
  466. *
  467. * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
  468. * => 4 DAIs = 2xCPU + 2xCodec
  469. * => 1 ccnf = 1xdummy-Codec
  470. */
  471. return graph_for_each_link(priv, li,
  472. graph_count_noml,
  473. graph_count_dpcm);
  474. }
  475. int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
  476. {
  477. struct snd_soc_card *card = simple_priv_to_card(priv);
  478. int ret;
  479. struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
  480. if (!li)
  481. return -ENOMEM;
  482. card->owner = THIS_MODULE;
  483. card->dev = dev;
  484. ret = graph_get_dais_count(priv, li);
  485. if (ret < 0)
  486. return ret;
  487. if (!li->link)
  488. return -EINVAL;
  489. ret = simple_util_init_priv(priv, li);
  490. if (ret < 0)
  491. return ret;
  492. priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
  493. if (IS_ERR(priv->pa_gpio)) {
  494. ret = PTR_ERR(priv->pa_gpio);
  495. dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
  496. return ret;
  497. }
  498. ret = simple_util_parse_widgets(card, NULL);
  499. if (ret < 0)
  500. return ret;
  501. ret = simple_util_parse_routing(card, NULL);
  502. if (ret < 0)
  503. return ret;
  504. memset(li, 0, sizeof(*li));
  505. ret = graph_for_each_link(priv, li,
  506. graph_dai_link_of,
  507. graph_dai_link_of_dpcm);
  508. if (ret < 0)
  509. goto err;
  510. ret = simple_util_parse_card_name(card, NULL);
  511. if (ret < 0)
  512. goto err;
  513. snd_soc_card_set_drvdata(card, priv);
  514. simple_util_debug_info(priv);
  515. ret = devm_snd_soc_register_card(dev, card);
  516. if (ret < 0)
  517. goto err;
  518. return 0;
  519. err:
  520. simple_util_clean_reference(card);
  521. return dev_err_probe(dev, ret, "parse error\n");
  522. }
  523. EXPORT_SYMBOL_GPL(audio_graph_parse_of);
  524. static int graph_probe(struct platform_device *pdev)
  525. {
  526. struct simple_util_priv *priv;
  527. struct device *dev = &pdev->dev;
  528. struct snd_soc_card *card;
  529. /* Allocate the private data and the DAI link array */
  530. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  531. if (!priv)
  532. return -ENOMEM;
  533. card = simple_priv_to_card(priv);
  534. card->dapm_widgets = graph_dapm_widgets;
  535. card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
  536. card->probe = graph_util_card_probe;
  537. if (of_device_get_match_data(dev))
  538. priv->dpcm_selectable = 1;
  539. return audio_graph_parse_of(priv, dev);
  540. }
  541. static const struct of_device_id graph_of_match[] = {
  542. { .compatible = "audio-graph-card", },
  543. { .compatible = "audio-graph-scu-card",
  544. .data = (void *)DPCM_SELECTABLE },
  545. {},
  546. };
  547. MODULE_DEVICE_TABLE(of, graph_of_match);
  548. static struct platform_driver graph_card = {
  549. .driver = {
  550. .name = "asoc-audio-graph-card",
  551. .pm = &snd_soc_pm_ops,
  552. .of_match_table = graph_of_match,
  553. },
  554. .probe = graph_probe,
  555. .remove = simple_util_remove,
  556. };
  557. module_platform_driver(graph_card);
  558. MODULE_ALIAS("platform:asoc-audio-graph-card");
  559. MODULE_LICENSE("GPL v2");
  560. MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
  561. MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");