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