1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2018-4-30 misonyo the first version.
9 */
10
11 #include <rtthread.h>
12 #include <rthw.h>
13 #include <rtdef.h>
14
15 #ifdef BSP_USING_AUDIO
16 #define LOG_TAG "drv.sai"
17 #include <drv_log.h>
18
19 #include <rtdevice.h>
20 #include "drivers/dev_audio.h"
21 #include "bsp_wm8960.h"
22 #include "drv_i2c.h"
23 #include "drv_sai.h"
24
25
26 #define RX_DMA_FIFO_SIZE (2048)
27 volatile rt_uint16_t rx_busy = 0;
28 volatile rt_uint16_t tx_busy = 0;
29
30 struct drv_sai sai_tx = {0};
31 struct drv_sai sai_rx = {0};
32 wm8960_config_t wm8960Config = {
33 .route = kWM8960_RoutePlaybackandRecord,
34 .rightInputSource = kWM8960_InputDifferentialMicInput2,
35 .playSource = kWM8960_PlaySourceDAC,
36 .slaveAddress = WM8960_I2C_ADDR,
37 .bus = kWM8960_BusI2S,
38 .format = {.mclk_HZ = 6144000U, .sampleRate = kWM8960_AudioSampleRate16KHz, .bitWidth = kWM8960_AudioBitWidth16bit},
39 .master_slave = false,
40 };
41 const clock_audio_pll_config_t audioPllConfig =
42 {
43 .loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
44 .postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
45 .numerator = 77, /* 30 bit numerator of fractional loop divider. */
46 .denominator = 100, /* 30 bit denominator of fractional loop divider */
47 };
48 sai_transfer_format_t format;
49 sai_config_t config;
50 sai_transfer_t xfer;
51 struct imxrt_sai
52 {
53 struct rt_audio_device audio;
54 struct rt_audio_configure play_config;
55 rt_uint16_t volume;
56 rt_uint8_t* tx_fifo;
57 struct rt_i2c_bus_device* i2c_bus;
58 rt_uint8_t* rx_fifo;
59 };
60
61 struct imxrt_sai imxrt_payer_dev = { 0 };
sai_config(void)62 static void sai_config(void)
63 {
64 #ifdef BSP_AUDIO_USING_DMA
65 static struct saidma_tx_config sai_txdma = { .channel = 0U, .request = kDmaRequestMuxSai1Tx };
66 sai_tx.dma_tx = &sai_txdma;
67 sai_tx.dma_flag |= RT_DEVICE_FLAG_DMA_TX;
68 #if defined (BSP_USING_AUDIO_RECORD)
69 static struct saidma_rx_config sai_rxdma = { .channel = 1U, .request = kDmaRequestMuxSai1Rx };
70 sai_rx.dma_rx = &sai_rxdma;
71 #endif
72 #endif
73 }
sai_TxDmaCallback(I2S_Type * base,sai_edma_handle_t * handle,rt_int32_t status,void * userData)74 static void sai_TxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
75 {
76 tx_busy = 1;
77 rt_audio_tx_complete(&imxrt_payer_dev.audio);
78 }
79 #if defined (BSP_USING_AUDIO_RECORD)
sai_RxDmaCallback(I2S_Type * base,sai_edma_handle_t * handle,rt_int32_t status,void * userData)80 static void sai_RxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
81 {
82 rx_busy = 1;
83 rt_audio_rx_done(&imxrt_payer_dev.audio, &imxrt_payer_dev.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
84 }
85 #endif
BOARD_EnableSaiMclkOutput(rt_bool_t enable)86 void BOARD_EnableSaiMclkOutput(rt_bool_t enable)
87 {
88 if(enable)
89 {
90 IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK;
91 }
92 else
93 {
94 IOMUXC_GPR->GPR1 &= (~IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK);
95 }
96 }
sai_format(void)97 void sai_format(void)
98 {
99 SAI_TransferTxCreateHandleEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, sai_TxDmaCallback, NULL, &sai_tx.dma_tx->edma);
100 SAI_TransferTxSetFormatEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
101 #if defined (BSP_USING_AUDIO_RECORD)
102 SAI_TransferRxCreateHandleEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, sai_RxDmaCallback, NULL, &sai_rx.dma_rx->edma);
103 SAI_TransferRxSetFormatEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
104 #endif
105 }
106
sai_init(void)107 void sai_init(void)
108 {
109 CLOCK_InitAudioPll(&audioPllConfig);
110 CLOCK_SetMux(kCLOCK_Sai1Mux, DEMO_SAI1_CLOCK_SOURCE_SELECT);
111 CLOCK_SetDiv(kCLOCK_Sai1PreDiv, DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER);
112 CLOCK_SetDiv(kCLOCK_Sai1Div, DEMO_SAI1_CLOCK_SOURCE_DIVIDER);
113 BOARD_EnableSaiMclkOutput(RT_TRUE);
114
115 EDMA_CreateHandle(&sai_tx.dma_tx->edma, DMA0, sai_tx.dma_tx->channel);
116 DMAMUX_SetSource(DMAMUX, sai_tx.dma_tx->channel, (rt_uint8_t)sai_tx.dma_tx->request);
117 DMAMUX_EnableChannel(DMAMUX, sai_tx.dma_tx->channel);
118 SAI_TxGetDefaultConfig(&config);
119 SAI_TxInit(sai_tx.base, &config);
120 #if defined (BSP_USING_AUDIO_RECORD)
121 EDMA_CreateHandle(&sai_rx.dma_rx->edma, DMA0, sai_rx.dma_rx->channel);
122 DMAMUX_SetSource(DMAMUX, sai_rx.dma_rx->channel, (rt_uint8_t)sai_rx.dma_rx->request);
123 DMAMUX_EnableChannel(DMAMUX, sai_rx.dma_rx->channel);
124 SAI_RxGetDefaultConfig(&config);
125 SAI_RxInit(sai_rx.base, &config);
126 #endif
127 format.bitWidth = kSAI_WordWidth16bits;
128 format.channel = 0U;
129 format.sampleRate_Hz = kSAI_SampleRate16KHz;
130 format.masterClockHz = DEMO_SAI_CLK_FREQ;
131 format.protocol = config.protocol;
132 format.stereo = kSAI_Stereo;
133 format.isFrameSyncCompact = true;
134 format.watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;
135 SAI_TransferTxCreateHandleEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, sai_TxDmaCallback, NULL, &sai_tx.dma_tx->edma);
136 SAI_TransferTxSetFormatEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
137 #if defined (BSP_USING_AUDIO_RECORD)
138 SAI_TransferRxCreateHandleEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, sai_RxDmaCallback, NULL, &sai_rx.dma_rx->edma);
139 SAI_TransferRxSetFormatEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
140 #endif
141 }
142
SAI_samplerate_set(rt_uint32_t freq)143 void SAI_samplerate_set(rt_uint32_t freq)
144 {
145 switch(freq)
146 {
147 case 48000:
148 format.sampleRate_Hz = kSAI_SampleRate48KHz;
149 break;
150 case 44100:
151 format.sampleRate_Hz = kSAI_SampleRate44100Hz;
152 break;
153 case 32000:
154 format.sampleRate_Hz = kSAI_SampleRate32KHz;
155 break;
156 case 24000:
157 format.sampleRate_Hz = kSAI_SampleRate24KHz;
158 break;
159 case 22050:
160 format.sampleRate_Hz = kSAI_SampleRate22050Hz;
161 break;
162 case 16000:
163 format.sampleRate_Hz = kSAI_SampleRate16KHz;
164 break;
165 case 12000:
166 format.sampleRate_Hz = kSAI_SampleRate12KHz;
167 break;
168 case 11025:
169 format.sampleRate_Hz = kSAI_SampleRate11025Hz;
170 break;
171 case 8000:
172 format.sampleRate_Hz = kSAI_SampleRate8KHz;
173 break;
174 default:
175 format.sampleRate_Hz = kSAI_SampleRate16KHz;
176 break;
177 }
178
179 }
180
SAI_channels_set(rt_uint16_t channels)181 void SAI_channels_set(rt_uint16_t channels)
182 {
183 switch(channels)
184 {
185 case 2:
186 format.stereo = kSAI_Stereo;
187 break;
188 case 1:
189 format.stereo = kSAI_MonoRight;
190 break;
191 case 0:
192 format.stereo = kSAI_MonoLeft;
193 break;
194 default:
195 format.stereo = kSAI_Stereo;
196 break;
197 }
198 }
199
SAI_samplebits_set(rt_uint16_t samplebits)200 void SAI_samplebits_set(rt_uint16_t samplebits)
201 {
202
203 switch(samplebits)
204 {
205 case 16:
206 format.bitWidth = kSAI_WordWidth16bits;
207 break;
208 case 24:
209 format.bitWidth = kSAI_WordWidth24bits;
210 break;
211 case 32:
212 format.bitWidth = kSAI_WordWidth32bits;
213 break;
214 default:
215 format.bitWidth = kSAI_WordWidth16bits;
216 break;
217 }
218 }
219
220
imxrt_payer_getcaps(struct rt_audio_device * audio,struct rt_audio_caps * caps)221 static rt_err_t imxrt_payer_getcaps(struct rt_audio_device* audio, struct rt_audio_caps* caps)
222 {
223 rt_err_t result = RT_EOK;
224
225 RT_ASSERT(audio != RT_NULL);
226 struct imxrt_sai* imxrt_audio = (struct imxrt_sai*)audio->parent.user_data;
227
228 switch(caps->main_type)
229 {
230 case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
231 {
232 switch(caps->sub_type)
233 {
234 case AUDIO_TYPE_QUERY:
235 caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
236 break;
237
238 default:
239 result = -RT_ERROR;
240 break;
241 }
242
243 break;
244 }
245 case AUDIO_TYPE_INPUT:
246 {
247 switch(caps->sub_type)
248 {
249 case AUDIO_DSP_PARAM:
250 caps->udata.config.channels = imxrt_audio->play_config.channels;
251 caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
252 caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
253 break;
254
255 case AUDIO_DSP_SAMPLERATE:
256 caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
257 break;
258
259 case AUDIO_DSP_CHANNELS:
260 caps->udata.config.channels = imxrt_audio->play_config.channels;
261 break;
262
263 case AUDIO_DSP_SAMPLEBITS:
264 caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
265 break;
266 default:
267 result = -RT_ERROR;
268 break;
269 }
270 break;
271 }
272 case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
273 {
274 switch(caps->sub_type)
275 {
276 case AUDIO_DSP_PARAM:
277 caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
278 caps->udata.config.channels = imxrt_audio->play_config.channels;
279 caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
280 break;
281
282 case AUDIO_DSP_SAMPLERATE:
283 caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
284 break;
285
286 case AUDIO_DSP_CHANNELS:
287 caps->udata.config.channels = imxrt_audio->play_config.channels;
288 break;
289
290 case AUDIO_DSP_SAMPLEBITS:
291 caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
292 break;
293
294 default:
295 result = -RT_ERROR;
296 break;
297 }
298
299 break;
300 }
301
302 case AUDIO_TYPE_MIXER: /* report the Mixer Units */
303 {
304 switch(caps->sub_type)
305 {
306 case AUDIO_MIXER_QUERY:
307 caps->udata.mask = AUDIO_MIXER_VOLUME;
308 break;
309
310 case AUDIO_MIXER_VOLUME:
311 caps->udata.value = WM8960_GetVolume(imxrt_payer_dev.i2c_bus,kWM8960_ModuleDAC);
312 break;
313
314 default:
315 result = -RT_ERROR;
316 break;
317 }
318
319 break;
320 }
321
322 default:
323 result = -RT_ERROR;
324 break;
325 }
326
327 return result;
328 }
329
imxrt_payer_configure(struct rt_audio_device * audio,struct rt_audio_caps * caps)330 static rt_err_t imxrt_payer_configure(struct rt_audio_device* audio, struct rt_audio_caps* caps)
331 {
332
333 rt_err_t result = RT_EOK;
334 RT_ASSERT(audio != RT_NULL);
335 struct imxrt_sai* imxrt_audio = (struct imxrt_sai*)audio->parent.user_data;
336
337 switch(caps->main_type)
338 {
339 case AUDIO_TYPE_MIXER:
340 {
341 switch(caps->sub_type)
342 {
343 case AUDIO_MIXER_MUTE:
344 {
345 /* set mute mode */
346 WM8960_SetMute(imxrt_payer_dev.i2c_bus, kWM8960_ModuleDAC, RT_FALSE);
347 break;
348 }
349
350 case AUDIO_MIXER_VOLUME:
351 {
352 int volume = caps->udata.value;
353
354 imxrt_audio->volume = volume;
355 /* set mixer volume */
356 WM8960_SetVolume(imxrt_payer_dev.i2c_bus, kWM8960_ModuleDAC, volume);
357
358
359 break;
360 }
361
362 default:
363 result = -RT_ERROR;
364 break;
365 }
366
367 break;
368 }
369
370 case AUDIO_TYPE_OUTPUT:
371 {
372 switch(caps->sub_type)
373 {
374 case AUDIO_DSP_PARAM:
375 {
376 struct rt_audio_configure config = caps->udata.config;
377
378 imxrt_audio->play_config.samplerate = config.samplerate;
379 imxrt_audio->play_config.samplebits = config.samplebits;
380 imxrt_audio->play_config.channels = config.channels;
381
382 SAI_channels_set(config.channels);
383 SAI_samplerate_set(config.samplerate);
384 SAI_samplebits_set(config.samplebits);
385 break;
386 }
387
388 case AUDIO_DSP_SAMPLERATE:
389 {
390 imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
391 SAI_samplerate_set(caps->udata.config.samplerate);
392 break;
393 }
394
395 case AUDIO_DSP_CHANNELS:
396 {
397 imxrt_audio->play_config.channels = caps->udata.config.channels;
398 SAI_channels_set(caps->udata.config.channels);
399 break;
400 }
401
402 case AUDIO_DSP_SAMPLEBITS:
403 {
404 imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
405 SAI_samplebits_set(caps->udata.config.samplebits);
406 break;
407 }
408
409 default:
410 result = -RT_ERROR;
411 break;
412 }
413 break;
414 }
415 case AUDIO_TYPE_INPUT:
416 {
417 switch(caps->sub_type)
418 {
419
420 case AUDIO_DSP_PARAM:
421 {
422 imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
423 imxrt_audio->play_config.channels = caps->udata.config.channels;
424 imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
425
426 SAI_TransferTerminateReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle);
427 SAI_samplerate_set(caps->udata.config.samplerate);
428 SAI_channels_set(caps->udata.config.channels);
429 SAI_samplebits_set(caps->udata.config.samplebits);
430 break;
431 }
432
433 case AUDIO_DSP_SAMPLERATE:
434 {
435 imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
436 SAI_samplerate_set(caps->udata.config.samplerate);
437 break;
438 }
439 case AUDIO_DSP_CHANNELS:
440 {
441 imxrt_audio->play_config.channels = caps->udata.config.channels;
442 SAI_channels_set(caps->udata.config.channels);
443 break;
444 }
445
446 case AUDIO_DSP_SAMPLEBITS:
447 {
448 imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
449 SAI_samplebits_set(caps->udata.config.samplebits);
450 break;
451 }
452
453 default:
454 result = -RT_ERROR;
455 break;
456 }
457 /* After set config, MCLK will stop */
458 SAI_TxSoftwareReset(sai_tx.base, kSAI_ResetTypeSoftware);
459 SAI_RxSoftwareReset(sai_rx.base, kSAI_ResetTypeSoftware);
460 xfer.data = imxrt_payer_dev.tx_fifo; // +i * (AUD_FIFO_SIZE / 4);
461 xfer.dataSize = AUD_BLOCK_SIZE;
462 SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
463 SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
464 break;
465 }
466
467 default:
468 break;
469 }
470 return result;
471
472 }
imxrt_payer_init(struct rt_audio_device * audio)473 static rt_err_t imxrt_payer_init(struct rt_audio_device* audio)
474 {
475 RT_ASSERT(audio != RT_NULL);
476 imxrt_payer_dev.i2c_bus = (struct rt_i2c_bus_device*)rt_device_find(CODEC_I2C_NAME);
477 sai_init();
478 return RT_EOK;
479 }
imxrt_payer_start(struct rt_audio_device * audio,int stream)480 static rt_err_t imxrt_payer_start(struct rt_audio_device* audio, int stream)
481 {
482 RT_ASSERT(audio != RT_NULL);
483
484 sai_format();
485 WM8960_init(imxrt_payer_dev.i2c_bus, &wm8960Config);
486
487 xfer.data = imxrt_payer_dev.rx_fifo;
488 xfer.dataSize = AUD_BLOCK_SIZE;
489 #if defined (BSP_USING_AUDIO_RECORD)
490 SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
491 #endif
492 SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
493 return RT_EOK;
494 }
495
imxrt_payer_stop(struct rt_audio_device * audio,int stream)496 static rt_err_t imxrt_payer_stop(struct rt_audio_device* audio, int stream)
497 {
498
499 RT_ASSERT(audio != RT_NULL);
500 SAI_TransferTerminateSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle);
501 SAI_TransferTerminateReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle);
502 WM8960_Deinit(imxrt_payer_dev.i2c_bus);
503
504 return RT_EOK;
505
506 }
imxrt_payer_transmit(struct rt_audio_device * audio,const void * writeBuf,void * readBuf,rt_size_t size)507 static rt_ssize_t imxrt_payer_transmit(struct rt_audio_device* audio, const void* writeBuf, void* readBuf, rt_size_t size)
508 {
509 RT_ASSERT(audio != RT_NULL);
510 #if defined (BSP_USING_AUDIO_RECORD)
511 xfer.data = imxrt_payer_dev.rx_fifo;
512 xfer.dataSize = RX_DMA_FIFO_SIZE;
513 SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
514 SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
515 #else
516 xfer.data = (rt_uint8_t*)writeBuf;
517 xfer.dataSize = size;
518 SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
519 #endif
520
521 return size;
522 }
523
imxrt_payer_buffer_info(struct rt_audio_device * audio,struct rt_audio_buf_info * info)524 static void imxrt_payer_buffer_info(struct rt_audio_device* audio, struct rt_audio_buf_info* info)
525 {
526 RT_ASSERT(audio != RT_NULL);
527 /**
528 * AUD_FIFO
529 * +----------------+----------------+
530 * | block1 | block2 |
531 * +----------------+----------------+
532 * \ block_size /
533 */
534 info->buffer = imxrt_payer_dev.tx_fifo;
535 info->total_size = AUD_DMA_FIFO_SIZE;
536 info->block_size = AUD_DMA_FIFO_SIZE / 2;
537 info->block_count = 2;
538 }
539
540 static struct rt_audio_ops imxrt_payer_ops =
541 {
542 .getcaps = imxrt_payer_getcaps,
543 .configure = imxrt_payer_configure,
544 .init = imxrt_payer_init,
545 .start = imxrt_payer_start,
546 .stop = imxrt_payer_stop,
547 .transmit = imxrt_payer_transmit,
548 .buffer_info = imxrt_payer_buffer_info,
549 };
550
rt_hw_sound_init(void)551 int rt_hw_sound_init(void)
552 {
553 rt_uint8_t* tx_fifo = RT_NULL;
554 rt_uint8_t* rx_fifo = RT_NULL;
555
556 sai_tx.base = SAI1;
557 sai_rx.base = SAI1;
558 sai_tx.irqn = SAI1_IRQn;
559 sai_config();
560
561 tx_fifo = rt_calloc(1, AUD_DMA_FIFO_SIZE);
562 rx_fifo = rt_calloc(1, AUD_DMA_FIFO_SIZE);
563 if(tx_fifo == RT_NULL)
564 {
565 return -RT_ENOMEM;
566 }
567
568 rt_memset(tx_fifo, 0, AUD_DMA_FIFO_SIZE);
569 imxrt_payer_dev.tx_fifo = tx_fifo;
570 rt_memset(rx_fifo, 0, AUD_DMA_FIFO_SIZE);
571 imxrt_payer_dev.rx_fifo = rx_fifo;
572
573 imxrt_payer_dev.audio.ops = &imxrt_payer_ops;
574 rt_audio_register(&imxrt_payer_dev.audio, "mic", RT_DEVICE_FLAG_RDWR, &imxrt_payer_dev);
575
576 return RT_EOK;
577 }
578 INIT_DEVICE_EXPORT(rt_hw_sound_init);
579
580 #endif /* BSP_USING_AUDIO*/
581