wcd9xxx-slimslave.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2012-2018, 2020-2021, The Linux Foundation. All rights reserved.
  3. */
  4. #include <linux/slab.h>
  5. #include <linux/mutex.h>
  6. #include <asoc/wcd9xxx-slimslave.h>
  7. #include <asoc/wcd9xxx_registers.h>
  8. struct wcd9xxx_slim_sch {
  9. u16 rx_port_ch_reg_base;
  10. u16 port_tx_cfg_reg_base;
  11. u16 port_rx_cfg_reg_base;
  12. };
  13. static struct wcd9xxx_slim_sch sh_ch;
  14. static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
  15. u8 wcd9xxx_pgd_la, u32 cnt,
  16. struct wcd9xxx_ch *channels, u32 path);
  17. static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
  18. u32 cnt, struct wcd9xxx_ch *channels);
  19. static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
  20. {
  21. if (wcd9xxx->codec_type->slim_slave_type ==
  22. WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) {
  23. sh_ch.rx_port_ch_reg_base = 0x180;
  24. sh_ch.port_rx_cfg_reg_base = 0x040;
  25. sh_ch.port_tx_cfg_reg_base = 0x040;
  26. } else {
  27. sh_ch.rx_port_ch_reg_base =
  28. 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
  29. sh_ch.port_rx_cfg_reg_base =
  30. 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
  31. sh_ch.port_tx_cfg_reg_base = 0x050;
  32. }
  33. return 0;
  34. }
  35. /**
  36. * wcd9xxx_init_slimslave
  37. *
  38. * @wcd9xxx: pointer to wcd9xxx struct
  39. * @wcd9xxx_pgd_la: pgd_la value
  40. * @tx_num: tx number
  41. * @rx_num: rx number
  42. * @tx_slot: pointer to tx slot
  43. * @rx_slot: pointer to rx slot
  44. *
  45. * Returns 0 on success, appropriate error code otherwise
  46. */
  47. int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
  48. unsigned int tx_num, unsigned int *tx_slot,
  49. unsigned int rx_num, unsigned int *rx_slot)
  50. {
  51. int ret = 0;
  52. int i;
  53. ret = wcd9xxx_configure_ports(wcd9xxx);
  54. if (ret) {
  55. pr_err("%s: Failed to configure register address offset\n",
  56. __func__);
  57. goto err;
  58. }
  59. if (!rx_num || rx_num > wcd9xxx->num_rx_port) {
  60. pr_err("%s: invalid rx num %d\n", __func__, rx_num);
  61. return -EINVAL;
  62. }
  63. if (wcd9xxx->rx_chs) {
  64. wcd9xxx->num_rx_port = rx_num;
  65. for (i = 0; i < rx_num; i++) {
  66. wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
  67. INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
  68. }
  69. ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
  70. wcd9xxx->num_rx_port,
  71. wcd9xxx->rx_chs,
  72. SLIM_SINK);
  73. if (ret) {
  74. pr_err("%s: Failed to alloc %d rx slimbus channels\n",
  75. __func__, wcd9xxx->num_rx_port);
  76. kfree(wcd9xxx->rx_chs);
  77. wcd9xxx->rx_chs = NULL;
  78. wcd9xxx->num_rx_port = 0;
  79. }
  80. } else {
  81. pr_err("Not able to allocate memory for %d slimbus rx ports\n",
  82. wcd9xxx->num_rx_port);
  83. }
  84. if (!tx_num || tx_num > wcd9xxx->num_tx_port) {
  85. pr_err("%s: invalid tx num %d\n", __func__, tx_num);
  86. return -EINVAL;
  87. }
  88. if (wcd9xxx->tx_chs) {
  89. wcd9xxx->num_tx_port = tx_num;
  90. for (i = 0; i < tx_num; i++) {
  91. wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
  92. INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
  93. }
  94. ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
  95. wcd9xxx->num_tx_port,
  96. wcd9xxx->tx_chs,
  97. SLIM_SRC);
  98. if (ret) {
  99. pr_err("%s: Failed to alloc %d tx slimbus channels\n",
  100. __func__, wcd9xxx->num_tx_port);
  101. kfree(wcd9xxx->tx_chs);
  102. wcd9xxx->tx_chs = NULL;
  103. wcd9xxx->num_tx_port = 0;
  104. }
  105. } else {
  106. pr_err("Not able to allocate memory for %d slimbus tx ports\n",
  107. wcd9xxx->num_tx_port);
  108. }
  109. return 0;
  110. err:
  111. return ret;
  112. }
  113. EXPORT_SYMBOL(wcd9xxx_init_slimslave);
  114. int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
  115. {
  116. if (wcd9xxx->num_rx_port) {
  117. wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
  118. wcd9xxx->num_rx_port,
  119. wcd9xxx->rx_chs);
  120. wcd9xxx->num_rx_port = 0;
  121. }
  122. if (wcd9xxx->num_tx_port) {
  123. wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
  124. wcd9xxx->num_tx_port,
  125. wcd9xxx->tx_chs);
  126. wcd9xxx->num_tx_port = 0;
  127. }
  128. return 0;
  129. }
  130. static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
  131. u8 wcd9xxx_pgd_la, u32 cnt,
  132. struct wcd9xxx_ch *channels, u32 path)
  133. {
  134. int ret = 0;
  135. u32 ch_idx;
  136. /* The slimbus channel allocation seem take longer time
  137. * so do the allocation up front to avoid delay in start of
  138. * playback
  139. */
  140. pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
  141. for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
  142. ret = slim_get_slaveport(wcd9xxx_pgd_la,
  143. channels[ch_idx].port,
  144. &channels[ch_idx].sph, path);
  145. pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
  146. "channels[%d].sph[%d] path[%d]\n",
  147. __func__, wcd9xxx_pgd_la, ch_idx,
  148. channels[ch_idx].port,
  149. ch_idx, channels[ch_idx].sph, path);
  150. if (ret < 0) {
  151. pr_err("%s: slave port failure id[%d] ret[%d]\n",
  152. __func__, channels[ch_idx].ch_num, ret);
  153. goto err;
  154. }
  155. ret = slim_query_ch(wcd9xxx->slim,
  156. channels[ch_idx].ch_num,
  157. &channels[ch_idx].ch_h);
  158. if (ret < 0) {
  159. pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
  160. __func__, channels[ch_idx].ch_num, ret);
  161. goto err;
  162. }
  163. }
  164. err:
  165. return ret;
  166. }
  167. static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
  168. u32 cnt, struct wcd9xxx_ch *channels)
  169. {
  170. int idx = 0;
  171. int ret = 0;
  172. /* slim_dealloc_ch */
  173. for (idx = 0; idx < cnt; idx++) {
  174. ret = slim_dealloc_ch(slim, channels[idx].ch_h);
  175. if (ret < 0) {
  176. pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
  177. __func__, ret, channels[idx].ch_h);
  178. }
  179. }
  180. return ret;
  181. }
  182. /* Enable slimbus slave device for RX path */
  183. int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
  184. struct list_head *wcd9xxx_ch_list,
  185. unsigned int rate, unsigned int bit_width,
  186. u16 *grph)
  187. {
  188. u8 ch_cnt = 0;
  189. u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
  190. u8 payload = 0;
  191. u16 codec_port = 0;
  192. int ret;
  193. struct slim_ch prop;
  194. struct wcd9xxx_ch *rx;
  195. int size = ARRAY_SIZE(ch_h);
  196. /* Configure slave interface device */
  197. list_for_each_entry(rx, wcd9xxx_ch_list, list) {
  198. payload |= 1 << rx->shift;
  199. if (ch_cnt < size) {
  200. ch_h[ch_cnt] = rx->ch_h;
  201. ch_cnt++;
  202. pr_debug("list ch->ch_h %d ch->sph %d\n",
  203. rx->ch_h, rx->sph);
  204. } else {
  205. pr_err("%s: allocated channel number %u is out of max rangae %d\n",
  206. __func__, ch_cnt,
  207. size);
  208. ret = EINVAL;
  209. goto err;
  210. }
  211. }
  212. pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
  213. __func__, ch_cnt, rate, WATER_MARK_VAL);
  214. /* slim_define_ch api */
  215. prop.prot = SLIM_AUTO_ISO;
  216. if ((rate == 44100) || (rate == 88200) || (rate == 176400) ||
  217. (rate == 352800)) {
  218. prop.baser = SLIM_RATE_11025HZ;
  219. prop.ratem = (rate/11025);
  220. } else {
  221. prop.baser = SLIM_RATE_4000HZ;
  222. prop.ratem = (rate/4000);
  223. }
  224. prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
  225. prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
  226. prop.sampleszbits = bit_width;
  227. pr_debug("Before slim_define_ch:\n"
  228. "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
  229. ch_cnt, ch_h[0], ch_h[1], *grph);
  230. ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
  231. true, grph);
  232. if (ret < 0) {
  233. pr_err("%s: slim_define_ch failed ret[%d]\n",
  234. __func__, ret);
  235. goto err;
  236. }
  237. list_for_each_entry(rx, wcd9xxx_ch_list, list) {
  238. codec_port = rx->port;
  239. pr_debug("%s: codec_port %d rx 0x%p, payload %d\n"
  240. "sh_ch.rx_port_ch_reg_base0 0x%x\n"
  241. "sh_ch.port_rx_cfg_reg_base 0x%x\n",
  242. __func__, codec_port, rx, payload,
  243. sh_ch.rx_port_ch_reg_base,
  244. sh_ch.port_rx_cfg_reg_base);
  245. /* look for the valid port range and chose the
  246. * payload accordingly
  247. */
  248. /* write to interface device */
  249. ret = wcd9xxx_interface_reg_write(wcd9xxx,
  250. SB_PGD_RX_PORT_MULTI_CHANNEL_0(
  251. sh_ch.rx_port_ch_reg_base, codec_port),
  252. payload);
  253. if (ret < 0) {
  254. pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
  255. __func__,
  256. SB_PGD_RX_PORT_MULTI_CHANNEL_0(
  257. sh_ch.rx_port_ch_reg_base, codec_port),
  258. payload, ret);
  259. goto err;
  260. }
  261. /* configure the slave port for water mark and enable*/
  262. ret = wcd9xxx_interface_reg_write(wcd9xxx,
  263. SB_PGD_PORT_CFG_BYTE_ADDR(
  264. sh_ch.port_rx_cfg_reg_base, codec_port),
  265. WATER_MARK_VAL);
  266. if (ret < 0) {
  267. pr_err("%s:watermark set failure for port[%d] ret[%d]",
  268. __func__, codec_port, ret);
  269. }
  270. ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
  271. if (ret < 0) {
  272. pr_err("%s: slim_connect_sink failed ret[%d]\n",
  273. __func__, ret);
  274. goto err_close_slim_sch;
  275. }
  276. }
  277. /* slim_control_ch */
  278. ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
  279. true);
  280. if (ret < 0) {
  281. pr_err("%s: slim_control_ch failed ret[%d]\n",
  282. __func__, ret);
  283. goto err_close_slim_sch;
  284. }
  285. return 0;
  286. err_close_slim_sch:
  287. /* release all acquired handles */
  288. wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
  289. err:
  290. return ret;
  291. }
  292. EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx);
  293. /* Enable slimbus slave device for RX path */
  294. int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
  295. struct list_head *wcd9xxx_ch_list,
  296. unsigned int rate, unsigned int bit_width,
  297. u16 *grph)
  298. {
  299. u16 ch_cnt = 0;
  300. u16 payload = 0;
  301. u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
  302. u16 codec_port;
  303. int ret = 0;
  304. struct wcd9xxx_ch *tx;
  305. int size = ARRAY_SIZE(ch_h);
  306. struct slim_ch prop;
  307. list_for_each_entry(tx, wcd9xxx_ch_list, list) {
  308. payload |= 1 << tx->shift;
  309. if (ch_cnt < size) {
  310. ch_h[ch_cnt] = tx->ch_h;
  311. ch_cnt++;
  312. } else {
  313. pr_err("%s: allocated channel number %u is out of max rangae %d\n",
  314. __func__, ch_cnt,
  315. size);
  316. ret = EINVAL;
  317. goto err;
  318. }
  319. }
  320. /* slim_define_ch api */
  321. prop.prot = SLIM_AUTO_ISO;
  322. prop.baser = SLIM_RATE_4000HZ;
  323. prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
  324. prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
  325. prop.ratem = (rate/4000);
  326. prop.sampleszbits = bit_width;
  327. ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
  328. true, grph);
  329. if (ret < 0) {
  330. pr_err("%s: slim_define_ch failed ret[%d]\n",
  331. __func__, ret);
  332. goto err;
  333. }
  334. pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt,
  335. rate, bit_width);
  336. list_for_each_entry(tx, wcd9xxx_ch_list, list) {
  337. codec_port = tx->port;
  338. pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n",
  339. __func__, codec_port, tx, payload);
  340. /* write to interface device */
  341. ret = wcd9xxx_interface_reg_write(wcd9xxx,
  342. SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
  343. payload & 0x00FF);
  344. if (ret < 0) {
  345. pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
  346. __func__,
  347. SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
  348. payload, ret);
  349. goto err;
  350. }
  351. /* ports 8,9 */
  352. ret = wcd9xxx_interface_reg_write(wcd9xxx,
  353. SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
  354. (payload & 0xFF00)>>8);
  355. if (ret < 0) {
  356. pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
  357. __func__,
  358. SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
  359. payload, ret);
  360. goto err;
  361. }
  362. /* configure the slave port for water mark and enable*/
  363. ret = wcd9xxx_interface_reg_write(wcd9xxx,
  364. SB_PGD_PORT_CFG_BYTE_ADDR(
  365. sh_ch.port_tx_cfg_reg_base, codec_port),
  366. WATER_MARK_VAL);
  367. if (ret < 0) {
  368. pr_err("%s:watermark set failure for port[%d] ret[%d]",
  369. __func__, codec_port, ret);
  370. }
  371. ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
  372. if (ret < 0) {
  373. pr_err("%s: slim_connect_src failed ret[%d]\n",
  374. __func__, ret);
  375. goto err;
  376. }
  377. }
  378. /* slim_control_ch */
  379. ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
  380. true);
  381. if (ret < 0) {
  382. pr_err("%s: slim_control_ch failed ret[%d]\n",
  383. __func__, ret);
  384. goto err;
  385. }
  386. return 0;
  387. err:
  388. /* release all acquired handles */
  389. wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
  390. return ret;
  391. }
  392. EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx);
  393. int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
  394. struct list_head *wcd9xxx_ch_list, u16 grph)
  395. {
  396. u32 sph[SLIM_MAX_RX_PORTS] = {0};
  397. int ch_cnt = 0;
  398. int ret = 0;
  399. struct wcd9xxx_ch *rx;
  400. list_for_each_entry(rx, wcd9xxx_ch_list, list)
  401. sph[ch_cnt++] = rx->sph;
  402. pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
  403. sph[0], sph[1]);
  404. /* slim_control_ch (REMOVE) */
  405. pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
  406. ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
  407. if (ret < 0) {
  408. pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
  409. goto err;
  410. }
  411. err:
  412. return ret;
  413. }
  414. EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx);
  415. int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
  416. struct list_head *wcd9xxx_ch_list,
  417. u16 grph)
  418. {
  419. u32 sph[SLIM_MAX_TX_PORTS] = {0};
  420. int ret = 0;
  421. int ch_cnt = 0;
  422. struct wcd9xxx_ch *tx;
  423. pr_debug("%s\n", __func__);
  424. list_for_each_entry(tx, wcd9xxx_ch_list, list)
  425. sph[ch_cnt++] = tx->sph;
  426. pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
  427. __func__, ch_cnt, sph[0], sph[1]);
  428. /* slim_control_ch (REMOVE) */
  429. ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
  430. if (ret < 0) {
  431. pr_err("%s: slim_control_ch failed ret[%d]\n",
  432. __func__, ret);
  433. goto err;
  434. }
  435. err:
  436. return ret;
  437. }
  438. EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx);
  439. int wcd9xxx_get_slave_port(unsigned int ch_num)
  440. {
  441. int ret = 0;
  442. ret = (ch_num - BASE_CH_NUM);
  443. pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
  444. if (ret < 0) {
  445. pr_err("%s: Error:- Invalid slave port found = %d\n",
  446. __func__, ret);
  447. return -EINVAL;
  448. }
  449. return ret;
  450. }
  451. EXPORT_SYMBOL(wcd9xxx_get_slave_port);
  452. int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
  453. struct list_head *wcd9xxx_ch_list, u16 grph)
  454. {
  455. u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
  456. int ch_cnt = 0;
  457. int ret = 0;
  458. struct wcd9xxx_ch *slim_ch;
  459. list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
  460. sph[ch_cnt++] = slim_ch->sph;
  461. /* slim_disconnect_port */
  462. ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
  463. if (ret < 0) {
  464. pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
  465. __func__, ret);
  466. }
  467. return ret;
  468. }
  469. EXPORT_SYMBOL(wcd9xxx_disconnect_port);
  470. /* This function is called with mutex acquired */
  471. int wcd9xxx_rx_vport_validation(u32 port_id,
  472. struct list_head *codec_dai_list)
  473. {
  474. struct wcd9xxx_ch *ch;
  475. int ret = 0;
  476. pr_debug("%s: port_id %u\n", __func__, port_id);
  477. list_for_each_entry(ch,
  478. codec_dai_list, list) {
  479. pr_debug("%s: ch->port %u\n", __func__, ch->port);
  480. if (ch->port == port_id) {
  481. ret = -EINVAL;
  482. break;
  483. }
  484. }
  485. return ret;
  486. }
  487. EXPORT_SYMBOL(wcd9xxx_rx_vport_validation);
  488. /* This function is called with mutex acquired */
  489. int wcd9xxx_tx_vport_validation(u32 table, u32 port_id,
  490. struct wcd9xxx_codec_dai_data *codec_dai,
  491. u32 num_codec_dais)
  492. {
  493. struct wcd9xxx_ch *ch;
  494. int ret = 0;
  495. u32 index;
  496. unsigned long vtable = table;
  497. u32 size = sizeof(table) * BITS_PER_BYTE;
  498. pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__,
  499. vtable, port_id, size);
  500. for_each_set_bit(index, &vtable, size) {
  501. if (index < num_codec_dais) {
  502. list_for_each_entry(ch,
  503. &codec_dai[index].wcd9xxx_ch_list,
  504. list) {
  505. pr_debug("%s: index %u ch->port %u vtable 0x%lx\n",
  506. __func__, index, ch->port,
  507. vtable);
  508. if (ch->port == port_id) {
  509. pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
  510. __func__, port_id + 1,
  511. (index + 1)/2);
  512. ret = -EINVAL;
  513. break;
  514. }
  515. }
  516. } else {
  517. pr_err("%s: Invalid index %d of codec dai",
  518. __func__, index);
  519. ret = -EINVAL;
  520. }
  521. if (ret)
  522. break;
  523. }
  524. return ret;
  525. }
  526. EXPORT_SYMBOL(wcd9xxx_tx_vport_validation);