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