aries_wm8994.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. // SPDX-License-Identifier: GPL-2.0+
  2. #include <linux/extcon.h>
  3. #include <linux/iio/consumer.h>
  4. #include <linux/input-event-codes.h>
  5. #include <linux/mfd/wm8994/registers.h>
  6. #include <linux/module.h>
  7. #include <linux/of.h>
  8. #include <linux/of_device.h>
  9. #include <linux/of_gpio.h>
  10. #include <linux/regulator/consumer.h>
  11. #include <sound/jack.h>
  12. #include <sound/pcm_params.h>
  13. #include <sound/soc.h>
  14. #include "i2s.h"
  15. #include "../codecs/wm8994.h"
  16. #define ARIES_MCLK1_FREQ 24000000
  17. struct aries_wm8994_variant {
  18. unsigned int modem_dai_fmt;
  19. bool has_fm_radio;
  20. };
  21. struct aries_wm8994_data {
  22. struct extcon_dev *usb_extcon;
  23. struct regulator *reg_main_micbias;
  24. struct regulator *reg_headset_micbias;
  25. struct gpio_desc *gpio_headset_detect;
  26. struct gpio_desc *gpio_headset_key;
  27. struct gpio_desc *gpio_earpath_sel;
  28. struct iio_channel *adc;
  29. const struct aries_wm8994_variant *variant;
  30. };
  31. /* USB dock */
  32. static struct snd_soc_jack aries_dock;
  33. static struct snd_soc_jack_pin dock_pins[] = {
  34. {
  35. .pin = "LINE",
  36. .mask = SND_JACK_LINEOUT,
  37. },
  38. };
  39. static int aries_extcon_notifier(struct notifier_block *this,
  40. unsigned long connected, void *_cmd)
  41. {
  42. if (connected)
  43. snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
  44. SND_JACK_LINEOUT);
  45. else
  46. snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
  47. return NOTIFY_DONE;
  48. }
  49. static struct notifier_block aries_extcon_notifier_block = {
  50. .notifier_call = aries_extcon_notifier,
  51. };
  52. /* Headset jack */
  53. static struct snd_soc_jack aries_headset;
  54. static struct snd_soc_jack_pin jack_pins[] = {
  55. {
  56. .pin = "HP",
  57. .mask = SND_JACK_HEADPHONE,
  58. }, {
  59. .pin = "Headset Mic",
  60. .mask = SND_JACK_MICROPHONE,
  61. },
  62. };
  63. static struct snd_soc_jack_zone headset_zones[] = {
  64. {
  65. .min_mv = 0,
  66. .max_mv = 241,
  67. .jack_type = SND_JACK_HEADPHONE,
  68. }, {
  69. .min_mv = 242,
  70. .max_mv = 2980,
  71. .jack_type = SND_JACK_HEADSET,
  72. }, {
  73. .min_mv = 2981,
  74. .max_mv = UINT_MAX,
  75. .jack_type = SND_JACK_HEADPHONE,
  76. },
  77. };
  78. static irqreturn_t headset_det_irq_thread(int irq, void *data)
  79. {
  80. struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
  81. int ret = 0;
  82. int time_left_ms = 300;
  83. int adc;
  84. while (time_left_ms > 0) {
  85. if (!gpiod_get_value(priv->gpio_headset_detect)) {
  86. snd_soc_jack_report(&aries_headset, 0,
  87. SND_JACK_HEADSET);
  88. gpiod_set_value(priv->gpio_earpath_sel, 0);
  89. return IRQ_HANDLED;
  90. }
  91. msleep(20);
  92. time_left_ms -= 20;
  93. }
  94. /* Temporarily enable micbias and earpath selector */
  95. ret = regulator_enable(priv->reg_headset_micbias);
  96. if (ret)
  97. pr_err("%s failed to enable micbias: %d", __func__, ret);
  98. gpiod_set_value(priv->gpio_earpath_sel, 1);
  99. ret = iio_read_channel_processed(priv->adc, &adc);
  100. if (ret < 0) {
  101. /* failed to read ADC, so assume headphone */
  102. pr_err("%s failed to read ADC, assuming headphones", __func__);
  103. snd_soc_jack_report(&aries_headset, SND_JACK_HEADPHONE,
  104. SND_JACK_HEADSET);
  105. } else {
  106. snd_soc_jack_report(&aries_headset,
  107. snd_soc_jack_get_type(&aries_headset, adc),
  108. SND_JACK_HEADSET);
  109. }
  110. ret = regulator_disable(priv->reg_headset_micbias);
  111. if (ret)
  112. pr_err("%s failed disable micbias: %d", __func__, ret);
  113. /* Disable earpath selector when no mic connected */
  114. if (!(aries_headset.status & SND_JACK_MICROPHONE))
  115. gpiod_set_value(priv->gpio_earpath_sel, 0);
  116. return IRQ_HANDLED;
  117. }
  118. static int headset_button_check(void *data)
  119. {
  120. struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
  121. /* Filter out keypresses when 4 pole jack not detected */
  122. if (gpiod_get_value_cansleep(priv->gpio_headset_key) &&
  123. aries_headset.status & SND_JACK_MICROPHONE)
  124. return SND_JACK_BTN_0;
  125. return 0;
  126. }
  127. static struct snd_soc_jack_gpio headset_button_gpio[] = {
  128. {
  129. .name = "Media Button",
  130. .report = SND_JACK_BTN_0,
  131. .debounce_time = 30,
  132. .jack_status_check = headset_button_check,
  133. },
  134. };
  135. static int aries_spk_cfg(struct snd_soc_dapm_widget *w,
  136. struct snd_kcontrol *kcontrol, int event)
  137. {
  138. struct snd_soc_card *card = w->dapm->card;
  139. struct snd_soc_pcm_runtime *rtd;
  140. struct snd_soc_component *component;
  141. int ret = 0;
  142. rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
  143. component = asoc_rtd_to_codec(rtd, 0)->component;
  144. /**
  145. * We have an odd setup - the SPKMODE pin is pulled up so
  146. * we only have access to the left side SPK configs,
  147. * but SPKOUTR isn't bridged so when playing back in
  148. * stereo, we only get the left hand channel. The only
  149. * option we're left with is to force the AIF into mono
  150. * mode.
  151. */
  152. switch (event) {
  153. case SND_SOC_DAPM_POST_PMU:
  154. ret = snd_soc_component_update_bits(component,
  155. WM8994_AIF1_DAC1_FILTERS_1,
  156. WM8994_AIF1DAC1_MONO, WM8994_AIF1DAC1_MONO);
  157. break;
  158. case SND_SOC_DAPM_PRE_PMD:
  159. ret = snd_soc_component_update_bits(component,
  160. WM8994_AIF1_DAC1_FILTERS_1,
  161. WM8994_AIF1DAC1_MONO, 0);
  162. break;
  163. }
  164. return ret;
  165. }
  166. static int aries_main_bias(struct snd_soc_dapm_widget *w,
  167. struct snd_kcontrol *kcontrol, int event)
  168. {
  169. struct snd_soc_card *card = w->dapm->card;
  170. struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
  171. int ret = 0;
  172. switch (event) {
  173. case SND_SOC_DAPM_PRE_PMU:
  174. ret = regulator_enable(priv->reg_main_micbias);
  175. break;
  176. case SND_SOC_DAPM_POST_PMD:
  177. ret = regulator_disable(priv->reg_main_micbias);
  178. break;
  179. }
  180. return ret;
  181. }
  182. static int aries_headset_bias(struct snd_soc_dapm_widget *w,
  183. struct snd_kcontrol *kcontrol, int event)
  184. {
  185. struct snd_soc_card *card = w->dapm->card;
  186. struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
  187. int ret = 0;
  188. switch (event) {
  189. case SND_SOC_DAPM_PRE_PMU:
  190. ret = regulator_enable(priv->reg_headset_micbias);
  191. break;
  192. case SND_SOC_DAPM_POST_PMD:
  193. ret = regulator_disable(priv->reg_headset_micbias);
  194. break;
  195. }
  196. return ret;
  197. }
  198. static const struct snd_kcontrol_new aries_controls[] = {
  199. SOC_DAPM_PIN_SWITCH("Modem In"),
  200. SOC_DAPM_PIN_SWITCH("Modem Out"),
  201. };
  202. static const struct snd_soc_dapm_widget aries_dapm_widgets[] = {
  203. SND_SOC_DAPM_HP("HP", NULL),
  204. SND_SOC_DAPM_SPK("SPK", aries_spk_cfg),
  205. SND_SOC_DAPM_SPK("RCV", NULL),
  206. SND_SOC_DAPM_LINE("LINE", NULL),
  207. SND_SOC_DAPM_MIC("Main Mic", aries_main_bias),
  208. SND_SOC_DAPM_MIC("Headset Mic", aries_headset_bias),
  209. SND_SOC_DAPM_MIC("Bluetooth Mic", NULL),
  210. SND_SOC_DAPM_SPK("Bluetooth SPK", NULL),
  211. SND_SOC_DAPM_LINE("Modem In", NULL),
  212. SND_SOC_DAPM_LINE("Modem Out", NULL),
  213. /* This must be last as it is conditionally not used */
  214. SND_SOC_DAPM_LINE("FM In", NULL),
  215. };
  216. static int aries_hw_params(struct snd_pcm_substream *substream,
  217. struct snd_pcm_hw_params *params)
  218. {
  219. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  220. struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  221. unsigned int pll_out;
  222. int ret;
  223. /* AIF1CLK should be >=3MHz for optimal performance */
  224. if (params_width(params) == 24)
  225. pll_out = params_rate(params) * 384;
  226. else if (params_rate(params) == 8000 || params_rate(params) == 11025)
  227. pll_out = params_rate(params) * 512;
  228. else
  229. pll_out = params_rate(params) * 256;
  230. ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
  231. ARIES_MCLK1_FREQ, pll_out);
  232. if (ret < 0)
  233. return ret;
  234. ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
  235. pll_out, SND_SOC_CLOCK_IN);
  236. if (ret < 0)
  237. return ret;
  238. return 0;
  239. }
  240. static int aries_hw_free(struct snd_pcm_substream *substream)
  241. {
  242. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  243. struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  244. int ret;
  245. /* Switch sysclk to MCLK1 */
  246. ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
  247. ARIES_MCLK1_FREQ, SND_SOC_CLOCK_IN);
  248. if (ret < 0)
  249. return ret;
  250. /* Stop PLL */
  251. ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
  252. ARIES_MCLK1_FREQ, 0);
  253. if (ret < 0)
  254. return ret;
  255. return 0;
  256. }
  257. /*
  258. * Main DAI operations
  259. */
  260. static const struct snd_soc_ops aries_ops = {
  261. .hw_params = aries_hw_params,
  262. .hw_free = aries_hw_free,
  263. };
  264. static int aries_baseband_init(struct snd_soc_pcm_runtime *rtd)
  265. {
  266. struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  267. unsigned int pll_out;
  268. int ret;
  269. pll_out = 8000 * 512;
  270. /* Set the codec FLL */
  271. ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1,
  272. ARIES_MCLK1_FREQ, pll_out);
  273. if (ret < 0)
  274. return ret;
  275. /* Set the codec system clock */
  276. ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
  277. pll_out, SND_SOC_CLOCK_IN);
  278. if (ret < 0)
  279. return ret;
  280. return 0;
  281. }
  282. static int aries_late_probe(struct snd_soc_card *card)
  283. {
  284. struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
  285. int ret, irq;
  286. ret = snd_soc_card_jack_new_pins(card, "Dock", SND_JACK_LINEOUT,
  287. &aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
  288. if (ret)
  289. return ret;
  290. ret = devm_extcon_register_notifier(card->dev,
  291. priv->usb_extcon, EXTCON_JACK_LINE_OUT,
  292. &aries_extcon_notifier_block);
  293. if (ret)
  294. return ret;
  295. if (extcon_get_state(priv->usb_extcon,
  296. EXTCON_JACK_LINE_OUT) > 0)
  297. snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
  298. SND_JACK_LINEOUT);
  299. else
  300. snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
  301. ret = snd_soc_card_jack_new_pins(card, "Headset",
  302. SND_JACK_HEADSET | SND_JACK_BTN_0,
  303. &aries_headset,
  304. jack_pins, ARRAY_SIZE(jack_pins));
  305. if (ret)
  306. return ret;
  307. ret = snd_soc_jack_add_zones(&aries_headset, ARRAY_SIZE(headset_zones),
  308. headset_zones);
  309. if (ret)
  310. return ret;
  311. irq = gpiod_to_irq(priv->gpio_headset_detect);
  312. if (irq < 0) {
  313. dev_err(card->dev, "Failed to map headset detect gpio to irq");
  314. return -EINVAL;
  315. }
  316. ret = devm_request_threaded_irq(card->dev, irq, NULL,
  317. headset_det_irq_thread,
  318. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
  319. IRQF_ONESHOT, "headset_detect", priv);
  320. if (ret) {
  321. dev_err(card->dev, "Failed to request headset detect irq");
  322. return ret;
  323. }
  324. headset_button_gpio[0].data = priv;
  325. headset_button_gpio[0].desc = priv->gpio_headset_key;
  326. snd_jack_set_key(aries_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
  327. return snd_soc_jack_add_gpios(&aries_headset,
  328. ARRAY_SIZE(headset_button_gpio), headset_button_gpio);
  329. }
  330. static const struct snd_soc_pcm_stream baseband_params = {
  331. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  332. .rate_min = 8000,
  333. .rate_max = 8000,
  334. .channels_min = 1,
  335. .channels_max = 1,
  336. };
  337. static const struct snd_soc_pcm_stream bluetooth_params = {
  338. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  339. .rate_min = 8000,
  340. .rate_max = 8000,
  341. .channels_min = 1,
  342. .channels_max = 2,
  343. };
  344. static const struct snd_soc_dapm_widget aries_modem_widgets[] = {
  345. SND_SOC_DAPM_INPUT("Modem RX"),
  346. SND_SOC_DAPM_OUTPUT("Modem TX"),
  347. };
  348. static const struct snd_soc_dapm_route aries_modem_routes[] = {
  349. { "Modem Capture", NULL, "Modem RX" },
  350. { "Modem TX", NULL, "Modem Playback" },
  351. };
  352. static const struct snd_soc_component_driver aries_component = {
  353. .name = "aries-audio",
  354. .dapm_widgets = aries_modem_widgets,
  355. .num_dapm_widgets = ARRAY_SIZE(aries_modem_widgets),
  356. .dapm_routes = aries_modem_routes,
  357. .num_dapm_routes = ARRAY_SIZE(aries_modem_routes),
  358. .idle_bias_on = 1,
  359. .use_pmdown_time = 1,
  360. .endianness = 1,
  361. };
  362. static struct snd_soc_dai_driver aries_ext_dai[] = {
  363. {
  364. .name = "Voice call",
  365. .playback = {
  366. .stream_name = "Modem Playback",
  367. .channels_min = 1,
  368. .channels_max = 1,
  369. .rate_min = 8000,
  370. .rate_max = 8000,
  371. .rates = SNDRV_PCM_RATE_8000,
  372. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  373. },
  374. .capture = {
  375. .stream_name = "Modem Capture",
  376. .channels_min = 1,
  377. .channels_max = 1,
  378. .rate_min = 8000,
  379. .rate_max = 8000,
  380. .rates = SNDRV_PCM_RATE_8000,
  381. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  382. },
  383. },
  384. };
  385. SND_SOC_DAILINK_DEFS(aif1,
  386. DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
  387. DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
  388. DAILINK_COMP_ARRAY(COMP_EMPTY()));
  389. SND_SOC_DAILINK_DEFS(baseband,
  390. DAILINK_COMP_ARRAY(COMP_CPU("Voice call")),
  391. DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")));
  392. SND_SOC_DAILINK_DEFS(bluetooth,
  393. DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")),
  394. DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")));
  395. static struct snd_soc_dai_link aries_dai[] = {
  396. {
  397. .name = "WM8994 AIF1",
  398. .stream_name = "HiFi",
  399. .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
  400. SND_SOC_DAIFMT_CBM_CFM,
  401. .ops = &aries_ops,
  402. SND_SOC_DAILINK_REG(aif1),
  403. },
  404. {
  405. .name = "WM8994 AIF2",
  406. .stream_name = "Baseband",
  407. .init = &aries_baseband_init,
  408. .params = &baseband_params,
  409. .ignore_suspend = 1,
  410. SND_SOC_DAILINK_REG(baseband),
  411. },
  412. {
  413. .name = "WM8994 AIF3",
  414. .stream_name = "Bluetooth",
  415. .params = &bluetooth_params,
  416. .ignore_suspend = 1,
  417. SND_SOC_DAILINK_REG(bluetooth),
  418. },
  419. };
  420. static struct snd_soc_card aries_card = {
  421. .name = "ARIES",
  422. .owner = THIS_MODULE,
  423. .dai_link = aries_dai,
  424. .num_links = ARRAY_SIZE(aries_dai),
  425. .controls = aries_controls,
  426. .num_controls = ARRAY_SIZE(aries_controls),
  427. .dapm_widgets = aries_dapm_widgets,
  428. .num_dapm_widgets = ARRAY_SIZE(aries_dapm_widgets),
  429. .late_probe = aries_late_probe,
  430. };
  431. static const struct aries_wm8994_variant fascinate4g_variant = {
  432. .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
  433. | SND_SOC_DAIFMT_IB_NF,
  434. .has_fm_radio = false,
  435. };
  436. static const struct aries_wm8994_variant aries_variant = {
  437. .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM
  438. | SND_SOC_DAIFMT_IB_NF,
  439. .has_fm_radio = true,
  440. };
  441. static const struct of_device_id samsung_wm8994_of_match[] = {
  442. {
  443. .compatible = "samsung,fascinate4g-wm8994",
  444. .data = &fascinate4g_variant,
  445. },
  446. {
  447. .compatible = "samsung,aries-wm8994",
  448. .data = &aries_variant,
  449. },
  450. { /* sentinel */ },
  451. };
  452. MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
  453. static int aries_audio_probe(struct platform_device *pdev)
  454. {
  455. struct device_node *np = pdev->dev.of_node;
  456. struct device_node *cpu, *codec, *extcon_np;
  457. struct device *dev = &pdev->dev;
  458. struct snd_soc_card *card = &aries_card;
  459. struct aries_wm8994_data *priv;
  460. struct snd_soc_dai_link *dai_link;
  461. const struct of_device_id *match;
  462. enum iio_chan_type channel_type;
  463. int ret, i;
  464. if (!np)
  465. return -EINVAL;
  466. card->dev = dev;
  467. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  468. if (!priv)
  469. return -ENOMEM;
  470. snd_soc_card_set_drvdata(card, priv);
  471. match = of_match_node(samsung_wm8994_of_match, np);
  472. priv->variant = match->data;
  473. /* Remove FM widget if not present */
  474. if (!priv->variant->has_fm_radio)
  475. card->num_dapm_widgets--;
  476. priv->reg_main_micbias = devm_regulator_get(dev, "main-micbias");
  477. if (IS_ERR(priv->reg_main_micbias)) {
  478. dev_err(dev, "Failed to get main micbias regulator\n");
  479. return PTR_ERR(priv->reg_main_micbias);
  480. }
  481. priv->reg_headset_micbias = devm_regulator_get(dev, "headset-micbias");
  482. if (IS_ERR(priv->reg_headset_micbias)) {
  483. dev_err(dev, "Failed to get headset micbias regulator\n");
  484. return PTR_ERR(priv->reg_headset_micbias);
  485. }
  486. priv->gpio_earpath_sel = devm_gpiod_get(dev, "earpath-sel",
  487. GPIOD_OUT_LOW);
  488. if (IS_ERR(priv->gpio_earpath_sel)) {
  489. dev_err(dev, "Failed to get earpath selector gpio");
  490. return PTR_ERR(priv->gpio_earpath_sel);
  491. }
  492. extcon_np = of_parse_phandle(np, "extcon", 0);
  493. priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
  494. of_node_put(extcon_np);
  495. if (IS_ERR(priv->usb_extcon))
  496. return dev_err_probe(dev, PTR_ERR(priv->usb_extcon),
  497. "Failed to get extcon device");
  498. priv->adc = devm_iio_channel_get(dev, "headset-detect");
  499. if (IS_ERR(priv->adc))
  500. return dev_err_probe(dev, PTR_ERR(priv->adc),
  501. "Failed to get ADC channel");
  502. ret = iio_get_channel_type(priv->adc, &channel_type);
  503. if (ret)
  504. return dev_err_probe(dev, ret,
  505. "Failed to get ADC channel type");
  506. if (channel_type != IIO_VOLTAGE)
  507. return -EINVAL;
  508. priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
  509. GPIOD_IN);
  510. if (IS_ERR(priv->gpio_headset_key)) {
  511. dev_err(dev, "Failed to get headset key gpio");
  512. return PTR_ERR(priv->gpio_headset_key);
  513. }
  514. priv->gpio_headset_detect = devm_gpiod_get(dev,
  515. "headset-detect", GPIOD_IN);
  516. if (IS_ERR(priv->gpio_headset_detect)) {
  517. dev_err(dev, "Failed to get headset detect gpio");
  518. return PTR_ERR(priv->gpio_headset_detect);
  519. }
  520. /* Update card-name if provided through DT, else use default name */
  521. snd_soc_of_parse_card_name(card, "model");
  522. ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
  523. if (ret < 0) {
  524. dev_err(dev, "Audio routing invalid/unspecified\n");
  525. return ret;
  526. }
  527. aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt;
  528. cpu = of_get_child_by_name(dev->of_node, "cpu");
  529. if (!cpu)
  530. return -EINVAL;
  531. codec = of_get_child_by_name(dev->of_node, "codec");
  532. if (!codec) {
  533. ret = -EINVAL;
  534. goto out;
  535. }
  536. for_each_card_prelinks(card, i, dai_link) {
  537. dai_link->codecs->of_node = of_parse_phandle(codec,
  538. "sound-dai", 0);
  539. if (!dai_link->codecs->of_node) {
  540. ret = -EINVAL;
  541. goto out;
  542. }
  543. }
  544. /* Set CPU and platform of_node for main DAI */
  545. aries_dai[0].cpus->of_node = of_parse_phandle(cpu,
  546. "sound-dai", 0);
  547. if (!aries_dai[0].cpus->of_node) {
  548. ret = -EINVAL;
  549. goto out;
  550. }
  551. aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
  552. /* Set CPU of_node for BT DAI */
  553. aries_dai[2].cpus->of_node = of_parse_phandle(cpu,
  554. "sound-dai", 1);
  555. if (!aries_dai[2].cpus->of_node) {
  556. ret = -EINVAL;
  557. goto out;
  558. }
  559. ret = devm_snd_soc_register_component(dev, &aries_component,
  560. aries_ext_dai, ARRAY_SIZE(aries_ext_dai));
  561. if (ret < 0) {
  562. dev_err(dev, "Failed to register component: %d\n", ret);
  563. goto out;
  564. }
  565. ret = devm_snd_soc_register_card(dev, card);
  566. if (ret)
  567. dev_err(dev, "snd_soc_register_card() failed:%d\n", ret);
  568. out:
  569. of_node_put(cpu);
  570. of_node_put(codec);
  571. return ret;
  572. }
  573. static struct platform_driver aries_audio_driver = {
  574. .driver = {
  575. .name = "aries-audio-wm8994",
  576. .of_match_table = of_match_ptr(samsung_wm8994_of_match),
  577. .pm = &snd_soc_pm_ops,
  578. },
  579. .probe = aries_audio_probe,
  580. };
  581. module_platform_driver(aries_audio_driver);
  582. MODULE_DESCRIPTION("ALSA SoC ARIES WM8994");
  583. MODULE_LICENSE("GPL");
  584. MODULE_ALIAS("platform:aries-audio-wm8994");