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