drv_pl041.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. * File : drv_pl041.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2017, RT-Thread Development Team
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Change Logs:
  21. * Date Author Notes
  22. * 2018-05-25 RT-Thread the first version
  23. */
  24. #include <rtthread.h>
  25. #include <rthw.h>
  26. #include "drv_pl041.h"
  27. #include "drv_ac97.h"
  28. #include "realview.h"
  29. #define DBG_ENABLE
  30. #define DBG_SECTION_NAME "[PL041]"
  31. // #define DBG_LEVEL DBG_LOG
  32. // #define DBG_LEVEL DBG_INFO
  33. #define DBG_LEVEL DBG_WARNING
  34. // #define DBG_LEVEL DBG_ERROR
  35. #define DBG_COLOR
  36. #include <rtdbg.h>
  37. #define FRAME_PERIOD_US (50)
  38. #define PL041_CHANNLE_NUM (4)
  39. #define PL041_READ(_a) (*(volatile rt_uint32_t *)(_a))
  40. #define PL041_WRITE(_a, _v) (*(volatile rt_uint32_t *)(_a) = (_v))
  41. struct pl041_irq_def
  42. {
  43. pl041_irq_fun_t fun;
  44. void *user_data;
  45. };
  46. static struct pl041_irq_def irq_tbl[PL041_CHANNLE_NUM];
  47. static void aaci_pl041_delay(rt_uint32_t us)
  48. {
  49. volatile int i;
  50. for (i = us * 10; i != 0; i--);
  51. }
  52. static void aaci_ac97_select_codec(void)
  53. {
  54. rt_uint32_t v, maincr;
  55. maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
  56. AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
  57. v = PL041_READ(&PL041->slfr);
  58. if (v & AACI_SLFR_2RXV)
  59. {
  60. PL041_READ(&PL041->sl2rx);
  61. }
  62. if (v & AACI_SLFR_1RXV)
  63. {
  64. PL041_READ(&PL041->sl1rx);
  65. }
  66. if (maincr != PL041_READ(&PL041->maincr))
  67. {
  68. PL041_WRITE(&PL041->maincr, maincr);
  69. aaci_pl041_delay(1);
  70. }
  71. }
  72. void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val)
  73. {
  74. rt_uint32_t v, timeout;
  75. aaci_ac97_select_codec();
  76. PL041_WRITE(&PL041->sl2tx, val << 4);
  77. PL041_WRITE(&PL041->sl1tx, reg << 12);
  78. aaci_pl041_delay(FRAME_PERIOD_US);
  79. timeout = FRAME_PERIOD_US * 8;
  80. do
  81. {
  82. aaci_pl041_delay(1);
  83. v = PL041_READ(&PL041->slfr);
  84. }
  85. while ((v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB)) && --timeout);
  86. if (v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB))
  87. {
  88. dbg_log(DBG_ERROR, "timeout waiting for write to complete\n");
  89. }
  90. }
  91. rt_uint16_t aaci_ac97_read(rt_uint16_t reg)
  92. {
  93. rt_uint32_t v, timeout, retries = 10;
  94. aaci_ac97_select_codec();
  95. PL041_WRITE(&PL041->sl1tx, (reg << 12) | (1 << 19));
  96. aaci_pl041_delay(FRAME_PERIOD_US);
  97. timeout = FRAME_PERIOD_US * 8;
  98. do
  99. {
  100. aaci_pl041_delay(1);
  101. v = PL041_READ(&PL041->slfr);
  102. }
  103. while ((v & AACI_SLFR_1TXB) && --timeout);
  104. if (v & AACI_SLFR_1TXB)
  105. {
  106. dbg_log(DBG_ERROR, "timeout on slot 1 TX busy\n");
  107. v = ~0x0;
  108. return v;
  109. }
  110. aaci_pl041_delay(FRAME_PERIOD_US);
  111. timeout = FRAME_PERIOD_US * 8;
  112. do
  113. {
  114. aaci_pl041_delay(1);
  115. v = PL041_READ(&PL041->slfr) & (AACI_SLFR_1RXV | AACI_SLFR_2RXV);
  116. }
  117. while ((v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV)) && --timeout);
  118. if (v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV))
  119. {
  120. dbg_log(DBG_ERROR, "timeout on RX valid\n");
  121. v = ~0x0;
  122. return v;
  123. }
  124. do
  125. {
  126. v = PL041_READ(&PL041->sl1rx) >> 12;
  127. if (v == reg)
  128. {
  129. v = PL041_READ(&PL041->sl2rx) >> 4;
  130. break;
  131. }
  132. else if (--retries)
  133. {
  134. dbg_log(DBG_ERROR, "ac97 read back fail. retry\n");
  135. continue;
  136. }
  137. else
  138. {
  139. dbg_log(DBG_ERROR, "wrong ac97 register read back (%x != %x)\n", v, reg);
  140. v = ~0x0;
  141. }
  142. }
  143. while (retries);
  144. return v;
  145. }
  146. int aaci_pl041_channle_disable(int channle)
  147. {
  148. rt_uint32_t v;
  149. void *p_rx, *p_tx;
  150. p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
  151. p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
  152. v = PL041_READ(p_rx);
  153. v &= ~AACI_CR_EN;
  154. PL041_WRITE(p_rx, v);
  155. v = PL041_READ(p_tx);
  156. v &= ~AACI_CR_EN;
  157. PL041_WRITE(p_tx, v);
  158. return 0;
  159. }
  160. int aaci_pl041_channle_enable(int channle)
  161. {
  162. rt_uint32_t v;
  163. void *p_rx, *p_tx;
  164. p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
  165. p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
  166. v = PL041_READ(p_rx);
  167. v |= AACI_CR_EN;
  168. PL041_WRITE(p_rx, v);
  169. v = PL041_READ(p_tx);
  170. v |= AACI_CR_EN;
  171. PL041_WRITE(p_tx, v);
  172. return 0;
  173. }
  174. int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count)
  175. {
  176. void *p_data, *p_status;
  177. int i = 0;
  178. p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
  179. p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
  180. for (i = 0; (!(PL041_READ(p_status) & AACI_SR_RXFE)) && (i < count); i++)
  181. {
  182. buff[i] = (rt_uint16_t)PL041_READ(p_data);
  183. }
  184. return i;
  185. }
  186. int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count)
  187. {
  188. void *p_data, *p_status;
  189. int i = 0;
  190. p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
  191. p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
  192. for (i = 0; (!(PL041_READ(p_status) & AACI_SR_TXFF)) && (i < count); i++)
  193. {
  194. PL041_WRITE(p_data, buff[i]);
  195. }
  196. return i;
  197. }
  198. int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf)
  199. {
  200. rt_uint32_t v;
  201. void *p_rx, *p_tx;
  202. p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
  203. p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
  204. v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->itype;
  205. PL041_WRITE(p_rx, v);
  206. v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->otype;
  207. PL041_WRITE(p_tx, v);
  208. ac97_set_vol(cgf->vol);
  209. ac97_set_rate(cgf->rate);
  210. return 0;
  211. }
  212. void aaci_pl041_irq_enable(int channle, rt_uint32_t vector)
  213. {
  214. rt_uint32_t v;
  215. void *p_irq;
  216. vector &= vector & 0x7f;
  217. p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
  218. v = PL041_READ(p_irq);
  219. v |= vector;
  220. PL041_WRITE(p_irq, v);
  221. }
  222. void aaci_pl041_irq_disable(int channle, rt_uint32_t vector)
  223. {
  224. rt_uint32_t v;
  225. void *p_irq;
  226. vector &= vector & 0x7f;
  227. p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
  228. v = PL041_READ(p_irq);
  229. v &= ~vector;
  230. PL041_WRITE(p_irq, v);
  231. }
  232. rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data)
  233. {
  234. if (channle < 0 || channle >= PL041_CHANNLE_NUM)
  235. {
  236. dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
  237. return -RT_ERROR;
  238. }
  239. irq_tbl[channle].fun = fun;
  240. irq_tbl[channle].user_data = user_data;
  241. return RT_EOK;
  242. }
  243. rt_err_t aaci_pl041_irq_unregister(int channle)
  244. {
  245. if (channle < 0 || channle >= PL041_CHANNLE_NUM)
  246. {
  247. dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
  248. return -RT_ERROR;
  249. }
  250. irq_tbl[channle].fun = RT_NULL;
  251. irq_tbl[channle].user_data = RT_NULL;
  252. return RT_EOK;
  253. }
  254. static void aaci_pl041_irq_handle(int irqno, void *param)
  255. {
  256. rt_uint32_t mask, channle, m;
  257. struct pl041_irq_def *_irq = param;
  258. void *p_status;
  259. mask = PL041_READ(&PL041->allints);
  260. PL041_WRITE(PL041->intclr, mask);
  261. for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
  262. {
  263. mask = mask >> 7;
  264. m = mask & 0x7f;
  265. if (m & AACI_ISR_ORINTR)
  266. {
  267. dbg_log(DBG_WARNING, "RX overrun on chan %d\n", channle);
  268. }
  269. if (m & AACI_ISR_RXTOINTR)
  270. {
  271. dbg_log(DBG_WARNING, "RX timeout on chan %d\n", channle);
  272. }
  273. if (mask & AACI_ISR_URINTR)
  274. {
  275. dbg_log(DBG_WARNING, "TX underrun on chan %d\n", channle);
  276. }
  277. p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
  278. if (_irq[channle].fun != RT_NULL)
  279. {
  280. _irq[channle].fun(PL041_READ(p_status), _irq[channle].user_data);
  281. }
  282. }
  283. }
  284. rt_err_t aaci_pl041_init(void)
  285. {
  286. rt_uint32_t i, maincr;
  287. maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
  288. AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
  289. for (i = 0; i < 4; i++)
  290. {
  291. void *base = (void *)((rt_uint32_t)(&PL041->rxcr1) + i * 0x14);
  292. PL041_WRITE(base + AACI_IE, 0);
  293. PL041_WRITE(base + AACI_TXCR, 0);
  294. PL041_WRITE(base + AACI_RXCR, 0);
  295. }
  296. PL041_WRITE(&PL041->intclr, 0x1fff);
  297. PL041_WRITE(&PL041->maincr, maincr);
  298. PL041_WRITE(&PL041->reset, 0);
  299. aaci_pl041_delay(2);
  300. PL041_WRITE(&PL041->reset, RESET_NRST);
  301. rt_hw_interrupt_install(43, aaci_pl041_irq_handle, &irq_tbl, "aaci_pl041");
  302. rt_hw_interrupt_umask(43);
  303. return 0;
  304. }
  305. #if 0
  306. #define PL041_DUMP(_v) rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, &(_v), (_v))
  307. int _aaci_pl041_reg_dump(int argc, char **argv)
  308. {
  309. PL041_DUMP(PL041->rxcr1);
  310. PL041_DUMP(PL041->txcr1);
  311. PL041_DUMP(PL041->sr1);
  312. PL041_DUMP(PL041->isr1);
  313. PL041_DUMP(PL041->iie1);
  314. PL041_DUMP(PL041->rxcr2);
  315. PL041_DUMP(PL041->txcr2);
  316. PL041_DUMP(PL041->sr2);
  317. PL041_DUMP(PL041->isr2);
  318. PL041_DUMP(PL041->iie2);
  319. PL041_DUMP(PL041->rxcr3);
  320. PL041_DUMP(PL041->txcr3);
  321. PL041_DUMP(PL041->sr3);
  322. PL041_DUMP(PL041->isr3);
  323. PL041_DUMP(PL041->iie3);
  324. PL041_DUMP(PL041->rxcr4);
  325. PL041_DUMP(PL041->txcr4);
  326. PL041_DUMP(PL041->sr4);
  327. PL041_DUMP(PL041->isr4);
  328. PL041_DUMP(PL041->iie4);
  329. PL041_DUMP(PL041->sl1rx);
  330. PL041_DUMP(PL041->sl1tx);
  331. PL041_DUMP(PL041->sl2rx);
  332. PL041_DUMP(PL041->sl2tx);
  333. PL041_DUMP(PL041->sl12rx);
  334. PL041_DUMP(PL041->sl12tx);
  335. PL041_DUMP(PL041->slfr);
  336. PL041_DUMP(PL041->slistat);
  337. PL041_DUMP(PL041->slien);
  338. PL041_DUMP(PL041->intclr);
  339. PL041_DUMP(PL041->maincr);
  340. PL041_DUMP(PL041->reset);
  341. PL041_DUMP(PL041->sync);
  342. PL041_DUMP(PL041->allints);
  343. PL041_DUMP(PL041->mainfr);
  344. PL041_DUMP(PL041->dr1[0]);
  345. PL041_DUMP(PL041->dr2[0]);
  346. PL041_DUMP(PL041->dr3[0]);
  347. PL041_DUMP(PL041->dr4[0]);
  348. return 0;
  349. }
  350. FINSH_FUNCTION_EXPORT_ALIAS(_aaci_pl041_reg_dump, __cmd_pl041_dump, aaci pl041 dump reg.);
  351. #endif