1 /*******************************************************************************
2 * File Name: cyhal_tdm.c
3 *
4 * Description:
5 * Provides a high level interface for interacting with the Infineon TDM. This is
6 * a wrapper around the lower level PDL API.
7 *
8 ********************************************************************************
9 * \copyright
10 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
11 * an affiliate of Cypress Semiconductor Corporation
12 *
13 * SPDX-License-Identifier: Apache-2.0
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 *     http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 *******************************************************************************/
27 
28 #include "cyhal_tdm.h"
29 #include "cyhal_audio_common.h"
30 #include "cyhal_system.h"
31 
32 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C)
33 /**
34 * \addtogroup group_hal_impl_tdm TDM (Time Division Multiplexing)
35 * \ingroup group_hal_impl
36 * \{
37 * The CAT1 (PSoC™ 6) TDM Supports the following values for word lengths:
38 * - 8 bits
39 * - 10 bits (CAT1B only)
40 * - 12 bits (CAT1B only)
41 * - 14 bits (CAT1B only)
42 * - 16 bits
43 * - 18 bits
44 * - 20 bits
45 * - 24 bits
46 * - 32 bits
47 *
48 * On CAT1A devices, the only supported channel length is 32 bits. On CAT1B devices, the channel
49 * length may be any value greater than or equal to the word length and less than or equal to 32 bits.
50 *
51 * On CAT1A devices, up to 8 channels are supported.
52 * On CAT1B devices, the number of supported channels is specified by the `TDM_NR<n>_CH_NR` macros.
53 * Disabling channels (so that they are included in the sequencing but ignored) is only supported
54 * on CAT1B devices.
55 *
56 * The sclk signal is formed by integer division of the input clock source (either internally
57 * provided or from the mclk pin). The CAT1A TDM supports sclk divider values from 1 to 64. On
58 * CAT1B devices, the TDM supports sclk divider values from 2 to 256.
59 * On CAT1A devices, if both RX and TX are used, the same GPIO must be specified for
60 * mclk in both directions. See the device datasheet for more details on valid pin selections.
61 *
62 * The following events are not supported on CAT1B:
63 * - \ref CYHAL_TDM_TX_EMPTY
64 * - \ref CYHAL_TDM_TX_NOT_FULL
65 * - \ref CYHAL_TDM_RX_FULL
66 * - \ref CYHAL_TDM_RX_NOT_EMPTY
67 *
68 * \note If the TDM hardware is initialized with a configurator-generated configuration via the
69 * @ref cyhal_tdm_init_cfg function, the @ref CYHAL_TDM_TX_HALF_EMPTY and @ref CYHAL_TDM_RX_HALF_FULL
70 * events will be raised at the configurator defined TX and RX FIFO trigger levels, respectively,
71 * instead of their usual trigger level of half the FIFO depth.
72 *
73 * \} group_hal_impl_tdm
74 */
75 #elif defined(COMPONENT_CAT2)
76 /**
77 * \addtogroup group_hal_impl_tdm TDM (Time Division Multiplexing)
78 * \ingroup group_hal_impl
79 * \{
80 *
81 * The CAT2 (PSoC™ 4) TDM only supports TX in master mode.
82 *
83 * There are no trigger connections available from the TDM peripheral to other peripherals on
84 * the CAT2 platform. Hence, the \ref cyhal_tdm_enable_output and \ref cyhal_tdm_disable_output
85 * are not supported on this platform.
86 *
87 * It supports the following values for word lengths:
88 * - 8 bits
89 * - 16 bits
90 * - 18 bits
91 * - 20 bits
92 * - 24 bits
93 * - 32 bits
94 *
95 * The channel length must be greater than or equal to the word length. On CAT2 devices, the
96 * set of supported channel lengths is the same as the set of supported word lengths.
97 *
98 * The sclk signal is formed by integer division of the input clock source (either internally
99 * provided or from the mclk pin). The CAT2 TDM supports sclk divider values from 1 to 64.
100 
101 * \note If the TDM hardware is initialized with a configurator-generated configuration via the
102 * @ref cyhal_tdm_init_cfg function, the @ref CYHAL_TDM_TX_HALF_EMPTY event will be raised at the
103 * configurator defined TX FIFO trigger level instead of the usual trigger level of half the FIFO depth.
104 *
105 * \} group_hal_impl_tdm
106 */
107 #endif
108 
109 #if (CYHAL_DRIVER_AVAILABLE_TDM)
110 
111 #if defined(__cplusplus)
112 extern "C"
113 {
114 #endif
115 
116 #if defined(CY_IP_MXAUDIOSS)
117 static uint32_t _cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause);
118 static uint32_t _cyhal_tdm_convert_event(uint32_t event);
119 #elif defined(CY_IP_MXTDM)
120 static uint32_t _cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause, bool is_tx);
121 static uint32_t _cyhal_tdm_convert_event(uint32_t event, bool is_tx);
122 #endif
123 
124 static void _cyhal_tdm_invoke_callback(_cyhal_audioss_t* obj, uint32_t event);
125 
126 static const _cyhal_audioss_interface_t _cyhal_tdm_interface =
127 {
128     .convert_interrupt_cause = _cyhal_tdm_convert_interrupt_cause,
129     .convert_to_pdl = _cyhal_tdm_convert_event,
130     .invoke_user_callback = _cyhal_tdm_invoke_callback,
131     .event_mask_empty = CYHAL_TDM_TX_EMPTY,
132     .event_mask_half_empty = CYHAL_TDM_TX_HALF_EMPTY,
133 #if (CYHAL_DRIVER_AVAILABLE_TDM_RX)
134     .event_mask_full = CYHAL_TDM_RX_FULL,
135     .event_mask_half_full = CYHAL_TDM_RX_HALF_FULL,
136     .event_rx_complete = CYHAL_TDM_ASYNC_RX_COMPLETE,
137 #endif
138     .event_tx_complete = CYHAL_TDM_ASYNC_TX_COMPLETE,
139     .err_invalid_pin = CYHAL_TDM_RSLT_ERR_INVALID_PIN,
140     .err_invalid_arg = CYHAL_TDM_RSLT_ERR_INVALID_ARG,
141     .err_clock = CYHAL_TDM_RSLT_ERR_CLOCK,
142     .err_not_supported = CYHAL_TDM_RSLT_NOT_SUPPORTED,
143 };
144 
cyhal_tdm_init(cyhal_tdm_t * obj,const cyhal_tdm_pins_t * tx_pins,const cyhal_tdm_pins_t * rx_pins,const cyhal_tdm_config_t * config,cyhal_clock_t * clk)145 cy_rslt_t cyhal_tdm_init(cyhal_tdm_t *obj, const cyhal_tdm_pins_t* tx_pins, const cyhal_tdm_pins_t* rx_pins,
146                          const cyhal_tdm_config_t* config, cyhal_clock_t* clk)
147 {
148     _cyhal_audioss_pins_t rx_converted, tx_converted;
149     _cyhal_audioss_pins_t* rx_pin_ptr = NULL;
150     _cyhal_audioss_pins_t* tx_pin_ptr = NULL;
151     if(NULL != rx_pins)
152     {
153         rx_converted.sck = rx_pins->sck;
154         rx_converted.ws = rx_pins->ws;
155         rx_converted.data = rx_pins->data;
156         rx_converted.mclk = rx_pins->mclk;
157         rx_pin_ptr = &rx_converted;
158     }
159 
160     if(NULL != tx_pins)
161     {
162         tx_converted.sck = tx_pins->sck;
163         tx_converted.ws = tx_pins->ws;
164         tx_converted.data = tx_pins->data;
165         tx_converted.mclk = tx_pins->mclk;
166         tx_pin_ptr = &tx_converted;
167     }
168 
169     _cyhal_audioss_config_t converted_config =
170     {
171         .is_tx_slave    = config->is_tx_slave,
172         .is_rx_slave    = config->is_rx_slave,
173         .mclk_hz        = config->mclk_hz,
174         .channel_length = config->channel_length,
175         .word_length    = config->word_length,
176         .sample_rate_hz = config->sample_rate_hz,
177         .num_channels   = config->num_channels,
178         .channel_mask   = config->channel_mask,
179         .tx_ws_full     = (config->tx_ws_width == CYHAL_TDM_WS_FULL),
180 #if (CYHAL_DRIVER_AVAILABLE_TDM_RX)
181         .rx_ws_full     = (config->rx_ws_width == CYHAL_TDM_WS_FULL),
182 #endif
183         .is_i2s         = false
184     };
185 
186     return _cyhal_audioss_init((_cyhal_audioss_t *)obj, tx_pin_ptr, rx_pin_ptr, &converted_config, clk, &_cyhal_tdm_interface);
187 }
188 
cyhal_tdm_init_cfg(cyhal_tdm_t * obj,const cyhal_tdm_configurator_t * cfg)189 cy_rslt_t cyhal_tdm_init_cfg(cyhal_tdm_t *obj, const cyhal_tdm_configurator_t *cfg)
190 {
191     return _cyhal_audioss_init_cfg((_cyhal_audioss_t *)obj, cfg, &_cyhal_tdm_interface);
192 }
193 
cyhal_tdm_register_callback(cyhal_tdm_t * obj,cyhal_tdm_event_callback_t callback,void * callback_arg)194 void cyhal_tdm_register_callback(cyhal_tdm_t *obj, cyhal_tdm_event_callback_t callback, void *callback_arg)
195 {
196     CY_ASSERT(NULL != obj);
197 
198     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
199     obj->callback_data.callback = (cy_israddress) callback;
200     obj->callback_data.callback_arg = callback_arg;
201     cyhal_system_critical_section_exit(savedIntrStatus);
202 }
203 
204 #if defined(CY_IP_MXAUDIOSS)
_cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause)205 static uint32_t _cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause)
206 {
207     cyhal_tdm_event_t result = (cyhal_tdm_event_t)0u;
208     if(0 != (pdl_cause & CY_I2S_INTR_TX_NOT_FULL))
209     {
210         result |= CYHAL_TDM_TX_NOT_FULL;
211     }
212     if(0 != (pdl_cause & CY_I2S_INTR_TX_TRIGGER))
213     {
214         result |= CYHAL_TDM_TX_HALF_EMPTY;
215     }
216     if(0 != (pdl_cause & CY_I2S_INTR_TX_EMPTY))
217     {
218         result |= CYHAL_TDM_TX_EMPTY;
219     }
220     if(0 != (pdl_cause & CY_I2S_INTR_TX_OVERFLOW))
221     {
222         result |= CYHAL_TDM_TX_OVERFLOW;
223     }
224     if(0 != (pdl_cause & CY_I2S_INTR_TX_UNDERFLOW))
225     {
226         result |= CYHAL_TDM_TX_UNDERFLOW ;
227     }
228 #if (CYHAL_DRIVER_AVAILABLE_TDM_RX)
229     if(0 != (pdl_cause & CY_I2S_INTR_RX_NOT_EMPTY))
230     {
231         result |= CYHAL_TDM_RX_NOT_EMPTY;
232     }
233     if(0 != (pdl_cause & CY_I2S_INTR_RX_TRIGGER))
234     {
235         result |= CYHAL_TDM_RX_HALF_FULL;
236     }
237     if(0 != (pdl_cause & CY_I2S_INTR_RX_FULL))
238     {
239         result |= CYHAL_TDM_RX_FULL;
240     }
241     if(0 != (pdl_cause & CY_I2S_INTR_RX_OVERFLOW))
242     {
243         result |= CYHAL_TDM_RX_OVERFLOW;
244     }
245     if(0 != (pdl_cause & CY_I2S_INTR_RX_UNDERFLOW))
246     {
247         result |= CYHAL_TDM_RX_UNDERFLOW;
248     }
249 #endif
250 
251     return (uint32_t)result;
252 }
253 
_cyhal_tdm_convert_event(uint32_t event)254 static uint32_t _cyhal_tdm_convert_event(uint32_t event)
255 {
256     cyhal_tdm_event_t hal_event = (cyhal_tdm_event_t)event;
257     uint32_t pdl_event = 0u;
258     if(0 != (hal_event & CYHAL_TDM_TX_NOT_FULL))
259     {
260         pdl_event |= CY_I2S_INTR_TX_NOT_FULL;
261     }
262     if(0 != (hal_event & CYHAL_TDM_TX_HALF_EMPTY))
263     {
264         pdl_event |= CY_I2S_INTR_TX_TRIGGER;
265     }
266     if(0 != (hal_event & CYHAL_TDM_TX_EMPTY))
267     {
268         pdl_event |= CY_I2S_INTR_TX_EMPTY;
269     }
270     if(0 != (hal_event & CYHAL_TDM_TX_OVERFLOW))
271     {
272         pdl_event |= CY_I2S_INTR_TX_OVERFLOW;
273     }
274     if(0 != (hal_event & CYHAL_TDM_TX_UNDERFLOW ))
275     {
276         pdl_event |= CY_I2S_INTR_TX_UNDERFLOW;
277     }
278 #if (CYHAL_DRIVER_AVAILABLE_TDM_RX)
279     if(0 != (hal_event & CYHAL_TDM_RX_NOT_EMPTY))
280     {
281         pdl_event |= CY_I2S_INTR_RX_NOT_EMPTY;
282     }
283     if(0 != (hal_event & CYHAL_TDM_RX_HALF_FULL))
284     {
285         pdl_event |= CY_I2S_INTR_RX_TRIGGER;
286     }
287     if(0 != (hal_event & CYHAL_TDM_RX_FULL))
288     {
289         pdl_event |= CY_I2S_INTR_RX_FULL;
290     }
291     if(0 != (hal_event & CYHAL_TDM_RX_OVERFLOW))
292     {
293         pdl_event |= CY_I2S_INTR_RX_OVERFLOW;
294     }
295     if(0 != (hal_event & CYHAL_TDM_RX_UNDERFLOW))
296     {
297         pdl_event |= CY_I2S_INTR_RX_UNDERFLOW;
298     }
299 #endif
300     return pdl_event;
301 }
302 #elif defined(CY_IP_MXTDM)
_cyhal_tdm_convert_event(uint32_t event,bool is_tx)303 static uint32_t _cyhal_tdm_convert_event(uint32_t event, bool is_tx)
304 {
305     cyhal_tdm_event_t hal_event = (cyhal_tdm_event_t)event;
306     uint32_t pdl_event = 0u;
307     if(is_tx)
308     {
309         /* Full/empty related interrupts not supported by this IP:
310          * * CYHAL_TDM_TX_NOT_FULL
311          * * CYHAL_TDM_TX_EMPTY
312          */
313         if(0 != (hal_event & CYHAL_TDM_TX_HALF_EMPTY))
314         {
315             pdl_event |= CY_TDM_INTR_TX_FIFO_TRIGGER;
316         }
317         if(0 != (hal_event & CYHAL_TDM_TX_OVERFLOW))
318         {
319             pdl_event |= CY_TDM_INTR_TX_FIFO_OVERFLOW;
320         }
321         if(0 != (hal_event & CYHAL_TDM_TX_UNDERFLOW ))
322         {
323             pdl_event |= CY_TDM_INTR_TX_FIFO_UNDERFLOW;
324         }
325     }
326     else
327     {
328         /* Full/empty related interrupts not supported by this IP:
329          * * CYHAL_TDM_RX_NOT_FULL
330          * * CYHAL_TDM_RX_EMPTY
331          */
332         if(0 != (hal_event & CYHAL_TDM_RX_HALF_FULL))
333         {
334             pdl_event |= CY_TDM_INTR_RX_FIFO_TRIGGER;
335         }
336         if(0 != (hal_event & CYHAL_TDM_RX_OVERFLOW))
337         {
338             pdl_event |= CY_TDM_INTR_RX_FIFO_OVERFLOW;
339         }
340         if(0 != (hal_event & CYHAL_TDM_RX_UNDERFLOW ))
341         {
342             pdl_event |= CY_TDM_INTR_RX_FIFO_UNDERFLOW;
343         }
344     }
345 
346     return pdl_event;
347 }
348 
_cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause,bool is_tx)349 static uint32_t _cyhal_tdm_convert_interrupt_cause(uint32_t pdl_cause, bool is_tx)
350 {
351     cyhal_tdm_event_t result = (cyhal_tdm_event_t)0u;
352     if(is_tx)
353     {
354         /* Full/empty related interrupts not supported by this IP:
355          * * CYHAL_TDM_TX_NOT_FULL
356          * * CYHAL_TDM_TX_EMPTY
357          */
358         if(0 != (pdl_cause & CY_TDM_INTR_TX_FIFO_TRIGGER))
359         {
360             result |= CYHAL_TDM_TX_HALF_EMPTY;
361         }
362         if(0 != (pdl_cause & CY_TDM_INTR_TX_FIFO_OVERFLOW))
363         {
364             result |= CYHAL_TDM_TX_OVERFLOW;
365         }
366         if(0 != (pdl_cause & CY_TDM_INTR_TX_FIFO_UNDERFLOW))
367         {
368             result |= CYHAL_TDM_TX_UNDERFLOW;
369         }
370     }
371     else
372     {
373         /* Full/empty related interrupts not supported by this IP:
374          * * CYHAL_TDM_RX_NOT_FULL
375          * * CYHAL_TDM_RX_EMPTY
376          */
377         if(0 != (pdl_cause & CY_TDM_INTR_RX_FIFO_TRIGGER))
378         {
379             result |= CYHAL_TDM_RX_HALF_FULL;
380         }
381         if(0 != (pdl_cause & CY_TDM_INTR_RX_FIFO_OVERFLOW))
382         {
383             result |= CYHAL_TDM_RX_OVERFLOW;
384         }
385         if(0 != (pdl_cause & CY_TDM_INTR_RX_FIFO_UNDERFLOW))
386         {
387             result |= CYHAL_TDM_RX_UNDERFLOW;
388         }
389     }
390 
391     return result;
392 }
393 #endif
394 
_cyhal_tdm_invoke_callback(_cyhal_audioss_t * obj,uint32_t event)395 static void _cyhal_tdm_invoke_callback(_cyhal_audioss_t* obj, uint32_t event)
396 {
397     cyhal_tdm_event_callback_t callback = (cyhal_tdm_event_callback_t)obj->callback_data.callback;
398     if(NULL != callback)
399     {
400         callback(obj->callback_data.callback_arg, (cyhal_tdm_event_t)event);
401     }
402 }
403 
cyhal_tdm_enable_output(cyhal_tdm_t * obj,cyhal_tdm_output_t output,cyhal_source_t * source)404 cy_rslt_t cyhal_tdm_enable_output(cyhal_tdm_t *obj, cyhal_tdm_output_t output, cyhal_source_t *source)
405 {
406     CY_ASSERT(CYHAL_TDM_TRIGGER_RX_HALF_FULL == output || CYHAL_TDM_TRIGGER_TX_HALF_EMPTY == output);
407     bool is_rx = CYHAL_TDM_TRIGGER_RX_HALF_FULL == output;
408     return _cyhal_audioss_enable_output(obj, is_rx, source);
409 }
410 
cyhal_tdm_disable_output(cyhal_tdm_t * obj,cyhal_tdm_output_t output)411 cy_rslt_t cyhal_tdm_disable_output(cyhal_tdm_t *obj, cyhal_tdm_output_t output)
412 {
413     CY_ASSERT(CYHAL_TDM_TRIGGER_RX_HALF_FULL == output || CYHAL_TDM_TRIGGER_TX_HALF_EMPTY == output);
414     bool is_rx = CYHAL_TDM_TRIGGER_RX_HALF_FULL == output;
415     return _cyhal_audioss_disable_output(obj, is_rx);
416 }
417 
418 #if defined(__cplusplus)
419 }
420 #endif
421 
422 #endif /* CYHAL_DRIVER_AVAILABLE_TDM */
423