1 /*
2  * Copyright (c) 2015 - 2025, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_I2S_ENABLED)
37 
38 #include <nrfx_i2s.h>
39 #include <haly/nrfy_gpio.h>
40 
41 #define NRFX_LOG_MODULE I2S
42 #include <nrfx_log.h>
43 
44 #define EVT_TO_STR(event)                                         \
45     (event == NRF_I2S_EVENT_RXPTRUPD ? "NRF_I2S_EVENT_RXPTRUPD" : \
46     (event == NRF_I2S_EVENT_TXPTRUPD ? "NRF_I2S_EVENT_TXPTRUPD" : \
47     (event == NRF_I2S_EVENT_STOPPED  ? "NRF_I2S_EVENT_STOPPED"  : \
48                                        "UNKNOWN EVENT")))
49 
50 #if !defined(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY) && \
51     (defined(NRF52_SERIES) || defined(NRF91_SERIES))
52 // Enable workaround for nRF52 Series anomaly 194 / nRF9160 anomaly 1
53 // (STOP task does not switch off all resources).
54 #define USE_WORKAROUND_FOR_I2S_STOP_ANOMALY 1
55 #endif
56 
57 #if !defined(USE_WORKAROUND_FOR_ANOMALY_170) && defined(NRF52_SERIES)
58 // Enable workaround for nRF52 Series anomaly 170
59 // (when reading the value of PSEL registers, the CONNECT field might not
60 //  return the same value that has been written to it).
61 #define USE_WORKAROUND_FOR_ANOMALY_170 1
62 #endif
63 
64 #if !defined(USE_WORKAROUND_FOR_ANOMALY_196) && defined(NRF52_SERIES)
65 // Enable workaround for nRF52 Series anomaly 196
66 // (PSEL acquires GPIO regardless of ENABLE).
67 #define USE_WORKAROUND_FOR_ANOMALY_196 1
68 #endif
69 
70 // Control block - driver instance local data.
71 typedef struct
72 {
73     nrfx_i2s_data_handler_t handler;
74     nrfx_drv_state_t        state;
75 
76     bool use_rx         : 1;
77     bool use_tx         : 1;
78     bool rx_ready       : 1;
79     bool tx_ready       : 1;
80     bool buffers_needed : 1;
81     bool buffers_reused : 1;
82     bool skip_gpio_cfg  : 1;
83     bool skip_psel_cfg  : 1;
84 
85 #if !NRFX_API_VER_AT_LEAST(3, 3, 0)
86     uint16_t            buffer_size;
87 #endif
88     nrfx_i2s_buffers_t  next_buffers;
89     nrfx_i2s_buffers_t  current_buffers;
90 } nrfx_i2s_cb_t;
91 
92 static nrfx_i2s_cb_t m_cb[NRFX_I2S_ENABLED_COUNT];
93 
configure_pins(nrfx_i2s_config_t const * p_config)94 static void configure_pins(nrfx_i2s_config_t const * p_config)
95 {
96     if (!p_config->skip_gpio_cfg)
97     {
98         // Configure pins used by the peripheral:
99 
100         // - SCK and LRCK (required) - depending on the mode of operation these
101         //   pins are configured as outputs (in Master mode) or inputs (in Slave
102         //   mode).
103         if (p_config->mode == NRF_I2S_MODE_MASTER)
104         {
105             nrfy_gpio_cfg_output(p_config->sck_pin);
106             nrfy_gpio_cfg_output(p_config->lrck_pin);
107 #if NRF_GPIO_HAS_CLOCKPIN && defined(NRF_I2S_CLOCKPIN_SCK_NEEDED)
108             nrfy_gpio_pin_clock_set(p_config->sck_pin, true);
109 #endif
110 #if NRF_GPIO_HAS_CLOCKPIN && defined(NRF_I2S_CLOCKPIN_LRCK_NEEDED)
111             nrfy_gpio_pin_clock_set(p_config->lrck_pin, true);
112 #endif
113         }
114         else
115         {
116             nrfy_gpio_cfg_input(p_config->sck_pin,  NRF_GPIO_PIN_NOPULL);
117             nrfy_gpio_cfg_input(p_config->lrck_pin, NRF_GPIO_PIN_NOPULL);
118         }
119         // - MCK (optional) - always output,
120         if (p_config->mck_pin != NRF_I2S_PIN_NOT_CONNECTED)
121         {
122             nrfy_gpio_cfg_output(p_config->mck_pin);
123 #if NRF_GPIO_HAS_CLOCKPIN && defined(NRF_I2S_CLOCKPIN_MCK_NEEDED)
124             nrfy_gpio_pin_clock_set(p_config->mck_pin, true);
125 #endif
126         }
127         // - SDOUT (optional) - always output,
128         if (p_config->sdout_pin != NRF_I2S_PIN_NOT_CONNECTED)
129         {
130             nrfy_gpio_cfg_output(p_config->sdout_pin);
131         }
132         // - SDIN (optional) - always input.
133         if (p_config->sdin_pin != NRF_I2S_PIN_NOT_CONNECTED)
134         {
135             nrfy_gpio_cfg_input(p_config->sdin_pin, NRF_GPIO_PIN_NOPULL);
136         }
137     }
138 }
139 
deconfigure_pins(nrfx_i2s_t const * p_instance)140 static void deconfigure_pins(nrfx_i2s_t const * p_instance)
141 {
142     nrf_i2s_pins_t pins;
143 
144     nrfy_i2s_pins_get(p_instance->p_reg, &pins);
145 
146 #if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_170)
147     // Create bitmask for extracting pin number from PSEL register.
148     uint32_t pin_mask = NRF_I2S_PSEL_SCK_PIN_MASK;
149 #if NRF_I2S_HAS_GPIO_PORT_SELECTION
150     // If device supports more than one GPIO port, take port number into account as well.
151     pin_mask |= NRF_I2S_PSEL_SCK_PORT_MASK;
152 #endif
153 #else
154     uint32_t pin_mask = 0xFFFFFFFF;
155 #endif // USE_WORKAROUND_FOR_ANOMALY_170
156 
157     nrfy_gpio_cfg_default(pins.sck_pin & pin_mask);
158     nrfy_gpio_cfg_default(pins.lrck_pin & pin_mask);
159 
160     if (pins.mck_pin != NRF_I2S_PIN_NOT_CONNECTED)
161     {
162         nrfy_gpio_cfg_default(pins.mck_pin & pin_mask);
163     }
164 
165     if (pins.sdout_pin != NRF_I2S_PIN_NOT_CONNECTED)
166     {
167         nrfy_gpio_cfg_default(pins.sdout_pin & pin_mask);
168     }
169 
170     if (pins.sdin_pin != NRF_I2S_PIN_NOT_CONNECTED)
171     {
172         nrfy_gpio_cfg_default(pins.sdin_pin & pin_mask);
173     }
174 }
175 
validate_config(nrf_i2s_mode_t mode,nrf_i2s_ratio_t ratio,nrf_i2s_swidth_t swidth)176 static inline bool validate_config(nrf_i2s_mode_t   mode,
177                                    nrf_i2s_ratio_t  ratio,
178                                    nrf_i2s_swidth_t swidth)
179 {
180     // The MCK/LRCK ratio has to be a multiple of 2 * sample width.
181     if (mode == NRF_I2S_MODE_MASTER)
182     {
183         if (swidth == NRF_I2S_SWIDTH_16BIT)
184         {
185             if (ratio == NRF_I2S_RATIO_48X)
186             {
187                 return false;
188             }
189         }
190 
191         if (swidth == NRF_I2S_SWIDTH_24BIT)
192         {
193             if ((ratio == NRF_I2S_RATIO_32X)  ||
194                 (ratio == NRF_I2S_RATIO_64X)  ||
195                 (ratio == NRF_I2S_RATIO_128X) ||
196                 (ratio == NRF_I2S_RATIO_256X) ||
197                 (ratio == NRF_I2S_RATIO_512X))
198             {
199                 return false;
200             }
201         }
202 
203 #if NRF_I2S_HAS_SWIDTH_32BIT
204         if (swidth == NRF_I2S_SWIDTH_32BIT)
205         {
206             if ((ratio == NRF_I2S_RATIO_32X) ||
207                 (ratio == NRF_I2S_RATIO_48X) ||
208                 (ratio == NRF_I2S_RATIO_96X))
209             {
210                 return false;
211             }
212         }
213 #endif
214 
215     }
216 
217     return true;
218 }
219 
nrfx_i2s_init(nrfx_i2s_t const * p_instance,nrfx_i2s_config_t const * p_config,nrfx_i2s_data_handler_t handler)220 nrfx_err_t nrfx_i2s_init(nrfx_i2s_t const *        p_instance,
221                          nrfx_i2s_config_t const * p_config,
222                          nrfx_i2s_data_handler_t   handler)
223 {
224     NRFX_ASSERT(p_config);
225     NRFX_ASSERT(handler);
226 
227     nrfx_err_t err_code;
228     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
229 
230     if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
231     {
232 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
233         err_code = NRFX_ERROR_ALREADY;
234 #else
235         err_code = NRFX_ERROR_INVALID_STATE;
236 #endif
237         NRFX_LOG_WARNING("Function: %s, error code: %s.",
238                          __func__,
239                          NRFX_LOG_ERROR_STRING_GET(err_code));
240         return err_code;
241     }
242 
243 
244     if (!validate_config(p_config->mode,
245                          p_config->ratio,
246                          p_config->sample_width))
247     {
248         err_code = NRFX_ERROR_INVALID_PARAM;
249         NRFX_LOG_WARNING("Function: %s, error code: %s.",
250                          __func__,
251                          NRFX_LOG_ERROR_STRING_GET(err_code));
252         return err_code;
253     }
254 
255     configure_pins(p_config);
256 
257     nrfy_i2s_config_t nrfy_config =
258     {
259         .config = {
260             .mode         = p_config->mode,
261             .format       = p_config->format,
262             .alignment    = p_config->alignment,
263             .sample_width = p_config->sample_width,
264             .channels     = p_config->channels,
265             .mck_setup    = p_config->mck_setup,
266             .ratio        = p_config->ratio,
267         },
268         .pins = {
269             .sck_pin      = p_config->sck_pin,
270             .lrck_pin     = p_config->lrck_pin,
271             .mck_pin      = p_config->mck_pin,
272             .sdout_pin    = p_config->sdout_pin,
273             .sdin_pin     = p_config->sdin_pin,
274         },
275 #if NRF_I2S_HAS_CLKCONFIG
276         .clksrc        = p_config->clksrc,
277         .enable_bypass = p_config->enable_bypass,
278 #endif
279         .skip_psel_cfg = p_config->skip_psel_cfg
280     };
281 
282     nrfy_i2s_periph_configure(p_instance->p_reg, &nrfy_config);
283 
284     p_cb->handler = handler;
285     p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
286     p_cb->skip_psel_cfg = p_config->skip_psel_cfg;
287 
288     nrfy_i2s_int_init(p_instance->p_reg,
289                       NRF_I2S_INT_RXPTRUPD_MASK |
290                       NRF_I2S_INT_TXPTRUPD_MASK |
291                       NRF_I2S_INT_STOPPED_MASK,
292                       p_config->irq_priority,
293                       false);
294 
295     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
296 
297     NRFX_LOG_INFO("Initialized.");
298     return NRFX_SUCCESS;
299 }
300 
nrfx_i2s_uninit(nrfx_i2s_t const * p_instance)301 void nrfx_i2s_uninit(nrfx_i2s_t const * p_instance)
302 {
303     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
304 
305     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
306 
307     nrfx_i2s_stop(p_instance);
308 
309     nrfy_i2s_int_uninit(p_instance->p_reg);
310     nrfy_i2s_disable(p_instance->p_reg);
311 
312     if (!p_cb->skip_gpio_cfg)
313     {
314         deconfigure_pins(p_instance);
315     }
316 
317 #if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_196)
318     if (!p_cb->skip_psel_cfg)
319     {
320         // Disabling I2S is insufficient to release pins acquired by the peripheral.
321         // Explicit disconnect is needed.
322         nrf_i2s_pins_t pins = {
323             .sck_pin   = NRF_I2S_PIN_NOT_CONNECTED,
324             .lrck_pin  = NRF_I2S_PIN_NOT_CONNECTED,
325             .mck_pin   = NRF_I2S_PIN_NOT_CONNECTED,
326             .sdout_pin = NRF_I2S_PIN_NOT_CONNECTED,
327             .sdin_pin  = NRF_I2S_PIN_NOT_CONNECTED,
328         };
329         nrfy_i2s_pins_set(p_instance->p_reg, &pins);
330     }
331 #endif
332 
333     p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
334     NRFX_LOG_INFO("Uninitialized.");
335 }
336 
nrfx_i2s_init_check(nrfx_i2s_t const * p_instance)337 bool nrfx_i2s_init_check(nrfx_i2s_t const * p_instance)
338 {
339     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
340 
341     return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
342 }
343 
nrfx_i2s_start(nrfx_i2s_t const * p_instance,nrfx_i2s_buffers_t const * p_initial_buffers,uint16_t buffer_size,uint8_t flags)344 nrfx_err_t nrfx_i2s_start(nrfx_i2s_t const *         p_instance,
345                           nrfx_i2s_buffers_t const * p_initial_buffers,
346 #if !NRFX_API_VER_AT_LEAST(3, 3, 0)
347                           uint16_t                   buffer_size,
348 #endif
349                           uint8_t                    flags)
350 {
351     NRFX_ASSERT(p_initial_buffers != NULL);
352     NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
353                 p_initial_buffers->p_tx_buffer != NULL);
354     NRFX_ASSERT((p_initial_buffers->p_rx_buffer == NULL) ||
355                 (nrfx_is_in_ram(p_initial_buffers->p_rx_buffer) &&
356                  nrfx_is_word_aligned(p_initial_buffers->p_rx_buffer)));
357     NRFX_ASSERT((p_initial_buffers->p_tx_buffer == NULL) ||
358                 (nrfx_is_in_ram(p_initial_buffers->p_tx_buffer) &&
359                  nrfx_is_word_aligned(p_initial_buffers->p_tx_buffer)));
360 #if NRFX_API_VER_AT_LEAST(3, 3, 0)
361     NRFX_ASSERT(p_initial_buffers->buffer_size != 0);
362 #else
363     NRFX_ASSERT(buffer_size != 0);
364 #endif
365     (void)(flags);
366 
367     nrfx_err_t err_code;
368     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
369 
370     if (p_cb->state != NRFX_DRV_STATE_INITIALIZED)
371     {
372         err_code = NRFX_ERROR_INVALID_STATE;
373         NRFX_LOG_WARNING("Function: %s, error code: %s.",
374                          __func__,
375                          NRFX_LOG_ERROR_STRING_GET(err_code));
376         return err_code;
377     }
378 
379     if (((p_initial_buffers->p_rx_buffer != NULL)
380          && !nrfx_is_in_ram(p_initial_buffers->p_rx_buffer))
381         ||
382         ((p_initial_buffers->p_tx_buffer != NULL)
383          && !nrfx_is_in_ram(p_initial_buffers->p_tx_buffer)))
384     {
385         err_code = NRFX_ERROR_INVALID_ADDR;
386         NRFX_LOG_WARNING("Function: %s, error code: %s.",
387                          __func__,
388                          NRFX_LOG_ERROR_STRING_GET(err_code));
389         return err_code;
390     }
391 
392     p_cb->use_rx         = (p_initial_buffers->p_rx_buffer != NULL);
393     p_cb->use_tx         = (p_initial_buffers->p_tx_buffer != NULL);
394     p_cb->rx_ready       = false;
395     p_cb->tx_ready       = false;
396     p_cb->buffers_needed = false;
397 #if !NRFX_API_VER_AT_LEAST(3, 3, 0)
398     p_cb->buffer_size    = buffer_size;
399 #endif
400 
401     // Set the provided initial buffers as next, they will become the current
402     // ones after the IRQ handler is called for the first time, what will occur
403     // right after the START task is triggered.
404     p_cb->next_buffers = *p_initial_buffers;
405     p_cb->current_buffers.p_rx_buffer = NULL;
406     p_cb->current_buffers.p_tx_buffer = NULL;
407 
408 
409     nrfy_i2s_enable(p_instance->p_reg);
410 
411     p_cb->state = NRFX_DRV_STATE_POWERED_ON;
412 
413     /* Clear spurious RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55). */
414     nrfy_i2s_event_clear(p_instance->p_reg, NRF_I2S_EVENT_RXPTRUPD);
415     nrfy_i2s_event_clear(p_instance->p_reg, NRF_I2S_EVENT_TXPTRUPD);
416 
417     nrfy_i2s_int_enable(p_instance->p_reg,
418                         (p_cb->use_rx ? NRF_I2S_INT_RXPTRUPD_MASK : 0UL) |
419                         (p_cb->use_tx ? NRF_I2S_INT_TXPTRUPD_MASK : 0UL) |
420                         NRF_I2S_INT_STOPPED_MASK);
421 
422 #if NRFX_API_VER_AT_LEAST(3, 3, 0)
423     nrfy_i2s_buffers_set(p_instance->p_reg, &p_cb->next_buffers);
424 #else
425     const nrfy_i2s_xfer_desc_t xfer = {
426         .p_rx_buffer = p_cb->next_buffers.p_rx_buffer,
427         .p_tx_buffer = p_cb->next_buffers.p_tx_buffer,
428         .buffer_size = p_cb->buffer_size,
429     };
430 
431     nrfy_i2s_buffers_set(p_instance->p_reg, &xfer);
432 #endif
433     nrfy_i2s_xfer_start(p_instance->p_reg, NULL);
434 
435     NRFX_LOG_INFO("Started.");
436     return NRFX_SUCCESS;
437 }
438 
nrfx_i2s_next_buffers_set(nrfx_i2s_t const * p_instance,nrfx_i2s_buffers_t const * p_buffers)439 nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_t const *         p_instance,
440                                      nrfx_i2s_buffers_t const * p_buffers)
441 {
442     nrfx_err_t err_code;
443     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
444 
445     NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_POWERED_ON);
446     NRFX_ASSERT(p_buffers);
447     NRFX_ASSERT((p_buffers->p_rx_buffer == NULL) ||
448                 (nrfx_is_in_ram(p_buffers->p_rx_buffer) &&
449                  nrfx_is_word_aligned(p_buffers->p_rx_buffer)));
450     NRFX_ASSERT((p_buffers->p_tx_buffer == NULL) ||
451                 (nrfx_is_in_ram(p_buffers->p_tx_buffer) &&
452                  nrfx_is_word_aligned(p_buffers->p_tx_buffer)));
453 #if NRFX_API_VER_AT_LEAST(3, 3, 0)
454     NRFX_ASSERT(p_buffers->buffer_size != 0);
455 #endif
456 
457     if (!p_cb->buffers_needed)
458     {
459         err_code = NRFX_ERROR_INVALID_STATE;
460         NRFX_LOG_WARNING("Function: %s, error code: %s.",
461                          __func__,
462                          NRFX_LOG_ERROR_STRING_GET(err_code));
463         return err_code;
464     }
465 
466     if (((p_buffers->p_rx_buffer != NULL)
467          && !nrfx_is_in_ram(p_buffers->p_rx_buffer))
468         ||
469         ((p_buffers->p_tx_buffer != NULL)
470          && !nrfx_is_in_ram(p_buffers->p_tx_buffer)))
471     {
472         err_code = NRFX_ERROR_INVALID_ADDR;
473         NRFX_LOG_WARNING("Function: %s, error code: %s.",
474                          __func__,
475                          NRFX_LOG_ERROR_STRING_GET(err_code));
476         return err_code;
477     }
478 
479     if (p_cb->use_tx)
480     {
481         NRFX_ASSERT(p_buffers->p_tx_buffer != NULL);
482     }
483     if (p_cb->use_rx)
484     {
485         NRFX_ASSERT(p_buffers->p_rx_buffer != NULL);
486     }
487 
488 #if NRFX_API_VER_AT_LEAST(3, 3, 0)
489     nrfy_i2s_buffers_set(p_instance->p_reg, p_buffers);
490 #else
491     nrfy_i2s_xfer_desc_t xfer = {
492         .p_rx_buffer = p_buffers->p_rx_buffer,
493         .p_tx_buffer = p_buffers->p_tx_buffer,
494         .buffer_size = p_cb->buffer_size,
495     };
496 
497     nrfy_i2s_buffers_set(p_instance->p_reg, &xfer);
498 #endif
499 
500     p_cb->next_buffers   = *p_buffers;
501     p_cb->buffers_needed = false;
502 
503     return NRFX_SUCCESS;
504 }
505 
nrfx_i2s_stop(nrfx_i2s_t const * p_instance)506 void nrfx_i2s_stop(nrfx_i2s_t const * p_instance)
507 {
508     nrfx_i2s_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
509 
510     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
511 
512     p_cb->buffers_needed = false;
513 
514     // First disable interrupts, then trigger the STOP task, so no spurious
515     // RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55) are processed.
516     nrfy_i2s_int_disable(p_instance->p_reg, NRF_I2S_INT_RXPTRUPD_MASK |
517                                             NRF_I2S_INT_TXPTRUPD_MASK);
518 
519     nrfy_i2s_abort(p_instance->p_reg, NULL);
520 
521 #if NRFX_CHECK(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY)
522     *((volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x38)) = 1;
523     *((volatile uint32_t *)(((uint32_t)p_instance->p_reg) + 0x3C)) = 1;
524 #endif
525 }
526 
irq_handler(NRF_I2S_Type * p_reg,nrfx_i2s_cb_t * p_cb)527 static void irq_handler(NRF_I2S_Type * p_reg, nrfx_i2s_cb_t * p_cb)
528 {
529     uint32_t event_mask;
530     nrfy_i2s_xfer_desc_t * p_xfer;
531 
532 #if NRFX_API_VER_AT_LEAST(3, 3, 0)
533     p_xfer = &p_cb->current_buffers;
534 #else
535     nrfy_i2s_xfer_desc_t xfer = {
536         .p_rx_buffer = p_cb->current_buffers.p_rx_buffer,
537         .p_tx_buffer = p_cb->current_buffers.p_tx_buffer,
538         .buffer_size = p_cb->buffer_size,
539     };
540     p_xfer = &xfer;
541 #endif
542 
543     event_mask = nrfy_i2s_events_process(p_reg,
544                                          NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_TXPTRUPD) |
545                                          NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_RXPTRUPD) |
546                                          NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_STOPPED),
547                                          p_xfer);
548 
549     if (event_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_TXPTRUPD))
550     {
551         p_cb->tx_ready = true;
552         if (p_cb->use_tx && p_cb->buffers_needed)
553         {
554             p_cb->buffers_reused = true;
555         }
556     }
557     if (event_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_RXPTRUPD))
558     {
559         p_cb->rx_ready = true;
560         if (p_cb->use_rx && p_cb->buffers_needed)
561         {
562             p_cb->buffers_reused = true;
563         }
564     }
565 
566     if (event_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_STOPPED))
567     {
568         nrfy_i2s_int_disable(p_reg, NRF_I2S_INT_STOPPED_MASK);
569         nrfy_i2s_disable(p_reg);
570 
571         // When stopped, release all buffers, including these scheduled for
572         // the next part of the transfer, and signal that the transfer has
573         // finished.
574 
575         p_cb->handler(&p_cb->current_buffers, 0);
576 
577         // Change the state of the driver before calling the handler with
578         // the flag signaling that the transfer has finished, so that it is
579         // possible to start a new transfer directly from the handler function.
580         p_cb->state = NRFX_DRV_STATE_INITIALIZED;
581         NRFX_LOG_INFO("Stopped.");
582 
583         p_cb->handler(&p_cb->next_buffers, NRFX_I2S_STATUS_TRANSFER_STOPPED);
584     }
585     else
586     {
587         // Check if the requested transfer has been completed:
588         // - full-duplex mode
589         if ((p_cb->use_tx && p_cb->use_rx &&
590              p_cb->tx_ready && p_cb->rx_ready) ||
591             // - TX only mode
592             (!p_cb->use_rx && p_cb->tx_ready) ||
593             // - RX only mode
594             (!p_cb->use_tx && p_cb->rx_ready))
595         {
596             p_cb->tx_ready = false;
597             p_cb->rx_ready = false;
598 
599             // If the application did not supply the buffers for the next
600             // part of the transfer until this moment, the current buffers
601             // cannot be released, since the I2S peripheral already started
602             // using them. Signal this situation to the application by
603             // passing NULL instead of the structure with released buffers.
604             if (p_cb->buffers_reused)
605             {
606                 p_cb->buffers_reused = false;
607                 // This will most likely be set at this point. However, there is
608                 // a small time window between TXPTRUPD and RXPTRUPD events,
609                 // and it is theoretically possible that next buffers will be
610                 // set in this window, so to be sure this flag is set to true,
611                 // set it explicitly.
612                 p_cb->buffers_needed = true;
613                 p_cb->handler(NULL, NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
614             }
615             else
616             {
617                 // Buffers that have been used by the I2S peripheral (current)
618                 // are now released and will be returned to the application,
619                 // and the ones scheduled to be used as next become the current
620                 // ones.
621                 nrfx_i2s_buffers_t released_buffers = p_cb->current_buffers;
622                 p_cb->current_buffers = p_cb->next_buffers;
623                 p_cb->next_buffers.p_rx_buffer = NULL;
624                 p_cb->next_buffers.p_tx_buffer = NULL;
625                 p_cb->buffers_needed = true;
626                 p_cb->handler(&released_buffers, NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
627             }
628 
629         }
630     }
631 }
632 
633 NRFX_INSTANCE_IRQ_HANDLERS(I2S, i2s)
634 
635 #endif // NRFX_CHECK(NRFX_I2S_ENABLED)
636