1 /*
2 * Copyright (c) 2021-2023 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_i2s_drv.h"
9
10 #define HPM_I2S_DRV_DEFAULT_RETRY_COUNT 5000U
11
12 #ifndef HPM_I2S_BCLK_TOLERANCE
13 #define HPM_I2S_BCLK_TOLERANCE (4U)
14 #endif
15
16 #define HPM_I2S_SLOT_MASK I2S_TXDSLOT_EN_MASK /* TX/RX has same SLOT MASK */
17
18
i2s_audio_depth_is_valid(uint8_t bits)19 static bool i2s_audio_depth_is_valid(uint8_t bits)
20 {
21 /* i2s audio depth only support 16bits, 24bits, 32bits */
22 if (bits == i2s_audio_depth_16_bits || bits == i2s_audio_depth_24_bits || bits == i2s_audio_depth_32_bits) {
23 return true;
24 }
25 return false;
26 }
27
i2s_channel_length_is_valid(uint8_t bits)28 static bool i2s_channel_length_is_valid(uint8_t bits)
29 {
30 /* i2s channel length only support 16bits or 32bits */
31 if (bits == i2s_channel_length_16_bits || bits == i2s_channel_length_32_bits) {
32 return true;
33 }
34 return false;
35 }
36
37 /* work around: fill dummy data into TX fifo to avoid TX underflow during tx start */
i2s_fill_tx_dummy_data(I2S_Type * ptr,uint8_t data_line,uint8_t data_count)38 hpm_stat_t i2s_fill_tx_dummy_data(I2S_Type *ptr, uint8_t data_line, uint8_t data_count)
39 {
40 uint32_t retry = 0;
41
42 if (data_count > I2S_SOC_MAX_TX_FIFO_DEPTH) {
43 return status_invalid_argument;
44 }
45
46 /* check dummy data count in TX FIFO */
47 while (i2s_get_tx_line_fifo_level(ptr, data_line) < data_count) {
48 ptr->TXD[data_line] = 0;
49 if (retry > HPM_I2S_DRV_DEFAULT_RETRY_COUNT * data_count) {
50 return false;
51 }
52 retry++;
53 }
54
55 return status_success;
56 }
57
i2s_reset_all(I2S_Type * ptr)58 void i2s_reset_all(I2S_Type *ptr)
59 {
60 /* disable I2S */
61 ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK;
62 /* gate off bclk */
63 ptr->CFGR |= I2S_CFGR_BCLK_GATEOFF_MASK;
64 /* gate off mclk */
65 ptr->MISC_CFGR |= I2S_MISC_CFGR_MCLK_GATEOFF_MASK;
66
67 /* reset function block and clear fifo */
68 ptr->CTRL |= (I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_CLKGEN_MASK | I2S_CTRL_SFTRST_TX_MASK | I2S_CTRL_SFTRST_RX_MASK);
69 ptr->CTRL &= ~(I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_CLKGEN_MASK | I2S_CTRL_SFTRST_TX_MASK | I2S_CTRL_SFTRST_RX_MASK);
70 }
71
i2s_get_default_config(I2S_Type * ptr,i2s_config_t * config)72 void i2s_get_default_config(I2S_Type *ptr, i2s_config_t *config)
73 {
74 (void) ptr;
75 config->invert_mclk_out = false;
76 config->invert_mclk_in = false;
77 config->use_external_mclk = false;
78 config->invert_bclk_out = false;
79 config->invert_bclk_in = false;
80 config->use_external_bclk = false;
81 config->invert_fclk_out = false;
82 config->invert_fclk_in = false;
83 config->use_external_fclk = false;
84 config->enable_mclk_out = false;
85 config->frame_start_at_rising_edge = false;
86 config->fifo_threshold = 4;
87 }
88
i2s_init(I2S_Type * ptr,i2s_config_t * config)89 void i2s_init(I2S_Type *ptr, i2s_config_t *config)
90 {
91 i2s_reset_all(ptr);
92
93 ptr->CFGR = I2S_CFGR_INV_MCLK_OUT_SET(config->invert_mclk_out)
94 | I2S_CFGR_INV_MCLK_IN_SET(config->invert_mclk_in)
95 | I2S_CFGR_MCK_SEL_OP_SET(config->use_external_mclk)
96 | I2S_CFGR_INV_BCLK_OUT_SET(config->invert_bclk_out)
97 | I2S_CFGR_INV_BCLK_IN_SET(config->invert_bclk_in)
98 | I2S_CFGR_BCLK_SEL_OP_SET(config->use_external_bclk)
99 | I2S_CFGR_INV_FCLK_OUT_SET(config->invert_fclk_out)
100 | I2S_CFGR_INV_FCLK_IN_SET(config->invert_fclk_in)
101 | I2S_CFGR_FCLK_SEL_OP_SET(config->use_external_fclk)
102 | I2S_CFGR_FRAME_EDGE_SET(config->frame_start_at_rising_edge);
103 ptr->MISC_CFGR = (ptr->MISC_CFGR
104 & ~(I2S_MISC_CFGR_MCLKOE_MASK
105 | I2S_MISC_CFGR_MCLK_GATEOFF_MASK))
106 | I2S_MISC_CFGR_MCLKOE_SET(config->enable_mclk_out);
107 ptr->FIFO_THRESH = I2S_FIFO_THRESH_TX_SET(config->fifo_threshold)
108 | I2S_FIFO_THRESH_RX_SET(config->fifo_threshold);
109 }
110
i2s_config_cfgr(I2S_Type * ptr,uint32_t bclk_div,i2s_transfer_config_t * config)111 static void i2s_config_cfgr(I2S_Type *ptr,
112 uint32_t bclk_div,
113 i2s_transfer_config_t *config)
114 {
115 i2s_gate_bclk(ptr);
116 ptr->CFGR = (ptr->CFGR & ~(I2S_CFGR_BCLK_DIV_MASK | I2S_CFGR_TDM_EN_MASK | I2S_CFGR_CH_MAX_MASK | I2S_CFGR_STD_MASK | I2S_CFGR_DATSIZ_MASK | I2S_CFGR_CHSIZ_MASK))
117 | I2S_CFGR_BCLK_DIV_SET(bclk_div)
118 | I2S_CFGR_TDM_EN_SET(config->enable_tdm_mode)
119 | I2S_CFGR_CH_MAX_SET(config->channel_num_per_frame)
120 | I2S_CFGR_STD_SET(config->protocol)
121 | I2S_CFGR_DATSIZ_SET(I2S_CFGR_DATASIZ(config->audio_depth))
122 | I2S_CFGR_CHSIZ_SET(I2S_CFGR_CHSIZ(config->channel_length));
123 i2s_ungate_bclk(ptr);
124 }
125
i2s_config_cfgr_slave(I2S_Type * ptr,i2s_transfer_config_t * config)126 static void i2s_config_cfgr_slave(I2S_Type *ptr,
127 i2s_transfer_config_t *config)
128 {
129 ptr->CFGR = (ptr->CFGR & ~(I2S_CFGR_TDM_EN_MASK | I2S_CFGR_CH_MAX_MASK | I2S_CFGR_STD_MASK | I2S_CFGR_DATSIZ_MASK | I2S_CFGR_CHSIZ_MASK))
130 | I2S_CFGR_TDM_EN_SET(config->enable_tdm_mode)
131 | I2S_CFGR_CH_MAX_SET(config->channel_num_per_frame)
132 | I2S_CFGR_STD_SET(config->protocol)
133 | I2S_CFGR_DATSIZ_SET(I2S_CFGR_DATASIZ(config->audio_depth))
134 | I2S_CFGR_CHSIZ_SET(I2S_CFGR_CHSIZ(config->channel_length));
135 }
136
i2s_calculate_bclk_divider(uint32_t mclk_in_hz,uint32_t bclk_in_hz,uint32_t * div_out)137 static bool i2s_calculate_bclk_divider(uint32_t mclk_in_hz, uint32_t bclk_in_hz, uint32_t *div_out)
138 {
139 uint32_t bclk_div;
140 uint32_t delta1, delta2;
141
142 bclk_div = mclk_in_hz / bclk_in_hz;
143
144 if ((bclk_div > (I2S_CFGR_BCLK_DIV_MASK >> I2S_CFGR_BCLK_DIV_SHIFT))) {
145 return false;
146 }
147
148 delta1 = mclk_in_hz - bclk_in_hz * bclk_div;
149 delta2 = bclk_in_hz * (bclk_div + 1) - mclk_in_hz;
150 if (delta2 < delta1) {
151 bclk_div++;
152 if ((bclk_div > (I2S_CFGR_BCLK_DIV_MASK >> I2S_CFGR_BCLK_DIV_SHIFT))) {
153 return false;
154 }
155 }
156
157 if (MIN(delta1, delta2) && ((MIN(delta1, delta2) * 100 / bclk_in_hz) > HPM_I2S_BCLK_TOLERANCE)) {
158 return false;
159 }
160
161 *div_out = bclk_div;
162 return true;
163 }
164
_i2s_config_tx(I2S_Type * ptr,i2s_transfer_config_t * config)165 static hpm_stat_t _i2s_config_tx(I2S_Type *ptr, i2s_transfer_config_t *config)
166 {
167 /* channel_num_per_frame has to even. non TDM mode, it has be 2 */
168 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
169 if (!i2s_audio_depth_is_valid(config->audio_depth)
170 || !i2s_channel_length_is_valid(config->channel_length)
171 || !config->sample_rate
172 || !channel_num_per_frame
173 || (channel_num_per_frame > I2S_SOC_MAX_CHANNEL_NUM)
174 || ((!config->enable_tdm_mode) && (channel_num_per_frame > 2))
175 || ((config->channel_slot_mask & HPM_I2S_SLOT_MASK) == 0)) {
176 return status_invalid_argument;
177 }
178
179 ptr->TXDSLOT[config->data_line] = config->channel_slot_mask;
180
181 /* work around: fill dummy data into TX fifo to avoid TX underflow during tx start */
182 if (i2s_fill_tx_dummy_data(ptr, config->data_line, config->channel_num_per_frame) != status_success) {
183 return status_invalid_argument;
184 }
185
186 ptr->CTRL = (ptr->CTRL & ~(I2S_CTRL_TX_EN_MASK))
187 | I2S_CTRL_TX_EN_SET(1 << config->data_line);
188
189 return status_success;
190 }
191
_i2s_config_rx(I2S_Type * ptr,i2s_transfer_config_t * config)192 static hpm_stat_t _i2s_config_rx(I2S_Type *ptr, i2s_transfer_config_t *config)
193 {
194 /* channel_num_per_frame has to even. non TDM mode, it has be 2 */
195 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
196 if (!i2s_audio_depth_is_valid(config->audio_depth)
197 || !i2s_channel_length_is_valid(config->channel_length)
198 || !config->sample_rate
199 || !channel_num_per_frame
200 || (channel_num_per_frame > I2S_SOC_MAX_CHANNEL_NUM)
201 || ((!config->enable_tdm_mode) && (channel_num_per_frame > 2))
202 || ((config->channel_slot_mask & HPM_I2S_SLOT_MASK) == 0)) {
203 return status_invalid_argument;
204 }
205
206 ptr->RXDSLOT[config->data_line] = config->channel_slot_mask;
207 ptr->CTRL = (ptr->CTRL & ~(I2S_CTRL_RX_EN_MASK))
208 | I2S_CTRL_RX_EN_SET(1 << config->data_line);
209
210 return status_success;
211 }
212
_i2s_config_transfer(I2S_Type * ptr,i2s_transfer_config_t * config)213 static hpm_stat_t _i2s_config_transfer(I2S_Type *ptr, i2s_transfer_config_t *config)
214 {
215 /* channel_num_per_frame has to even. non TDM mode, it has be 2 */
216 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
217 if (!i2s_audio_depth_is_valid(config->audio_depth)
218 || !i2s_channel_length_is_valid(config->channel_length)
219 || !config->sample_rate
220 || !channel_num_per_frame
221 || (channel_num_per_frame > I2S_SOC_MAX_CHANNEL_NUM)
222 || ((!config->enable_tdm_mode) && (channel_num_per_frame > 2))
223 || ((config->channel_slot_mask & HPM_I2S_SLOT_MASK) == 0)) {
224 return status_invalid_argument;
225 }
226
227 /* Suppose RX and TX use same channel */
228 ptr->RXDSLOT[config->data_line] = config->channel_slot_mask;
229 ptr->TXDSLOT[config->data_line] = config->channel_slot_mask;
230
231 /* work around: fill dummy data into TX fifo to avoid TX underflow during tx start */
232 if (i2s_fill_tx_dummy_data(ptr, config->data_line, config->channel_num_per_frame) != status_success) {
233 return status_invalid_argument;
234 }
235
236 ptr->CTRL = (ptr->CTRL & ~(I2S_CTRL_RX_EN_MASK | I2S_CTRL_TX_EN_MASK))
237 | I2S_CTRL_RX_EN_SET(1 << config->data_line)
238 | I2S_CTRL_TX_EN_SET(1 << config->data_line);
239
240 return status_success;
241 }
242
i2s_config_tx(I2S_Type * ptr,uint32_t mclk_in_hz,i2s_transfer_config_t * config)243 hpm_stat_t i2s_config_tx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config)
244 {
245 uint32_t bclk_in_hz;
246 uint32_t bclk_div;
247 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
248
249 bclk_in_hz = config->sample_rate * config->channel_length * channel_num_per_frame;
250 if (!i2s_calculate_bclk_divider(mclk_in_hz, bclk_in_hz, &bclk_div)) {
251 return status_invalid_argument;
252 }
253
254 i2s_disable(ptr);
255 i2s_config_cfgr(ptr, bclk_div, config);
256
257 return _i2s_config_tx(ptr, config);
258 }
259
i2s_config_tx_slave(I2S_Type * ptr,i2s_transfer_config_t * config)260 hpm_stat_t i2s_config_tx_slave(I2S_Type *ptr, i2s_transfer_config_t *config)
261 {
262 i2s_disable(ptr);
263 i2s_config_cfgr_slave(ptr, config);
264
265 return _i2s_config_tx(ptr, config);
266 }
267
i2s_config_rx(I2S_Type * ptr,uint32_t mclk_in_hz,i2s_transfer_config_t * config)268 hpm_stat_t i2s_config_rx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config)
269 {
270 uint32_t bclk_in_hz;
271 uint32_t bclk_div;
272
273 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
274 bclk_in_hz = config->sample_rate * config->channel_length * channel_num_per_frame;
275 if (!i2s_calculate_bclk_divider(mclk_in_hz, bclk_in_hz, &bclk_div)) {
276 return status_invalid_argument;
277 }
278
279 i2s_disable(ptr);
280 i2s_config_cfgr(ptr, bclk_div, config);
281
282 return _i2s_config_rx(ptr, config);
283 }
284
i2s_config_rx_slave(I2S_Type * ptr,i2s_transfer_config_t * config)285 hpm_stat_t i2s_config_rx_slave(I2S_Type *ptr, i2s_transfer_config_t *config)
286 {
287 i2s_disable(ptr);
288 i2s_config_cfgr_slave(ptr, config);
289
290 return _i2s_config_rx(ptr, config);
291 }
292
i2s_config_transfer(I2S_Type * ptr,uint32_t mclk_in_hz,i2s_transfer_config_t * config)293 hpm_stat_t i2s_config_transfer(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config)
294 {
295 uint32_t bclk_in_hz;
296 uint32_t bclk_div;
297
298 uint8_t channel_num_per_frame = HPM_NUM_TO_EVEN_CEILING(config->channel_num_per_frame);
299 bclk_in_hz = config->sample_rate * config->channel_length * channel_num_per_frame;
300 if (!i2s_calculate_bclk_divider(mclk_in_hz, bclk_in_hz, &bclk_div)) {
301 return status_invalid_argument;
302 }
303
304 i2s_disable(ptr);
305 i2s_config_cfgr(ptr, bclk_div, config);
306
307 return _i2s_config_transfer(ptr, config);
308 }
309
i2s_config_transfer_slave(I2S_Type * ptr,i2s_transfer_config_t * config)310 hpm_stat_t i2s_config_transfer_slave(I2S_Type *ptr, i2s_transfer_config_t *config)
311 {
312 i2s_disable(ptr);
313 i2s_config_cfgr_slave(ptr, config);
314
315 return _i2s_config_transfer(ptr, config);
316 }
317
i2s_send_buff(I2S_Type * ptr,uint8_t tx_line_index,uint8_t samplebits,uint8_t * src,uint32_t size)318 uint32_t i2s_send_buff(I2S_Type *ptr, uint8_t tx_line_index, uint8_t samplebits, uint8_t *src, uint32_t size)
319 {
320 uint32_t data;
321 uint32_t retry = 0;
322 uint8_t bytes = samplebits / 8U;
323 uint32_t left;
324
325 if (!i2s_audio_depth_is_valid(samplebits)) {
326 return 0;
327 }
328
329 if ((size % bytes) != 0) {
330 return 0;
331 }
332
333 left = size;
334 while (left) {
335 /* check fifo status */
336 if (i2s_get_tx_line_fifo_level(ptr, tx_line_index) < I2S_FIFO_THRESH_TX_GET(ptr->FIFO_THRESH)) {
337 /* Move valid data to high position */
338 data = *((uint32_t *)(src)) << (32 - samplebits);
339 ptr->TXD[tx_line_index] = data;
340 src += bytes;
341 left -= bytes;
342 retry = 0;
343 } else {
344 if (retry > HPM_I2S_DRV_DEFAULT_RETRY_COUNT) {
345 break;
346 }
347 retry++;
348 }
349 }
350
351 return size - left;
352 }
353
i2s_receive_buff(I2S_Type * ptr,uint8_t rx_line_index,uint8_t samplebits,uint8_t * dst,uint32_t size)354 uint32_t i2s_receive_buff(I2S_Type *ptr, uint8_t rx_line_index, uint8_t samplebits, uint8_t *dst, uint32_t size)
355 {
356 uint32_t data;
357 uint32_t left;
358 uint32_t retry = 0;
359 uint8_t bytes = samplebits / 8U;
360
361 if (!i2s_audio_depth_is_valid(samplebits)) {
362 return 0;
363 }
364
365 if ((size % bytes) != 0) {
366 return 0;
367 }
368
369 left = size;
370 while (left) {
371 /* check fifo status */
372 if (i2s_get_rx_line_fifo_level(ptr, rx_line_index) < I2S_FIFO_THRESH_RX_GET(ptr->FIFO_THRESH)) {
373 /* valid data on high position */
374 data = ptr->RXD[rx_line_index] >> (32 - samplebits);
375 for (uint8_t n = 0; n < bytes; n++) {
376 *dst = (uint8_t)(data >> (8U * n)) & 0xFFU;
377 dst++;
378 left--;
379 retry = 0;
380 }
381 } else {
382 if (retry > HPM_I2S_DRV_DEFAULT_RETRY_COUNT) {
383 break;
384 }
385 retry++;
386 }
387 }
388
389 return size - left;
390 }
391
i2s_get_default_transfer_config_for_pdm(i2s_transfer_config_t * transfer)392 void i2s_get_default_transfer_config_for_pdm(i2s_transfer_config_t *transfer)
393 {
394 transfer->sample_rate = PDM_SOC_SAMPLE_RATE_IN_HZ;
395 transfer->channel_num_per_frame = 8;
396 transfer->channel_length = i2s_channel_length_32_bits;
397 transfer->audio_depth = i2s_audio_depth_32_bits;
398 transfer->enable_tdm_mode = true;
399 transfer->protocol = I2S_PROTOCOL_MSB_JUSTIFIED;
400 }
401
i2s_get_default_transfer_config_for_dao(i2s_transfer_config_t * transfer)402 void i2s_get_default_transfer_config_for_dao(i2s_transfer_config_t *transfer)
403 {
404 transfer->sample_rate = DAO_SOC_SAMPLE_RATE_IN_HZ;
405 transfer->channel_num_per_frame = 2;
406 transfer->channel_length = i2s_channel_length_32_bits;
407 transfer->audio_depth = i2s_audio_depth_32_bits;
408 transfer->enable_tdm_mode = false;
409 transfer->protocol = I2S_PROTOCOL_MSB_JUSTIFIED;
410 transfer->data_line = I2S_DATA_LINE_0;
411 transfer->channel_slot_mask = 0x3;
412 }
413
i2s_get_default_transfer_config(i2s_transfer_config_t * transfer)414 void i2s_get_default_transfer_config(i2s_transfer_config_t *transfer)
415 {
416 transfer->sample_rate = 48000U;
417 transfer->channel_num_per_frame = 2;
418 transfer->channel_length = i2s_channel_length_32_bits;
419 transfer->audio_depth = i2s_audio_depth_32_bits;
420 transfer->enable_tdm_mode = false;
421 transfer->protocol = I2S_PROTOCOL_MSB_JUSTIFIED;
422 transfer->data_line = I2S_DATA_LINE_0;
423 transfer->channel_slot_mask = 0x3;
424 }
425