1 /*
2 * Copyright (c) 2022 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <rtthread.h>
9 #include <rtdevice.h>
10
11 #define DBG_TAG "pdm"
12 #define DBG_LVL DBG_INFO
13 #include <rtdbg.h>
14
15 #ifdef BSP_USING_PDM
16 #include "board.h"
17 #include "drivers/dev_audio.h"
18 #include "hpm_i2s_drv.h"
19 #include "hpm_pdm_drv.h"
20 #include "drv_pdm.h"
21 #ifdef HPMSOC_HAS_HPMSDK_DMAV2
22 #include "hpm_dmav2_drv.h"
23 #else
24 #include "hpm_dma_drv.h"
25 #endif
26 #include "hpm_dmamux_drv.h"
27 #include "hpm_l1c_drv.h"
28 #include "hpm_dma_mgr.h"
29
30
31 /* PDM connect to I2S0 RX */
32 #define PDM_DMA_REQ HPM_DMA_SRC_I2S0_RX
33 #define PDM_I2S_DATA_LINE 0
34
35 struct hpm_pdm
36 {
37 struct rt_audio_device audio;
38 struct rt_audio_configure record_config;
39 rt_uint8_t* rx_fifo;
40 };
41
42 struct hpm_pdm hpm_pdm_dev = { 0 };
43 static dma_resource_t dma_resource = { 0 };
44 static rt_err_t hpm_pdm_dma_transmit();
45
46
pdm_dma_tc_callback(DMA_Type * ptr,uint32_t channel,void * user_data)47 void pdm_dma_tc_callback(DMA_Type *ptr, uint32_t channel, void *user_data)
48 {
49 rt_audio_rx_done(&hpm_pdm_dev.audio, hpm_pdm_dev.rx_fifo, PDM_FIFO_SIZE);
50 hpm_pdm_dma_transmit();
51 }
52
hpm_pdm_getcaps(struct rt_audio_device * audio,struct rt_audio_caps * caps)53 static rt_err_t hpm_pdm_getcaps(struct rt_audio_device* audio, struct rt_audio_caps* caps)
54 {
55 rt_err_t result = RT_EOK;
56
57 RT_ASSERT(audio != RT_NULL);
58 struct hpm_pdm* hpm_audio = (struct hpm_pdm*)audio->parent.user_data;
59
60 switch(caps->main_type)
61 {
62 case AUDIO_TYPE_INPUT:
63 {
64 switch(caps->sub_type)
65 {
66 case AUDIO_DSP_PARAM:
67 caps->udata.config.samplerate = hpm_audio->record_config.samplerate;
68 caps->udata.config.channels = hpm_audio->record_config.channels;
69 caps->udata.config.samplebits = hpm_audio->record_config.samplebits;
70 break;
71
72 case AUDIO_DSP_SAMPLERATE:
73 caps->udata.config.samplerate = hpm_audio->record_config.samplerate;
74 break;
75
76 case AUDIO_DSP_CHANNELS:
77 caps->udata.config.channels = hpm_audio->record_config.channels;
78 break;
79
80 case AUDIO_DSP_SAMPLEBITS:
81 caps->udata.config.samplebits = hpm_audio->record_config.samplebits;
82 break;
83
84 default:
85 result = -RT_ERROR;
86 break;
87 }
88
89 break;
90 }
91
92 default:
93 result = -RT_ERROR;
94 break;
95 }
96
97 return result;
98 }
99
i2s_is_enabled(I2S_Type * ptr)100 static bool i2s_is_enabled(I2S_Type *ptr)
101 {
102 return ((ptr->CTRL & I2S_CTRL_I2S_EN_MASK) != 0);
103 }
104
hpm_pdm_set_channels(uint32_t channel)105 static rt_err_t hpm_pdm_set_channels(uint32_t channel)
106 {
107 uint32_t mclk_hz;
108 i2s_transfer_config_t transfer;
109
110 mclk_hz = clock_get_frequency(clock_i2s0);
111 i2s_get_default_transfer_config_for_pdm(&transfer);
112 transfer.data_line = PDM_I2S_DATA_LINE;
113 if (channel == 1) {
114 transfer.channel_slot_mask = BOARD_PDM_SINGLE_CHANNEL_MASK;
115 } else if(channel == 2) {
116 transfer.channel_slot_mask = BOARD_PDM_DUAL_CHANNEL_MASK;
117 } else {
118 LOG_E("PDM not support channels number %d.\n", channel);
119 return -RT_ERROR;
120 }
121
122 bool is_enabled = i2s_is_enabled(PDM_I2S);
123 if (is_enabled) {
124 dma_abort_channel(dma_resource.base, dma_resource.channel);
125 }
126 if (status_success != i2s_config_rx(PDM_I2S, mclk_hz, &transfer))
127 {
128 LOG_E("pdm_i2s configure transfer failed\n");
129 return -RT_ERROR;
130 }
131 if (is_enabled)
132 {
133 i2s_enable(PDM_I2S);
134 }
135
136 return RT_EOK;
137 }
138
hpm_pdm_configure(struct rt_audio_device * audio,struct rt_audio_caps * caps)139 static rt_err_t hpm_pdm_configure(struct rt_audio_device* audio, struct rt_audio_caps* caps)
140 {
141
142 rt_err_t result = RT_EOK;
143 RT_ASSERT(audio != RT_NULL);
144
145 struct hpm_pdm* hpm_audio = (struct hpm_pdm*)audio->parent.user_data;
146
147 switch(caps->main_type)
148 {
149 case AUDIO_TYPE_INPUT:
150 {
151 switch(caps->sub_type)
152 {
153 case AUDIO_DSP_CHANNELS:
154 {
155 hpm_audio->record_config.channels = caps->udata.config.channels;
156 hpm_pdm_set_channels(caps->udata.config.channels);
157 break;
158 }
159
160 default:
161 result = -RT_ERROR;
162 break;
163 }
164 }
165
166 default:
167 result = -RT_ERROR;
168 break;
169 }
170 return result;
171 }
172
173
hpm_pdm_init(struct rt_audio_device * audio)174 static rt_err_t hpm_pdm_init(struct rt_audio_device* audio)
175 {
176 RT_ASSERT(audio != RT_NULL);
177
178 i2s_config_t i2s_config;
179 i2s_transfer_config_t transfer;
180 pdm_config_t pdm_config;
181
182 init_pdm_pins();
183 board_init_pdm_clock();
184
185 i2s_get_default_config(PDM_I2S, &i2s_config);
186 i2s_enable_rx_dma_request(PDM_I2S);
187 i2s_init(PDM_I2S, &i2s_config);
188
189 i2s_get_default_transfer_config_for_pdm(&transfer);
190 transfer.data_line = PDM_I2S_DATA_LINE;
191 transfer.channel_slot_mask = BOARD_PDM_SINGLE_CHANNEL_MASK;
192 if (status_success != i2s_config_rx(PDM_I2S, clock_get_frequency(clock_i2s0), &transfer))
193 {
194 LOG_E("pdm_i2s configure receive failed\n");
195 return -RT_ERROR;
196 }
197
198 /* init audio configure */
199 hpm_pdm_dev.record_config.channels = 1U;
200 hpm_pdm_dev.record_config.samplebits = 32U; /* the actual significant bit is 24 bits, and the low bit is 0 */
201 hpm_pdm_dev.record_config.samplerate = PDM_SOC_SAMPLE_RATE_IN_HZ; /* fix 16KHz */
202
203 pdm_get_default_config(HPM_PDM, &pdm_config);
204 if (status_success != pdm_init(HPM_PDM, &pdm_config)) {
205 LOG_E("pdm init failed\n");
206 return -RT_ERROR;
207 }
208
209 return RT_EOK;
210 }
211
hpm_pdm_start(struct rt_audio_device * audio,int stream)212 static rt_err_t hpm_pdm_start(struct rt_audio_device* audio, int stream)
213 {
214 RT_ASSERT(audio != RT_NULL);
215 struct hpm_pdm* hpm_audio = (struct hpm_pdm*)audio->parent.user_data;
216
217 if (stream == AUDIO_STREAM_RECORD)
218 {
219 i2s_disable(PDM_I2S);
220 i2s_disable_rx_dma_request(PDM_I2S);
221 pdm_stop(HPM_PDM);
222 pdm_software_reset(HPM_PDM);
223
224 if (dma_mgr_request_resource(&dma_resource) == status_success) {
225 uint8_t dmamux_ch;
226 dma_mgr_install_chn_tc_callback(&dma_resource, pdm_dma_tc_callback, NULL);
227 dma_mgr_enable_dma_irq_with_priority(&dma_resource, 1);
228 dmamux_ch = DMA_SOC_CHN_TO_DMAMUX_CHN(dma_resource.base, dma_resource.channel);
229 dmamux_config(HPM_DMAMUX, dmamux_ch, PDM_DMA_REQ, true);
230 } else {
231 LOG_E("no dma resource available for PDM transfer.\n");
232 return -RT_ERROR;
233 }
234
235 i2s_reset_rx(PDM_I2S);
236 if (RT_EOK != hpm_pdm_dma_transmit()) {
237 return -RT_ERROR;
238 }
239
240 pdm_start(HPM_PDM);
241 i2s_enable_rx_dma_request(PDM_I2S);
242 i2s_start(PDM_I2S);
243 }
244
245 return RT_EOK;
246 }
247
hpm_pdm_stop(struct rt_audio_device * audio,int stream)248 static rt_err_t hpm_pdm_stop(struct rt_audio_device* audio, int stream)
249 {
250 RT_ASSERT(audio != RT_NULL);
251
252 if (stream == AUDIO_STREAM_RECORD) {
253 pdm_stop(HPM_PDM);
254 i2s_stop(PDM_I2S);
255
256 dma_abort_channel(dma_resource.base, dma_resource.channel);
257 dma_mgr_release_resource(&dma_resource);
258 }
259
260 return RT_EOK;
261 }
262
hpm_pdm_dma_transmit()263 static rt_err_t hpm_pdm_dma_transmit()
264 {
265 dma_channel_config_t ch_config = {0};
266 dma_default_channel_config(dma_resource.base, &ch_config);
267 ch_config.src_addr = (uint32_t)&PDM_I2S->RXD[PDM_I2S_DATA_LINE];
268 ch_config.dst_addr = core_local_mem_to_sys_address(HPM_CORE0, (uint32_t)hpm_pdm_dev.rx_fifo);
269 ch_config.src_width = DMA_TRANSFER_WIDTH_WORD;
270 ch_config.dst_width = DMA_TRANSFER_WIDTH_WORD;
271 ch_config.src_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
272 ch_config.dst_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
273 ch_config.size_in_byte = PDM_FIFO_SIZE;
274 ch_config.src_mode = DMA_HANDSHAKE_MODE_HANDSHAKE;
275 ch_config.src_burst_size = DMA_NUM_TRANSFER_PER_BURST_1T;
276
277 if (status_success != dma_setup_channel(dma_resource.base, dma_resource.channel, &ch_config, true)) {
278 LOG_E("dma setup channel failed\n");
279 return -RT_ERROR;
280 }
281
282 if (l1c_dc_is_enabled()) {
283 /* cache invalidate for receive buff */
284 l1c_dc_invalidate((uint32_t)hpm_pdm_dev.rx_fifo, PDM_FIFO_SIZE);
285 }
286
287 return RT_EOK;
288 }
289
290
291 static struct rt_audio_ops hpm_pdm_ops =
292 {
293 .getcaps = hpm_pdm_getcaps,
294 .configure = hpm_pdm_configure,
295 .init = hpm_pdm_init,
296 .start = hpm_pdm_start,
297 .stop = hpm_pdm_stop,
298 .transmit = NULL,
299 .buffer_info = NULL,
300 };
301
ATTR_ALIGN(HPM_L1C_CACHELINE_SIZE)302 ATTR_ALIGN(HPM_L1C_CACHELINE_SIZE) uint8_t pdm_rx_fifo[PDM_FIFO_SIZE];
303
304 int rt_hw_pdm_init(void)
305 {
306 hpm_pdm_dev.rx_fifo = pdm_rx_fifo;
307
308 hpm_pdm_dev.audio.ops = &hpm_pdm_ops;
309
310 LOG_I("audio pdm registered.\n");
311 LOG_I("!!!Note: pdm depends on i2s0, they share clock.\n");
312 rt_audio_register(&hpm_pdm_dev.audio, "pdm", RT_DEVICE_FLAG_RDONLY, &hpm_pdm_dev);
313
314 return RT_EOK;
315 }
316 INIT_DEVICE_EXPORT(rt_hw_pdm_init);
317
318 #endif /* BSP_USING_PDM */
319