1 /*******************************************************************************
2 * File Name: cyhal_audio_common.c
3 *
4 * Description:
5 * Provides common functionality for the I2S and TDM audio drivers.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
10 * an affiliate of Cypress Semiconductor Corporation
11 *
12 * SPDX-License-Identifier: Apache-2.0
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 *******************************************************************************/
26
27 #include <math.h>
28 #include <stdlib.h>
29 #include <string.h> // For memset
30 #include "cyhal_clock.h"
31 #include "cyhal_gpio.h"
32 #include "cyhal_hw_resources.h"
33 #include "cyhal_hwmgr.h"
34 #include "cyhal_utils.h"
35 #include "cyhal_irq_impl.h"
36 #include "cyhal_dma.h"
37 #include "cyhal_syspm.h"
38 #include "cy_device.h"
39 #include "cyhal_audio_common.h"
40 #include "cyhal_system.h"
41
42 #if (CYHAL_DRIVER_AVAILABLE_I2S || CYHAL_DRIVER_AVAILABLE_TDM)
43
44 #if defined(__cplusplus)
45 extern "C"
46 {
47 #endif
48
49 #if defined(COMPONENT_CAT2)
50 /* PDL register naming varies slightly between CAT1 and CAT2 */
51 #define REG_I2S_CTL(base) I2S_CTL(base)
52 #define REG_I2S_CLOCK_CTL(base) I2S_CLOCK_CTL(base)
53 #define REG_I2S_TX_CTL(base) I2S_TX_CTL(base)
54 #define REG_I2S_TX_FIFO_CTL(base) I2S_TX_FIFO_CTL(base)
55 #else
56 #define _CYHAL_AUDIOSS_TX_SLAVE_AVAILABLE
57 #define _CYHAL_AUDIOSS_TX_WATCHDOG_AVAILABLE
58 /* Some devices have an extra layer of hierarchy in their parameters */
59 #if defined(AUDIOSS0_I2S_I2S)
60 #define AUDIOSS0_I2S (AUDIOSS0_I2S_I2S)
61 #endif
62 #if defined(AUDIOSS1_I2S_I2S)
63 #define AUDIOSS1_I2S (AUDIOSS1_I2S_I2S)
64 #endif
65 #if defined(AUDIOSS2_I2S_I2S)
66 #define AUDIOSS2_I2S (AUDIOSS2_I2S_I2S)
67 #endif
68 #endif
69
70 #define _CYHAL_AUDIOSS_MAX_CHANNEL_LENGTH (32u)
71
72 #if defined(CY_IP_MXAUDIOSS)
73 static cy_rslt_t _cyhal_audioss_length_to_pdl(uint8_t user_length, cy_en_i2s_len_t *pdl_length, const _cyhal_audioss_t *obj);
74 static uint8_t _cyhal_audioss_length_from_pdl(cy_en_i2s_len_t pdl_length);
75 #define _CYHAL_AUDIOSS_FIFO_DEPTH (256)
76
77 #define _CYHAL_AUDIOSS_TX_SCK_MAP cyhal_pin_map_audioss_tx_sck
78 #define _CYHAL_AUDIOSS_TX_WS_MAP cyhal_pin_map_audioss_tx_ws
79 #define _CYHAL_AUDIOSS_TX_SDO_MAP cyhal_pin_map_audioss_tx_sdo
80
81 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_SCK CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_TX_SCK
82 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_WS CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_TX_WS
83 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_SDO CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_TX_SDO
84
85 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
86 #define _CYHAL_AUDIOSS_RX_SCK_MAP cyhal_pin_map_audioss_rx_sck
87 #define _CYHAL_AUDIOSS_RX_WS_MAP cyhal_pin_map_audioss_rx_ws
88 #define _CYHAL_AUDIOSS_RX_SDI_MAP cyhal_pin_map_audioss_rx_sdi
89
90 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_SCK CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_RX_SCK
91 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_WS CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_RX_WS
92 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_SDI CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_RX_SDI
93 #endif
94
95
96 static I2S_Type *const _cyhal_audioss_base[] =
97 {
98 #if (CY_IP_MXAUDIOSS_INSTANCES == 1 && defined(AUDIOSS_I2S) && AUDIOSS_I2S)
99 I2S,
100 #elif (CY_IP_MXAUDIOSS_INSTANCES >= 1 && ((defined(AUDIOSS_I2S_I2S) && AUDIOSS_I2S_I2S) || (defined(AUDIOSS0_I2S) && AUDIOSS0_I2S) || (defined(AUDIOSS0_I2S_I2S) && AUDIOSS0_I2S_I2S)))
101 I2S0,
102 #endif
103 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && ((defined(AUDIOSS1_I2S) && AUDIOSS1_I2S) || (defined(AUDIOSS1_I2S_I2S) && AUDIOSS1_I2S_I2S)))
104 I2S1,
105 #endif
106 #if (CY_IP_MXAUDIOSS_INSTANCES >= 3 && ((defined(AUDIOSS2_I2S) && AUDIOSS2_I2S) || (defined(AUDIOSS2_I2S_I2S) && AUDIOSS2_I2S_I2S)))
107 I2S2,
108 #endif
109
110 #if (CY_IP_MXS40AUDIOSS_INSTANCES > 2)
111 #warning Unhandled audioss instance count
112 #endif
113 };
114
115 #if (!defined(COMPONENT_CAT2))
116 #define _CYHAL_AUDIOSS_TRIGGERS_AVAILABLE
117 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
118 static const cyhal_source_t _cyhal_audioss_rx_trigger[] =
119 {
120 #if (CY_IP_MXAUDIOSS_INSTANCES == 1 && defined(AUDIOSS_I2S) && AUDIOSS_I2S)
121 CYHAL_TRIGGER_AUDIOSS_TR_I2S_RX_REQ,
122 #elif (CY_IP_MXAUDIOSS_INSTANCES >= 1 && defined(AUDIOSS0_I2S) && AUDIOSS0_I2S)
123 CYHAL_TRIGGER_AUDIOSS0_TR_I2S_RX_REQ,
124 #endif
125 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && defined(AUDIOSS1_I2S) && AUDIOSS1_I2S)
126 CYHAL_TRIGGER_AUDIOSS1_TR_I2S_RX_REQ,
127 #endif
128 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && defined(AUDIOSS2_I2S) && AUDIOSS2_I2S)
129 CYHAL_TRIGGER_AUDIOSS2_TR_I2S_RX_REQ,
130 #endif
131
132 #if (CY_IP_MXS40AUDIOSS_INSTANCES > 3)
133 #warning Unhandled audioss instance count
134 #endif
135 };
136 #endif
137
138 static const cyhal_source_t _cyhal_audioss_tx_trigger[] =
139 {
140 #if (CY_IP_MXAUDIOSS_INSTANCES == 1 && defined(AUDIOSS_I2S) && AUDIOSS_I2S)
141 CYHAL_TRIGGER_AUDIOSS_TR_I2S_TX_REQ,
142 #elif (CY_IP_MXAUDIOSS_INSTANCES >= 1 && defined(AUDIOSS0_I2S) && AUDIOSS0_I2S)
143 CYHAL_TRIGGER_AUDIOSS0_TR_I2S_TX_REQ,
144 #endif
145 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && defined(AUDIOSS1_I2S) && AUDIOSS1_I2S)
146 CYHAL_TRIGGER_AUDIOSS1_TR_I2S_TX_REQ,
147 #endif
148 #if (CY_IP_MXAUDIOSS_INSTANCES >= 3 && defined(AUDIOSS2_I2S) && AUDIOSS2_I2S)
149 CYHAL_TRIGGER_AUDIOSS2_TR_I2S_TX_REQ,
150 #endif
151
152 #if (CY_IP_MXS40AUDIOSS_INSTANCES > 3)
153 #warning Unhandled audioss instance count
154 #endif
155 };
156 #endif
157
158 static _cyhal_audioss_t* _cyhal_audioss_config_structs[CY_IP_MXAUDIOSS_INSTANCES];
159
160 static const _cyhal_system_irq_t _cyhal_audioss_irq_n[] =
161 {
162 #if (CY_IP_MXAUDIOSS_INSTANCES == 1 && (defined(AUDIOSS_I2S) && AUDIOSS_I2S || defined(AUDIOSS_I2S_I2S) && AUDIOSS_I2S_I2S)) // Without index suffix
163 audioss_interrupt_i2s_IRQn,
164 #elif (CY_IP_MXAUDIOSS_INSTANCES >= 1 && defined(AUDIOSS0_I2S) && AUDIOSS0_I2S)
165 audioss_0_interrupt_i2s_IRQn,
166 #endif
167 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && defined(AUDIOSS1_I2S) && AUDIOSS1_I2S)
168 audioss_1_interrupt_i2s_IRQn,
169 #endif
170 #if (CY_IP_MXAUDIOSS_INSTANCES >= 3 && defined(AUDIOSS2_I2S) && AUDIOSS2_I2S)
171 audioss_2_interrupt_i2s_IRQn,
172 #endif
173
174 #if (CY_IP_MXS40AUDIOSS_INSTANCES > 3)
175 #warning Unhandled audioss instance count
176 #endif
177 };
178
_cyhal_audioss_get_block_from_irqn(_cyhal_system_irq_t irqn)179 static uint8_t _cyhal_audioss_get_block_from_irqn(_cyhal_system_irq_t irqn)
180 {
181 switch (irqn)
182 {
183 #if !defined(CY_CPU_CORTEX_M4) || (CY_CPU_CORTEX_M4) || defined(COMPONENT_CAT1C)
184 #if (CY_IP_MXAUDIOSS_INSTANCES == 1 && (defined(AUDIOSS_I2S) && AUDIOSS_I2S || defined(AUDIOSS_I2S_I2S) && AUDIOSS_I2S_I2S)) // Without index suffix
185 case audioss_interrupt_i2s_IRQn:
186 return 0;
187 #elif (CY_IP_MXAUDIOSS_INSTANCES >= 1 && defined(AUDIOSS0_I2S) && AUDIOSS0_I2S)
188 case audioss_0_interrupt_i2s_IRQn:
189 return 0;
190 #endif
191 #if (CY_IP_MXAUDIOSS_INSTANCES >= 2 && defined(AUDIOSS1_I2S) && AUDIOSS1_I2S)
192 case audioss_1_interrupt_i2s_IRQn:
193 return 1;
194 #endif
195 #if (CY_IP_MXAUDIOSS_INSTANCES >= 3 && defined(AUDIOSS2_I2S) && AUDIOSS2_I2S)
196 case audioss_2_interrupt_i2s_IRQn:
197 return 2;
198 #endif
199 #if (CY_IP_MXS40AUDIOSS_INSTANCES > 3)
200 #warning Unhandled audioss instance count
201 #endif
202 #endif /* (CY_CPU_CORTEX_M4) */
203 default:
204 CY_ASSERT(false); // Should never be called with a non-I2S IRQn
205 return 0;
206 }
207 }
208
209 #if defined(COMPONENT_CAT2) /* PSoC™ 4 uses a PCLK */
210 #define _CYHAL_AUDIOSS_USES_PCLK
211 static const en_clk_dst_t _cyhal_audioss_clock[] =
212 {
213 #if (CY_IP_MXAUDIOSS_INSTANCES == 1)
214 PCLK_AUDIOSS_CLOCK_POS_I2S_EN,
215 #else
216 #warning Unhandled audioss instance count
217 #endif
218 };
219 #endif
220
221 static void _cyhal_audioss_irq_handler(void);
222 typedef cy_stc_i2s_config_t _cyhal_audioss_pdl_config_t;
223
224 #elif defined(CY_IP_MXTDM) && (CY_IP_MXTDM_INSTANCES > 0)
225 #define _CYHAL_AUDIOSS_FIFO_DEPTH (128)
226
227 #define _CYHAL_AUDIOSS_TX_SCK_MAP cyhal_pin_map_tdm_tdm_tx_sck
228 #define _CYHAL_AUDIOSS_TX_WS_MAP cyhal_pin_map_tdm_tdm_tx_fsync
229 #define _CYHAL_AUDIOSS_TX_SDO_MAP cyhal_pin_map_tdm_tdm_tx_sd
230 #define _CYHAL_AUDIOSS_TX_MCK_MAP cyhal_pin_map_tdm_tdm_tx_mck
231 #define _CYHAL_AUDIOSS_RX_SCK_MAP cyhal_pin_map_tdm_tdm_rx_sck
232 #define _CYHAL_AUDIOSS_RX_WS_MAP cyhal_pin_map_tdm_tdm_rx_fsync
233 #define _CYHAL_AUDIOSS_RX_SDI_MAP cyhal_pin_map_tdm_tdm_rx_sd
234 #define _CYHAL_AUDIOSS_RX_MCK_MAP cyhal_pin_map_tdm_tdm_rx_mck
235
236 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_SCK CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_TX_SCK
237 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_WS CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_TX_FSYNC
238 #define _CYHAL_AUDIOSS_DRIVE_MODE_TX_SDO CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_TX_SD
239 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_SCK CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_RX_SCK
240 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_WS CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_RX_FSYNC
241 #define _CYHAL_AUDIOSS_DRIVE_MODE_RX_SDI CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_RX_SD
242
243 /* Per cy_en_tdm_clock_sel_t, only TX mclk_in is selectable for both.
244 * This is probably a bug in the PDL, will be updated as necessary
245 * following the resolution of DRIVERS-5376
246 */
247 #define Cy_I2S_Init Cy_AudioTDM_Init
248 #define Cy_I2S_DeInit Cy_AudioTDM_DeInit
249
250 static cy_rslt_t _cyhal_audioss_length_to_pdl(uint8_t user_length, cy_en_tdm_ws_t *pdl_length, const _cyhal_audioss_t *obj);
251 static uint8_t _cyhal_audioss_length_from_pdl(cy_en_tdm_ws_t pdl_length);
252
253 static TDM_Type *const _cyhal_audioss_base[] =
254 {
255 #if defined (TDM0)
256 TDM0,
257 #endif
258 #if defined (TDM1)
259 TDM1,
260 #endif
261 };
262
263 static const uint8_t _cyhal_audioss_max_channels[] =
264 {
265 #if (CY_IP_MXTDM_INSTANCES == 1)
266 #if (TDM_NR == 1)
267 TDM_NR_CH_NR,
268 #elif (TDM_NR == 2)
269 TDM_NR0_CH_NR,
270 TDM_NR1_CH_NR,
271 #else
272 #warning Unhandled TDM struct count
273 #endif
274 #elif (CY_IP_MXTDM_INSTANCES == 2)
275 #if (TDM_NR == 2)
276 TDM_NR_CH_NR,
277 #else
278 #warning Unhandled TDM struct count
279 #endif
280 #else
281 #warning Unhandled tdm instance count
282 #endif
283 };
284
285 #if !defined (COMPONENT_CAT5)
286 #define _CYHAL_AUDIOSS_USES_PCLK
287 static const en_clk_dst_t _cyhal_audioss_clock[] =
288 {
289 #if (CY_IP_MXTDM_INSTANCES == 1)
290 PCLK_TDM0_CLK_IF_SRSS0,
291 #else
292 #warning Unhandled tdm instance count
293 #endif
294 };
295
296 #define _CYHAL_AUDIOSS_TRIGGERS_AVAILABLE
297 static const cyhal_source_t _cyhal_audioss_rx_trigger[] =
298 {
299 #if (CY_IP_MXTDM_INSTANCES == 1)
300 CYHAL_TRIGGER_TDM_TR_RX_REQ0,
301 #else
302 #warning Unhandled tdm instance count
303 #endif
304 };
305
306 static const cyhal_source_t _cyhal_audioss_tx_trigger[] =
307 {
308 #if (CY_IP_MXTDM_INSTANCES == 1)
309 CYHAL_TRIGGER_TDM_TR_TX_REQ0,
310 #else
311 #warning Unhandled tdm instance count
312 #endif
313 };
314 #endif /* !defined (COMPONENT_CAT5) */
315
316 #if defined(COMPONENT_CAT5)
317 static _cyhal_audioss_t* _cyhal_audioss_config_structs[TDM_NR];
318 #else
319 static _cyhal_audioss_t* _cyhal_audioss_config_structs[CY_IP_MXTDM_INSTANCES];
320 #endif
321
322 // These structures will most probably be cleaned up a bit once we have more details
323 // on TDM interrupts for CAT5. Seems like there are no separate lines for tx and
324 // rx interrupts but it will be confirmed once we have a working patch.
325 static const _cyhal_system_irq_t _cyhal_audioss_tx_irq_n[] =
326 {
327 #if defined (TDM0)
328 #if defined(COMPONENT_CAT5)
329 tdm_0_interrupts_IRQn,
330 #else
331 tdm_0_interrupts_tx_0_IRQn,
332 #endif
333 #endif
334 #if defined (TDM1)
335 #if defined(COMPONENT_CAT5)
336 tdm_1_interrupts_IRQn,
337 #else
338 tdm_1_interrupts_tx_0_IRQn,
339 #endif
340 #endif
341 };
342
343 static const _cyhal_system_irq_t _cyhal_audioss_rx_irq_n[] =
344 {
345 #if defined (TDM0)
346 #if defined(COMPONENT_CAT5)
347 tdm_0_interrupts_IRQn,
348 #else
349 tdm_0_interrupts_rx_0_IRQn,
350 #endif
351 #endif
352 #if defined (TDM1)
353 #if defined(COMPONENT_CAT5)
354 tdm_1_interrupts_IRQn,
355 #else
356 tdm_1_interrupts_rx_0_IRQn,
357 #endif
358 #endif
359 };
360
361 #if !defined (COMPONENT_CAT5)
362 static uint8_t _cyhal_audioss_get_block_from_irqn(_cyhal_system_irq_t irqn)
363 {
364 #if defined (TDM0)
365 if ((irqn == tdm_0_interrupts_tx_0_IRQn) || (irqn == tdm_0_interrupts_rx_0_IRQn))
366 return 0;
367 #endif
368 #if defined (TDM1)
369 if ((irqn == tdm_1_interrupts_tx_0_IRQn) || (irqn == tdm_1_interrupts_rx_0_IRQn))
370 return 1;
371 #endif
372 CY_ASSERT(false); // Should never be called with a non-TDM IRQn
373 return 0;
374 }
375 #endif
376
377 #if defined (COMPONENT_CAT5)
378 static void _cyhal_audioss_irq_handler(UINT8 instance, BOOL8 rx_int);
379 static void _cyhal_audioss_irq_handler_rx(uint8_t instance);
380 static void _cyhal_audioss_irq_handler_tx(uint8_t instance);
381 #else
382 static void _cyhal_audioss_irq_handler_rx(void);
383 static void _cyhal_audioss_irq_handler_tx(void);
384 #endif
385 typedef cy_stc_tdm_config_t _cyhal_audioss_pdl_config_t;
386
387 #else
388 #error "Unrecognized audio IP"
389 #endif
390
391 static void _cyhal_audioss_update_enabled_events(_cyhal_audioss_t* obj);
392 static void _cyhal_audioss_process_event(_cyhal_audioss_t *obj, uint32_t event);
393 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
394 static void _cyhal_audioss_update_rx_trigger_level(_cyhal_audioss_t* obj);
395 static uint32_t _cyhal_audioss_read_fifo(_cyhal_audioss_t *obj);
396 #if (CYHAL_DRIVER_AVAILABLE_DMA)
397 static cy_rslt_t _cyhal_audioss_dma_perform_rx(_cyhal_audioss_t *obj);
398 static void _cyhal_audioss_dma_handler_rx(void *callback_arg, cyhal_dma_event_t event);
399 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
400 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
401 static uint32_t _cyhal_audioss_disable_events(_cyhal_audioss_t *obj, bool tx);
402 static void _cyhal_audioss_restore_events(_cyhal_audioss_t *obj, bool tx, uint32_t old_events);
403 #if (CYHAL_DRIVER_AVAILABLE_DMA)
404 static cy_rslt_t _cyhal_audioss_dma_perform_tx(_cyhal_audioss_t *obj);
405 static void _cyhal_audioss_dma_handler_tx(void *callback_arg, cyhal_dma_event_t event);
406 static uint8_t _cyhal_audioss_rounded_word_length(_cyhal_audioss_t *obj, bool is_tx);
407 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
408 static bool _cyhal_audioss_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg);
409 static cy_rslt_t _cyhal_audioss_populate_pdl_config(_cyhal_audioss_t *obj, _cyhal_audioss_pdl_config_t* pdl_config,
410 const _cyhal_audioss_config_t* hal_cfg,
411 uint16_t sclk_div_rx, uint16_t sclk_div_tx,
412 bool rx_en, bool tx_en, bool mclk_rx, bool mclk_tx);
413 static void _cyhal_audioss_reconstruct_pdl_config(_cyhal_audioss_t *obj, _cyhal_audioss_pdl_config_t* pdl_config);
414 static cy_rslt_t _cyhal_audioss_compute_sclk_div(_cyhal_audioss_t *obj, uint32_t sample_rate_hz, uint32_t mclk_hz, uint8_t channel_length, uint8_t num_channels, uint16_t *sclk_div);
415 static uint32_t _cyhal_audioss_get_num_in_fifo(_cyhal_audioss_t *obj, bool is_tx);
416 static void _cyhal_audioss_write_fifo(_cyhal_audioss_t *obj, uint32_t value);
417
_cyhal_audioss_init_clock(_cyhal_audioss_t * obj,const cyhal_clock_t * clk,bool all_mclk)418 cy_rslt_t _cyhal_audioss_init_clock(_cyhal_audioss_t *obj, const cyhal_clock_t* clk, bool all_mclk)
419 {
420 cy_rslt_t result = CY_RSLT_SUCCESS;
421 if (clk != NULL)
422 {
423 obj->clock = *clk;
424 }
425 else if (false == all_mclk) // No need to reserve a clock if we're using the mclk pin
426 {
427 // The hardware is generally going to be hardwired to an hfclk (or equivalent dedicated clock),
428 // which has very limited divider options. In the event that we're hooked up a PERI divider,
429 // we don't have any particular expectations about its width - so just ask for 16-bit or larger
430 result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
431 if(CY_RSLT_SUCCESS == result)
432 {
433 obj->is_clock_owned = true;
434 result = cyhal_clock_set_enabled(&(obj->clock), true, true);
435 }
436 }
437
438 #if defined(_CYHAL_AUDIOSS_USES_PCLK)
439 // If we're not using MCLK, and we're using a peri divider, hook it up to ourself
440 bool any_mclk = (0u != obj->mclk_hz_tx);
441 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
442 any_mclk = any_mclk || (0u != obj->mclk_hz_rx);
443 #endif
444 if (CY_RSLT_SUCCESS == result && false == any_mclk && obj->clock.block != CYHAL_CLOCK_BLOCK_HF)
445 {
446 en_clk_dst_t pclk = _cyhal_audioss_clock[obj->resource.block_num];
447 if (CY_SYSCLK_SUCCESS != _cyhal_utils_peri_pclk_assign_divider(pclk, &(obj->clock)))
448 result = obj->interface->err_clock;
449 }
450 #endif
451
452 return result;
453 }
454
_cyhal_audioss_init_pdl(_cyhal_audioss_t * obj,const _cyhal_audioss_pdl_config_t * pdl_config)455 cy_rslt_t _cyhal_audioss_init_pdl(_cyhal_audioss_t *obj, const _cyhal_audioss_pdl_config_t* pdl_config)
456 {
457 cy_rslt_t result = (cy_rslt_t)Cy_I2S_Init(obj->base, pdl_config);
458
459 #if defined(CY_IP_MXTDM)
460 if(CY_RSLT_SUCCESS == result)
461 {
462 // PDL init doesn't automatically enable the RX-TX sub-blocks
463 if(pdl_config->tx_config->enable)
464 {
465 Cy_AudioTDM_EnableTx(&obj->base->TDM_TX_STRUCT);
466 }
467 if(pdl_config->rx_config->enable)
468 {
469 Cy_AudioTDM_EnableRx(&obj->base->TDM_RX_STRUCT);
470 }
471 }
472 #endif
473 return result;
474 }
475
_cyhal_audioss_init_hw(_cyhal_audioss_t * obj,const _cyhal_audioss_pdl_config_t * pdl_config)476 cy_rslt_t _cyhal_audioss_init_hw(_cyhal_audioss_t *obj, const _cyhal_audioss_pdl_config_t* pdl_config)
477 {
478 uint8_t tdm_inst = obj->resource.block_num;
479 #if defined(CY_IP_MXAUDIOSS)
480 obj->base = _cyhal_audioss_base[obj->resource.block_num];
481 #elif defined(CY_IP_MXTDM)
482 #if defined (COMPONENT_CAT5)
483 tdm_inst = obj->resource.channel_num;
484 #endif
485 obj->base = &(_cyhal_audioss_base[obj->resource.block_num]->TDM_STRUCT[obj->resource.channel_num]);
486 #endif
487
488 cy_rslt_t result = _cyhal_audioss_init_pdl(obj, pdl_config);
489
490 if (CY_RSLT_SUCCESS == result)
491 {
492 #if defined(CY_IP_MXAUDIOSS) && defined(_CYHAL_AUDIOSS_RX_ENABLED)
493 obj->user_fifo_level_rx = pdl_config->rxFifoTriggerLevel;
494 #elif defined(CY_IP_MXTDM)
495 obj->user_fifo_level_rx = pdl_config->rx_config->fifoTriggerLevel;
496 #endif
497 /* No way to explicitly clear the FIFO on the TDM IP, but
498 * it is cleared as a side effect of the FIFO being disabled
499 * which it should be both at startup and after a free/init cycle
500 */
501 #if defined(CY_IP_MXAUDIOSS)
502 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
503 if(pdl_config->txEnabled)
504 {
505 Cy_I2S_ClearTxFifo(obj->base);
506 }
507 if(pdl_config->rxEnabled)
508 {
509 Cy_I2S_ClearRxFifo(obj->base);
510 }
511 #else
512 Cy_I2S_ClearTxFifo(obj->base);
513 #endif
514 #endif
515
516 obj->pm_callback.states = (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE);
517 obj->pm_callback.callback = &_cyhal_audioss_pm_callback;
518 obj->pm_callback.next = NULL;
519 obj->pm_callback.args = (void*)obj;
520 obj->pm_callback.ignore_modes = (cyhal_syspm_callback_mode_t)(CYHAL_SYSPM_BEFORE_TRANSITION | CYHAL_SYSPM_AFTER_DS_WFI_TRANSITION);
521 obj->pm_transition_ready = false;
522 #if (CYHAL_DRIVER_AVAILABLE_SYSPM)
523 _cyhal_syspm_register_peripheral_callback(&(obj->pm_callback));
524 #endif /* (CYHAL_DRIVER_AVAILABLE_SYSPM) */
525 _cyhal_audioss_config_structs[tdm_inst] = obj;
526 #if defined(CY_IP_MXAUDIOSS)
527 _cyhal_irq_register(_cyhal_audioss_irq_n[tdm_inst], CYHAL_ISR_PRIORITY_DEFAULT, _cyhal_audioss_irq_handler);
528 _cyhal_irq_enable(_cyhal_audioss_irq_n[tdm_inst]);
529 #elif defined(CY_IP_MXTDM)
530 #if defined (COMPONENT_CAT5)
531 Cy_AudioTDM_RegisterInterruptCallback(obj->base, _cyhal_audioss_irq_handler);
532 Cy_AudioTDM_EnableInterrupt(obj->base); // Enables both TX and RX
533 #endif
534
535 _cyhal_irq_register(_cyhal_audioss_rx_irq_n[tdm_inst], CYHAL_ISR_PRIORITY_DEFAULT, (cy_israddress)_cyhal_audioss_irq_handler_rx);
536 _cyhal_irq_enable(_cyhal_audioss_rx_irq_n[tdm_inst]);
537
538 _cyhal_irq_register(_cyhal_audioss_tx_irq_n[tdm_inst], CYHAL_ISR_PRIORITY_DEFAULT, (cy_israddress)_cyhal_audioss_irq_handler_tx );
539 _cyhal_irq_enable(_cyhal_audioss_tx_irq_n[tdm_inst]);
540 #else
541 #error "Unrecognized audio IP"
542 #endif
543 }
544
545 return result;
546 }
547
_cyhal_audioss_init(_cyhal_audioss_t * obj,const _cyhal_audioss_pins_t * tx_pins,const _cyhal_audioss_pins_t * rx_pins,const _cyhal_audioss_config_t * config,cyhal_clock_t * clk,const _cyhal_audioss_interface_t * interface)548 cy_rslt_t _cyhal_audioss_init(_cyhal_audioss_t *obj, const _cyhal_audioss_pins_t* tx_pins, const _cyhal_audioss_pins_t* rx_pins, const _cyhal_audioss_config_t* config, cyhal_clock_t* clk, const _cyhal_audioss_interface_t* interface)
549 {
550 CY_ASSERT(NULL != obj);
551
552 memset(obj, 0, sizeof(_cyhal_audioss_t));
553 /* Explicitly marked not allocated resources as invalid to prevent freeing them. */
554 obj->resource.type = CYHAL_RSC_INVALID;
555 obj->pin_tx_sck = CYHAL_NC_PIN_VALUE;
556 obj->pin_tx_ws = CYHAL_NC_PIN_VALUE;
557 obj->pin_tx_sdo = CYHAL_NC_PIN_VALUE;
558 obj->pin_tx_mclk = CYHAL_NC_PIN_VALUE;
559 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
560 obj->pin_rx_sck = CYHAL_NC_PIN_VALUE;
561 obj->pin_rx_ws = CYHAL_NC_PIN_VALUE;
562 obj->pin_rx_sdi = CYHAL_NC_PIN_VALUE;
563 obj->pin_rx_mclk = CYHAL_NC_PIN_VALUE;
564 #endif
565
566 obj->interface = interface;
567 obj->channel_length_tx = config->channel_length;
568 obj->word_length_tx = config->word_length;
569 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
570 obj->channel_length_rx = config->channel_length;
571 obj->word_length_rx = config->word_length;
572 #endif
573
574 /*
575 * We will update this to owned later if appropriate - for now set to false
576 * so we don't try to free if we fail before allocating a clock
577 */
578 obj->is_clock_owned = false;
579
580 obj->user_enabled_events = 0u;
581
582 obj->callback_data.callback = NULL;
583 obj->callback_data.callback_arg = NULL;
584 obj->async_mode = CYHAL_ASYNC_SW;
585 obj->async_tx_buff = NULL;
586 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
587 obj->async_rx_buff = NULL;
588 #endif
589 obj->tx_dma.resource.type = CYHAL_RSC_INVALID;
590 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
591 obj->rx_dma.resource.type = CYHAL_RSC_INVALID;
592 #endif
593
594 cy_rslt_t result = CY_RSLT_SUCCESS;
595
596 /* Determine which I2S instance to use */
597 const cyhal_resource_pin_mapping_t *tx_sck_map = (NULL != tx_pins) ? _CYHAL_UTILS_GET_RESOURCE(tx_pins->sck, _CYHAL_AUDIOSS_TX_SCK_MAP) : NULL;
598 const cyhal_resource_pin_mapping_t *tx_ws_map = (NULL != tx_pins) ? _CYHAL_UTILS_GET_RESOURCE(tx_pins->ws, _CYHAL_AUDIOSS_TX_WS_MAP) : NULL;
599 const cyhal_resource_pin_mapping_t *tx_sdo_map = (NULL != tx_pins) ? _CYHAL_UTILS_GET_RESOURCE(tx_pins->data, _CYHAL_AUDIOSS_TX_SDO_MAP) : NULL;
600
601 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
602 const cyhal_resource_pin_mapping_t *rx_sck_map = (NULL != rx_pins) ? _CYHAL_UTILS_GET_RESOURCE(rx_pins->sck, _CYHAL_AUDIOSS_RX_SCK_MAP) : NULL;
603 const cyhal_resource_pin_mapping_t *rx_ws_map = (NULL != rx_pins) ? _CYHAL_UTILS_GET_RESOURCE(rx_pins->ws, _CYHAL_AUDIOSS_RX_WS_MAP) : NULL;
604 const cyhal_resource_pin_mapping_t *rx_sdi_map = (NULL != rx_pins) ? _CYHAL_UTILS_GET_RESOURCE(rx_pins->data, _CYHAL_AUDIOSS_RX_SDI_MAP) : NULL;
605 #endif
606
607 const cyhal_resource_pin_mapping_t *mclk_map_rx = NULL;
608 const cyhal_resource_pin_mapping_t *mclk_map_tx = NULL;
609
610 #if !defined(_CYHAL_AUDIOSS_RX_ENABLED)
611 if(rx_pins != NULL)
612 {
613 result = interface->err_not_supported;
614 }
615 #endif
616 #if !defined(_CYHAL_AUDIOSS_TX_SLAVE_AVAILABLE)
617 if(config->is_tx_slave)
618 {
619 result = interface->err_not_supported;
620 }
621 #endif
622 #if defined(CY_IP_MXAUDIOSS)
623 if(NULL != tx_pins && NULL != rx_pins && tx_pins->mclk != rx_pins->mclk)
624 {
625 /* RX and TX must share the same MCLK pin (or lack thereof) on this IP */
626 result = obj->interface->err_invalid_pin;
627 }
628
629 mclk_map_rx = (NULL != rx_pins) ? _CYHAL_UTILS_GET_RESOURCE(rx_pins->mclk, cyhal_pin_map_audioss_clk_i2s_if) : NULL;
630 mclk_map_tx = (NULL != tx_pins) /* If non-null, we know the mclk pins must be the same, so can reuse the rx value */
631 ? ((NULL != mclk_map_rx) ? mclk_map_rx : _CYHAL_UTILS_GET_RESOURCE(tx_pins->mclk, cyhal_pin_map_audioss_clk_i2s_if))
632 : NULL;
633
634 uint8_t mclk_tx_dm = CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_CLK_I2S_IF;
635 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
636 uint8_t mclk_rx_dm = CYHAL_PIN_MAP_DRIVE_MODE_AUDIOSS_CLK_I2S_IF;
637 #endif
638 #elif defined(CY_IP_MXTDM)
639 mclk_map_rx = (NULL != rx_pins) ? _CYHAL_UTILS_GET_RESOURCE(rx_pins->mclk, _CYHAL_AUDIOSS_RX_MCK_MAP) : NULL;
640 mclk_map_tx = (NULL != tx_pins) ? _CYHAL_UTILS_GET_RESOURCE(tx_pins->mclk, _CYHAL_AUDIOSS_TX_MCK_MAP) : NULL;
641 uint8_t mclk_tx_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_TX_MCK;
642 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
643 uint8_t mclk_rx_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_TDM_TDM_RX_MCK;
644 #endif
645 #endif
646
647 #if defined(CY_IP_MXAUDIOSS)
648 cyhal_resource_t rsc_type = CYHAL_RSC_I2S;
649 #elif defined(CY_IP_MXTDM)
650 cyhal_resource_t rsc_type = CYHAL_RSC_TDM;
651 #endif
652
653 if(CY_RSLT_SUCCESS == result && NULL != tx_pins) /* It is valid to leave either tx or rx empty */
654 {
655 if(NULL != tx_sck_map && NULL != tx_ws_map && NULL != tx_sdo_map
656 && _cyhal_utils_map_resources_equal_all(3, tx_sck_map, tx_ws_map, tx_sdo_map))
657 {
658 _CYHAL_UTILS_ASSIGN_RESOURCE(obj->resource, rsc_type, tx_sck_map);
659 }
660 else
661 {
662 result = obj->interface->err_invalid_pin;
663 }
664 }
665
666 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
667 if(CY_RSLT_SUCCESS == result && NULL != rx_pins)
668 {
669 if(NULL == rx_sck_map || NULL == rx_ws_map || NULL == rx_sdi_map ||
670 (false == _cyhal_utils_map_resources_equal_all(3, rx_sck_map, rx_ws_map, rx_sdi_map)))
671 {
672 result = obj->interface->err_invalid_pin;
673 }
674 else
675 {
676 if((obj->resource.type != CYHAL_RSC_INVALID)
677 && (false == _cyhal_utils_map_resource_equal(&(obj->resource), rx_sck_map, false)))
678 {
679 /* TX pins and RX pins don't map to the same instance */
680 result = obj->interface->err_invalid_pin;
681 }
682 _CYHAL_UTILS_ASSIGN_RESOURCE(obj->resource, rsc_type, rx_sck_map);
683 }
684 }
685 #endif
686
687 if(CYHAL_RSC_INVALID == obj->resource.type) /* If this happens it means neither rx nor tx was specified */
688 {
689 result = obj->interface->err_invalid_pin;
690 }
691
692 if(CY_RSLT_SUCCESS == result && NULL != rx_pins && CYHAL_NC_PIN_VALUE != rx_pins->mclk )
693 {
694 if(NULL == mclk_map_rx || (false == _cyhal_utils_map_resource_equal(&(obj->resource), mclk_map_rx, false)))
695 {
696 result = obj->interface->err_invalid_pin;
697 }
698 }
699
700 if(CY_RSLT_SUCCESS == result && NULL != tx_pins && CYHAL_NC_PIN_VALUE != tx_pins->mclk )
701 {
702 if(NULL == mclk_map_tx || (false == _cyhal_utils_map_resource_equal(&(obj->resource), mclk_map_tx, false)))
703 {
704 result = obj->interface->err_invalid_pin;
705 }
706 }
707
708 #if defined(CY_IP_MXAUDIOSS)
709 const uint8_t MAX_CHANNELS = 8u; /* I2S will already have limited to a lower count */
710 #elif defined(CY_IP_MXTDM)
711 const uint8_t MAX_CHANNELS = _cyhal_audioss_max_channels[obj->resource.block_num];
712 #endif
713 if(CY_RSLT_SUCCESS == result && config->num_channels > MAX_CHANNELS)
714 {
715 result = obj->interface->err_invalid_arg;
716 }
717
718 #if defined(CY_IP_MXAUDIOSS)
719 // This IP doesn't support disabling individual channels, just reducing the overall count
720 for(size_t i = 0; i < sizeof(config->channel_mask) * 8 /* bits per byte */; ++i)
721 {
722 bool chan_enabled = (0u != (config->channel_mask & ((uint32_t)1u << i)));
723 bool is_selected_channel = (i < config->num_channels);
724 if(is_selected_channel != chan_enabled)
725 {
726 result = obj->interface->err_invalid_arg;
727 break;
728 }
729 }
730 #endif
731
732 if(CY_RSLT_SUCCESS == result)
733 {
734 result = cyhal_hwmgr_reserve(&(obj->resource));
735 }
736
737 if(CY_RSLT_SUCCESS != result)
738 {
739 // If we aren't successful by here, we didn't manage to reserve the hardware resource,
740 // so mark it as invalid to ensure it isn't incorrectly freed.
741 obj->resource.type = CYHAL_RSC_INVALID;
742 }
743
744 uint8_t dm_tx_sck, dm_tx_ws;
745 #if defined (COMPONENT_CAT5)
746 dm_tx_sck = (config->is_tx_slave) ? CYHAL_PIN_MAP_DRIVE_MODE_TDM_SLAVE : _CYHAL_AUDIOSS_DRIVE_MODE_TX_SCK;
747 dm_tx_ws = (config->is_tx_slave) ? CYHAL_PIN_MAP_DRIVE_MODE_TDM_SLAVE : _CYHAL_AUDIOSS_DRIVE_MODE_TX_WS;
748 #else
749 dm_tx_sck = _CYHAL_AUDIOSS_DRIVE_MODE_TX_SCK;
750 dm_tx_ws = _CYHAL_AUDIOSS_DRIVE_MODE_TX_WS;
751 #endif
752
753 /* Reserve the pins */
754 if(CY_RSLT_SUCCESS == result && NULL != tx_pins)
755 {
756 result = _cyhal_utils_reserve_and_connect(tx_sck_map, dm_tx_sck);
757 if(CY_RSLT_SUCCESS == result)
758 {
759 obj->pin_tx_sck = tx_pins->sck;
760 result = _cyhal_utils_reserve_and_connect(tx_ws_map, dm_tx_ws);
761 }
762 if(CY_RSLT_SUCCESS == result)
763 {
764 obj->pin_tx_ws = tx_pins->ws;
765 result = _cyhal_utils_reserve_and_connect(tx_sdo_map, (uint8_t)_CYHAL_AUDIOSS_DRIVE_MODE_TX_SDO);
766 }
767 if(CY_RSLT_SUCCESS == result)
768 {
769 obj->pin_tx_sdo = tx_pins->data;
770 }
771 #if defined(_CYHAL_AUDIOSS_TX_SLAVE_AVAILABLE) && !defined (COMPONENT_CAT5)
772 // In slave mode, the clock and word select pins are inputs
773 if(CY_RSLT_SUCCESS == result && config->is_tx_slave)
774 {
775 result = cyhal_gpio_configure(obj->pin_tx_sck, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE);
776 if(CY_RSLT_SUCCESS == result)
777 {
778 result = cyhal_gpio_configure(obj->pin_tx_ws, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE);
779 }
780 }
781 #endif
782
783 }
784
785 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
786 if(CY_RSLT_SUCCESS == result && NULL != rx_pins)
787 {
788 result = _cyhal_utils_reserve_and_connect(rx_sdi_map, (uint8_t)_CYHAL_AUDIOSS_DRIVE_MODE_RX_SDI);
789
790 #if defined (COMPONENT_CAT5)
791 if(CY_RSLT_SUCCESS == result)
792 {
793 obj->pin_rx_sdi = rx_pins->data;
794 obj->pin_rx_sck = rx_pins->sck; // The SCK and WS are tied to the TX on this device
795 obj->pin_rx_ws = rx_pins->ws;
796 }
797 #else
798 if(CY_RSLT_SUCCESS == result)
799 {
800 obj->pin_rx_sdi = rx_pins->data;
801 result = _cyhal_utils_reserve_and_connect(rx_sck_map, (uint8_t)_CYHAL_AUDIOSS_DRIVE_MODE_RX_SCK);
802 }
803 if(CY_RSLT_SUCCESS == result)
804 {
805 obj->pin_rx_sck = rx_pins->sck;
806 result = _cyhal_utils_reserve_and_connect(rx_ws_map, (uint8_t)_CYHAL_AUDIOSS_DRIVE_MODE_RX_WS);
807 }
808 if(CY_RSLT_SUCCESS == result)
809 {
810 obj->pin_rx_ws = rx_pins->ws;
811 }
812
813 // In slave mode, the clock and word select pins are inputs
814 if(CY_RSLT_SUCCESS == result && config->is_rx_slave)
815 {
816 result = cyhal_gpio_configure(obj->pin_rx_sck, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE);
817 if(CY_RSLT_SUCCESS == result)
818 {
819 result = cyhal_gpio_configure(obj->pin_rx_ws, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE);
820 }
821 }
822 #endif
823
824 }
825 #endif
826
827 bool has_mclk = NULL != mclk_map_rx || NULL != mclk_map_tx;
828 if(CY_RSLT_SUCCESS == result && has_mclk)
829 {
830 if(config->mclk_hz == 0)
831 {
832 // Must specify mclk frequency when using mclk
833 result = obj->interface->err_invalid_arg;
834 }
835 else
836 {
837 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
838 if(NULL != mclk_map_rx)
839 {
840 result = _cyhal_utils_reserve_and_connect(mclk_map_rx, mclk_rx_dm);
841 if(CY_RSLT_SUCCESS == result)
842 {
843 obj->pin_rx_mclk = mclk_map_rx->pin;
844 obj->mclk_hz_rx = config->mclk_hz;
845 }
846 }
847 #endif
848 if(CY_RSLT_SUCCESS == result && NULL != mclk_map_tx)
849 {
850 /* Don't try to reserve twice if rx and tx mclk are the same pin */
851 if(NULL == mclk_map_rx || mclk_map_tx->pin != mclk_map_rx->pin)
852 {
853 result = _cyhal_utils_reserve_and_connect(mclk_map_tx, mclk_tx_dm);
854 }
855 if(CY_RSLT_SUCCESS == result)
856 {
857 obj->pin_tx_mclk = mclk_map_tx->pin;
858 obj->mclk_hz_tx = config->mclk_hz;
859 }
860 }
861 }
862 }
863
864 if(CY_RSLT_SUCCESS == result && false == has_mclk)
865 {
866 // Must not specify mclk frequency when mclk pin is not in use
867 if(config->mclk_hz != 0)
868 {
869 result = obj->interface->err_invalid_arg;
870 }
871 }
872
873 /* In this init flow rx and tx have the same lengths so we just need to check tx */
874 if(CY_RSLT_SUCCESS == result && obj->word_length_tx > obj->channel_length_tx)
875 {
876 // Word length must be less than or equal to channel length
877 result = obj->interface->err_invalid_arg;
878 }
879
880 if(CY_RSLT_SUCCESS == result && obj->channel_length_tx > _CYHAL_AUDIOSS_MAX_CHANNEL_LENGTH)
881 {
882 // Channel length on MXAUDIOSS is more restricted than this, but that is
883 // checked in populate_pdl_config. There is also a lower bound of 4
884 // on MXTDM but that is taken care of by the check above because
885 // 8 bits is the smallest word length we support.
886 result = obj->interface->err_invalid_arg;
887 }
888
889 if (CY_RSLT_SUCCESS == result)
890 {
891 bool all_mclk = (NULL == rx_pins || CYHAL_NC_PIN_VALUE != rx_pins->mclk)
892 && (NULL == tx_pins || CYHAL_NC_PIN_VALUE != tx_pins->mclk);
893
894 result = _cyhal_audioss_init_clock(obj, clk, all_mclk);
895 }
896
897 uint16_t sclk_div_tx = 0;
898 uint16_t sclk_div_rx = 0;
899 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
900 if(CY_RSLT_SUCCESS == result && NULL != rx_pins)
901 {
902 result = _cyhal_audioss_compute_sclk_div(obj, config->sample_rate_hz, obj->mclk_hz_rx, obj->channel_length_rx, config->num_channels, &sclk_div_rx);
903 }
904 #endif
905 if(CY_RSLT_SUCCESS == result && NULL != tx_pins)
906 {
907 result = _cyhal_audioss_compute_sclk_div(obj, config->sample_rate_hz, obj->mclk_hz_tx, obj->channel_length_tx, config->num_channels, &sclk_div_tx);
908 }
909
910 _cyhal_audioss_pdl_config_t pdl_config;
911 memset(&pdl_config, 0, sizeof(pdl_config));
912 #if defined(CY_IP_MXTDM)
913 cy_stc_tdm_config_tx_t tx_config = {0};
914 cy_stc_tdm_config_rx_t rx_config = {0};
915 pdl_config.tx_config = &tx_config;
916 pdl_config.rx_config = &rx_config;
917 #endif
918 if (CY_RSLT_SUCCESS == result)
919 {
920 bool tx_en = (CYHAL_NC_PIN_VALUE != obj->pin_tx_sdo);
921 bool tx_mclk = (CYHAL_NC_PIN_VALUE != obj->pin_tx_mclk);
922 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
923 bool rx_en = (CYHAL_NC_PIN_VALUE != obj->pin_rx_sdi);
924 bool rx_mclk = (CYHAL_NC_PIN_VALUE != obj->pin_rx_mclk);
925 #else
926 bool rx_en = false;
927 bool rx_mclk = false;
928 #endif
929 result = _cyhal_audioss_populate_pdl_config(obj, &pdl_config, config, sclk_div_rx, sclk_div_tx, rx_en, tx_en, rx_mclk, tx_mclk);
930 }
931
932 if (CY_RSLT_SUCCESS == result)
933 {
934 result = _cyhal_audioss_init_hw(obj, &pdl_config);
935 }
936
937 if (CY_RSLT_SUCCESS != result)
938 {
939 _cyhal_audioss_free(obj);
940 }
941 return result;
942 }
943
_cyhal_audioss_init_cfg(_cyhal_audioss_t * obj,const _cyhal_audioss_configurator_t * cfg,const _cyhal_audioss_interface_t * interface)944 cy_rslt_t _cyhal_audioss_init_cfg(_cyhal_audioss_t *obj, const _cyhal_audioss_configurator_t *cfg, const _cyhal_audioss_interface_t* interface)
945 {
946 CY_ASSERT(NULL != obj);
947
948 memset(obj, 0, sizeof(_cyhal_audioss_t));
949 obj->owned_by_configurator = true;
950 obj->resource = *cfg->resource;
951 obj->mclk_hz_tx = cfg->mclk_hz_tx;
952 obj->interface = interface;
953
954 obj->async_mode = CYHAL_ASYNC_SW;
955 obj->tx_dma.resource.type = CYHAL_RSC_INVALID;
956 #if defined(CY_IP_MXAUDIOSS)
957 obj->channel_length_tx = _cyhal_audioss_length_from_pdl(cfg->config->txChannelLength);
958 obj->word_length_tx = _cyhal_audioss_length_from_pdl(cfg->config->txWordLength);
959 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
960 obj->channel_length_rx = _cyhal_audioss_length_from_pdl(cfg->config->rxChannelLength);
961 obj->word_length_rx = _cyhal_audioss_length_from_pdl(cfg->config->rxWordLength);
962 #endif
963 #elif defined(CY_IP_MXTDM)
964 obj->channel_length_tx = cfg->config->tx_config->channelSize;
965 obj->word_length_tx = _cyhal_audioss_length_from_pdl(cfg->config->tx_config->wordSize);
966 obj->channel_length_rx = cfg->config->rx_config->channelSize;
967 obj->word_length_rx = _cyhal_audioss_length_from_pdl(cfg->config->rx_config->wordSize);
968 #endif
969 obj->pin_tx_sck = CYHAL_NC_PIN_VALUE;
970 obj->pin_tx_ws = CYHAL_NC_PIN_VALUE;
971 obj->pin_tx_sdo = CYHAL_NC_PIN_VALUE;
972 obj->pin_tx_mclk = CYHAL_NC_PIN_VALUE;
973 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
974 obj->rx_dma.resource.type = CYHAL_RSC_INVALID;
975 obj->pin_rx_sck = CYHAL_NC_PIN_VALUE;
976 obj->pin_rx_ws = CYHAL_NC_PIN_VALUE;
977 obj->pin_rx_sdi = CYHAL_NC_PIN_VALUE;
978 obj->pin_rx_mclk = CYHAL_NC_PIN_VALUE;
979 #endif
980
981 #if defined(CY_IP_MXAUDIOSS)
982 bool all_mclk = (0u != obj->mclk_hz_tx); /* No separate rx/tx clock selection */
983 #elif defined(CY_IP_MXTDM)
984 bool all_mclk = ((false == cfg->config->rx_config->enable) || (CY_TDM_SEL_MCLK_IN == cfg->config->rx_config->clkSel))
985 && ((false == cfg->config->tx_config->enable) || (CY_TDM_SEL_MCLK_IN == cfg->config->tx_config->clkSel));
986 #endif
987 cy_rslt_t result = _cyhal_audioss_init_clock(obj, cfg->clock, all_mclk);
988
989 if(CY_RSLT_SUCCESS == result)
990 {
991 result = _cyhal_audioss_init_hw(obj, cfg->config);
992 }
993
994 if(CY_RSLT_SUCCESS != result)
995 {
996 _cyhal_audioss_free(obj);
997 }
998 return result;
999 }
1000
_cyhal_audioss_free(_cyhal_audioss_t * obj)1001 void _cyhal_audioss_free(_cyhal_audioss_t *obj)
1002 {
1003 CY_ASSERT(NULL != obj);
1004
1005 if(CYHAL_RSC_INVALID != obj->resource.type)
1006 {
1007 #if defined(CY_IP_MXAUDIOSS)
1008 _cyhal_system_irq_t irqn = _cyhal_audioss_irq_n[obj->resource.block_num];
1009 _cyhal_irq_free(irqn);
1010 #elif defined(CY_IP_MXTDM)
1011 _cyhal_system_irq_t irqn = _cyhal_audioss_rx_irq_n[obj->resource.block_num];
1012 _cyhal_irq_free(irqn);
1013 irqn = _cyhal_audioss_tx_irq_n[obj->resource.block_num];
1014 _cyhal_irq_free(irqn);
1015 #endif
1016
1017 #if (CYHAL_DRIVER_AVAILABLE_SYSPM)
1018 _cyhal_syspm_unregister_peripheral_callback(&(obj->pm_callback));
1019 #endif /* (CYHAL_DRIVER_AVAILABLE_SYSPM) */
1020
1021 if(NULL != obj->base)
1022 {
1023 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1024 _cyhal_audioss_stop_rx(obj);
1025 #endif
1026 _cyhal_audioss_stop_tx(obj);
1027
1028 #if defined(CY_IP_MXTDM)
1029 Cy_AudioTDM_DisableTx(&obj->base->TDM_TX_STRUCT);
1030 Cy_AudioTDM_DisableRx(&obj->base->TDM_RX_STRUCT);
1031 #endif
1032 }
1033 if(false == obj->owned_by_configurator)
1034 {
1035 cyhal_hwmgr_free(&(obj->resource));
1036 }
1037
1038 obj->base = NULL;
1039 obj->resource.type = CYHAL_RSC_INVALID;
1040 }
1041
1042 /* Need to check this before we start releasing because release_if_used
1043 * sets the pin to NC once it is done */
1044 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1045 bool different_mclk = (obj->pin_rx_mclk != obj->pin_tx_mclk);
1046 #endif
1047 _cyhal_utils_release_if_used(&(obj->pin_tx_sck));
1048 _cyhal_utils_release_if_used(&(obj->pin_tx_ws));
1049 _cyhal_utils_release_if_used(&(obj->pin_tx_sdo));
1050 _cyhal_utils_release_if_used(&(obj->pin_tx_mclk));
1051 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1052 _cyhal_utils_release_if_used(&(obj->pin_rx_sck));
1053 _cyhal_utils_release_if_used(&(obj->pin_rx_ws));
1054 _cyhal_utils_release_if_used(&(obj->pin_rx_sdi));
1055 if(different_mclk)
1056 {
1057 _cyhal_utils_release_if_used(&(obj->pin_rx_mclk));
1058 }
1059 #endif
1060 if(obj->is_clock_owned)
1061 {
1062 cyhal_clock_free(&(obj->clock));
1063 }
1064
1065 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1066 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1067 if(CYHAL_RSC_INVALID != obj->rx_dma.resource.type)
1068 {
1069 cyhal_dma_free(&obj->rx_dma);
1070 }
1071 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
1072
1073 if(CYHAL_RSC_INVALID != obj->tx_dma.resource.type)
1074 {
1075 cyhal_dma_free(&obj->tx_dma);
1076 }
1077 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
1078 }
1079
_cyhal_audioss_fifo_trigger_level(_cyhal_audioss_t * obj,bool is_tx)1080 static uint8_t _cyhal_audioss_fifo_trigger_level(_cyhal_audioss_t* obj, bool is_tx)
1081 {
1082 #if defined(CY_IP_MXAUDIOSS)
1083 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1084 return is_tx
1085 ? (uint8_t)_FLD2VAL(I2S_TX_FIFO_CTL_TRIGGER_LEVEL, REG_I2S_TX_FIFO_CTL(obj->base))
1086 : (uint8_t)_FLD2VAL(I2S_RX_FIFO_CTL_TRIGGER_LEVEL, REG_I2S_RX_FIFO_CTL(obj->base));
1087 #else
1088 CY_UNUSED_PARAMETER(is_tx);
1089 return (uint8_t)_FLD2VAL(I2S_TX_FIFO_CTL_TRIGGER_LEVEL, REG_I2S_TX_FIFO_CTL(obj->base));
1090 #endif
1091 #elif defined(CY_IP_MXTDM)
1092 return is_tx
1093 ? (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_FIFO_CTL_TRIGGER_LEVEL, TDM_STRUCT_TX_FIFO_CTL(&obj->base->TDM_TX_STRUCT))
1094 : (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_FIFO_CTL_TRIGGER_LEVEL, TDM_STRUCT_RX_FIFO_CTL(&obj->base->TDM_RX_STRUCT));
1095 #endif
1096 }
1097
_cyhal_audioss_is_direction_enabled(_cyhal_audioss_t * obj,bool is_tx)1098 static bool _cyhal_audioss_is_direction_enabled(_cyhal_audioss_t *obj, bool is_tx)
1099 {
1100 #if defined(CY_IP_MXAUDIOSS)
1101 return is_tx
1102 ? (0u != (REG_I2S_CTL(obj->base) & I2S_CTL_TX_ENABLED_Msk))
1103 : (0u != (REG_I2S_CTL(obj->base) & I2S_CTL_RX_ENABLED_Msk));
1104 #elif defined(CY_IP_MXTDM)
1105 return is_tx
1106 ? (0u != (TDM_STRUCT_TX_CTL(&obj->base->TDM_TX_STRUCT) & TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_CTL_ENABLED_Msk))
1107 : (0u != (TDM_STRUCT_RX_CTL(&obj->base->TDM_RX_STRUCT) & TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_CTL_ENABLED_Msk));
1108 #endif
1109 }
1110
1111 /* Note: This function is called prior to PDL init, and again from set_sample_rate. This means that
1112 * it cannot safely query either register values (which may not be initialized), nor pin values (which
1113 * are not populated in the configurator init flow) */
_cyhal_audioss_compute_sclk_div(_cyhal_audioss_t * obj,uint32_t sample_rate_hz,uint32_t mclk_hz,uint8_t channel_length,uint8_t num_channels,uint16_t * sclk_div)1114 static cy_rslt_t _cyhal_audioss_compute_sclk_div(_cyhal_audioss_t *obj, uint32_t sample_rate_hz, uint32_t mclk_hz, uint8_t channel_length, uint8_t num_channels, uint16_t *sclk_div)
1115 {
1116 #if defined(CY_IP_MXAUDIOSS)
1117 const uint8_t SCLK_INCREMENT = 1;
1118 const uint8_t MIN_SCLK_DIVIDER = 1;
1119 const uint8_t MAX_SCLK_DIVIDER = 64; // Divider value internal to the I2S block
1120 #elif defined(CY_IP_MXTDM)
1121 const uint8_t MIN_SCLK_DIVIDER = 2;
1122 const uint8_t SCLK_INCREMENT = 2; // Per PDL, should be set to an even value to ensure 50/50 duty cycle
1123 const uint16_t MAX_SCLK_DIVIDER = 256; // Divider value internal to the I2S block
1124 #endif
1125
1126 const cyhal_clock_tolerance_t SCLK_TOLERANCE = { .type = CYHAL_TOLERANCE_PERCENT, .value = 1 };
1127 uint32_t sclk_target = sample_rate_hz * channel_length * num_channels;
1128 *sclk_div = 0;
1129
1130 if(obj->is_clock_owned)
1131 {
1132 // Try each of the divider values that we support internally, and see whether any of them gets us
1133 // within our tolerance of a frequency that our source clock can provide.
1134 for(uint16_t i = MIN_SCLK_DIVIDER; i <= MAX_SCLK_DIVIDER; i += SCLK_INCREMENT)
1135 {
1136 #if defined(CY_IP_MXAUDIOSS)
1137 // This IP has a hard-wired 8x divider
1138 uint32_t desired_source_freq = sclk_target * i * 8;
1139 #elif defined(CY_IP_MXTDM)
1140 uint32_t desired_source_freq = sclk_target * i;
1141 #endif
1142 cy_rslt_t freq_result = _cyhal_utils_set_clock_frequency(&(obj->clock), desired_source_freq, &SCLK_TOLERANCE);
1143 if(CY_RSLT_SUCCESS == freq_result)
1144 {
1145 *sclk_div = i;
1146 break;
1147 }
1148 }
1149 }
1150 else // Using user-provided clock, or using the mclk pin
1151 {
1152 // We can't change the clock, so just check if it's within tolerance
1153 #if defined(CY_IP_MXAUDIOSS)
1154 uint32_t desired_divided_freq = sclk_target * 8; // I2S hw has a hard-wired 8x divider
1155 #elif defined(CY_IP_MXTDM)
1156 uint32_t desired_divided_freq = sclk_target;
1157 #endif
1158 uint32_t actual_source_freq = (0u != mclk_hz) ? mclk_hz : cyhal_clock_get_frequency(&obj->clock);
1159 uint32_t best_divider = (actual_source_freq + (desired_divided_freq / 2)) / desired_divided_freq; // Round to nearest divider
1160 #if !defined (COMPONENT_CAT5)
1161 uint32_t desired_source_freq = desired_divided_freq * best_divider;
1162 uint32_t diff = (uint32_t)abs(_cyhal_utils_calculate_tolerance(SCLK_TOLERANCE.type, desired_source_freq, actual_source_freq));
1163 if(diff <= SCLK_TOLERANCE.value && best_divider <= MAX_SCLK_DIVIDER)
1164 {
1165 *sclk_div = (uint16_t)best_divider;
1166 }
1167 #else
1168 // Tolerance check cannot be reliably done on this device. Therefore skip it.
1169 *sclk_div = best_divider;
1170 #endif
1171 }
1172
1173 return (0 == *sclk_div) ? obj->interface->err_clock : CY_RSLT_SUCCESS;
1174 }
1175
_cyhal_audioss_set_sample_rate(_cyhal_audioss_t * obj,uint32_t sample_rate_hz)1176 cy_rslt_t _cyhal_audioss_set_sample_rate(_cyhal_audioss_t *obj, uint32_t sample_rate_hz)
1177 {
1178 uint16_t sclk_div_tx = 0;
1179 _cyhal_audioss_pdl_config_t pdl_config;
1180 memset(&pdl_config, 0, sizeof(pdl_config));
1181 #if defined(CY_IP_MXTDM)
1182 cy_stc_tdm_config_tx_t tx_config;
1183 cy_stc_tdm_config_rx_t rx_config;
1184 pdl_config.tx_config = &tx_config;
1185 pdl_config.rx_config = &rx_config;
1186 #endif
1187 _cyhal_audioss_reconstruct_pdl_config(obj, &pdl_config);
1188 #if defined(CY_IP_MXAUDIOSS)
1189 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1190 uint8_t rx_channels = pdl_config.rxChannels;
1191 #endif
1192 uint8_t tx_channels = pdl_config.txChannels;
1193 #elif defined(CY_IP_MXTDM)
1194 uint8_t rx_channels = pdl_config.rx_config->channelNum;
1195 uint8_t tx_channels = pdl_config.tx_config->channelNum;
1196 #endif /* _CYHAL_AUDIOSS_RX_ENABLED */
1197 cy_rslt_t result = _cyhal_audioss_compute_sclk_div(obj, sample_rate_hz, obj->mclk_hz_tx, obj->channel_length_tx, tx_channels, &sclk_div_tx);
1198 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1199 uint16_t sclk_div_rx = 0;
1200 if(CY_RSLT_SUCCESS == result)
1201 {
1202 result = _cyhal_audioss_compute_sclk_div(obj, sample_rate_hz, obj->mclk_hz_rx, obj->channel_length_rx, rx_channels, &sclk_div_rx);
1203 }
1204 #endif /* _CYHAL_AUDIOSS_RX_ENABLED */
1205 if(CY_RSLT_SUCCESS == result)
1206 {
1207 #if defined(CY_IP_MXAUDIOSS)
1208 pdl_config.clkDiv = (uint8_t)(sclk_div_tx);
1209 #elif defined(CY_IP_MXTDM)
1210 pdl_config.tx_config->clkDiv = sclk_div_tx;
1211 pdl_config.rx_config->clkDiv = sclk_div_rx;
1212 #endif
1213 Cy_I2S_DeInit(obj->base);
1214
1215 result = _cyhal_audioss_init_pdl(obj, &pdl_config);
1216 }
1217
1218 return result;
1219 }
1220
_cyhal_audioss_enable_event(_cyhal_audioss_t * obj,uint32_t event,uint8_t intr_priority,bool enable)1221 void _cyhal_audioss_enable_event(_cyhal_audioss_t *obj, uint32_t event, uint8_t intr_priority, bool enable)
1222 {
1223 CY_ASSERT(NULL != obj);
1224
1225 if (enable)
1226 {
1227 obj->user_enabled_events |= event;
1228 }
1229 else
1230 {
1231 obj->user_enabled_events &= ~event;
1232 }
1233
1234 _cyhal_audioss_update_enabled_events(obj);
1235 #if defined(CY_IP_MXAUDIOSS)
1236 _cyhal_system_irq_t irqn = _cyhal_audioss_irq_n[obj->resource.block_num];
1237 _cyhal_irq_set_priority(irqn, intr_priority);
1238 #elif defined(CY_IP_MXTDM)
1239 _cyhal_system_irq_t irqn = _cyhal_audioss_tx_irq_n[obj->resource.block_num];
1240 _cyhal_irq_set_priority(irqn, intr_priority);
1241 irqn = _cyhal_audioss_rx_irq_n[obj->resource.block_num];
1242 _cyhal_irq_set_priority(irqn, intr_priority);
1243 #endif
1244 }
1245
_cyhal_audioss_start_tx(_cyhal_audioss_t * obj)1246 cy_rslt_t _cyhal_audioss_start_tx(_cyhal_audioss_t *obj)
1247 {
1248 if (obj->pm_transition_ready)
1249 {
1250 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1251 }
1252 #if defined(CY_IP_MXAUDIOSS)
1253 Cy_I2S_EnableTx(obj->base);
1254 #elif defined(CY_IP_MXTDM)
1255 Cy_AudioTDM_ActivateTx(&obj->base->TDM_TX_STRUCT);
1256 #endif
1257 return CY_RSLT_SUCCESS;
1258 }
1259
_cyhal_audioss_stop_tx(_cyhal_audioss_t * obj)1260 cy_rslt_t _cyhal_audioss_stop_tx(_cyhal_audioss_t *obj)
1261 {
1262 #if defined(CY_IP_MXAUDIOSS)
1263 Cy_I2S_DisableTx(obj->base);
1264 #elif defined(CY_IP_MXTDM)
1265 // Not disable - that is more aggressive than we want
1266 Cy_AudioTDM_DeActivateTx(&obj->base->TDM_TX_STRUCT);
1267 #endif
1268 return CY_RSLT_SUCCESS;
1269 }
1270
_cyhal_audioss_clear_tx(_cyhal_audioss_t * obj)1271 cy_rslt_t _cyhal_audioss_clear_tx(_cyhal_audioss_t *obj)
1272 {
1273 #if defined(CY_IP_MXAUDIOSS)
1274 Cy_I2S_ClearTxFifo(obj->base);
1275 #elif defined(CY_IP_MXTDM)
1276 /* No way to explicitly clear the FIFO, so disable and re-enable
1277 * which will clear the FIFO as a side effect, while retaining
1278 * all other configuration */
1279 bool was_active = _cyhal_audioss_is_tx_enabled(obj);
1280 Cy_AudioTDM_DisableTx(&obj->base->TDM_TX_STRUCT);
1281 Cy_AudioTDM_EnableTx(&obj->base->TDM_TX_STRUCT);
1282 if(was_active)
1283 {
1284 Cy_AudioTDM_ActivateTx(&obj->base->TDM_TX_STRUCT);
1285 }
1286 #endif
1287 return CY_RSLT_SUCCESS;
1288 }
1289
_cyhal_audioss_start_rx(_cyhal_audioss_t * obj)1290 cy_rslt_t _cyhal_audioss_start_rx(_cyhal_audioss_t *obj)
1291 {
1292 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1293 if (obj->pm_transition_ready)
1294 {
1295 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1296 }
1297 #if defined(CY_IP_MXAUDIOSS)
1298 Cy_I2S_EnableRx(obj->base);
1299 #elif defined(CY_IP_MXTDM)
1300 Cy_AudioTDM_ActivateRx(&obj->base->TDM_RX_STRUCT);
1301 #endif
1302 return CY_RSLT_SUCCESS;
1303 #else
1304 return obj->interface->err_not_supported;
1305 #endif
1306 }
1307
_cyhal_audioss_stop_rx(_cyhal_audioss_t * obj)1308 cy_rslt_t _cyhal_audioss_stop_rx(_cyhal_audioss_t *obj)
1309 {
1310 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1311 #if defined(CY_IP_MXAUDIOSS)
1312 Cy_I2S_DisableRx(obj->base);
1313 #elif defined(CY_IP_MXTDM)
1314 // Not disable - that is more aggressive than we want
1315 Cy_AudioTDM_DeActivateRx(&obj->base->TDM_RX_STRUCT);
1316 #endif
1317 return CY_RSLT_SUCCESS;
1318 #else
1319 return obj->interface->err_not_supported;
1320 #endif
1321 }
1322
_cyhal_audioss_clear_rx(_cyhal_audioss_t * obj)1323 cy_rslt_t _cyhal_audioss_clear_rx(_cyhal_audioss_t *obj)
1324 {
1325 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1326 #if defined(CY_IP_MXAUDIOSS)
1327 Cy_I2S_ClearRxFifo(obj->base);
1328 #elif defined(CY_IP_MXTDM)
1329 /* No way to explicitly clear the FIFO, so disable and re-enable
1330 * which will clear the FIFO as a side effect, while retaining
1331 * all other configuration */
1332 bool was_active = _cyhal_audioss_is_rx_enabled(obj);
1333 Cy_AudioTDM_DisableRx(&obj->base->TDM_RX_STRUCT);
1334 Cy_AudioTDM_EnableRx(&obj->base->TDM_RX_STRUCT);
1335 if(was_active)
1336 {
1337 Cy_AudioTDM_ActivateRx(&obj->base->TDM_RX_STRUCT);
1338 }
1339 #endif
1340 return CY_RSLT_SUCCESS;
1341 #else
1342 return obj->interface->err_not_supported;
1343 #endif
1344 }
1345
1346 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1347 // Reads until empty, then updates the length and buffer address to their new locations
_cyhal_audioss_read_until_empty(_cyhal_audioss_t * obj,void ** buffer,size_t * length)1348 static void _cyhal_audioss_read_until_empty(_cyhal_audioss_t *obj, void** buffer, size_t* length)
1349 {
1350 // The buffer is the smallest type that will hold the word length
1351 // The structure of this function deliberately accepts duplication of the outer loop
1352 // structure in order to avoid having to recheck the word length every time around,
1353 // because this function is in a performance sensitive code path.
1354 if(obj->word_length_rx <= 8)
1355 {
1356 uint8_t *cast_buffer = (uint8_t*)(*buffer);
1357
1358 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, false) > 0)
1359 {
1360 *cast_buffer = (uint8_t)_cyhal_audioss_read_fifo(obj);
1361 ++cast_buffer;
1362 --(*length);
1363 }
1364 *buffer = (void*)cast_buffer;
1365 }
1366 else if(obj->word_length_rx <= 16)
1367 {
1368 uint16_t *cast_buffer = (uint16_t*)(*buffer);
1369
1370 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, false) > 0)
1371 {
1372 *cast_buffer = (uint16_t)_cyhal_audioss_read_fifo(obj);
1373 ++cast_buffer;
1374 --(*length);
1375 }
1376 *buffer = (void*)cast_buffer;
1377 }
1378 else
1379 {
1380 CY_ASSERT(obj->word_length_rx <= 32);
1381 uint32_t *cast_buffer = (uint32_t*)(*buffer);
1382
1383 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, false) > 0)
1384 {
1385 *cast_buffer = _cyhal_audioss_read_fifo(obj);
1386 ++cast_buffer;
1387 --(*length);
1388 }
1389 *buffer = (void*)cast_buffer;
1390 }
1391 }
1392 #endif
1393
_cyhal_audioss_read(_cyhal_audioss_t * obj,void * data,size_t * length)1394 cy_rslt_t _cyhal_audioss_read(_cyhal_audioss_t *obj, void *data, size_t* length)
1395 {
1396 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1397 CY_ASSERT(NULL != obj);
1398 if (obj->pm_transition_ready)
1399 {
1400 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1401 }
1402
1403 size_t remaining = *length;
1404 _cyhal_audioss_read_until_empty(obj, &data, &remaining);
1405 *length -= remaining;
1406 return CY_RSLT_SUCCESS;
1407 #else
1408 CY_UNUSED_PARAMETER(data);
1409 CY_UNUSED_PARAMETER(length);
1410 return obj->interface->err_not_supported;
1411 #endif
1412 }
1413
_cyhal_audioss_write_until_full(_cyhal_audioss_t * obj,const void ** buffer,size_t * length)1414 static void _cyhal_audioss_write_until_full(_cyhal_audioss_t *obj, const void** buffer, size_t *length)
1415 {
1416 // The buffer is the smallest type that will hold the word length
1417 // The structure of this function deliberately accepts duplication of the outer loop
1418 // structure in order to avoid having to recheck the word length every time around,
1419 // because this function is in a performance sensitive code path.
1420 if(obj->word_length_tx <= 8)
1421 {
1422 const uint8_t *cast_buffer = (const uint8_t*)(*buffer);
1423
1424 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, true) < _CYHAL_AUDIOSS_FIFO_DEPTH)
1425 {
1426 _cyhal_audioss_write_fifo(obj, *cast_buffer);
1427 ++cast_buffer;
1428 --(*length);
1429 }
1430 *buffer = (void*)cast_buffer;
1431 }
1432 else if(obj->word_length_tx <= 16)
1433 {
1434 const uint16_t *cast_buffer = (const uint16_t*)(*buffer);
1435
1436 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, true) < _CYHAL_AUDIOSS_FIFO_DEPTH)
1437 {
1438 _cyhal_audioss_write_fifo(obj, *cast_buffer);
1439 ++cast_buffer;
1440 --(*length);
1441 }
1442 *buffer = (void*)cast_buffer;
1443 }
1444 else
1445 {
1446 CY_ASSERT(obj->word_length_tx <= 32);
1447 const uint32_t *cast_buffer = (const uint32_t*)(*buffer);
1448
1449 while(*length > 0 && _cyhal_audioss_get_num_in_fifo(obj, true) < _CYHAL_AUDIOSS_FIFO_DEPTH)
1450 {
1451 _cyhal_audioss_write_fifo(obj, *cast_buffer);
1452 ++cast_buffer;
1453 --(*length);
1454 }
1455 *buffer = (void*)cast_buffer;
1456 }
1457 }
1458
_cyhal_audioss_write(_cyhal_audioss_t * obj,const void * data,size_t * length)1459 cy_rslt_t _cyhal_audioss_write(_cyhal_audioss_t *obj, const void *data, size_t *length)
1460 {
1461 CY_ASSERT(NULL != obj);
1462 if (obj->pm_transition_ready)
1463 {
1464 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1465 }
1466
1467 size_t remaining = *length;
1468 _cyhal_audioss_write_until_full(obj, &data, &remaining);
1469 *length -= remaining;
1470 return CY_RSLT_SUCCESS;
1471 }
1472
_cyhal_audioss_is_tx_enabled(_cyhal_audioss_t * obj)1473 bool _cyhal_audioss_is_tx_enabled(_cyhal_audioss_t *obj)
1474 {
1475 CY_ASSERT(NULL != obj);
1476
1477 #if defined(CY_IP_MXAUDIOSS)
1478 return (0 != (CY_I2S_TX_START & Cy_I2S_GetCurrentState(obj->base)));
1479 #elif(defined(CY_IP_MXTDM))
1480 return (0u != (TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_FIFO_CTL_ACTIVE_Msk & obj->base->TDM_TX_STRUCT.TX_FIFO_CTL));
1481 #endif
1482 }
1483
_cyhal_audioss_is_tx_busy(_cyhal_audioss_t * obj)1484 bool _cyhal_audioss_is_tx_busy(_cyhal_audioss_t *obj)
1485 {
1486 CY_ASSERT(NULL != obj);
1487
1488 return (0 != _cyhal_audioss_get_num_in_fifo(obj, true)) || _cyhal_audioss_is_write_pending(obj);
1489 }
1490
_cyhal_audioss_is_rx_enabled(_cyhal_audioss_t * obj)1491 bool _cyhal_audioss_is_rx_enabled(_cyhal_audioss_t *obj)
1492 {
1493 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1494 CY_ASSERT(NULL != obj);
1495 #if defined(CY_IP_MXAUDIOSS)
1496 return (0 != (CY_I2S_RX_START & Cy_I2S_GetCurrentState(obj->base)));
1497 #elif(defined(CY_IP_MXTDM))
1498 return (0u != (TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_FIFO_CTL_ACTIVE_Msk & obj->base->TDM_RX_STRUCT.RX_FIFO_CTL));
1499 #endif
1500 #else
1501 CY_UNUSED_PARAMETER(obj);
1502 return false;
1503 #endif
1504 }
1505
_cyhal_audioss_is_rx_busy(_cyhal_audioss_t * obj)1506 bool _cyhal_audioss_is_rx_busy(_cyhal_audioss_t *obj)
1507 {
1508 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1509 CY_ASSERT(NULL != obj);
1510 return (0 != _cyhal_audioss_get_num_in_fifo(obj, false)) || _cyhal_audioss_is_read_pending(obj);
1511 #else
1512 CY_UNUSED_PARAMETER(obj);
1513 return false;
1514 #endif
1515 }
1516
_cyhal_audioss_read_async(_cyhal_audioss_t * obj,void * rx,size_t rx_length)1517 cy_rslt_t _cyhal_audioss_read_async(_cyhal_audioss_t *obj, void *rx, size_t rx_length)
1518 {
1519 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1520 CY_ASSERT(NULL != obj);
1521 if (obj->pm_transition_ready)
1522 {
1523 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1524 }
1525
1526 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1527 obj->async_rx_buff = rx;
1528 obj->async_rx_length = rx_length;
1529 cyhal_system_critical_section_exit(savedIntrStatus);
1530 switch(obj->async_mode)
1531 {
1532 case CYHAL_ASYNC_SW:
1533 {
1534 /* Read as much as we can now, then set up an interrupt to do the rest
1535 * This is a potentially long operation but we don't want other I2S operations to
1536 * interleave with it. So do a "mini critical section" and disable the interrupts for this block only.
1537 */
1538 uint32_t old_events = _cyhal_audioss_disable_events(obj, false);
1539 // Safe to cast away volatile here because we're calling read_until_empty from within
1540 // a critical section, so it should not change out from under us during this call
1541 _cyhal_audioss_read_until_empty(obj, (void**)(&obj->async_rx_buff), (size_t*)(&obj->async_rx_length));
1542 _cyhal_audioss_update_rx_trigger_level(obj);
1543 _cyhal_audioss_restore_events(obj, false, old_events);
1544 if(obj->async_rx_length > 0)
1545 {
1546 _cyhal_audioss_update_enabled_events(obj);
1547 }
1548 else
1549 {
1550 _cyhal_audioss_process_event(obj, obj->interface->event_rx_complete);
1551 }
1552 break;
1553 }
1554 case CYHAL_ASYNC_DMA:
1555 {
1556 // Don't directly kick off the DMA here - it will be triggered
1557 // from the interrupt handler when the FIFO rised above the threshold
1558 // (which may have already happened by the time we get here if the
1559 // application already had the full or half-full event enabled)
1560 _cyhal_audioss_update_rx_trigger_level(obj);
1561 _cyhal_audioss_update_enabled_events(obj);
1562 break;
1563 }
1564 default:
1565 CY_ASSERT(0); /* Unrecognized async mode */
1566 }
1567
1568 return CY_RSLT_SUCCESS;
1569 #else
1570 CY_UNUSED_PARAMETER(rx);
1571 CY_UNUSED_PARAMETER(rx_length);
1572 return obj->interface->err_not_supported;
1573 #endif
1574 }
1575
_cyhal_audioss_reconstruct_pdl_config(_cyhal_audioss_t * obj,_cyhal_audioss_pdl_config_t * pdl_config)1576 static void _cyhal_audioss_reconstruct_pdl_config(_cyhal_audioss_t *obj, _cyhal_audioss_pdl_config_t* pdl_config)
1577 {
1578 #if defined(CY_IP_MXAUDIOSS)
1579 I2S_Type* base = obj->base;
1580 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1581 pdl_config->txEnabled = _FLD2BOOL(I2S_CTL_TX_ENABLED, REG_I2S_CTL(base));
1582 pdl_config->rxEnabled = _FLD2BOOL(I2S_CTL_RX_ENABLED, REG_I2S_CTL(base));
1583
1584 uint32_t rx_ctl_val = REG_I2S_RX_CTL(base);
1585 pdl_config->rxMasterMode = _FLD2BOOL(I2S_RX_CTL_MS, rx_ctl_val);
1586 pdl_config->rxAlignment = (cy_en_i2s_alignment_t)_FLD2VAL(I2S_RX_CTL_I2S_MODE, rx_ctl_val);
1587 pdl_config->rxWsPulseWidth = (cy_en_i2s_ws_pw_t)_FLD2VAL(I2S_RX_CTL_WS_PULSE, rx_ctl_val);
1588 pdl_config->rxWatchdogEnable = _FLD2BOOL(I2S_RX_CTL_WD_EN, rx_ctl_val);
1589 pdl_config->rxWatchdogValue = REG_I2S_RX_WATCHDOG(base);
1590 pdl_config->rxSdiLatchingTime = _FLD2BOOL(I2S_RX_CTL_B_CLOCK_INV, rx_ctl_val);
1591 pdl_config->rxSckoInversion = _FLD2BOOL(I2S_RX_CTL_SCKO_POL, rx_ctl_val);
1592 pdl_config->rxSckiInversion = _FLD2BOOL(I2S_RX_CTL_SCKI_POL, rx_ctl_val);
1593 /* Register is one less than desired value */
1594 pdl_config->rxChannels = ((uint8_t)_FLD2VAL(I2S_RX_CTL_CH_NR, rx_ctl_val)) + 1;
1595 pdl_config->rxChannelLength = (cy_en_i2s_len_t)_FLD2VAL(I2S_RX_CTL_CH_LEN, rx_ctl_val);
1596 pdl_config->rxWordLength = (cy_en_i2s_len_t)_FLD2VAL(I2S_RX_CTL_WORD_LEN, rx_ctl_val);
1597 pdl_config->rxSignExtension = _FLD2BOOL(I2S_RX_CTL_SCKI_POL, rx_ctl_val);
1598 pdl_config->rxFifoTriggerLevel = _cyhal_audioss_fifo_trigger_level(obj, false);
1599 #endif /* _CYHAL_AUDIOSS_RX_ENABLED */
1600
1601 #if defined(_CYHAL_AUDIOSS_TX_WATCHDOG_AVAILABLE)
1602 pdl_config->txWatchdogEnable = _FLD2BOOL(I2S_TX_CTL_WD_EN, rx_ctl_val);;
1603 pdl_config->txWatchdogValue = REG_I2S_TX_WATCHDOG(base);
1604 #endif
1605
1606 #if defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE)
1607 pdl_config->txDmaTrigger = _FLD2BOOL(I2S_TR_CTL_TX_REQ_EN, REG_I2S_TR_CTL(base));
1608 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1609 pdl_config->rxDmaTrigger = _FLD2BOOL(I2S_TR_CTL_RX_REQ_EN, REG_I2S_TR_CTL(base));;
1610 #endif
1611 #endif /* _CYHAL_AUDIOSS_RX_ENABLED */
1612
1613 uint32_t tx_ctl_val = REG_I2S_TX_CTL(base);
1614 #if defined(_CYHAL_AUDIOSS_TX_SLAVE_AVAILABLE)
1615 pdl_config->txMasterMode = _FLD2BOOL(I2S_TX_CTL_MS, tx_ctl_val);
1616 pdl_config->txSckiInversion = _FLD2BOOL(I2S_TX_CTL_SCKI_POL, tx_ctl_val);
1617 pdl_config->txSdoLatchingTime = _FLD2BOOL(I2S_TX_CTL_B_CLOCK_INV, tx_ctl_val);;
1618 #endif
1619 /* Register value is 1 less than the desired divider */
1620 pdl_config->clkDiv = ((uint8_t)_FLD2VAL(I2S_CLOCK_CTL_CLOCK_DIV, REG_I2S_CLOCK_CTL(base))) + 1;
1621 pdl_config->extClk = _FLD2BOOL(I2S_CLOCK_CTL_CLOCK_SEL, REG_I2S_CLOCK_CTL(base));
1622
1623 /* TDM mode A == channel data starts on rising edge of WS */
1624 pdl_config->txAlignment = (cy_en_i2s_alignment_t)_FLD2VAL(I2S_TX_CTL_I2S_MODE, tx_ctl_val);
1625 pdl_config->txWsPulseWidth = (cy_en_i2s_ws_pw_t)_FLD2VAL(I2S_TX_CTL_WS_PULSE, tx_ctl_val);
1626
1627 pdl_config->txSckoInversion = _FLD2BOOL(I2S_TX_CTL_SCKO_POL, tx_ctl_val);
1628
1629 /* Register is one less than desired value */
1630 pdl_config->txChannels = ((uint8_t)_FLD2VAL(I2S_TX_CTL_CH_NR, tx_ctl_val)) + 1;
1631 pdl_config->txChannelLength = (cy_en_i2s_len_t)_FLD2VAL(I2S_TX_CTL_CH_LEN, tx_ctl_val);
1632 pdl_config->txWordLength = (cy_en_i2s_len_t)_FLD2VAL(I2S_TX_CTL_WORD_LEN, tx_ctl_val);
1633 pdl_config->txOverheadValue = (cy_en_i2s_overhead_t)_FLD2VAL(I2S_TX_CTL_OVHDATA, tx_ctl_val);
1634 pdl_config->txFifoTriggerLevel = _cyhal_audioss_fifo_trigger_level(obj, true);
1635 #elif defined(CY_IP_MXTDM)
1636 TDM_TX_STRUCT_Type* base_tx = &obj->base->TDM_TX_STRUCT;
1637 TDM_RX_STRUCT_Type* base_rx = &obj->base->TDM_RX_STRUCT;
1638
1639 /* Configure TX */
1640 pdl_config->tx_config->enable = _cyhal_audioss_is_direction_enabled(obj, true);
1641 pdl_config->tx_config->masterMode = (cy_en_tdm_device_cfg_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_CTL_MS, TDM_STRUCT_TX_CTL(base_tx));
1642 pdl_config->tx_config->wordSize = (cy_en_tdm_ws_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_CTL_WORD_SIZE, TDM_STRUCT_TX_CTL(base_tx));
1643 pdl_config->tx_config->format = (cy_en_tdm_format_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_CTL_FORMAT, TDM_STRUCT_TX_CTL(base_tx));
1644
1645 uint32_t tx_if_ctl = TDM_STRUCT_TX_IF_CTL(base_tx);
1646 /* Divider in the config register is a less than the desired value */
1647 pdl_config->tx_config->clkDiv = (uint16_t)(_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_CLOCK_DIV, tx_if_ctl) + 1);
1648 pdl_config->tx_config->clkSel = (cy_en_tdm_clock_sel_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_CLOCK_SEL, tx_if_ctl);
1649 pdl_config->tx_config->sckPolarity = (cy_en_tdm_sckpolarity_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_SCK_POLARITY, tx_if_ctl);;
1650 pdl_config->tx_config->fsyncPolarity = (cy_en_tdm_fsyncpolarity_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_FSYNC_POLARITY, tx_if_ctl);;
1651 pdl_config->tx_config->fsyncFormat = (cy_en_tdm_fsyncformat_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_FSYNC_FORMAT, tx_if_ctl);
1652 /* Channel size and count in the register are one less than the desired value */
1653 pdl_config->tx_config->channelNum = (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_CH_NR, tx_if_ctl) + 1;
1654 pdl_config->tx_config->channelSize = (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_CH_SIZE, tx_if_ctl) + 1;
1655 pdl_config->tx_config->fifoTriggerLevel = _cyhal_audioss_fifo_trigger_level(obj, true);
1656 pdl_config->tx_config->chEn = TDM_STRUCT_TX_CH_CTL(base_tx);
1657 pdl_config->tx_config->signalInput = _FLD2VAL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_ROUTE_CTL_MODE, TDM_STRUCT_TX_ROUTE_CTL(base_tx));
1658 pdl_config->tx_config->i2sMode = _FLD2BOOL(TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_IF_CTL_I2S_MODE, tx_if_ctl);
1659
1660 /* Configure RX */
1661 pdl_config->rx_config->enable = _cyhal_audioss_is_direction_enabled(obj, false);
1662 pdl_config->rx_config->masterMode = (cy_en_tdm_device_cfg_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_CTL_MS, TDM_STRUCT_RX_CTL(base_rx));
1663 pdl_config->rx_config->wordSize = (cy_en_tdm_ws_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_CTL_WORD_SIZE, TDM_STRUCT_RX_CTL(base_rx));
1664 pdl_config->rx_config->format = (cy_en_tdm_format_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_CTL_FORMAT, TDM_STRUCT_RX_CTL(base_rx));
1665
1666 uint32_t rx_if_ctl = TDM_STRUCT_RX_IF_CTL(base_rx);
1667 /* Divider in the config register is a less than the desired value */
1668 pdl_config->rx_config->clkDiv = (uint16_t)(_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_CLOCK_DIV, rx_if_ctl) + 1);
1669 pdl_config->rx_config->clkSel = (cy_en_tdm_clock_sel_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_CLOCK_SEL, rx_if_ctl);
1670 pdl_config->rx_config->sckPolarity = (cy_en_tdm_sckpolarity_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_SCK_POLARITY, rx_if_ctl);
1671 pdl_config->rx_config->fsyncPolarity = (cy_en_tdm_fsyncpolarity_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_FSYNC_POLARITY, rx_if_ctl);;
1672 pdl_config->rx_config->fsyncFormat = (cy_en_tdm_fsyncformat_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_FSYNC_FORMAT, rx_if_ctl);
1673 /* Channel size and count in the register are one less than the desired value */
1674 pdl_config->rx_config->channelNum = (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_CH_NR, rx_if_ctl) + 1;
1675 pdl_config->rx_config->channelSize = (uint8_t)_FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_CH_SIZE, rx_if_ctl) + 1;
1676 pdl_config->rx_config->fifoTriggerLevel = _cyhal_audioss_fifo_trigger_level(obj, false);
1677 pdl_config->rx_config->chEn = TDM_STRUCT_RX_CH_CTL(base_rx);
1678 pdl_config->rx_config->signalInput = _FLD2VAL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_ROUTE_CTL_MODE, TDM_STRUCT_RX_ROUTE_CTL(base_rx));
1679 pdl_config->rx_config->i2sMode = _FLD2BOOL(TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_IF_CTL_I2S_MODE, rx_if_ctl);
1680 #else
1681 #error "Unrecognized audio IP"
1682 #endif
1683 }
1684
_cyhal_audioss_populate_pdl_config(_cyhal_audioss_t * obj,_cyhal_audioss_pdl_config_t * pdl_config,const _cyhal_audioss_config_t * hal_cfg,uint16_t sclk_div_rx,uint16_t sclk_div_tx,bool rx_en,bool tx_en,bool mclk_rx,bool mclk_tx)1685 static cy_rslt_t _cyhal_audioss_populate_pdl_config(_cyhal_audioss_t *obj, _cyhal_audioss_pdl_config_t* pdl_config,
1686 const _cyhal_audioss_config_t* hal_cfg,
1687 uint16_t sclk_div_rx, uint16_t sclk_div_tx,
1688 bool rx_en, bool tx_en, bool mclk_rx, bool mclk_tx)
1689 {
1690 #if defined(CY_IP_MXAUDIOSS)
1691 cy_en_i2s_len_t pdl_word_length_tx, pdl_channel_length_tx;
1692 cy_rslt_t result = _cyhal_audioss_length_to_pdl(obj->channel_length_tx, &pdl_channel_length_tx, obj);
1693 if (CY_RSLT_SUCCESS == result)
1694 {
1695 result = _cyhal_audioss_length_to_pdl(obj->word_length_tx, &pdl_word_length_tx, obj);
1696 }
1697 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1698 cy_en_i2s_len_t pdl_word_length_rx, pdl_channel_length_rx;
1699 if (CY_RSLT_SUCCESS == result)
1700 {
1701 result = _cyhal_audioss_length_to_pdl(obj->channel_length_rx, &pdl_channel_length_rx, obj);
1702 }
1703 if (CY_RSLT_SUCCESS == result)
1704 {
1705 result = _cyhal_audioss_length_to_pdl(obj->word_length_rx, &pdl_word_length_rx, obj);
1706 }
1707 #endif
1708
1709 if(CY_RSLT_SUCCESS == result)
1710 {
1711 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1712 pdl_config->txEnabled = tx_en;
1713 pdl_config->rxEnabled = rx_en;
1714
1715 pdl_config->rxMasterMode = !hal_cfg->is_rx_slave;
1716 pdl_config->rxAlignment = hal_cfg->is_i2s ? CY_I2S_I2S_MODE : CY_I2S_TDM_MODE_A;
1717 pdl_config->rxWsPulseWidth = hal_cfg->rx_ws_full
1718 ? CY_I2S_WS_ONE_CHANNEL_LENGTH : CY_I2S_WS_ONE_SCK_CYCLE;
1719 pdl_config->rxWatchdogEnable = false;
1720 pdl_config->rxWatchdogValue = 0u;
1721 pdl_config->rxSdiLatchingTime = false;
1722 pdl_config->rxSckoInversion = false;
1723 pdl_config->rxSckiInversion = false;
1724 pdl_config->rxChannels = hal_cfg->num_channels;
1725 pdl_config->rxChannelLength = pdl_channel_length_rx;
1726 pdl_config->rxWordLength = pdl_word_length_rx;
1727 pdl_config->rxSignExtension = false; /* All MSB are filled by zeros, per HAL API specification */
1728 pdl_config->rxFifoTriggerLevel = _CYHAL_AUDIOSS_FIFO_DEPTH / 2 - 1; // Trigger at half full
1729 #else
1730 CY_UNUSED_PARAMETER(rx_en);
1731 CY_UNUSED_PARAMETER(tx_en);
1732 #endif
1733
1734 #if defined(_CYHAL_AUDIOSS_TX_WATCHDOG_AVAILABLE)
1735 pdl_config->txWatchdogEnable = false;
1736 pdl_config->txWatchdogValue = 0u;
1737 #endif
1738
1739 #if defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE)
1740 pdl_config->txDmaTrigger = false;
1741 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1742 pdl_config->rxDmaTrigger = false;
1743 #endif
1744 #endif
1745
1746 #if defined(_CYHAL_AUDIOSS_TX_SLAVE_AVAILABLE)
1747 pdl_config->txMasterMode = !hal_cfg->is_tx_slave;
1748 pdl_config->txSckiInversion = false;
1749 pdl_config->txSdoLatchingTime = false;
1750 #endif
1751 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1752 /* Should be the same because RX and TX mclk must be the same if they are both enabled */
1753 CY_ASSERT(sclk_div_rx == sclk_div_tx || false == pdl_config->txEnabled || false == pdl_config->rxEnabled);
1754 pdl_config->clkDiv = (uint8_t)(pdl_config->txEnabled ? sclk_div_tx : sclk_div_rx);
1755 #else
1756 CY_UNUSED_PARAMETER(sclk_div_rx);
1757 pdl_config->clkDiv = (uint8_t)(sclk_div_tx);
1758 #endif
1759 pdl_config->extClk = mclk_tx; /* No separate rx/tx mclk selection on this IP */
1760 CY_UNUSED_PARAMETER(mclk_rx);
1761 /* TDM mode A == channel data starts on rising edge of WS */
1762 pdl_config->txAlignment = hal_cfg->is_i2s ? CY_I2S_I2S_MODE : CY_I2S_TDM_MODE_A;
1763 pdl_config->txWsPulseWidth = hal_cfg->tx_ws_full
1764 ? CY_I2S_WS_ONE_CHANNEL_LENGTH : CY_I2S_WS_ONE_SCK_CYCLE;
1765
1766 pdl_config->txSckoInversion = false;
1767
1768 pdl_config->txChannels = hal_cfg->num_channels;
1769 pdl_config->txChannelLength = pdl_channel_length_tx;
1770 pdl_config->txWordLength = pdl_word_length_tx;
1771 pdl_config->txOverheadValue = CY_I2S_OVHDATA_ZERO; /* Per HAL API specification */
1772 pdl_config->txFifoTriggerLevel = _CYHAL_AUDIOSS_FIFO_DEPTH / 2 + 1; /* Trigger at half empty */
1773 }
1774 #elif defined(CY_IP_MXTDM)
1775 cy_en_tdm_ws_t pdl_word_length_rx, pdl_word_length_tx;
1776 cy_rslt_t result = _cyhal_audioss_length_to_pdl(obj->word_length_tx, &pdl_word_length_tx, obj);
1777 if(CY_RSLT_SUCCESS == result)
1778 {
1779 result = _cyhal_audioss_length_to_pdl(obj->word_length_rx, &pdl_word_length_rx, obj);
1780 }
1781 if(CY_RSLT_SUCCESS == result)
1782 {
1783 /* Configure TX */
1784 pdl_config->tx_config->enable = tx_en;
1785 pdl_config->tx_config->masterMode = hal_cfg->is_tx_slave ? CY_TDM_DEVICE_SLAVE : CY_TDM_DEVICE_MASTER;
1786 pdl_config->tx_config->wordSize = pdl_word_length_tx;
1787 pdl_config->tx_config->format = hal_cfg->is_i2s ? CY_TDM_LEFT_DELAYED : CY_TDM_LEFT;
1788 pdl_config->tx_config->clkDiv = sclk_div_tx;
1789 /* Only clock 0 from the srss hooked up on current hardware */
1790 pdl_config->tx_config->clkSel = mclk_tx ? CY_TDM_SEL_MCLK_IN : CY_TDM_SEL_SRSS_CLK0;
1791 pdl_config->tx_config->sckPolarity = CY_TDM_CLK;
1792 /* For I2S, frame sync aka word select starts out low */
1793 pdl_config->tx_config->fsyncPolarity = hal_cfg->is_i2s ? CY_TDM_SIGN_INVERTED : CY_TDM_SIGN;
1794 pdl_config->tx_config->fsyncFormat = hal_cfg->tx_ws_full ? CY_TDM_CH_PERIOD : CY_TDM_BIT_PERIOD;
1795 pdl_config->tx_config->channelNum = hal_cfg->num_channels;
1796 pdl_config->tx_config->channelSize = obj->channel_length_tx;
1797 pdl_config->tx_config->fifoTriggerLevel = _CYHAL_AUDIOSS_FIFO_DEPTH / 2 + 1; /* Trigger at half empty */
1798 pdl_config->tx_config->chEn = hal_cfg->channel_mask;
1799 pdl_config->tx_config->signalInput = 0; /* TX and RX signaling independent */
1800 pdl_config->tx_config->i2sMode = hal_cfg->is_i2s;
1801
1802 /* Configure RX */
1803 pdl_config->rx_config->enable = rx_en;
1804 pdl_config->rx_config->masterMode = hal_cfg->is_rx_slave ? CY_TDM_DEVICE_SLAVE : CY_TDM_DEVICE_MASTER;
1805 pdl_config->rx_config->wordSize = pdl_word_length_rx;
1806 pdl_config->rx_config->format = hal_cfg->is_i2s ? CY_TDM_LEFT_DELAYED : CY_TDM_LEFT;
1807 pdl_config->rx_config->clkDiv = sclk_div_rx;
1808 /* Only clock 0 from the srss hooked up on current hardware */
1809 pdl_config->rx_config->clkSel = mclk_rx ? CY_TDM_SEL_MCLK_IN : CY_TDM_SEL_SRSS_CLK0;
1810 pdl_config->rx_config->sckPolarity = CY_TDM_CLK;
1811 /* For I2S, frame sync aka word select starts out low */
1812 pdl_config->rx_config->fsyncPolarity = hal_cfg->is_i2s ? CY_TDM_SIGN_INVERTED : CY_TDM_SIGN;
1813 pdl_config->rx_config->fsyncFormat = hal_cfg->rx_ws_full ? CY_TDM_CH_PERIOD : CY_TDM_BIT_PERIOD;
1814 pdl_config->rx_config->channelNum = hal_cfg->num_channels;
1815 pdl_config->rx_config->channelSize = obj->channel_length_rx;
1816 pdl_config->rx_config->fifoTriggerLevel = _CYHAL_AUDIOSS_FIFO_DEPTH / 2 + 1; /* Trigger at half empty */
1817 pdl_config->rx_config->chEn = hal_cfg->channel_mask;
1818 pdl_config->rx_config->signalInput = 0; /* TX and RX signaling independent */
1819 pdl_config->rx_config->i2sMode = hal_cfg->is_i2s;
1820 pdl_config->rx_config->signExtend = CY_ZERO_EXTEND;
1821 pdl_config->rx_config->lateSample = false;
1822 }
1823 #else
1824 #error "Unrecognized audio IP"
1825 #endif
1826 return result;
1827 }
1828
1829 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1830 /* Round up the word length to the next power of 2
1831 * NOTE: This method used only in I2S HAL function related to DMA.
1832 * To avoid compilation warning declare this function only when DMA is available
1833 */
_cyhal_audioss_rounded_word_length(_cyhal_audioss_t * obj,bool is_tx)1834 static uint8_t _cyhal_audioss_rounded_word_length(_cyhal_audioss_t *obj, bool is_tx)
1835 {
1836 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1837 uint8_t word_length = is_tx ? obj->word_length_tx : obj->word_length_rx;
1838 #else
1839 CY_UNUSED_PARAMETER(is_tx);
1840 uint8_t word_length = obj->word_length_tx;
1841 #endif
1842 CY_ASSERT(word_length <= 32);
1843 if(word_length <= 8)
1844 {
1845 return 8u;
1846 }
1847 else if(word_length <= 16)
1848 {
1849 return 16u;
1850 }
1851
1852 return 32u;
1853 }
1854 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
1855
_cyhal_audioss_write_async(_cyhal_audioss_t * obj,const void * tx,size_t tx_length)1856 cy_rslt_t _cyhal_audioss_write_async(_cyhal_audioss_t *obj, const void *tx, size_t tx_length)
1857 {
1858 CY_ASSERT(NULL != obj);
1859 if (obj->pm_transition_ready)
1860 {
1861 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1862 }
1863
1864 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1865 obj->async_tx_buff = tx;
1866 obj->async_tx_length = tx_length;
1867 cyhal_system_critical_section_exit(savedIntrStatus);
1868 switch(obj->async_mode)
1869 {
1870 case CYHAL_ASYNC_SW:
1871 {
1872 /* Write as much as we can now, then set up an interrupt to do the rest
1873 * This is a potentially long operation but we don't want other I2S operations to
1874 * interleave with it. So do a "mini critical section" and disable the interrupts for this block only.
1875 */
1876 uint32_t old_events = _cyhal_audioss_disable_events(obj, true);
1877 // Safe to cast away volatile here because we're calling write_until_full from within
1878 // a critical section, so it should not change out from under us during this call
1879 _cyhal_audioss_write_until_full(obj, (const void**)(&obj->async_tx_buff), (size_t *)(&obj->async_tx_length));
1880 _cyhal_audioss_restore_events(obj, true, old_events);
1881 if(obj->async_tx_length > 0)
1882 {
1883 _cyhal_audioss_update_enabled_events(obj);
1884 }
1885 else
1886 {
1887 _cyhal_audioss_process_event(obj, obj->interface->event_tx_complete);
1888 }
1889 break;
1890 }
1891 case CYHAL_ASYNC_DMA:
1892 {
1893 // Don't directly kick off the DMA here - it will be triggered
1894 // from the interrupt handler when the FIFO drops below the threshold
1895 // (which may have already happened by the time we get here if the
1896 // application already had the half-empty or empty event enabled)
1897 _cyhal_audioss_update_enabled_events(obj);
1898 break;
1899 }
1900 default:
1901 CY_ASSERT(0); /* Unrecognized async mode */
1902 break;
1903 }
1904
1905 return CY_RSLT_SUCCESS;
1906 }
1907
_cyhal_audioss_set_async_mode(_cyhal_audioss_t * obj,cyhal_async_mode_t mode,uint8_t dma_priority)1908 cy_rslt_t _cyhal_audioss_set_async_mode(_cyhal_audioss_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)
1909 {
1910 CY_ASSERT(NULL != obj);
1911
1912 // We don't support swapping the async mode out from under a pending transfer.
1913 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1914 CY_ASSERT(false == _cyhal_audioss_is_read_pending(obj));
1915 #endif
1916 CY_ASSERT(false == _cyhal_audioss_is_write_pending(obj));
1917
1918 cy_rslt_t result = CY_RSLT_SUCCESS;
1919
1920
1921 if(mode == CYHAL_ASYNC_DMA)
1922 {
1923 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1924 // Reserve a DMA channel for each direction that is enabled
1925 if(_cyhal_audioss_is_direction_enabled(obj, true) && CYHAL_RSC_INVALID == obj->tx_dma.resource.type)
1926 {
1927 /* Reserve a DMA channel for async transmit if tx is enabled */
1928 result = cyhal_dma_init(&obj->tx_dma, CYHAL_DMA_PRIORITY_DEFAULT, CYHAL_DMA_DIRECTION_MEM2PERIPH);
1929 cyhal_dma_register_callback(&obj->tx_dma, &_cyhal_audioss_dma_handler_tx, obj);
1930 }
1931 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1932 if(_cyhal_audioss_is_direction_enabled(obj, false) && CYHAL_RSC_INVALID == obj->rx_dma.resource.type)
1933 {
1934 /* Reserve a DMA channel for async receive if rx is enabled */
1935 result = cyhal_dma_init(&obj->rx_dma, CYHAL_DMA_PRIORITY_DEFAULT, CYHAL_DMA_DIRECTION_PERIPH2MEM);
1936 cyhal_dma_register_callback(&obj->rx_dma, &_cyhal_audioss_dma_handler_rx, obj);
1937 }
1938 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
1939 #else
1940 CY_ASSERT(0); /* DMA driver is not available */
1941 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
1942 }
1943 else
1944 {
1945 /* Free the DMA instances if we reserved them but don't need them anymore */
1946 if(CYHAL_RSC_INVALID != obj->tx_dma.resource.type)
1947 {
1948 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1949 cyhal_dma_free(&obj->tx_dma);
1950 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
1951 obj->tx_dma.resource.type = CYHAL_RSC_INVALID;
1952 }
1953 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1954 if(CYHAL_RSC_INVALID != obj->rx_dma.resource.type)
1955 {
1956 #if (CYHAL_DRIVER_AVAILABLE_DMA)
1957 cyhal_dma_free(&obj->rx_dma);
1958 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
1959 obj->rx_dma.resource.type = CYHAL_RSC_INVALID;
1960 }
1961 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
1962 }
1963
1964 if(CY_RSLT_SUCCESS == result)
1965 {
1966 obj->async_mode = mode;
1967 obj->async_dma_priority = dma_priority;
1968 }
1969 return result;
1970 }
1971
_cyhal_audioss_is_read_pending(_cyhal_audioss_t * obj)1972 bool _cyhal_audioss_is_read_pending(_cyhal_audioss_t *obj)
1973 {
1974 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1975 return (NULL != obj->async_rx_buff);
1976 #else
1977 CY_UNUSED_PARAMETER(obj);
1978 return false;
1979 #endif
1980 }
1981
_cyhal_audioss_is_write_pending(_cyhal_audioss_t * obj)1982 bool _cyhal_audioss_is_write_pending(_cyhal_audioss_t *obj)
1983 {
1984 return (NULL != obj->async_tx_buff);
1985 }
1986
_cyhal_audioss_abort_read_async(_cyhal_audioss_t * obj)1987 cy_rslt_t _cyhal_audioss_abort_read_async(_cyhal_audioss_t *obj)
1988 {
1989 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
1990 uint32_t saved_intr = cyhal_system_critical_section_enter();
1991 obj->async_rx_buff = NULL;
1992 _cyhal_audioss_update_enabled_events(obj);
1993 cyhal_system_critical_section_exit(saved_intr);
1994 return CY_RSLT_SUCCESS;
1995 #else
1996 return obj->interface->err_not_supported;
1997 #endif
1998 }
1999
_cyhal_audioss_abort_write_async(_cyhal_audioss_t * obj)2000 cy_rslt_t _cyhal_audioss_abort_write_async(_cyhal_audioss_t *obj)
2001 {
2002 uint32_t saved_intr = cyhal_system_critical_section_enter();
2003 obj->async_tx_buff = NULL;
2004 _cyhal_audioss_update_enabled_events(obj);
2005 cyhal_system_critical_section_exit(saved_intr);
2006 return CY_RSLT_SUCCESS;
2007 }
2008
2009 #if defined(CY_IP_MXAUDIOSS)
_cyhal_audioss_length_from_pdl(cy_en_i2s_len_t pdl_length)2010 static uint8_t _cyhal_audioss_length_from_pdl(cy_en_i2s_len_t pdl_length)
2011 {
2012 switch(pdl_length)
2013 {
2014 case CY_I2S_LEN8:
2015 return 8u;
2016 case CY_I2S_LEN16:
2017 return 16u;
2018 case CY_I2S_LEN18:
2019 return 18u;
2020 case CY_I2S_LEN20:
2021 return 20u;
2022 case CY_I2S_LEN24:
2023 return 24u;
2024 case CY_I2S_LEN32:
2025 return 32u;
2026 default:
2027 CY_ASSERT(false); /* Should never get here */
2028 return 32u;
2029 }
2030 }
2031
_cyhal_audioss_length_to_pdl(uint8_t user_length,cy_en_i2s_len_t * pdl_length,const _cyhal_audioss_t * obj)2032 static cy_rslt_t _cyhal_audioss_length_to_pdl(uint8_t user_length, cy_en_i2s_len_t *pdl_length, const _cyhal_audioss_t *obj)
2033 {
2034 cy_rslt_t result = CY_RSLT_SUCCESS;
2035 switch(user_length)
2036 {
2037 case 8u:
2038 *pdl_length = CY_I2S_LEN8;
2039 break;
2040 case 16u:
2041 *pdl_length = CY_I2S_LEN16;
2042 break;
2043 case 18u:
2044 *pdl_length = CY_I2S_LEN18;
2045 break;
2046 case 20u:
2047 *pdl_length = CY_I2S_LEN20;
2048 break;
2049 case 24u:
2050 *pdl_length = CY_I2S_LEN24;
2051 break;
2052 case 32u:
2053 *pdl_length = CY_I2S_LEN32;
2054 break;
2055 default:
2056 *pdl_length = CY_I2S_LEN32;
2057 result = obj->interface->err_invalid_arg;
2058 }
2059 return result;
2060 }
2061 #elif defined(CY_IP_MXTDM)
_cyhal_audioss_length_from_pdl(cy_en_tdm_ws_t pdl_length)2062 static uint8_t _cyhal_audioss_length_from_pdl(cy_en_tdm_ws_t pdl_length)
2063 {
2064 switch(pdl_length)
2065 {
2066 case CY_TDM_SIZE_8:
2067 return 8u;
2068 case CY_TDM_SIZE_10:
2069 return 10u;
2070 case CY_TDM_SIZE_12:
2071 return 12u;
2072 case CY_TDM_SIZE_14:
2073 return 14u;
2074 case CY_TDM_SIZE_16:
2075 return 16u;
2076 case CY_TDM_SIZE_18:
2077 return 18u;
2078 case CY_TDM_SIZE_20:
2079 return 20u;
2080 case CY_TDM_SIZE_24:
2081 return 24u;
2082 case CY_TDM_SIZE_32:
2083 return 32u;
2084 default:
2085 CY_ASSERT(false); /* Should never get here */
2086 return 32u;
2087 }
2088 }
2089
_cyhal_audioss_length_to_pdl(uint8_t user_length,cy_en_tdm_ws_t * pdl_length,const _cyhal_audioss_t * obj)2090 static cy_rslt_t _cyhal_audioss_length_to_pdl(uint8_t user_length, cy_en_tdm_ws_t *pdl_length, const _cyhal_audioss_t *obj)
2091 {
2092 cy_rslt_t result = CY_RSLT_SUCCESS;
2093 switch(user_length)
2094 {
2095 case 8u:
2096 *pdl_length = CY_TDM_SIZE_8;
2097 break;
2098 case 10u:
2099 *pdl_length = CY_TDM_SIZE_10;
2100 break;
2101 case 12u:
2102 *pdl_length = CY_TDM_SIZE_12;
2103 break;
2104 case 14u:
2105 *pdl_length = CY_TDM_SIZE_14;
2106 break;
2107 case 16u:
2108 *pdl_length = CY_TDM_SIZE_16;
2109 break;
2110 case 18u:
2111 *pdl_length = CY_TDM_SIZE_18;
2112 break;
2113 case 20u:
2114 *pdl_length = CY_TDM_SIZE_20;
2115 break;
2116 case 24u:
2117 *pdl_length = CY_TDM_SIZE_24;
2118 break;
2119 case 32u:
2120 *pdl_length = CY_TDM_SIZE_32;
2121 break;
2122 default:
2123 *pdl_length = CY_TDM_SIZE_32;
2124 result = obj->interface->err_invalid_arg;
2125 }
2126 return result;
2127 }
2128 #else
2129 #error "Unrecognized audio IP"
2130 #endif
2131
2132 #if defined(CY_IP_MXAUDIOSS)
_cyhal_audioss_irq_handler(void)2133 static void _cyhal_audioss_irq_handler(void)
2134 {
2135 _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
2136 uint8_t block = _cyhal_audioss_get_block_from_irqn(irqn);
2137 _cyhal_audioss_t* obj = _cyhal_audioss_config_structs[block];
2138
2139 uint32_t interrupt_status = Cy_I2S_GetInterruptStatusMasked(obj->base);
2140 Cy_I2S_ClearInterrupt(obj->base, interrupt_status);
2141 uint32_t event = obj->interface->convert_interrupt_cause(interrupt_status);
2142 _cyhal_audioss_process_event(obj, event);
2143 }
2144 #elif defined(CY_IP_MXTDM)
2145
2146 #if defined (COMPONENT_CAT5)
_cyhal_audioss_irq_handler_rx(uint8_t instance)2147 static void _cyhal_audioss_irq_handler_rx(uint8_t instance)
2148 {
2149 _cyhal_audioss_t* obj = _cyhal_audioss_config_structs[instance];
2150 #else
2151 static void _cyhal_audioss_irq_handler_rx()
2152 {
2153 _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
2154 uint8_t block = _cyhal_audioss_get_block_from_irqn(irqn);
2155 _cyhal_audioss_t* obj = _cyhal_audioss_config_structs[block];
2156 #endif
2157
2158 uint32_t interrupt_status = Cy_AudioTDM_GetRxInterruptStatusMasked(&obj->base->TDM_RX_STRUCT);
2159 Cy_AudioTDM_ClearRxInterrupt(&obj->base->TDM_RX_STRUCT, interrupt_status);
2160 uint32_t event = obj->interface->convert_interrupt_cause(interrupt_status, false);
2161 _cyhal_audioss_process_event(obj, event);
2162 #if defined (COMPONENT_CAT5)
2163 Cy_AudioTDM_EnableInterrupt(obj->base);
2164 #endif
2165 }
2166
2167 #if defined (COMPONENT_CAT5)
2168 static void _cyhal_audioss_irq_handler_tx(uint8_t instance)
2169 {
2170 _cyhal_audioss_t* obj = _cyhal_audioss_config_structs[instance];
2171 #else
2172 static void _cyhal_audioss_irq_handler_tx()
2173 {
2174 _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
2175 uint8_t block = _cyhal_audioss_get_block_from_irqn(irqn);
2176 _cyhal_audioss_t* obj = _cyhal_audioss_config_structs[block];
2177 #endif
2178
2179 uint32_t interrupt_status = Cy_AudioTDM_GetTxInterruptStatusMasked(&obj->base->TDM_TX_STRUCT);
2180 Cy_AudioTDM_ClearTxInterrupt(&obj->base->TDM_TX_STRUCT, interrupt_status);
2181 uint32_t event = obj->interface->convert_interrupt_cause(interrupt_status, true);
2182 _cyhal_audioss_process_event(obj, event);
2183 #if defined (COMPONENT_CAT5)
2184 Cy_AudioTDM_EnableInterrupt(obj->base);
2185 #endif
2186 }
2187
2188 #if defined (COMPONENT_CAT5)
2189 static void _cyhal_audioss_irq_handler(UINT8 instance, BOOL8 rx_int)
2190 {
2191 (rx_int) ? _cyhal_audioss_irq_handler_rx(instance) : _cyhal_audioss_irq_handler_tx(instance);
2192 }
2193 #endif
2194 #endif
2195
2196 static void _cyhal_audioss_update_enabled_events(_cyhal_audioss_t *obj)
2197 {
2198 uint32_t events = obj->user_enabled_events;
2199 if(NULL != obj->async_tx_buff && obj->async_tx_length > 0)
2200 {
2201 events |= (obj->interface->event_mask_empty | obj->interface->event_mask_half_empty);
2202 }
2203 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2204 if(NULL != obj->async_rx_buff && obj->async_rx_length > 0)
2205 {
2206 events |= (obj->interface->event_mask_full | obj->interface->event_mask_half_full);
2207 }
2208 #endif
2209
2210 #if defined(CY_IP_MXAUDIOSS)
2211 uint32_t mask = obj->interface->convert_to_pdl(events);
2212 // The register is 24 bits wide but the hardware pads the value out with 1's when read.
2213 // So mask down to just the bits that we actually care about.
2214 uint32_t old_mask = Cy_I2S_GetInterruptMask(obj->base) & CY_I2S_INTR_MASK;
2215
2216 // Clear the interrupts that are about to be enabled to avoid spurious firing
2217 uint32_t new_interrupts = mask & (~old_mask);
2218 Cy_I2S_ClearInterrupt(obj->base, new_interrupts);
2219
2220 Cy_I2S_SetInterruptMask(obj->base, mask);
2221 #elif defined(CY_IP_MXTDM)
2222 uint32_t tx_mask = obj->interface->convert_to_pdl(events, true);
2223 uint32_t old_tx_mask = Cy_AudioTDM_GetTxInterruptMask(&obj->base->TDM_TX_STRUCT);
2224
2225 // Clear the interrupts that are about to be enabled to avoid spurious firing
2226 uint32_t new_interrupts_tx = tx_mask & (~old_tx_mask);
2227 Cy_AudioTDM_ClearTxInterrupt(&obj->base->TDM_TX_STRUCT, new_interrupts_tx);
2228 Cy_AudioTDM_SetTxInterruptMask(&obj->base->TDM_TX_STRUCT, tx_mask);
2229
2230 uint32_t rx_mask = obj->interface->convert_to_pdl(events, false);
2231 uint32_t old_rx_mask = Cy_AudioTDM_GetRxInterruptMask(&obj->base->TDM_RX_STRUCT);
2232
2233 // Clear the interrupts that are about to be enabled to avoid spurious firing
2234 uint32_t new_interrupts_rx = rx_mask & (~old_rx_mask);
2235 Cy_AudioTDM_ClearRxInterrupt(&obj->base->TDM_RX_STRUCT, new_interrupts_rx);
2236 Cy_AudioTDM_SetRxInterruptMask(&obj->base->TDM_RX_STRUCT, rx_mask);
2237 #endif
2238 }
2239
2240 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2241 static void _cyhal_audioss_update_rx_trigger_level(_cyhal_audioss_t *obj)
2242 {
2243 // If we're doing an async read and the amount remaining is less than
2244 // the standard trigger level, temporarily reduce it so that we get
2245 // an interrupt as soon as the amount the user requested is ready
2246 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
2247 uint8_t trigger_level = obj->user_fifo_level_rx;
2248 if(NULL != obj->async_rx_buff
2249 && obj->async_rx_length < trigger_level
2250 && obj->async_rx_length > 0)
2251 {
2252 trigger_level = obj->async_rx_length;
2253 }
2254
2255 #if defined(CY_IP_MXAUDIOSS)
2256 // Safe to do a blind write of this register because the only other bits are
2257 // CLEAR, which is only set temporarily from clear_tx, and FREEZE, which is
2258 // never used by this driver (it exists for debugging purposes only)
2259 obj->base->RX_FIFO_CTL = (uint32_t) (trigger_level << I2S_RX_FIFO_CTL_TRIGGER_LEVEL_Pos);
2260 #elif defined(CY_IP_MXTDM)
2261 uint32_t value = obj->base->TDM_RX_STRUCT.RX_FIFO_CTL;
2262 value &= ~TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_FIFO_CTL_TRIGGER_LEVEL_Msk;
2263 value |= trigger_level << TDM_TDM_STRUCT_TDM_TX_STRUCT_TX_FIFO_CTL_TRIGGER_LEVEL_Pos;
2264 obj->base->TDM_RX_STRUCT.RX_FIFO_CTL = value;
2265 #endif
2266 cyhal_system_critical_section_exit(savedIntrStatus);
2267 }
2268 #endif
2269
2270 static uint32_t _cyhal_audioss_disable_events(_cyhal_audioss_t *obj, bool tx)
2271 {
2272 #if defined(CY_IP_MXAUDIOSS)
2273 CY_UNUSED_PARAMETER(tx);
2274 uint32_t old_interrupt_mask = Cy_I2S_GetInterruptMask(obj->base);
2275 Cy_I2S_SetInterruptMask(obj->base, 0u);
2276 return old_interrupt_mask;
2277 #elif defined(CY_IP_MXTDM)
2278 uint32_t old_interrupt_mask;
2279 if(tx)
2280 {
2281 old_interrupt_mask = Cy_AudioTDM_GetTxInterruptMask(&obj->base->TDM_TX_STRUCT);
2282 Cy_AudioTDM_SetTxInterruptMask(&obj->base->TDM_TX_STRUCT, 0u);
2283 }
2284 else
2285 {
2286 old_interrupt_mask = Cy_AudioTDM_GetRxInterruptMask(&obj->base->TDM_RX_STRUCT);
2287 Cy_AudioTDM_SetRxInterruptMask(&obj->base->TDM_RX_STRUCT, 0u);
2288 }
2289 return old_interrupt_mask;
2290 #endif
2291 }
2292
2293 static void _cyhal_audioss_restore_events(_cyhal_audioss_t *obj, bool tx, uint32_t old_events)
2294 {
2295 #if defined(CY_IP_MXAUDIOSS)
2296 CY_UNUSED_PARAMETER(tx);
2297 Cy_I2S_SetInterruptMask(obj->base, old_events);
2298 #elif defined(CY_IP_MXTDM)
2299 if(tx)
2300 {
2301 Cy_AudioTDM_SetTxInterruptMask(&obj->base->TDM_TX_STRUCT, old_events);
2302 }
2303 else
2304 {
2305 Cy_AudioTDM_SetRxInterruptMask(&obj->base->TDM_RX_STRUCT, old_events);
2306 }
2307 #endif
2308 }
2309
2310 #if defined(_CYHAL_AUDIOSS_RX_ENABLED) && (CYHAL_DRIVER_AVAILABLE_DMA)
2311 static cy_rslt_t _cyhal_audioss_dma_perform_rx(_cyhal_audioss_t *obj)
2312 {
2313 // We could have received an event after we started the DMA but before it
2314 // managed to bring the FIFO below the threshold
2315 if(cyhal_dma_is_busy(&(obj->rx_dma)))
2316 return CY_RSLT_SUCCESS;
2317 /* ISR triggers when we have one more entry in the FIFO than the trigger level */
2318 size_t transfer_size = _cyhal_audioss_fifo_trigger_level(obj, false) + 1;
2319 if (transfer_size >= obj->async_rx_length)
2320 {
2321 transfer_size = obj->async_rx_length;
2322 // Only want the user callback to be call on the last dma transfer.
2323 cyhal_dma_enable_event(&(obj->rx_dma), CYHAL_DMA_TRANSFER_COMPLETE, obj->async_dma_priority, true);
2324 }
2325
2326 cyhal_dma_cfg_t dma_cfg =
2327 {
2328 #if defined(CY_IP_MXAUDIOSS)
2329 .src_addr = (uint32_t)(&(obj->base->RX_FIFO_RD)),
2330 #elif defined(CY_IP_MXTDM)
2331 .src_addr = (uint32_t)(&(obj->base->TDM_RX_STRUCT.RX_FIFO_RD)),
2332 #endif
2333 .src_increment = 0,
2334 .dst_addr = (uint32_t)obj->async_rx_buff,
2335 .dst_increment = 1,
2336 .transfer_width = _cyhal_audioss_rounded_word_length(obj, false),
2337 .length = transfer_size,
2338 .burst_size = 0,
2339 .action = CYHAL_DMA_TRANSFER_FULL,
2340 };
2341 cy_rslt_t result = cyhal_dma_configure(&(obj->rx_dma), &dma_cfg);
2342
2343 if(CY_RSLT_SUCCESS == result)
2344 {
2345 result = cyhal_dma_enable(&(obj->rx_dma));
2346 }
2347
2348 // Update the buffer first so that it's guaranteed to be correct whenever the DMA completes
2349 if(CY_RSLT_SUCCESS == result)
2350 {
2351 size_t increment_bytes = transfer_size * (_cyhal_audioss_rounded_word_length(obj, false) / 8);
2352 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
2353 obj->async_rx_buff = (void*)(((uint8_t*) obj->async_rx_buff) + increment_bytes);
2354 obj->async_rx_length -= transfer_size;
2355 _cyhal_audioss_update_rx_trigger_level(obj);
2356 _cyhal_audioss_update_enabled_events(obj);
2357 cyhal_system_critical_section_exit(savedIntrStatus);
2358
2359 result = cyhal_dma_start_transfer(&(obj->rx_dma));
2360 }
2361
2362 return result;
2363 }
2364 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) && (CYHAL_DRIVER_AVAILABLE_DMA) */
2365
2366 #if (CYHAL_DRIVER_AVAILABLE_DMA)
2367 static cy_rslt_t _cyhal_audioss_dma_perform_tx(_cyhal_audioss_t *obj)
2368 {
2369 // We could have received an event after the DMA brought the FIFO below the
2370 // threshold but before the DMA is entirely complete
2371 if(cyhal_dma_is_busy(&(obj->tx_dma)))
2372 return CY_RSLT_SUCCESS;
2373
2374 CY_ASSERT(NULL != obj->async_tx_buff);
2375
2376 /* ISR triggers when the FIFO count less than the trigger level */
2377 size_t transfer_size = _cyhal_audioss_fifo_trigger_level(obj, true) + 1;
2378 if (transfer_size >= obj->async_tx_length)
2379 {
2380 transfer_size = obj->async_tx_length;
2381 // Only want the user callback to be call on the last dma transfer.
2382 cyhal_dma_enable_event(&(obj->tx_dma), CYHAL_DMA_TRANSFER_COMPLETE, obj->async_dma_priority, true);
2383 }
2384
2385 cyhal_dma_cfg_t dma_cfg =
2386 {
2387 .src_addr = (uint32_t)obj->async_tx_buff,
2388 .src_increment = 1,
2389 #if defined(CY_IP_MXAUDIOSS)
2390 .dst_addr = (uint32_t)(&(obj->base->TX_FIFO_WR)),
2391 #elif defined(CY_IP_MXTDM)
2392 .dst_addr = (uint32_t)(&(obj->base->TDM_TX_STRUCT.TX_FIFO_WR)),
2393 #endif
2394 .dst_increment = 0,
2395 .transfer_width = _cyhal_audioss_rounded_word_length(obj, true),
2396 .length = transfer_size,
2397 .burst_size = 0,
2398 .action = CYHAL_DMA_TRANSFER_FULL,
2399 };
2400 cy_rslt_t result = cyhal_dma_configure(&(obj->tx_dma), &dma_cfg);
2401
2402 if(CY_RSLT_SUCCESS == result)
2403 {
2404 result = cyhal_dma_enable(&(obj->tx_dma));
2405 }
2406
2407 // Update the buffer first so that it's guaranteed to be correct whenever the DMA completes
2408 if(CY_RSLT_SUCCESS == result)
2409 {
2410 size_t increment_bytes = transfer_size * (_cyhal_audioss_rounded_word_length(obj, true) / 8);
2411 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
2412 obj->async_tx_buff = (void*)(((uint8_t*) obj->async_tx_buff) + increment_bytes);
2413 obj->async_tx_length -= transfer_size;
2414
2415 // Do this after we've updated async_tx_buff/length because once we have kicked
2416 // off the final DMA transfer there is no further action we will take on the
2417 // half-empty/empty events, and we don't want those to wind upstarving the
2418 // DMA complete event
2419 _cyhal_audioss_update_enabled_events(obj);
2420 cyhal_system_critical_section_exit(savedIntrStatus);
2421
2422 result = cyhal_dma_start_transfer(&(obj->tx_dma));
2423 }
2424
2425 return result;
2426 }
2427 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
2428
2429 static uint32_t _cyhal_audioss_get_num_in_fifo(_cyhal_audioss_t *obj, bool is_tx)
2430 {
2431 #if defined(CY_IP_MXAUDIOSS)
2432 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2433 return is_tx ? Cy_I2S_GetNumInTxFifo(obj->base) : Cy_I2S_GetNumInRxFifo(obj->base);
2434 #else
2435 CY_UNUSED_PARAMETER(is_tx);
2436 return Cy_I2S_GetNumInTxFifo(obj->base);
2437 #endif
2438 #elif defined(CY_IP_MXTDM)
2439 return is_tx ? Cy_AudioTDM_GetNumInTxFifo(&obj->base->TDM_TX_STRUCT)
2440 : Cy_AudioTDM_GetNumInRxFifo(&obj->base->TDM_RX_STRUCT);
2441 #endif
2442 }
2443
2444 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2445 static uint32_t _cyhal_audioss_read_fifo(_cyhal_audioss_t *obj)
2446 {
2447 #if defined(CY_IP_MXAUDIOSS)
2448 return Cy_I2S_ReadRxData(obj->base);
2449 #elif defined(CY_IP_MXTDM)
2450 return Cy_AudioTDM_ReadRxData(&obj->base->TDM_RX_STRUCT);
2451 #endif
2452 }
2453 #endif
2454
2455 static void _cyhal_audioss_write_fifo(_cyhal_audioss_t *obj, uint32_t value)
2456 {
2457 #if defined(CY_IP_MXAUDIOSS)
2458 Cy_I2S_WriteTxData(obj->base, value);
2459 #elif defined(CY_IP_MXTDM)
2460 Cy_AudioTDM_WriteTxData(&obj->base->TDM_TX_STRUCT, value);
2461 #endif
2462 }
2463
2464 #if defined(_CYHAL_AUDIOSS_RX_ENABLED) && (CYHAL_DRIVER_AVAILABLE_DMA)
2465 /* Callback argument is the I2S instance */
2466 static void _cyhal_audioss_dma_handler_rx(void *callback_arg, cyhal_dma_event_t event)
2467 {
2468 CY_UNUSED_PARAMETER(event);
2469 /* We only hook this handler up when we're doing the final transfer, so send the completed event */
2470 CY_ASSERT(CYHAL_DMA_TRANSFER_COMPLETE == event);
2471
2472 _cyhal_audioss_t *obj = (_cyhal_audioss_t*)callback_arg;
2473 obj->async_rx_buff = NULL;
2474 cyhal_dma_enable_event(&obj->rx_dma, CYHAL_DMA_TRANSFER_COMPLETE, obj->async_dma_priority, false);
2475 _cyhal_audioss_process_event(obj, obj->interface->event_rx_complete);
2476 }
2477 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) && (CYHAL_DRIVER_AVAILABLE_DMA) */
2478
2479 #if (CYHAL_DRIVER_AVAILABLE_DMA)
2480 /* Callback argument is the I2S instance */
2481 static void _cyhal_audioss_dma_handler_tx(void *callback_arg, cyhal_dma_event_t event)
2482 {
2483 CY_UNUSED_PARAMETER(event);
2484 /* We only hook this handler up when we're doing the final transfer, so send the completed event */
2485 CY_ASSERT(CYHAL_DMA_TRANSFER_COMPLETE == event);
2486
2487 _cyhal_audioss_t *obj = (_cyhal_audioss_t*)callback_arg;
2488 obj->async_tx_buff = NULL;
2489 cyhal_dma_enable_event(&obj->tx_dma, CYHAL_DMA_TRANSFER_COMPLETE, obj->async_dma_priority, false);
2490 _cyhal_audioss_process_event(obj, obj->interface->event_tx_complete);
2491 }
2492 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
2493
2494 static void _cyhal_audioss_process_event(_cyhal_audioss_t *obj, uint32_t event)
2495 {
2496 if(0 != (event & (obj->interface->event_mask_empty | obj->interface->event_mask_half_empty)))
2497 {
2498 /* We should normally not get the "empty" interrupt during an async transfer because we
2499 * should be topping the FIFO back up after each half-empty interrupt. But in case something
2500 * delays our response and the FIFO gets all the way to empty, listen for that as well
2501 */
2502 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
2503 if(NULL != obj->async_tx_buff && obj->async_tx_length > 0)
2504 {
2505 switch(obj->async_mode)
2506 {
2507 case CYHAL_ASYNC_SW:
2508 {
2509 /* Write as much as we can out until the FIFO is full
2510 * This is a potentially long operation but we don't want other I2S operations to
2511 * interleave with it. So switch to a "mini critical section" and disable the
2512 * interrupts for this block only while we're copying
2513 */
2514 uint32_t old_events = _cyhal_audioss_disable_events(obj, true);
2515 cyhal_system_critical_section_exit(savedIntrStatus);
2516 // Safe to cast away volatile here because we're calling write_until_full from within
2517 // a critical section, so it should not change out from under us during this call
2518 _cyhal_audioss_write_until_full(obj, (const void**)(&obj->async_tx_buff), (size_t *)(&obj->async_tx_length));
2519
2520 // Re-enter the global critical section so that the exit below behaves correctly
2521 savedIntrStatus = cyhal_system_critical_section_enter();
2522 _cyhal_audioss_restore_events(obj, true, old_events);
2523 if(0 == obj->async_tx_length)
2524 {
2525 /* We finished the async transfer. */
2526 event |= obj->interface->event_tx_complete;
2527 }
2528 break;
2529 }
2530 case CYHAL_ASYNC_DMA:
2531 {
2532 #if (CYHAL_DRIVER_AVAILABLE_DMA)
2533 cy_rslt_t result = _cyhal_audioss_dma_perform_tx(obj);
2534 CY_UNUSED_PARAMETER(result);
2535 CY_ASSERT(CY_RSLT_SUCCESS == result);
2536 #else
2537 CY_ASSERT(0); /* DMA driver is not available */
2538 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
2539 break;
2540 }
2541 default:
2542 CY_ASSERT(0); /* Unrecognized async mode */
2543 break;
2544 }
2545 }
2546
2547 cyhal_system_critical_section_exit(savedIntrStatus);
2548 }
2549
2550 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2551 if(0 != (event & (obj->interface->event_mask_full | obj->interface->event_mask_half_full)))
2552 {
2553 /* Similar to TX, we don't expect to receive the "full" interrupt, but check for it out of caution */
2554 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
2555 if(NULL != obj->async_rx_buff && obj->async_rx_length > 0)
2556 {
2557 switch(obj->async_mode)
2558 {
2559 case CYHAL_ASYNC_SW:
2560 {
2561 /* Read as much as we can until the FIFO is empty
2562 * This is a potentially long operation but we don't want other I2S operations to
2563 * interleave with it. So switch to a "mini critical section" and disable the
2564 * interrupts for this block only while we're copying
2565 */
2566 uint32_t old_events = _cyhal_audioss_disable_events(obj, false);
2567 cyhal_system_critical_section_exit(savedIntrStatus);
2568 // Safe to cast away volatile here because we're calling read_until_empty from within
2569 // a critical section, so it should not change out from under us during this call
2570 _cyhal_audioss_read_until_empty(obj, (void**)(&obj->async_rx_buff), (size_t*)(&obj->async_rx_length));
2571 // Re-enter the global critical section so that the exit below behaves correctly
2572 savedIntrStatus = cyhal_system_critical_section_enter();
2573 _cyhal_audioss_restore_events(obj, false, old_events);
2574 _cyhal_audioss_update_enabled_events(obj);
2575 if(0 == obj->async_rx_length)
2576 {
2577 /* We finished the async transfer. */
2578 event |= obj->interface->event_rx_complete;
2579 }
2580 break;
2581 }
2582 case CYHAL_ASYNC_DMA:
2583 #if (CYHAL_DRIVER_AVAILABLE_DMA)
2584 _cyhal_audioss_dma_perform_rx(obj);
2585 #else
2586 CY_ASSERT(0); /* DMA driver is not available */
2587 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
2588 break;
2589
2590 default:
2591 CY_ASSERT(0); /* Unrecognized async mode */
2592 }
2593
2594 // During async rx transfers, we may temporarily set the trigger level below half-full.
2595 // So make sure that it's a real "half full" and skip propagating to the user if it isn't
2596 #if defined(CY_IP_MXAUDIOSS)
2597 uint8_t trigger_level = (obj->base->RX_FIFO_CTL & I2S_RX_FIFO_CTL_TRIGGER_LEVEL_Msk) >> I2S_RX_FIFO_CTL_TRIGGER_LEVEL_Pos;
2598 #elif defined(CY_IP_MXTDM)
2599 uint8_t trigger_level = (obj->base->TDM_RX_STRUCT.RX_FIFO_CTL & TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_FIFO_CTL_TRIGGER_LEVEL_Msk) >> TDM_TDM_STRUCT_TDM_RX_STRUCT_RX_FIFO_CTL_TRIGGER_LEVEL_Pos;
2600 #endif
2601 if(trigger_level != obj->user_fifo_level_rx)
2602 {
2603 event &= ~(obj->interface->event_mask_half_full);
2604 }
2605 }
2606
2607 cyhal_system_critical_section_exit(savedIntrStatus);
2608 }
2609 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
2610
2611 /* Mark async transfer as complete if we just finished one. */
2612 if(0 != (event & obj->interface->event_tx_complete))
2613 {
2614 obj->async_tx_buff = NULL;
2615 _cyhal_audioss_update_enabled_events(obj);
2616 }
2617
2618 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2619 if(0 != (event & obj->interface->event_rx_complete))
2620 {
2621 obj->async_rx_buff = NULL;
2622 _cyhal_audioss_update_enabled_events(obj);
2623 }
2624 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
2625
2626 if(0 != (event & obj->user_enabled_events))
2627 {
2628 obj->interface->invoke_user_callback(obj, event & obj->user_enabled_events);
2629 }
2630 }
2631
2632 static bool _cyhal_audioss_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg)
2633 {
2634 _cyhal_audioss_t *obj = (_cyhal_audioss_t *)callback_arg;
2635 CY_UNUSED_PARAMETER(state);
2636
2637 switch(mode)
2638 {
2639 case CYHAL_SYSPM_CHECK_READY:
2640 {
2641 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2642 bool is_active = _cyhal_audioss_is_rx_enabled(obj)
2643 || _cyhal_audioss_is_tx_enabled(obj);
2644 obj->pm_transition_ready = !is_active && !(_cyhal_audioss_is_read_pending(obj) || _cyhal_audioss_is_tx_busy(obj));
2645 #else
2646 bool is_active = _cyhal_audioss_is_tx_enabled(obj);
2647 obj->pm_transition_ready = !is_active && !_cyhal_audioss_is_tx_busy(obj);
2648 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
2649 return obj->pm_transition_ready;
2650 }
2651 case CYHAL_SYSPM_CHECK_FAIL:
2652 case CYHAL_SYSPM_AFTER_TRANSITION:
2653 obj->pm_transition_ready = false;
2654 return true;
2655 default:
2656 return true;
2657 }
2658 }
2659
2660 #if defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE)
2661 static cyhal_source_t _cyhal_audioss_calculate_source(_cyhal_audioss_t *obj, bool is_rx)
2662 {
2663 #if defined(_CYHAL_AUDIOSS_RX_ENABLED)
2664 return is_rx ? _cyhal_audioss_rx_trigger[obj->resource.block_num] : _cyhal_audioss_tx_trigger[obj->resource.block_num];
2665 #else
2666 CY_UNUSED_PARAMETER(is_rx);
2667 return _cyhal_audioss_tx_trigger[obj->resource.block_num];
2668 #endif /* defined(_CYHAL_AUDIOSS_RX_ENABLED) */
2669 }
2670 #endif
2671
2672 cy_rslt_t _cyhal_audioss_enable_output(_cyhal_audioss_t *obj, bool is_rx, cyhal_source_t *source)
2673 {
2674 #if defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE)
2675 #if defined(CY_IP_MXAUDIOSS) /* On MXTDM the trigger lines are always enabled */
2676 if(is_rx)
2677 {
2678 REG_I2S_TR_CTL(obj->base) |= I2S_TR_CTL_RX_REQ_EN_Msk;
2679 }
2680 else
2681 {
2682 REG_I2S_TR_CTL(obj->base) |= I2S_TR_CTL_TX_REQ_EN_Msk;
2683 }
2684 #endif /* defined(CY_IP_MXAUDIOSS) */
2685
2686 *source = _cyhal_audioss_calculate_source(obj, is_rx);
2687 return CY_RSLT_SUCCESS;
2688 #else
2689 CY_UNUSED_PARAMETER(is_rx);
2690 CY_UNUSED_PARAMETER(source);
2691 return obj->interface->err_not_supported;
2692 #endif /* defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE) */
2693 }
2694
2695 cy_rslt_t _cyhal_audioss_disable_output(_cyhal_audioss_t *obj, bool is_rx)
2696 {
2697 #if defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE)
2698 #if defined(CY_IP_MXAUDIOSS)
2699 if(is_rx)
2700 {
2701 REG_I2S_TR_CTL(obj->base) &= ~I2S_TR_CTL_RX_REQ_EN_Msk;
2702 }
2703 else
2704 {
2705 REG_I2S_TR_CTL(obj->base) &= ~I2S_TR_CTL_TX_REQ_EN_Msk;
2706 }
2707 #else
2708 /* On MXTDM the trigger lines are always enabled */
2709 CY_UNUSED_PARAMETER(obj);
2710 CY_UNUSED_PARAMETER(is_rx);
2711 #endif /* defined(CY_IP_MXAUDIOSS) */
2712
2713 return CY_RSLT_SUCCESS;
2714 #else
2715 CY_UNUSED_PARAMETER(is_rx);
2716 return obj->interface->err_not_supported;
2717 #endif /* defined(_CYHAL_AUDIOSS_TRIGGERS_AVAILABLE) */
2718 }
2719
2720
2721 #if defined(__cplusplus)
2722 }
2723 #endif
2724
2725 #endif /* CYHAL_DRIVER_AVAILABLE_I2S */
2726