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