1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author         Notes
8  * 2019-07-28     Ernest         the first version
9  */
10 
11 #include "board.h"
12 #include "drv_mic.h"
13 #include "drv_wm8978.h"
14 #include "drv_sound.h"
15 
16 #define DBG_TAG              "drv.mic"
17 #define DBG_LVL              DBG_INFO
18 #include <rtdbg.h>
19 
20 #define CODEC_I2C_NAME ("i2c1")
21 #define RX_DMA_FIFO_SIZE (2048)
22 
23 extern struct drv_sai _sai_a;
24 static struct drv_sai _sai_b = {0};
25 
26 struct stm32_mic
27 {
28     struct rt_i2c_bus_device *i2c_bus;
29     struct rt_audio_device audio;
30     struct rt_audio_configure config;
31     rt_uint8_t *rx_fifo;
32     rt_bool_t startup;
33 };
34 static struct stm32_mic _stm32_audio_record = {0};
35 
SAIB_samplerate_set(rt_uint32_t freq)36 static  rt_err_t SAIB_samplerate_set(rt_uint32_t freq)
37 {
38     __HAL_SAI_DISABLE(&_sai_b.hsai);
39     _sai_b.hsai.Init.AudioFrequency = freq;
40     HAL_SAI_Init(&_sai_b.hsai);
41     __HAL_SAI_ENABLE(&_sai_b.hsai);
42 
43     return RT_EOK;
44 }
45 
SAIB_channels_set(rt_uint16_t channels)46 void SAIB_channels_set(rt_uint16_t channels)
47 {
48     if (channels == 2)
49     {
50         _sai_b.hsai.Init.MonoStereoMode = SAI_STEREOMODE;
51     }
52     else
53     {
54         _sai_b.hsai.Init.MonoStereoMode = SAI_MONOMODE;
55     }
56     __HAL_SAI_DISABLE(&_sai_b.hsai);
57     HAL_SAI_Init(&_sai_b.hsai);
58     __HAL_SAI_ENABLE(&_sai_b.hsai);
59 }
60 
SAIB_samplebits_set(rt_uint16_t samplebits)61 void SAIB_samplebits_set(rt_uint16_t samplebits)
62 {
63     switch (samplebits)
64     {
65     case 16:
66         _sai_b.hsai.Init.DataSize = SAI_DATASIZE_16;
67         break;
68     case 24:
69         _sai_b.hsai.Init.DataSize = SAI_DATASIZE_24;
70         break;
71     case 32:
72         _sai_b.hsai.Init.DataSize = SAI_DATASIZE_32;
73         break;
74     default:
75         _sai_b.hsai.Init.DataSize = SAI_DATASIZE_16;
76         break;
77     }
78     __HAL_SAI_DISABLE(&_sai_b.hsai);
79     HAL_SAI_Init(&_sai_b.hsai);
80     __HAL_SAI_ENABLE(&_sai_b.hsai);
81 }
82 
SAIB_config_set(struct rt_audio_configure config)83 void SAIB_config_set(struct rt_audio_configure config)
84 {
85     SAIB_channels_set(config.channels);
86     SAIB_samplerate_set(config.samplerate);
87     SAIB_samplebits_set(config.samplebits);
88 }
89 
SAIB_config_init()90 static void SAIB_config_init()
91 {
92     _sai_b.hsai.Instance = SAI1_Block_B;
93     _sai_b.hsai.Init.AudioMode = SAI_MODESLAVE_RX;
94     _sai_b.hsai.Init.Synchro = SAI_SYNCHRONOUS;
95     _sai_b.hsai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
96     _sai_b.hsai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
97     _sai_b.hsai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
98     _sai_b.hsai.Init.ClockSource = SAI_CLKSOURCE_PLLI2S;
99     _sai_b.hsai.Init.MonoStereoMode = SAI_STEREOMODE;
100     _sai_b.hsai.Init.Protocol = SAI_FREE_PROTOCOL;
101     _sai_b.hsai.Init.DataSize = SAI_DATASIZE_16;
102     _sai_b.hsai.Init.FirstBit = SAI_FIRSTBIT_MSB;
103     _sai_b.hsai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
104 
105     /* frame */
106     _sai_b.hsai.FrameInit.FrameLength = 64;
107     _sai_b.hsai.FrameInit.ActiveFrameLength = 32;
108     _sai_b.hsai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
109     _sai_b.hsai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
110     _sai_b.hsai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;
111 
112     /* slot */
113     _sai_b.hsai.SlotInit.FirstBitOffset = 0;
114     _sai_b.hsai.SlotInit.SlotSize = SAI_SLOTSIZE_32B;
115     _sai_b.hsai.SlotInit.SlotNumber = 2;
116     _sai_b.hsai.SlotInit.SlotActive = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1;
117 
118     HAL_SAI_Init(&_sai_b.hsai);
119     __HAL_SAI_ENABLE(&_sai_b.hsai);
120 }
121 
SAIB_tx_dma(void)122 static void SAIB_tx_dma(void)
123 {
124     __HAL_RCC_DMA2_CLK_ENABLE();
125     __HAL_LINKDMA(&_sai_b.hsai, hdmarx, _sai_b.hdma);
126     _sai_b.hdma.Instance = DMA2_Stream5;
127     _sai_b.hdma.Init.Channel = DMA_CHANNEL_0;
128     _sai_b.hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
129     _sai_b.hdma.Init.PeriphInc = DMA_PINC_DISABLE;
130     _sai_b.hdma.Init.MemInc = DMA_MINC_ENABLE;
131     _sai_b.hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
132     _sai_b.hdma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
133     _sai_b.hdma.Init.Mode = DMA_CIRCULAR;
134     _sai_b.hdma.Init.Priority = DMA_PRIORITY_MEDIUM;
135     _sai_b.hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
136     _sai_b.hdma.Init.MemBurst = DMA_MBURST_SINGLE;
137     _sai_b.hdma.Init.PeriphBurst = DMA_PBURST_SINGLE;
138     HAL_DMA_DeInit(&_sai_b.hdma);
139     HAL_DMA_Init(&_sai_b.hdma);
140 
141     __HAL_DMA_DISABLE(&_sai_b.hdma);
142 
143     __HAL_DMA_CLEAR_FLAG(&_sai_b.hdma, DMA_FLAG_TCIF1_5);
144     __HAL_DMA_ENABLE_IT(&_sai_b.hdma, DMA_IT_TC);
145 
146     HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 5, 1);
147     HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
148 }
149 
sai_record_init()150 static rt_err_t sai_record_init()
151 {
152     SAIA_config_init();
153     SAIB_config_init();
154 
155     /* set record samplerate */
156     SAIA_config_set(_stm32_audio_record.config);
157     SAIB_config_set(_stm32_audio_record.config);
158     SAIA_tx_dma();
159     SAIB_tx_dma();
160 
161     return RT_EOK;
162 }
163 
DMA2_Stream5_IRQHandler(void)164 void DMA2_Stream5_IRQHandler(void)
165 {
166     rt_interrupt_enter();
167     HAL_DMA_IRQHandler(_sai_b.hsai.hdmarx);
168     rt_interrupt_leave();
169 }
170 
HAL_SAI_RxCpltCallback(SAI_HandleTypeDef * hsai)171 void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
172 {
173     rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
174 }
175 
HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef * hsai)176 void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
177 {
178 
179     rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[RX_DMA_FIFO_SIZE / 2], RX_DMA_FIFO_SIZE / 2);
180 }
181 
stm32_mic_getcaps(struct rt_audio_device * audio,struct rt_audio_caps * caps)182 static rt_err_t stm32_mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
183 {
184     rt_err_t result = RT_EOK;
185 
186     LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
187 
188     switch (caps->main_type)
189     {
190     /* Provide capabilities of INTPUT unit */
191     case AUDIO_TYPE_INPUT:
192     {
193         switch (caps->sub_type)
194         {
195         case AUDIO_DSP_PARAM:
196             caps->udata.config.channels     = _stm32_audio_record.config.channels;
197             caps->udata.config.samplebits   = _stm32_audio_record.config.samplebits;
198             caps->udata.config.samplerate   = _stm32_audio_record.config.samplerate;
199             break;
200 
201         case AUDIO_DSP_SAMPLERATE:
202             caps->udata.config.samplerate   = _stm32_audio_record.config.samplerate;
203             break;
204 
205         case AUDIO_DSP_CHANNELS:
206             caps->udata.config.channels     = _stm32_audio_record.config.channels;
207             break;
208 
209         case AUDIO_DSP_SAMPLEBITS:
210             caps->udata.config.samplebits   = _stm32_audio_record.config.samplebits;
211             break;
212         default:
213             result = -RT_ERROR;
214             break;
215         }
216         break;
217     }
218 
219     default:
220         result = -RT_ERROR;
221         break;
222     }
223 
224     return result;
225 }
226 
start_record_mode(void)227 static void start_record_mode(void)
228 {
229     rt_uint8_t temp[4] = {0};
230 
231     HAL_SAI_DMAStop(&_sai_b.hsai);
232     HAL_SAI_Transmit(&_sai_a.hsai, temp, 4, 0);
233     HAL_SAI_Receive_DMA(&_sai_b.hsai, _stm32_audio_record.rx_fifo, RX_DMA_FIFO_SIZE / 2);
234 }
235 
stm32_mic_configure(struct rt_audio_device * audio,struct rt_audio_caps * caps)236 static rt_err_t  stm32_mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
237 {
238     rt_err_t result = RT_EOK;
239 
240     LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
241 
242     switch (caps->main_type)
243     {
244     case AUDIO_TYPE_INPUT:
245     {
246         switch (caps->sub_type)
247         {
248 
249         case AUDIO_DSP_PARAM:
250         {
251             _stm32_audio_record.config.samplerate = caps->udata.config.samplerate;
252             _stm32_audio_record.config.channels   = caps->udata.config.channels;
253             _stm32_audio_record.config.samplebits = caps->udata.config.samplebits;
254 
255             HAL_SAI_DMAStop(&_sai_b.hsai);
256             SAIA_config_set(caps->udata.config);
257             SAIB_config_set(caps->udata.config);
258             break;
259         }
260 
261         case AUDIO_DSP_SAMPLERATE:
262         {
263             _stm32_audio_record.config.samplerate = caps->udata.config.samplerate;
264             SAIA_samplerate_set(caps->udata.config.samplerate);
265             break;
266         }
267         case AUDIO_DSP_CHANNELS:
268         {
269             _stm32_audio_record.config.channels = caps->udata.config.channels;
270             SAIA_channels_set(caps->udata.config.channels);
271             SAIB_channels_set(caps->udata.config.channels);
272             break;
273         }
274 
275         case AUDIO_DSP_SAMPLEBITS:
276         {
277             _stm32_audio_record.config.samplebits = caps->udata.config.samplebits;
278             SAIA_samplebits_set(caps->udata.config.samplebits);
279             break;
280         }
281 
282         default:
283             result = -RT_ERROR;
284             break;
285         }
286         /* After set config, MCLK will stop */
287         start_record_mode();
288         break;
289     }
290 
291     default:
292         break;
293     }
294 
295     return result;
296 }
297 
stm32_mic_init(struct rt_audio_device * audio)298 static rt_err_t stm32_mic_init(struct rt_audio_device *audio)
299 {
300     rt_err_t result = RT_EOK;
301 
302     /* initialize wm8978 */
303     _stm32_audio_record.i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(CODEC_I2C_NAME);
304     if (_stm32_audio_record.i2c_bus != RT_NULL)
305     {
306         LOG_D("Find device i2c1 success");
307     }
308     else
309     {
310         LOG_E("Find device i2c1 error");
311         return -RT_ERROR;
312     }
313 
314     result = wm8978_init(_stm32_audio_record.i2c_bus);
315     if (result != RT_EOK)
316     {
317         LOG_E("initialize wm8978 failed");
318         return result;
319     }
320 
321     sai_record_init();
322 
323     return RT_EOK;
324 }
325 
stm32_mic_start(struct rt_audio_device * audio,int stream)326 static rt_err_t stm32_mic_start(struct rt_audio_device *audio, int stream)
327 {
328     rt_err_t result = RT_EOK;
329 
330     if (stream == AUDIO_STREAM_RECORD)
331     {
332         /* set mic start */
333         wm8978_record_start(_stm32_audio_record.i2c_bus);
334         /* start transfer data */
335         start_record_mode();
336     }
337 
338     return result;
339 }
340 
stm32_mic_stop(struct rt_audio_device * audio,int stream)341 static rt_err_t stm32_mic_stop(struct rt_audio_device *audio, int stream)
342 {
343     if (stream == AUDIO_STREAM_RECORD)
344     {
345         HAL_SAI_DMAStop(&_sai_b.hsai);
346         HAL_SAI_DMAStop(&_sai_a.hsai);
347         wm8978_mic_enabled(_stm32_audio_record.i2c_bus, 0);
348     }
349 
350     return RT_EOK;
351 }
352 
353 static struct rt_audio_ops _mic_audio_ops =
354 {
355     .getcaps     = stm32_mic_getcaps,
356     .configure   = stm32_mic_configure,
357     .init        = stm32_mic_init,
358     .start       = stm32_mic_start,
359     .stop        = stm32_mic_stop,
360     .transmit    = RT_NULL,
361     .buffer_info = RT_NULL,
362 };
363 
rt_hw_mic_init(void)364 int rt_hw_mic_init(void)
365 {
366     struct rt_audio_device *audio = &_stm32_audio_record.audio;
367     /* mic default */
368     _stm32_audio_record.rx_fifo = rt_calloc(1, RX_DMA_FIFO_SIZE);
369     if (_stm32_audio_record.rx_fifo == RT_NULL)
370     {
371         return -RT_ENOMEM;
372     }
373 
374     _stm32_audio_record.config.channels = 1;
375     _stm32_audio_record.config.samplerate = 16000;
376     _stm32_audio_record.config.samplebits = 16;
377 
378     /* register mic device */
379     audio->ops = &_mic_audio_ops;
380     rt_audio_register(audio, "mic0", RT_DEVICE_FLAG_RDONLY, &_stm32_audio_record);
381 
382     return RT_EOK;
383 }
384 
385 INIT_DEVICE_EXPORT(rt_hw_mic_init);
386