soc_sdw_utils.c 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. // This file incorporates work covered by the following copyright notice:
  3. // Copyright (c) 2020 Intel Corporation
  4. // Copyright(c) 2024 Advanced Micro Devices, Inc.
  5. /*
  6. * soc-sdw-utils.c - common SoundWire machine driver helper functions
  7. */
  8. #include <linux/device.h>
  9. #include <linux/module.h>
  10. #include <linux/soundwire/sdw.h>
  11. #include <linux/soundwire/sdw_type.h>
  12. #include <sound/soc_sdw_utils.h>
  13. static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
  14. SND_SOC_DAPM_MIC("DMIC", NULL),
  15. };
  16. static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
  17. SND_SOC_DAPM_HP("Headphone", NULL),
  18. SND_SOC_DAPM_MIC("Headset Mic", NULL),
  19. };
  20. static const struct snd_kcontrol_new generic_jack_controls[] = {
  21. SOC_DAPM_PIN_SWITCH("Headphone"),
  22. SOC_DAPM_PIN_SWITCH("Headset Mic"),
  23. };
  24. static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
  25. SND_SOC_DAPM_SPK("Speaker", NULL),
  26. };
  27. static const struct snd_kcontrol_new generic_spk_controls[] = {
  28. SOC_DAPM_PIN_SWITCH("Speaker"),
  29. };
  30. static const struct snd_soc_dapm_widget maxim_widgets[] = {
  31. SND_SOC_DAPM_SPK("Left Spk", NULL),
  32. SND_SOC_DAPM_SPK("Right Spk", NULL),
  33. };
  34. static const struct snd_kcontrol_new maxim_controls[] = {
  35. SOC_DAPM_PIN_SWITCH("Left Spk"),
  36. SOC_DAPM_PIN_SWITCH("Right Spk"),
  37. };
  38. static const struct snd_soc_dapm_widget rt700_widgets[] = {
  39. SND_SOC_DAPM_HP("Headphones", NULL),
  40. SND_SOC_DAPM_MIC("AMIC", NULL),
  41. SND_SOC_DAPM_SPK("Speaker", NULL),
  42. };
  43. static const struct snd_kcontrol_new rt700_controls[] = {
  44. SOC_DAPM_PIN_SWITCH("Headphones"),
  45. SOC_DAPM_PIN_SWITCH("AMIC"),
  46. SOC_DAPM_PIN_SWITCH("Speaker"),
  47. };
  48. struct asoc_sdw_codec_info codec_info_list[] = {
  49. {
  50. .part_id = 0x700,
  51. .dais = {
  52. {
  53. .direction = {true, true},
  54. .dai_name = "rt700-aif1",
  55. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  56. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  57. .rtd_init = asoc_sdw_rt700_rtd_init,
  58. .controls = rt700_controls,
  59. .num_controls = ARRAY_SIZE(rt700_controls),
  60. .widgets = rt700_widgets,
  61. .num_widgets = ARRAY_SIZE(rt700_widgets),
  62. },
  63. },
  64. .dai_num = 1,
  65. },
  66. {
  67. .part_id = 0x711,
  68. .version_id = 3,
  69. .dais = {
  70. {
  71. .direction = {true, true},
  72. .dai_name = "rt711-sdca-aif1",
  73. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  74. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  75. .init = asoc_sdw_rt_sdca_jack_init,
  76. .exit = asoc_sdw_rt_sdca_jack_exit,
  77. .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
  78. .controls = generic_jack_controls,
  79. .num_controls = ARRAY_SIZE(generic_jack_controls),
  80. .widgets = generic_jack_widgets,
  81. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  82. },
  83. },
  84. .dai_num = 1,
  85. },
  86. {
  87. .part_id = 0x711,
  88. .version_id = 2,
  89. .dais = {
  90. {
  91. .direction = {true, true},
  92. .dai_name = "rt711-aif1",
  93. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  94. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  95. .init = asoc_sdw_rt711_init,
  96. .exit = asoc_sdw_rt711_exit,
  97. .rtd_init = asoc_sdw_rt711_rtd_init,
  98. .controls = generic_jack_controls,
  99. .num_controls = ARRAY_SIZE(generic_jack_controls),
  100. .widgets = generic_jack_widgets,
  101. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  102. },
  103. },
  104. .dai_num = 1,
  105. },
  106. {
  107. .part_id = 0x712,
  108. .version_id = 3,
  109. .dais = {
  110. {
  111. .direction = {true, true},
  112. .dai_name = "rt712-sdca-aif1",
  113. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  114. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  115. .init = asoc_sdw_rt_sdca_jack_init,
  116. .exit = asoc_sdw_rt_sdca_jack_exit,
  117. .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
  118. .controls = generic_jack_controls,
  119. .num_controls = ARRAY_SIZE(generic_jack_controls),
  120. .widgets = generic_jack_widgets,
  121. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  122. },
  123. {
  124. .direction = {true, false},
  125. .dai_name = "rt712-sdca-aif2",
  126. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  127. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  128. .init = asoc_sdw_rt_amp_init,
  129. .exit = asoc_sdw_rt_amp_exit,
  130. .rtd_init = asoc_sdw_rt712_spk_rtd_init,
  131. .controls = generic_spk_controls,
  132. .num_controls = ARRAY_SIZE(generic_spk_controls),
  133. .widgets = generic_spk_widgets,
  134. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  135. },
  136. },
  137. .dai_num = 2,
  138. },
  139. {
  140. .part_id = 0x1712,
  141. .version_id = 3,
  142. .dais = {
  143. {
  144. .direction = {false, true},
  145. .dai_name = "rt712-sdca-dmic-aif1",
  146. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  147. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  148. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  149. },
  150. },
  151. .dai_num = 1,
  152. },
  153. {
  154. .part_id = 0x713,
  155. .version_id = 3,
  156. .dais = {
  157. {
  158. .direction = {true, true},
  159. .dai_name = "rt712-sdca-aif1",
  160. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  161. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  162. .init = asoc_sdw_rt_sdca_jack_init,
  163. .exit = asoc_sdw_rt_sdca_jack_exit,
  164. .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
  165. .controls = generic_jack_controls,
  166. .num_controls = ARRAY_SIZE(generic_jack_controls),
  167. .widgets = generic_jack_widgets,
  168. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  169. },
  170. },
  171. .dai_num = 1,
  172. },
  173. {
  174. .part_id = 0x1713,
  175. .version_id = 3,
  176. .dais = {
  177. {
  178. .direction = {false, true},
  179. .dai_name = "rt712-sdca-dmic-aif1",
  180. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  181. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  182. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  183. },
  184. },
  185. .dai_num = 1,
  186. },
  187. {
  188. .part_id = 0x1308,
  189. .acpi_id = "10EC1308",
  190. .dais = {
  191. {
  192. .direction = {true, false},
  193. .dai_name = "rt1308-aif",
  194. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  195. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  196. .init = asoc_sdw_rt_amp_init,
  197. .exit = asoc_sdw_rt_amp_exit,
  198. .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
  199. .controls = generic_spk_controls,
  200. .num_controls = ARRAY_SIZE(generic_spk_controls),
  201. .widgets = generic_spk_widgets,
  202. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  203. },
  204. },
  205. .dai_num = 1,
  206. .ops = &soc_sdw_rt1308_i2s_ops,
  207. },
  208. {
  209. .part_id = 0x1316,
  210. .dais = {
  211. {
  212. .direction = {true, true},
  213. .dai_name = "rt1316-aif",
  214. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  215. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  216. .init = asoc_sdw_rt_amp_init,
  217. .exit = asoc_sdw_rt_amp_exit,
  218. .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
  219. .controls = generic_spk_controls,
  220. .num_controls = ARRAY_SIZE(generic_spk_controls),
  221. .widgets = generic_spk_widgets,
  222. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  223. },
  224. },
  225. .dai_num = 1,
  226. },
  227. {
  228. .part_id = 0x1318,
  229. .dais = {
  230. {
  231. .direction = {true, true},
  232. .dai_name = "rt1318-aif",
  233. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  234. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  235. .init = asoc_sdw_rt_amp_init,
  236. .exit = asoc_sdw_rt_amp_exit,
  237. .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
  238. .controls = generic_spk_controls,
  239. .num_controls = ARRAY_SIZE(generic_spk_controls),
  240. .widgets = generic_spk_widgets,
  241. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  242. },
  243. },
  244. .dai_num = 1,
  245. },
  246. {
  247. .part_id = 0x1320,
  248. .dais = {
  249. {
  250. .direction = {true, false},
  251. .dai_name = "rt1320-aif1",
  252. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  253. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  254. .init = asoc_sdw_rt_amp_init,
  255. .exit = asoc_sdw_rt_amp_exit,
  256. .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
  257. .controls = generic_spk_controls,
  258. .num_controls = ARRAY_SIZE(generic_spk_controls),
  259. .widgets = generic_spk_widgets,
  260. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  261. },
  262. },
  263. .dai_num = 1,
  264. },
  265. {
  266. .part_id = 0x714,
  267. .version_id = 3,
  268. .ignore_internal_dmic = true,
  269. .dais = {
  270. {
  271. .direction = {false, true},
  272. .dai_name = "rt715-sdca-aif2",
  273. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  274. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  275. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  276. },
  277. },
  278. .dai_num = 1,
  279. },
  280. {
  281. .part_id = 0x715,
  282. .version_id = 3,
  283. .ignore_internal_dmic = true,
  284. .dais = {
  285. {
  286. .direction = {false, true},
  287. .dai_name = "rt715-sdca-aif2",
  288. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  289. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  290. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  291. },
  292. },
  293. .dai_num = 1,
  294. },
  295. {
  296. .part_id = 0x714,
  297. .version_id = 2,
  298. .ignore_internal_dmic = true,
  299. .dais = {
  300. {
  301. .direction = {false, true},
  302. .dai_name = "rt715-aif2",
  303. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  304. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  305. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  306. },
  307. },
  308. .dai_num = 1,
  309. },
  310. {
  311. .part_id = 0x715,
  312. .version_id = 2,
  313. .ignore_internal_dmic = true,
  314. .dais = {
  315. {
  316. .direction = {false, true},
  317. .dai_name = "rt715-aif2",
  318. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  319. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  320. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  321. },
  322. },
  323. .dai_num = 1,
  324. },
  325. {
  326. .part_id = 0x722,
  327. .version_id = 3,
  328. .dais = {
  329. {
  330. .direction = {true, true},
  331. .dai_name = "rt722-sdca-aif1",
  332. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  333. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  334. .init = asoc_sdw_rt_sdca_jack_init,
  335. .exit = asoc_sdw_rt_sdca_jack_exit,
  336. .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
  337. .controls = generic_jack_controls,
  338. .num_controls = ARRAY_SIZE(generic_jack_controls),
  339. .widgets = generic_jack_widgets,
  340. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  341. },
  342. {
  343. .direction = {true, false},
  344. .dai_name = "rt722-sdca-aif2",
  345. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  346. /* No feedback capability is provided by rt722-sdca codec driver*/
  347. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  348. .init = asoc_sdw_rt_amp_init,
  349. .exit = asoc_sdw_rt_amp_exit,
  350. .rtd_init = asoc_sdw_rt722_spk_rtd_init,
  351. .controls = generic_spk_controls,
  352. .num_controls = ARRAY_SIZE(generic_spk_controls),
  353. .widgets = generic_spk_widgets,
  354. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  355. .quirk = SOC_SDW_CODEC_SPKR,
  356. .quirk_exclude = true,
  357. },
  358. {
  359. .direction = {false, true},
  360. .dai_name = "rt722-sdca-aif3",
  361. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  362. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  363. .rtd_init = asoc_sdw_rt_dmic_rtd_init,
  364. },
  365. },
  366. .dai_num = 3,
  367. },
  368. {
  369. .part_id = 0x8373,
  370. .dais = {
  371. {
  372. .direction = {true, true},
  373. .dai_name = "max98373-aif1",
  374. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  375. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  376. .init = asoc_sdw_maxim_init,
  377. .rtd_init = asoc_sdw_maxim_spk_rtd_init,
  378. .controls = maxim_controls,
  379. .num_controls = ARRAY_SIZE(maxim_controls),
  380. .widgets = maxim_widgets,
  381. .num_widgets = ARRAY_SIZE(maxim_widgets),
  382. },
  383. },
  384. .dai_num = 1,
  385. },
  386. {
  387. .part_id = 0x8363,
  388. .dais = {
  389. {
  390. .direction = {true, false},
  391. .dai_name = "max98363-aif1",
  392. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  393. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  394. .init = asoc_sdw_maxim_init,
  395. .rtd_init = asoc_sdw_maxim_spk_rtd_init,
  396. .controls = maxim_controls,
  397. .num_controls = ARRAY_SIZE(maxim_controls),
  398. .widgets = maxim_widgets,
  399. .num_widgets = ARRAY_SIZE(maxim_widgets),
  400. },
  401. },
  402. .dai_num = 1,
  403. },
  404. {
  405. .part_id = 0x5682,
  406. .dais = {
  407. {
  408. .direction = {true, true},
  409. .dai_name = "rt5682-sdw",
  410. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  411. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  412. .rtd_init = asoc_sdw_rt5682_rtd_init,
  413. .controls = generic_jack_controls,
  414. .num_controls = ARRAY_SIZE(generic_jack_controls),
  415. .widgets = generic_jack_widgets,
  416. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  417. },
  418. },
  419. .dai_num = 1,
  420. },
  421. {
  422. .part_id = 0x3556,
  423. .dais = {
  424. {
  425. .direction = {true, true},
  426. .dai_name = "cs35l56-sdw1",
  427. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  428. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  429. .init = asoc_sdw_cs_amp_init,
  430. .rtd_init = asoc_sdw_cs_spk_rtd_init,
  431. .controls = generic_spk_controls,
  432. .num_controls = ARRAY_SIZE(generic_spk_controls),
  433. .widgets = generic_spk_widgets,
  434. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  435. },
  436. },
  437. .dai_num = 1,
  438. },
  439. {
  440. .part_id = 0x4242,
  441. .dais = {
  442. {
  443. .direction = {true, true},
  444. .dai_name = "cs42l42-sdw",
  445. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  446. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  447. .rtd_init = asoc_sdw_cs42l42_rtd_init,
  448. .controls = generic_jack_controls,
  449. .num_controls = ARRAY_SIZE(generic_jack_controls),
  450. .widgets = generic_jack_widgets,
  451. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  452. },
  453. },
  454. .dai_num = 1,
  455. },
  456. {
  457. .part_id = 0x4243,
  458. .codec_name = "cs42l43-codec",
  459. .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
  460. .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
  461. .dais = {
  462. {
  463. .direction = {true, false},
  464. .dai_name = "cs42l43-dp5",
  465. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  466. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  467. .rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
  468. .controls = generic_jack_controls,
  469. .num_controls = ARRAY_SIZE(generic_jack_controls),
  470. .widgets = generic_jack_widgets,
  471. .num_widgets = ARRAY_SIZE(generic_jack_widgets),
  472. },
  473. {
  474. .direction = {false, true},
  475. .dai_name = "cs42l43-dp1",
  476. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  477. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  478. .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
  479. .widgets = generic_dmic_widgets,
  480. .num_widgets = ARRAY_SIZE(generic_dmic_widgets),
  481. .quirk = SOC_SDW_CODEC_MIC,
  482. .quirk_exclude = true,
  483. },
  484. {
  485. .direction = {false, true},
  486. .dai_name = "cs42l43-dp2",
  487. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  488. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  489. },
  490. {
  491. .direction = {true, false},
  492. .dai_name = "cs42l43-dp6",
  493. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  494. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
  495. .init = asoc_sdw_cs42l43_spk_init,
  496. .rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
  497. .controls = generic_spk_controls,
  498. .num_controls = ARRAY_SIZE(generic_spk_controls),
  499. .widgets = generic_spk_widgets,
  500. .num_widgets = ARRAY_SIZE(generic_spk_widgets),
  501. .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
  502. },
  503. },
  504. .dai_num = 4,
  505. },
  506. {
  507. .part_id = 0xaaaa, /* generic codec mockup */
  508. .version_id = 0,
  509. .dais = {
  510. {
  511. .direction = {true, true},
  512. .dai_name = "sdw-mockup-aif1",
  513. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  514. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  515. },
  516. },
  517. .dai_num = 1,
  518. },
  519. {
  520. .part_id = 0xaa55, /* headset codec mockup */
  521. .version_id = 0,
  522. .dais = {
  523. {
  524. .direction = {true, true},
  525. .dai_name = "sdw-mockup-aif1",
  526. .dai_type = SOC_SDW_DAI_TYPE_JACK,
  527. .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
  528. },
  529. },
  530. .dai_num = 1,
  531. },
  532. {
  533. .part_id = 0x55aa, /* amplifier mockup */
  534. .version_id = 0,
  535. .dais = {
  536. {
  537. .direction = {true, true},
  538. .dai_name = "sdw-mockup-aif1",
  539. .dai_type = SOC_SDW_DAI_TYPE_AMP,
  540. .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
  541. },
  542. },
  543. .dai_num = 1,
  544. },
  545. {
  546. .part_id = 0x5555,
  547. .version_id = 0,
  548. .dais = {
  549. {
  550. .dai_name = "sdw-mockup-aif1",
  551. .direction = {false, true},
  552. .dai_type = SOC_SDW_DAI_TYPE_MIC,
  553. .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
  554. },
  555. },
  556. .dai_num = 1,
  557. },
  558. };
  559. EXPORT_SYMBOL_NS(codec_info_list, SND_SOC_SDW_UTILS);
  560. int asoc_sdw_get_codec_info_list_count(void)
  561. {
  562. return ARRAY_SIZE(codec_info_list);
  563. };
  564. EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, SND_SOC_SDW_UTILS);
  565. struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
  566. {
  567. unsigned int part_id, sdw_version;
  568. int i;
  569. part_id = SDW_PART_ID(adr);
  570. sdw_version = SDW_VERSION(adr);
  571. for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
  572. /*
  573. * A codec info is for all sdw version with the part id if
  574. * version_id is not specified in the codec info.
  575. */
  576. if (part_id == codec_info_list[i].part_id &&
  577. (!codec_info_list[i].version_id ||
  578. sdw_version == codec_info_list[i].version_id))
  579. return &codec_info_list[i];
  580. return NULL;
  581. }
  582. EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, SND_SOC_SDW_UTILS);
  583. struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
  584. {
  585. int i;
  586. if (!acpi_id[0])
  587. return NULL;
  588. for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
  589. if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
  590. return &codec_info_list[i];
  591. return NULL;
  592. }
  593. EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, SND_SOC_SDW_UTILS);
  594. struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
  595. {
  596. int i, j;
  597. for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
  598. for (j = 0; j < codec_info_list[i].dai_num; j++) {
  599. if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
  600. *dai_index = j;
  601. return &codec_info_list[i];
  602. }
  603. }
  604. }
  605. return NULL;
  606. }
  607. EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, SND_SOC_SDW_UTILS);
  608. int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
  609. {
  610. struct snd_soc_card *card = rtd->card;
  611. struct asoc_sdw_codec_info *codec_info;
  612. struct snd_soc_dai *dai;
  613. int dai_index;
  614. int ret;
  615. int i;
  616. for_each_rtd_codec_dais(rtd, i, dai) {
  617. codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index);
  618. if (!codec_info)
  619. return -EINVAL;
  620. /*
  621. * A codec dai can be connected to different dai links for capture and playback,
  622. * but we only need to call the rtd_init function once.
  623. * The rtd_init for each codec dai is independent. So, the order of rtd_init
  624. * doesn't matter.
  625. */
  626. if (codec_info->dais[dai_index].rtd_init_done)
  627. continue;
  628. /*
  629. * Add card controls and dapm widgets for the first codec dai.
  630. * The controls and widgets will be used for all codec dais.
  631. */
  632. if (i > 0)
  633. goto skip_add_controls_widgets;
  634. if (codec_info->dais[dai_index].controls) {
  635. ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
  636. codec_info->dais[dai_index].num_controls);
  637. if (ret) {
  638. dev_err(card->dev, "%#x controls addition failed: %d\n",
  639. codec_info->part_id, ret);
  640. return ret;
  641. }
  642. }
  643. if (codec_info->dais[dai_index].widgets) {
  644. ret = snd_soc_dapm_new_controls(&card->dapm,
  645. codec_info->dais[dai_index].widgets,
  646. codec_info->dais[dai_index].num_widgets);
  647. if (ret) {
  648. dev_err(card->dev, "%#x widgets addition failed: %d\n",
  649. codec_info->part_id, ret);
  650. return ret;
  651. }
  652. }
  653. skip_add_controls_widgets:
  654. if (codec_info->dais[dai_index].rtd_init) {
  655. ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
  656. if (ret)
  657. return ret;
  658. }
  659. codec_info->dais[dai_index].rtd_init_done = true;
  660. }
  661. return 0;
  662. }
  663. EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, SND_SOC_SDW_UTILS);
  664. /* these wrappers are only needed to avoid typecast compilation errors */
  665. int asoc_sdw_startup(struct snd_pcm_substream *substream)
  666. {
  667. return sdw_startup_stream(substream);
  668. }
  669. EXPORT_SYMBOL_NS(asoc_sdw_startup, SND_SOC_SDW_UTILS);
  670. int asoc_sdw_prepare(struct snd_pcm_substream *substream)
  671. {
  672. struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  673. struct sdw_stream_runtime *sdw_stream;
  674. struct snd_soc_dai *dai;
  675. /* Find stream from first CPU DAI */
  676. dai = snd_soc_rtd_to_cpu(rtd, 0);
  677. sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
  678. if (IS_ERR(sdw_stream)) {
  679. dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
  680. return PTR_ERR(sdw_stream);
  681. }
  682. return sdw_prepare_stream(sdw_stream);
  683. }
  684. EXPORT_SYMBOL_NS(asoc_sdw_prepare, SND_SOC_SDW_UTILS);
  685. int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
  686. {
  687. struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  688. struct sdw_stream_runtime *sdw_stream;
  689. struct snd_soc_dai *dai;
  690. int ret;
  691. /* Find stream from first CPU DAI */
  692. dai = snd_soc_rtd_to_cpu(rtd, 0);
  693. sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
  694. if (IS_ERR(sdw_stream)) {
  695. dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
  696. return PTR_ERR(sdw_stream);
  697. }
  698. switch (cmd) {
  699. case SNDRV_PCM_TRIGGER_START:
  700. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  701. case SNDRV_PCM_TRIGGER_RESUME:
  702. ret = sdw_enable_stream(sdw_stream);
  703. break;
  704. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  705. case SNDRV_PCM_TRIGGER_SUSPEND:
  706. case SNDRV_PCM_TRIGGER_STOP:
  707. ret = sdw_disable_stream(sdw_stream);
  708. break;
  709. default:
  710. ret = -EINVAL;
  711. break;
  712. }
  713. if (ret)
  714. dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
  715. return ret;
  716. }
  717. EXPORT_SYMBOL_NS(asoc_sdw_trigger, SND_SOC_SDW_UTILS);
  718. int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
  719. struct snd_pcm_hw_params *params)
  720. {
  721. struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  722. struct snd_soc_dai_link_ch_map *ch_maps;
  723. int ch = params_channels(params);
  724. unsigned int ch_mask;
  725. int num_codecs;
  726. int step;
  727. int i;
  728. if (!rtd->dai_link->ch_maps)
  729. return 0;
  730. /* Identical data will be sent to all codecs in playback */
  731. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  732. ch_mask = GENMASK(ch - 1, 0);
  733. step = 0;
  734. } else {
  735. num_codecs = rtd->dai_link->num_codecs;
  736. if (ch < num_codecs || ch % num_codecs != 0) {
  737. dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
  738. ch, num_codecs);
  739. return -EINVAL;
  740. }
  741. ch_mask = GENMASK(ch / num_codecs - 1, 0);
  742. step = hweight_long(ch_mask);
  743. }
  744. /*
  745. * The captured data will be combined from each cpu DAI if the dai
  746. * link has more than one codec DAIs. Set codec channel mask and
  747. * ASoC will set the corresponding channel numbers for each cpu dai.
  748. */
  749. for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
  750. ch_maps->ch_mask = ch_mask << (i * step);
  751. return 0;
  752. }
  753. EXPORT_SYMBOL_NS(asoc_sdw_hw_params, SND_SOC_SDW_UTILS);
  754. int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
  755. {
  756. struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  757. struct sdw_stream_runtime *sdw_stream;
  758. struct snd_soc_dai *dai;
  759. /* Find stream from first CPU DAI */
  760. dai = snd_soc_rtd_to_cpu(rtd, 0);
  761. sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
  762. if (IS_ERR(sdw_stream)) {
  763. dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
  764. return PTR_ERR(sdw_stream);
  765. }
  766. return sdw_deprepare_stream(sdw_stream);
  767. }
  768. EXPORT_SYMBOL_NS(asoc_sdw_hw_free, SND_SOC_SDW_UTILS);
  769. void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
  770. {
  771. sdw_shutdown_stream(substream);
  772. }
  773. EXPORT_SYMBOL_NS(asoc_sdw_shutdown, SND_SOC_SDW_UTILS);
  774. static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
  775. unsigned int sdw_version,
  776. unsigned int mfg_id,
  777. unsigned int part_id,
  778. unsigned int class_id,
  779. int index_in_link)
  780. {
  781. int i;
  782. for (i = 0; i < adr_link->num_adr; i++) {
  783. unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
  784. u64 adr;
  785. /* skip itself */
  786. if (i == index_in_link)
  787. continue;
  788. adr = adr_link->adr_d[i].adr;
  789. sdw1_version = SDW_VERSION(adr);
  790. mfg1_id = SDW_MFG_ID(adr);
  791. part1_id = SDW_PART_ID(adr);
  792. class1_id = SDW_CLASS_ID(adr);
  793. if (sdw_version == sdw1_version &&
  794. mfg_id == mfg1_id &&
  795. part_id == part1_id &&
  796. class_id == class1_id)
  797. return false;
  798. }
  799. return true;
  800. }
  801. const char *asoc_sdw_get_codec_name(struct device *dev,
  802. const struct asoc_sdw_codec_info *codec_info,
  803. const struct snd_soc_acpi_link_adr *adr_link,
  804. int adr_index)
  805. {
  806. u64 adr = adr_link->adr_d[adr_index].adr;
  807. unsigned int sdw_version = SDW_VERSION(adr);
  808. unsigned int link_id = SDW_DISCO_LINK_ID(adr);
  809. unsigned int unique_id = SDW_UNIQUE_ID(adr);
  810. unsigned int mfg_id = SDW_MFG_ID(adr);
  811. unsigned int part_id = SDW_PART_ID(adr);
  812. unsigned int class_id = SDW_CLASS_ID(adr);
  813. if (codec_info->codec_name)
  814. return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
  815. else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
  816. class_id, adr_index))
  817. return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
  818. link_id, mfg_id, part_id, class_id);
  819. else
  820. return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
  821. link_id, mfg_id, part_id, class_id, unique_id);
  822. return NULL;
  823. }
  824. EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, SND_SOC_SDW_UTILS);
  825. /* helper to get the link that the codec DAI is used */
  826. struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
  827. const char *dai_name)
  828. {
  829. struct snd_soc_dai_link *dai_link;
  830. int i;
  831. int j;
  832. for_each_card_prelinks(card, i, dai_link) {
  833. for (j = 0; j < dai_link->num_codecs; j++) {
  834. /* Check each codec in a link */
  835. if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
  836. return dai_link;
  837. }
  838. }
  839. return NULL;
  840. }
  841. EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, SND_SOC_SDW_UTILS);
  842. void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
  843. {
  844. struct snd_soc_dai_link *dai_link;
  845. struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
  846. int ret;
  847. int i, j;
  848. for (i = 0; i < ctx->codec_info_list_count; i++) {
  849. for (j = 0; j < codec_info_list[i].dai_num; j++) {
  850. codec_info_list[i].dais[j].rtd_init_done = false;
  851. /* Check each dai in codec_info_lis to see if it is used in the link */
  852. if (!codec_info_list[i].dais[j].exit)
  853. continue;
  854. /*
  855. * We don't need to call .exit function if there is no matched
  856. * dai link found.
  857. */
  858. dai_link = asoc_sdw_mc_find_codec_dai_used(card,
  859. codec_info_list[i].dais[j].dai_name);
  860. if (dai_link) {
  861. /* Do the .exit function if the codec dai is used in the link */
  862. ret = codec_info_list[i].dais[j].exit(card, dai_link);
  863. if (ret)
  864. dev_warn(card->dev,
  865. "codec exit failed %d\n",
  866. ret);
  867. break;
  868. }
  869. }
  870. }
  871. }
  872. EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, SND_SOC_SDW_UTILS);
  873. int asoc_sdw_card_late_probe(struct snd_soc_card *card)
  874. {
  875. int ret = 0;
  876. int i;
  877. for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
  878. if (codec_info_list[i].codec_card_late_probe) {
  879. ret = codec_info_list[i].codec_card_late_probe(card);
  880. if (ret < 0)
  881. return ret;
  882. }
  883. }
  884. return ret;
  885. }
  886. EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, SND_SOC_SDW_UTILS);
  887. void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
  888. int *be_id, char *name, int playback, int capture,
  889. struct snd_soc_dai_link_component *cpus, int cpus_num,
  890. struct snd_soc_dai_link_component *platform_component,
  891. int num_platforms, struct snd_soc_dai_link_component *codecs,
  892. int codecs_num, int (*init)(struct snd_soc_pcm_runtime *rtd),
  893. const struct snd_soc_ops *ops)
  894. {
  895. dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
  896. dai_links->id = (*be_id)++;
  897. dai_links->name = name;
  898. dai_links->platforms = platform_component;
  899. dai_links->num_platforms = num_platforms;
  900. dai_links->no_pcm = 1;
  901. dai_links->cpus = cpus;
  902. dai_links->num_cpus = cpus_num;
  903. dai_links->codecs = codecs;
  904. dai_links->num_codecs = codecs_num;
  905. dai_links->playback_only = playback && !capture;
  906. dai_links->capture_only = !playback && capture;
  907. dai_links->init = init;
  908. dai_links->ops = ops;
  909. }
  910. EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, SND_SOC_SDW_UTILS);
  911. int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
  912. int *be_id, char *name, int playback, int capture,
  913. const char *cpu_dai_name, const char *platform_comp_name,
  914. int num_platforms, const char *codec_name,
  915. const char *codec_dai_name,
  916. int (*init)(struct snd_soc_pcm_runtime *rtd),
  917. const struct snd_soc_ops *ops)
  918. {
  919. struct snd_soc_dai_link_component *dlc;
  920. /* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
  921. dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
  922. if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
  923. return -ENOMEM;
  924. dlc[0].dai_name = cpu_dai_name;
  925. dlc[1].name = platform_comp_name;
  926. dlc[2].name = codec_name;
  927. dlc[2].dai_name = codec_dai_name;
  928. asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
  929. &dlc[0], 1, &dlc[1], num_platforms,
  930. &dlc[2], 1, init, ops);
  931. return 0;
  932. }
  933. EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, SND_SOC_SDW_UTILS);
  934. int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends)
  935. {
  936. struct device *dev = card->dev;
  937. struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
  938. struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
  939. const struct snd_soc_acpi_link_adr *adr_link;
  940. int i;
  941. for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
  942. *num_devs += adr_link->num_adr;
  943. for (i = 0; i < adr_link->num_adr; i++)
  944. *num_ends += adr_link->adr_d[i].num_endpoints;
  945. }
  946. dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
  947. return 0;
  948. }
  949. EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, SND_SOC_SDW_UTILS);
  950. struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
  951. const struct snd_soc_acpi_endpoint *new)
  952. {
  953. while (dailinks->initialised) {
  954. if (new->aggregated && dailinks->group_id == new->group_id)
  955. return dailinks;
  956. dailinks++;
  957. }
  958. INIT_LIST_HEAD(&dailinks->endpoints);
  959. dailinks->group_id = new->group_id;
  960. dailinks->initialised = true;
  961. return dailinks;
  962. }
  963. EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, SND_SOC_SDW_UTILS);
  964. int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
  965. struct asoc_sdw_dailink *soc_dais,
  966. struct asoc_sdw_endpoint *soc_ends,
  967. int *num_devs)
  968. {
  969. struct device *dev = card->dev;
  970. struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
  971. struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
  972. struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
  973. const struct snd_soc_acpi_link_adr *adr_link;
  974. struct asoc_sdw_endpoint *soc_end = soc_ends;
  975. int num_dais = 0;
  976. int i, j;
  977. int ret;
  978. for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
  979. int num_link_dailinks = 0;
  980. if (!is_power_of_2(adr_link->mask)) {
  981. dev_err(dev, "link with multiple mask bits: 0x%x\n",
  982. adr_link->mask);
  983. return -EINVAL;
  984. }
  985. for (i = 0; i < adr_link->num_adr; i++) {
  986. const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
  987. struct asoc_sdw_codec_info *codec_info;
  988. const char *codec_name;
  989. if (!adr_dev->name_prefix) {
  990. dev_err(dev, "codec 0x%llx does not have a name prefix\n",
  991. adr_dev->adr);
  992. return -EINVAL;
  993. }
  994. codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
  995. if (!codec_info)
  996. return -EINVAL;
  997. ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
  998. codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i);
  999. if (!codec_name)
  1000. return -ENOMEM;
  1001. dev_dbg(dev, "Adding prefix %s for %s\n",
  1002. adr_dev->name_prefix, codec_name);
  1003. soc_end->name_prefix = adr_dev->name_prefix;
  1004. if (codec_info->count_sidecar && codec_info->add_sidecar) {
  1005. ret = codec_info->count_sidecar(card, &num_dais, num_devs);
  1006. if (ret)
  1007. return ret;
  1008. soc_end->include_sidecar = true;
  1009. }
  1010. for (j = 0; j < adr_dev->num_endpoints; j++) {
  1011. const struct snd_soc_acpi_endpoint *adr_end;
  1012. const struct asoc_sdw_dai_info *dai_info;
  1013. struct asoc_sdw_dailink *soc_dai;
  1014. int stream;
  1015. adr_end = &adr_dev->endpoints[j];
  1016. dai_info = &codec_info->dais[adr_end->num];
  1017. soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
  1018. if (dai_info->quirk &&
  1019. !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
  1020. continue;
  1021. dev_dbg(dev,
  1022. "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
  1023. ffs(adr_link->mask) - 1, adr_dev->adr,
  1024. adr_end->num, dai_info->dai_type,
  1025. dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
  1026. dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
  1027. adr_end->aggregated ? "group" : "solo",
  1028. adr_end->group_id);
  1029. if (adr_end->num >= codec_info->dai_num) {
  1030. dev_err(dev,
  1031. "%d is too many endpoints for codec: 0x%x\n",
  1032. adr_end->num, codec_info->part_id);
  1033. return -EINVAL;
  1034. }
  1035. for_each_pcm_streams(stream) {
  1036. if (dai_info->direction[stream] &&
  1037. dai_info->dailink[stream] < 0) {
  1038. dev_err(dev,
  1039. "Invalid dailink id %d for codec: 0x%x\n",
  1040. dai_info->dailink[stream],
  1041. codec_info->part_id);
  1042. return -EINVAL;
  1043. }
  1044. if (dai_info->direction[stream]) {
  1045. num_dais += !soc_dai->num_devs[stream];
  1046. soc_dai->num_devs[stream]++;
  1047. soc_dai->link_mask[stream] |= adr_link->mask;
  1048. }
  1049. }
  1050. num_link_dailinks += !!list_empty(&soc_dai->endpoints);
  1051. list_add_tail(&soc_end->list, &soc_dai->endpoints);
  1052. soc_end->link_mask = adr_link->mask;
  1053. soc_end->codec_name = codec_name;
  1054. soc_end->codec_info = codec_info;
  1055. soc_end->dai_info = dai_info;
  1056. soc_end++;
  1057. }
  1058. }
  1059. ctx->append_dai_type |= (num_link_dailinks > 1);
  1060. }
  1061. return num_dais;
  1062. }
  1063. EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, SND_SOC_SDW_UTILS);
  1064. MODULE_LICENSE("GPL");
  1065. MODULE_DESCRIPTION("SoundWire ASoC helpers");