simple-card.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*
  2. * ASoC simple sound card support
  3. *
  4. * Copyright (C) 2012 Renesas Solutions Corp.
  5. * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. */
  11. #include <linux/clk.h>
  12. #include <linux/device.h>
  13. #include <linux/gpio.h>
  14. #include <linux/module.h>
  15. #include <linux/of.h>
  16. #include <linux/of_gpio.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/string.h>
  19. #include <sound/jack.h>
  20. #include <sound/simple_card.h>
  21. #include <sound/soc-dai.h>
  22. #include <sound/soc.h>
  23. struct simple_card_data {
  24. struct snd_soc_card snd_card;
  25. struct simple_dai_props {
  26. struct asoc_simple_dai cpu_dai;
  27. struct asoc_simple_dai codec_dai;
  28. unsigned int mclk_fs;
  29. } *dai_props;
  30. unsigned int mclk_fs;
  31. int gpio_hp_det;
  32. int gpio_hp_det_invert;
  33. int gpio_mic_det;
  34. int gpio_mic_det_invert;
  35. struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
  36. };
  37. #define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
  38. #define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
  39. #define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
  40. static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
  41. {
  42. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  43. struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  44. struct simple_dai_props *dai_props =
  45. &priv->dai_props[rtd - rtd->card->rtd];
  46. int ret;
  47. ret = clk_prepare_enable(dai_props->cpu_dai.clk);
  48. if (ret)
  49. return ret;
  50. ret = clk_prepare_enable(dai_props->codec_dai.clk);
  51. if (ret)
  52. clk_disable_unprepare(dai_props->cpu_dai.clk);
  53. return ret;
  54. }
  55. static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
  56. {
  57. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  58. struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  59. struct simple_dai_props *dai_props =
  60. &priv->dai_props[rtd - rtd->card->rtd];
  61. clk_disable_unprepare(dai_props->cpu_dai.clk);
  62. clk_disable_unprepare(dai_props->codec_dai.clk);
  63. }
  64. static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
  65. struct snd_pcm_hw_params *params)
  66. {
  67. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  68. struct snd_soc_dai *codec_dai = rtd->codec_dai;
  69. struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  70. struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  71. struct simple_dai_props *dai_props =
  72. &priv->dai_props[rtd - rtd->card->rtd];
  73. unsigned int mclk, mclk_fs = 0;
  74. int ret = 0;
  75. if (priv->mclk_fs)
  76. mclk_fs = priv->mclk_fs;
  77. else if (dai_props->mclk_fs)
  78. mclk_fs = dai_props->mclk_fs;
  79. if (mclk_fs) {
  80. mclk = params_rate(params) * mclk_fs;
  81. ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
  82. SND_SOC_CLOCK_IN);
  83. if (ret && ret != -ENOTSUPP)
  84. goto err;
  85. ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
  86. SND_SOC_CLOCK_OUT);
  87. if (ret && ret != -ENOTSUPP)
  88. goto err;
  89. }
  90. return 0;
  91. err:
  92. return ret;
  93. }
  94. static struct snd_soc_ops asoc_simple_card_ops = {
  95. .startup = asoc_simple_card_startup,
  96. .shutdown = asoc_simple_card_shutdown,
  97. .hw_params = asoc_simple_card_hw_params,
  98. };
  99. static struct snd_soc_jack simple_card_hp_jack;
  100. static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
  101. {
  102. .pin = "Headphones",
  103. .mask = SND_JACK_HEADPHONE,
  104. },
  105. };
  106. static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
  107. .name = "Headphone detection",
  108. .report = SND_JACK_HEADPHONE,
  109. .debounce_time = 150,
  110. };
  111. static struct snd_soc_jack simple_card_mic_jack;
  112. static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
  113. {
  114. .pin = "Mic Jack",
  115. .mask = SND_JACK_MICROPHONE,
  116. },
  117. };
  118. static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
  119. .name = "Mic detection",
  120. .report = SND_JACK_MICROPHONE,
  121. .debounce_time = 150,
  122. };
  123. static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
  124. struct asoc_simple_dai *set)
  125. {
  126. int ret;
  127. if (set->sysclk) {
  128. ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
  129. if (ret && ret != -ENOTSUPP) {
  130. dev_err(dai->dev, "simple-card: set_sysclk error\n");
  131. goto err;
  132. }
  133. }
  134. if (set->slots) {
  135. ret = snd_soc_dai_set_tdm_slot(dai,
  136. set->tx_slot_mask,
  137. set->rx_slot_mask,
  138. set->slots,
  139. set->slot_width);
  140. if (ret && ret != -ENOTSUPP) {
  141. dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
  142. goto err;
  143. }
  144. }
  145. ret = 0;
  146. err:
  147. return ret;
  148. }
  149. static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
  150. {
  151. struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  152. struct snd_soc_dai *codec = rtd->codec_dai;
  153. struct snd_soc_dai *cpu = rtd->cpu_dai;
  154. struct simple_dai_props *dai_props;
  155. int num, ret;
  156. num = rtd - rtd->card->rtd;
  157. dai_props = &priv->dai_props[num];
  158. ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
  159. if (ret < 0)
  160. return ret;
  161. ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
  162. if (ret < 0)
  163. return ret;
  164. if (gpio_is_valid(priv->gpio_hp_det)) {
  165. snd_soc_card_jack_new(rtd->card, "Headphones",
  166. SND_JACK_HEADPHONE,
  167. &simple_card_hp_jack,
  168. simple_card_hp_jack_pins,
  169. ARRAY_SIZE(simple_card_hp_jack_pins));
  170. simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
  171. simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
  172. snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
  173. &simple_card_hp_jack_gpio);
  174. }
  175. if (gpio_is_valid(priv->gpio_mic_det)) {
  176. snd_soc_card_jack_new(rtd->card, "Mic Jack",
  177. SND_JACK_MICROPHONE,
  178. &simple_card_mic_jack,
  179. simple_card_mic_jack_pins,
  180. ARRAY_SIZE(simple_card_mic_jack_pins));
  181. simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
  182. simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
  183. snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
  184. &simple_card_mic_jack_gpio);
  185. }
  186. return 0;
  187. }
  188. static int
  189. asoc_simple_card_sub_parse_of(struct device_node *np,
  190. struct asoc_simple_dai *dai,
  191. struct device_node **p_node,
  192. const char **name,
  193. int *args_count)
  194. {
  195. struct of_phandle_args args;
  196. struct clk *clk;
  197. u32 val;
  198. int ret;
  199. /*
  200. * Get node via "sound-dai = <&phandle port>"
  201. * it will be used as xxx_of_node on soc_bind_dai_link()
  202. */
  203. ret = of_parse_phandle_with_args(np, "sound-dai",
  204. "#sound-dai-cells", 0, &args);
  205. if (ret)
  206. return ret;
  207. *p_node = args.np;
  208. if (args_count)
  209. *args_count = args.args_count;
  210. /* Get dai->name */
  211. ret = snd_soc_of_get_dai_name(np, name);
  212. if (ret < 0)
  213. return ret;
  214. /* Parse TDM slot */
  215. ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
  216. &dai->rx_slot_mask,
  217. &dai->slots, &dai->slot_width);
  218. if (ret)
  219. return ret;
  220. /*
  221. * Parse dai->sysclk come from "clocks = <&xxx>"
  222. * (if system has common clock)
  223. * or "system-clock-frequency = <xxx>"
  224. * or device's module clock.
  225. */
  226. if (of_property_read_bool(np, "clocks")) {
  227. clk = of_clk_get(np, 0);
  228. if (IS_ERR(clk)) {
  229. ret = PTR_ERR(clk);
  230. return ret;
  231. }
  232. dai->sysclk = clk_get_rate(clk);
  233. dai->clk = clk;
  234. } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
  235. dai->sysclk = val;
  236. } else {
  237. clk = of_clk_get(args.np, 0);
  238. if (!IS_ERR(clk))
  239. dai->sysclk = clk_get_rate(clk);
  240. }
  241. return 0;
  242. }
  243. static int asoc_simple_card_parse_daifmt(struct device_node *node,
  244. struct simple_card_data *priv,
  245. struct device_node *codec,
  246. char *prefix, int idx)
  247. {
  248. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
  249. struct device *dev = simple_priv_to_dev(priv);
  250. struct device_node *bitclkmaster = NULL;
  251. struct device_node *framemaster = NULL;
  252. unsigned int daifmt;
  253. daifmt = snd_soc_of_parse_daifmt(node, prefix,
  254. &bitclkmaster, &framemaster);
  255. daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
  256. if (strlen(prefix) && !bitclkmaster && !framemaster) {
  257. /*
  258. * No dai-link level and master setting was not found from
  259. * sound node level, revert back to legacy DT parsing and
  260. * take the settings from codec node.
  261. */
  262. dev_dbg(dev, "Revert to legacy daifmt parsing\n");
  263. daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
  264. (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
  265. } else {
  266. if (codec == bitclkmaster)
  267. daifmt |= (codec == framemaster) ?
  268. SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
  269. else
  270. daifmt |= (codec == framemaster) ?
  271. SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
  272. }
  273. dai_link->dai_fmt = daifmt;
  274. of_node_put(bitclkmaster);
  275. of_node_put(framemaster);
  276. return 0;
  277. }
  278. static int asoc_simple_card_dai_link_of(struct device_node *node,
  279. struct simple_card_data *priv,
  280. int idx,
  281. bool is_top_level_node)
  282. {
  283. struct device *dev = simple_priv_to_dev(priv);
  284. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
  285. struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
  286. struct device_node *cpu = NULL;
  287. struct device_node *plat = NULL;
  288. struct device_node *codec = NULL;
  289. char *name;
  290. char prop[128];
  291. char *prefix = "";
  292. int ret, cpu_args;
  293. u32 val;
  294. /* For single DAI link & old style of DT node */
  295. if (is_top_level_node)
  296. prefix = "simple-audio-card,";
  297. snprintf(prop, sizeof(prop), "%scpu", prefix);
  298. cpu = of_get_child_by_name(node, prop);
  299. if (!cpu) {
  300. ret = -EINVAL;
  301. dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
  302. goto dai_link_of_err;
  303. }
  304. snprintf(prop, sizeof(prop), "%splat", prefix);
  305. plat = of_get_child_by_name(node, prop);
  306. snprintf(prop, sizeof(prop), "%scodec", prefix);
  307. codec = of_get_child_by_name(node, prop);
  308. if (!codec) {
  309. ret = -EINVAL;
  310. dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
  311. goto dai_link_of_err;
  312. }
  313. ret = asoc_simple_card_parse_daifmt(node, priv,
  314. codec, prefix, idx);
  315. if (ret < 0)
  316. goto dai_link_of_err;
  317. if (!of_property_read_u32(node, "mclk-fs", &val))
  318. dai_props->mclk_fs = val;
  319. ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
  320. &dai_link->cpu_of_node,
  321. &dai_link->cpu_dai_name,
  322. &cpu_args);
  323. if (ret < 0)
  324. goto dai_link_of_err;
  325. ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
  326. &dai_link->codec_of_node,
  327. &dai_link->codec_dai_name, NULL);
  328. if (ret < 0)
  329. goto dai_link_of_err;
  330. if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
  331. ret = -EINVAL;
  332. goto dai_link_of_err;
  333. }
  334. if (plat) {
  335. struct of_phandle_args args;
  336. ret = of_parse_phandle_with_args(plat, "sound-dai",
  337. "#sound-dai-cells", 0, &args);
  338. dai_link->platform_of_node = args.np;
  339. } else {
  340. /* Assumes platform == cpu */
  341. dai_link->platform_of_node = dai_link->cpu_of_node;
  342. }
  343. /* DAI link name is created from CPU/CODEC dai name */
  344. name = devm_kzalloc(dev,
  345. strlen(dai_link->cpu_dai_name) +
  346. strlen(dai_link->codec_dai_name) + 2,
  347. GFP_KERNEL);
  348. if (!name) {
  349. ret = -ENOMEM;
  350. goto dai_link_of_err;
  351. }
  352. sprintf(name, "%s-%s", dai_link->cpu_dai_name,
  353. dai_link->codec_dai_name);
  354. dai_link->name = dai_link->stream_name = name;
  355. dai_link->ops = &asoc_simple_card_ops;
  356. dai_link->init = asoc_simple_card_dai_init;
  357. dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
  358. dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
  359. dev_dbg(dev, "\tcpu : %s / %d\n",
  360. dai_link->cpu_dai_name,
  361. dai_props->cpu_dai.sysclk);
  362. dev_dbg(dev, "\tcodec : %s / %d\n",
  363. dai_link->codec_dai_name,
  364. dai_props->codec_dai.sysclk);
  365. /*
  366. * In soc_bind_dai_link() will check cpu name after
  367. * of_node matching if dai_link has cpu_dai_name.
  368. * but, it will never match if name was created by
  369. * fmt_single_name() remove cpu_dai_name if cpu_args
  370. * was 0. See:
  371. * fmt_single_name()
  372. * fmt_multiple_name()
  373. */
  374. if (!cpu_args)
  375. dai_link->cpu_dai_name = NULL;
  376. dai_link_of_err:
  377. of_node_put(cpu);
  378. of_node_put(codec);
  379. return ret;
  380. }
  381. static int asoc_simple_card_parse_of(struct device_node *node,
  382. struct simple_card_data *priv)
  383. {
  384. struct device *dev = simple_priv_to_dev(priv);
  385. enum of_gpio_flags flags;
  386. u32 val;
  387. int ret;
  388. if (!node)
  389. return -EINVAL;
  390. /* Parse the card name from DT */
  391. snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
  392. /* The off-codec widgets */
  393. if (of_property_read_bool(node, "simple-audio-card,widgets")) {
  394. ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
  395. "simple-audio-card,widgets");
  396. if (ret)
  397. return ret;
  398. }
  399. /* DAPM routes */
  400. if (of_property_read_bool(node, "simple-audio-card,routing")) {
  401. ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
  402. "simple-audio-card,routing");
  403. if (ret)
  404. return ret;
  405. }
  406. /* Factor to mclk, used in hw_params() */
  407. ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
  408. if (ret == 0)
  409. priv->mclk_fs = val;
  410. dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
  411. priv->snd_card.name : "");
  412. /* Single/Muti DAI link(s) & New style of DT node */
  413. if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
  414. struct device_node *np = NULL;
  415. int i = 0;
  416. for_each_child_of_node(node, np) {
  417. dev_dbg(dev, "\tlink %d:\n", i);
  418. ret = asoc_simple_card_dai_link_of(np, priv,
  419. i, false);
  420. if (ret < 0) {
  421. of_node_put(np);
  422. return ret;
  423. }
  424. i++;
  425. }
  426. } else {
  427. /* For single DAI link & old style of DT node */
  428. ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
  429. if (ret < 0)
  430. return ret;
  431. }
  432. priv->gpio_hp_det = of_get_named_gpio_flags(node,
  433. "simple-audio-card,hp-det-gpio", 0, &flags);
  434. priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
  435. if (priv->gpio_hp_det == -EPROBE_DEFER)
  436. return -EPROBE_DEFER;
  437. priv->gpio_mic_det = of_get_named_gpio_flags(node,
  438. "simple-audio-card,mic-det-gpio", 0, &flags);
  439. priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
  440. if (priv->gpio_mic_det == -EPROBE_DEFER)
  441. return -EPROBE_DEFER;
  442. if (!priv->snd_card.name)
  443. priv->snd_card.name = priv->snd_card.dai_link->name;
  444. return 0;
  445. }
  446. /* Decrease the reference count of the device nodes */
  447. static int asoc_simple_card_unref(struct snd_soc_card *card)
  448. {
  449. struct snd_soc_dai_link *dai_link;
  450. int num_links;
  451. for (num_links = 0, dai_link = card->dai_link;
  452. num_links < card->num_links;
  453. num_links++, dai_link++) {
  454. of_node_put(dai_link->cpu_of_node);
  455. of_node_put(dai_link->codec_of_node);
  456. }
  457. return 0;
  458. }
  459. static int asoc_simple_card_probe(struct platform_device *pdev)
  460. {
  461. struct simple_card_data *priv;
  462. struct snd_soc_dai_link *dai_link;
  463. struct device_node *np = pdev->dev.of_node;
  464. struct device *dev = &pdev->dev;
  465. int num_links, ret;
  466. /* Get the number of DAI links */
  467. if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
  468. num_links = of_get_child_count(np);
  469. else
  470. num_links = 1;
  471. /* Allocate the private data and the DAI link array */
  472. priv = devm_kzalloc(dev,
  473. sizeof(*priv) + sizeof(*dai_link) * num_links,
  474. GFP_KERNEL);
  475. if (!priv)
  476. return -ENOMEM;
  477. /* Init snd_soc_card */
  478. priv->snd_card.owner = THIS_MODULE;
  479. priv->snd_card.dev = dev;
  480. dai_link = priv->dai_link;
  481. priv->snd_card.dai_link = dai_link;
  482. priv->snd_card.num_links = num_links;
  483. priv->gpio_hp_det = -ENOENT;
  484. priv->gpio_mic_det = -ENOENT;
  485. /* Get room for the other properties */
  486. priv->dai_props = devm_kzalloc(dev,
  487. sizeof(*priv->dai_props) * num_links,
  488. GFP_KERNEL);
  489. if (!priv->dai_props)
  490. return -ENOMEM;
  491. if (np && of_device_is_available(np)) {
  492. ret = asoc_simple_card_parse_of(np, priv);
  493. if (ret < 0) {
  494. if (ret != -EPROBE_DEFER)
  495. dev_err(dev, "parse error %d\n", ret);
  496. goto err;
  497. }
  498. } else {
  499. struct asoc_simple_card_info *cinfo;
  500. cinfo = dev->platform_data;
  501. if (!cinfo) {
  502. dev_err(dev, "no info for asoc-simple-card\n");
  503. return -EINVAL;
  504. }
  505. if (!cinfo->name ||
  506. !cinfo->codec_dai.name ||
  507. !cinfo->codec ||
  508. !cinfo->platform ||
  509. !cinfo->cpu_dai.name) {
  510. dev_err(dev, "insufficient asoc_simple_card_info settings\n");
  511. return -EINVAL;
  512. }
  513. priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
  514. dai_link->name = cinfo->name;
  515. dai_link->stream_name = cinfo->name;
  516. dai_link->platform_name = cinfo->platform;
  517. dai_link->codec_name = cinfo->codec;
  518. dai_link->cpu_dai_name = cinfo->cpu_dai.name;
  519. dai_link->codec_dai_name = cinfo->codec_dai.name;
  520. dai_link->dai_fmt = cinfo->daifmt;
  521. dai_link->init = asoc_simple_card_dai_init;
  522. memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
  523. sizeof(priv->dai_props->cpu_dai));
  524. memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
  525. sizeof(priv->dai_props->codec_dai));
  526. }
  527. snd_soc_card_set_drvdata(&priv->snd_card, priv);
  528. ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
  529. if (ret >= 0)
  530. return ret;
  531. err:
  532. asoc_simple_card_unref(&priv->snd_card);
  533. return ret;
  534. }
  535. static int asoc_simple_card_remove(struct platform_device *pdev)
  536. {
  537. struct snd_soc_card *card = platform_get_drvdata(pdev);
  538. struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
  539. if (gpio_is_valid(priv->gpio_hp_det))
  540. snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
  541. &simple_card_hp_jack_gpio);
  542. if (gpio_is_valid(priv->gpio_mic_det))
  543. snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
  544. &simple_card_mic_jack_gpio);
  545. return asoc_simple_card_unref(card);
  546. }
  547. static const struct of_device_id asoc_simple_of_match[] = {
  548. { .compatible = "simple-audio-card", },
  549. {},
  550. };
  551. MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
  552. static struct platform_driver asoc_simple_card = {
  553. .driver = {
  554. .name = "asoc-simple-card",
  555. .of_match_table = asoc_simple_of_match,
  556. },
  557. .probe = asoc_simple_card_probe,
  558. .remove = asoc_simple_card_remove,
  559. };
  560. module_platform_driver(asoc_simple_card);
  561. MODULE_ALIAS("platform:asoc-simple-card");
  562. MODULE_LICENSE("GPL");
  563. MODULE_DESCRIPTION("ASoC Simple Sound Card");
  564. MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");