drv_codec.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (c) 2006-2018, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2019-03-11 JiCheng Adapt RT1020's IO MAP
  9. */
  10. #include <rthw.h>
  11. #include <rtthread.h>
  12. #include <rtdevice.h>
  13. #include "board.h"
  14. #include "drv_codec.h"
  15. #include "fsl_wm8960.h"
  16. #include <fsl_sai.h>
  17. #include <fsl_sai_edma.h>
  18. #include <fsl_lpi2c.h>
  19. #include <fsl_dmamux.h>
  20. #define DEMO_CODEC_WM8960
  21. #define DEMO_SAI SAI1
  22. #define DEMO_SAI_IRQ SAI1_IRQn
  23. #define SAI_TxIRQHandler SAI1_IRQHandler
  24. /* Select Audio/Video PLL (786.48 MHz) as sai1 clock source */
  25. #define DEMO_SAI1_CLOCK_SOURCE_SELECT (2U)
  26. /* Clock pre divider for sai1 clock source */
  27. #define DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER (1U)
  28. /* Clock divider for sai1 clock source */
  29. #define DEMO_SAI1_CLOCK_SOURCE_DIVIDER (63U)
  30. /* Get frequency of sai1 clock */
  31. #define DEMO_SAI_CLK_FREQ (CLOCK_GetFreq(kCLOCK_AudioPllClk) / (DEMO_SAI1_CLOCK_SOURCE_DIVIDER + 1U) / (DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER + 1U))
  32. /* I2C instance and clock */
  33. #define DEMO_I2C LPI2C1
  34. /* Select USB1 PLL (480 MHz) as master lpi2c clock source */
  35. #define DEMO_LPI2C_CLOCK_SOURCE_SELECT (0U)
  36. /* Clock divider for master lpi2c clock source */
  37. #define DEMO_LPI2C_CLOCK_SOURCE_DIVIDER (5U)
  38. /* Get frequency of lpi2c clock */
  39. #define DEMO_I2C_CLK_FREQ ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (DEMO_LPI2C_CLOCK_SOURCE_DIVIDER + 1U))
  40. /* DMA */
  41. #define DMAMUX0 DMAMUX
  42. #define EXAMPLE_DMA DMA0
  43. #define EXAMPLE_CHANNEL (0U)
  44. #define EXAMPLE_SAI_TX_SOURCE kDmaRequestMuxSai1Tx
  45. struct imxcodec
  46. {
  47. I2S_Type *sai;
  48. sai_edma_handle_t txHandle;
  49. wm8960_handle_t codecHandle;
  50. edma_handle_t dmaHandle;
  51. lpi2c_master_handle_t i2cHandle;
  52. sai_transfer_format_t format;
  53. };
  54. static void _InitPins(void)
  55. {
  56. CLOCK_EnableClock(kCLOCK_Iomuxc);
  57. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_14_LPI2C1_SCL, 1);
  58. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_15_LPI2C1_SDA, 1);
  59. IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_B1_14_LPI2C1_SCL, 0xD8B0u);
  60. IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_B1_15_LPI2C1_SDA, 0xD8B0u);
  61. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_00_SAI1_MCLK, 1U);
  62. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_03_SAI1_TX_DATA00, 1U);
  63. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_01_SAI1_TX_BCLK, 1U);
  64. IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_SAI1_TX_SYNC, 1U);
  65. IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_00_SAI1_MCLK, 0x10B0u);
  66. IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_03_SAI1_TX_DATA00, 0x10B0u);
  67. IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_01_SAI1_TX_BCLK, 0x10B0u);
  68. IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_SAI1_TX_SYNC, 0x10B0u);
  69. }
  70. static void BOARD_EnableSaiMclkOutput(bool enable)
  71. {
  72. if (enable)
  73. {
  74. IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK;
  75. }
  76. else
  77. {
  78. IOMUXC_GPR->GPR1 &= (~IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK);
  79. }
  80. }
  81. static void saidma_callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
  82. {
  83. int ind = 0;
  84. rt_uint8_t *saddr;
  85. ind = handle->queueDriver;
  86. saddr = (rt_uint8_t*)handle->saiQueue[ind].data;
  87. rt_audio_tx_complete(userData, saddr);
  88. }
  89. /*********************************************************************************************************
  90. ** Audio device
  91. *********************************************************************************************************/
  92. static rt_err_t icodec_getcaps(struct rt_audio_device *audio,struct rt_audio_caps *caps)
  93. {
  94. rt_err_t result = RT_EOK;
  95. struct imxcodec *icodec = (struct imxcodec *)audio->parent.user_data;
  96. switch (caps->main_type)
  97. {
  98. case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
  99. {
  100. switch (caps->sub_type)
  101. {
  102. case AUDIO_TYPE_QUERY:
  103. caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
  104. break;
  105. default:
  106. result = -RT_ERROR;
  107. break;
  108. }
  109. break;
  110. }
  111. case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
  112. switch (caps->sub_type)
  113. {
  114. case AUDIO_DSP_PARAM:
  115. if (audio->replay == NULL)
  116. {
  117. result = -RT_ERROR;
  118. break;
  119. }
  120. caps->udata.config.channels = 1;
  121. caps->udata.config.samplefmt = 1;
  122. caps->udata.config.samplerate = 1;
  123. caps->udata.config.samplefmts = 1;
  124. break;
  125. default:
  126. result = -RT_ERROR;
  127. break;
  128. }
  129. break;
  130. case AUDIO_TYPE_MIXER: /* report the Mixer Units */
  131. switch (caps->sub_type)
  132. {
  133. case AUDIO_MIXER_QUERY:
  134. caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_DIGITAL | AUDIO_MIXER_LINE;
  135. break;
  136. case AUDIO_MIXER_VOLUME:
  137. caps->udata.value = WM8960_GetVolume(&icodec->codecHandle, kWM8960_ModuleDAC);
  138. break;
  139. case AUDIO_MIXER_DIGITAL:
  140. break;
  141. case AUDIO_MIXER_LINE:
  142. break;
  143. default:
  144. result = -RT_ERROR;
  145. break;
  146. }
  147. break;
  148. default:
  149. result = -RT_ERROR;
  150. break;
  151. }
  152. return result;
  153. }
  154. static rt_err_t icodec_configure(struct rt_audio_device *audio,struct rt_audio_caps *caps)
  155. {
  156. rt_err_t result = RT_EOK;
  157. struct imxcodec *icodec = (struct imxcodec *)audio->parent.user_data;
  158. switch (caps->main_type)
  159. {
  160. case AUDIO_TYPE_MIXER:
  161. {
  162. switch (caps->sub_type)
  163. {
  164. case AUDIO_MIXER_VOLUME:
  165. {
  166. WM8960_SetVolume(&icodec->codecHandle, kWM8960_ModuleDAC,
  167. caps->udata.value);
  168. }
  169. break;
  170. default:
  171. {
  172. result = -RT_ERROR;
  173. }
  174. break;
  175. }
  176. }
  177. break;
  178. case AUDIO_TYPE_OUTPUT:
  179. {
  180. switch (caps->sub_type)
  181. {
  182. case AUDIO_DSP_PARAM:
  183. {
  184. } break;
  185. case AUDIO_DSP_SAMPLERATE:
  186. {
  187. int rate = caps->udata.value;
  188. icodec->format.sampleRate_Hz = rate;
  189. SAI_TxSetFormat(icodec->sai, &icodec->format, icodec->format.masterClockHz, icodec->format.masterClockHz);
  190. }
  191. break;
  192. default:
  193. {
  194. result = -RT_ERROR;
  195. }
  196. break;
  197. }
  198. }
  199. break;
  200. default:
  201. result = -RT_ERROR;
  202. break;
  203. }
  204. return result;
  205. }
  206. static rt_err_t icodec_init(struct rt_audio_device *audio)
  207. {
  208. sai_config_t config;
  209. uint32_t mclkSourceClockHz = 0U;
  210. edma_config_t dmaConfig = {0};
  211. lpi2c_master_config_t i2cConfig = {0};
  212. uint32_t i2cSourceClock;
  213. clock_audio_pll_config_t audioPllConfig = {32, 1, 77, 100};
  214. struct imxcodec *icodec = audio->parent.user_data;
  215. sai_transfer_format_t *format;
  216. icodec->sai = DEMO_SAI;
  217. format = &icodec->format;
  218. _InitPins();
  219. CLOCK_InitAudioPll(&audioPllConfig);
  220. /*Clock setting for LPI2C*/
  221. CLOCK_SetMux(kCLOCK_Lpi2cMux, DEMO_LPI2C_CLOCK_SOURCE_SELECT);
  222. CLOCK_SetDiv(kCLOCK_Lpi2cDiv, DEMO_LPI2C_CLOCK_SOURCE_DIVIDER);
  223. /*Clock setting for SAI1*/
  224. CLOCK_SetMux(kCLOCK_Sai1Mux, DEMO_SAI1_CLOCK_SOURCE_SELECT);
  225. CLOCK_SetDiv(kCLOCK_Sai1PreDiv, DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER);
  226. CLOCK_SetDiv(kCLOCK_Sai1Div, DEMO_SAI1_CLOCK_SOURCE_DIVIDER);
  227. /*Enable MCLK clock*/
  228. BOARD_EnableSaiMclkOutput(true);
  229. /* Create EDMA handle */
  230. EDMA_GetDefaultConfig(&dmaConfig);
  231. EDMA_Init(EXAMPLE_DMA, &dmaConfig);
  232. EDMA_CreateHandle(&icodec->dmaHandle, EXAMPLE_DMA, EXAMPLE_CHANNEL);
  233. DMAMUX_Init(DMAMUX0);
  234. DMAMUX_SetSource(DMAMUX0, EXAMPLE_CHANNEL, EXAMPLE_SAI_TX_SOURCE);
  235. DMAMUX_EnableChannel(DMAMUX0, EXAMPLE_CHANNEL);
  236. /* Init SAI module */
  237. SAI_TxGetDefaultConfig(&config);
  238. config.protocol = kSAI_BusLeftJustified;
  239. SAI_TxInit(DEMO_SAI, &config);
  240. /* Configure the audio format */
  241. format->bitWidth = kSAI_WordWidth16bits;
  242. format->channel = 0U;
  243. format->sampleRate_Hz = kSAI_SampleRate48KHz;
  244. format->masterClockHz = DEMO_SAI_CLK_FREQ;
  245. format->protocol = config.protocol;
  246. format->stereo = kSAI_Stereo;
  247. format->isFrameSyncCompact = 0;
  248. format->watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;
  249. /* Configure Sgtl5000 I2C */
  250. icodec->codecHandle.base = DEMO_I2C;
  251. icodec->codecHandle.i2cHandle = &icodec->i2cHandle;
  252. i2cSourceClock = DEMO_I2C_CLK_FREQ;
  253. LPI2C_MasterGetDefaultConfig(&i2cConfig);
  254. LPI2C_MasterInit(DEMO_I2C, &i2cConfig, i2cSourceClock);
  255. LPI2C_MasterTransferCreateHandle(DEMO_I2C, &icodec->i2cHandle, NULL, NULL);
  256. WM8960_Init(&icodec->codecHandle, NULL);
  257. WM8960_ConfigDataFormat(&icodec->codecHandle, format->masterClockHz, format->sampleRate_Hz, format->bitWidth);
  258. SAI_TransferTxCreateHandleEDMA(icodec->sai, &icodec->txHandle, saidma_callback, audio, &icodec->dmaHandle);
  259. mclkSourceClockHz = DEMO_SAI_CLK_FREQ;
  260. SAI_TransferTxSetFormatEDMA(icodec->sai, &icodec->txHandle, format, mclkSourceClockHz, format->masterClockHz);
  261. return RT_EOK;
  262. }
  263. static rt_err_t icodec_shutdown(struct rt_audio_device *audio)
  264. {
  265. return RT_EOK;
  266. }
  267. rt_err_t icodec_start(struct rt_audio_device *audio,int stream)
  268. {
  269. return RT_EOK;
  270. }
  271. rt_err_t icodec_stop(struct rt_audio_device *audio,int stream)
  272. {
  273. return RT_EOK;
  274. }
  275. static rt_err_t icodec_suspend(struct rt_audio_device *audio,int stream)
  276. {
  277. return RT_EOK;
  278. }
  279. static rt_err_t icodec_resume(struct rt_audio_device *audio,int stream)
  280. {
  281. return RT_EOK;
  282. }
  283. static rt_err_t icodec_control (struct rt_audio_device *audio, int cmd, void *args)
  284. {
  285. rt_err_t result = RT_EOK;
  286. switch (cmd)
  287. {
  288. case AUDIO_CTL_HWRESET:
  289. break;
  290. default:
  291. result = -RT_ERROR;
  292. break;
  293. }
  294. return result;
  295. }
  296. static rt_size_t icodec_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
  297. {
  298. struct imxcodec *icodec = (struct imxcodec *)audio->parent.user_data;
  299. if(writeBuf != RT_NULL)
  300. {
  301. sai_transfer_t xfer;
  302. xfer.data = (uint8_t *)writeBuf;
  303. xfer.dataSize = size;
  304. if (size%32 == 0)
  305. icodec->txHandle.count = 16;
  306. else
  307. icodec->txHandle.count = 1;
  308. rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void*)writeBuf, size);
  309. if (SAI_TransferSendEDMA(icodec->sai, &icodec->txHandle, &xfer) != kStatus_Success)
  310. return 0;
  311. return size;
  312. }
  313. return 0;
  314. }
  315. static struct imxcodec _g_imxcodec;
  316. static struct rt_audio_device _g_audio_device;
  317. const struct rt_audio_ops _g_audio_ops =
  318. {
  319. .getcaps = icodec_getcaps,
  320. .configure = icodec_configure,
  321. .init = icodec_init,
  322. .shutdown = icodec_shutdown,
  323. .start = icodec_start,
  324. .stop = icodec_stop,
  325. .suspend = icodec_suspend,
  326. .resume = icodec_resume,
  327. .control = icodec_control,
  328. .transmit = icodec_transmit,
  329. };
  330. int rt_hw_codec_init(void)
  331. {
  332. int result;
  333. struct rt_audio_device *audio = &_g_audio_device;
  334. audio->ops = (struct rt_audio_ops*)&_g_audio_ops;
  335. _g_imxcodec.sai = DEMO_SAI;
  336. result = rt_audio_register(audio,"sound0", RT_DEVICE_FLAG_WRONLY, &_g_imxcodec);
  337. return result;
  338. }
  339. INIT_DEVICE_EXPORT(rt_hw_codec_init);