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