xref: /nrf52832-nimble/nordic/nrfx/drivers/src/nrfx_i2s.c (revision 150812a83cab50279bd772ef6db1bfaf255f2c5b)
1 /*
2  * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  *    list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  *    contributors may be used to endorse or promote products derived from this
17  *    software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <nrfx.h>
33 
34 #if NRFX_CHECK(NRFX_I2S_ENABLED)
35 
36 #include <nrfx_i2s.h>
37 #include <hal/nrf_gpio.h>
38 
39 #define NRFX_LOG_MODULE I2S
40 #include <nrfx_log.h>
41 
42 #define EVT_TO_STR(event)                                         \
43     (event == NRF_I2S_EVENT_RXPTRUPD ? "NRF_I2S_EVENT_RXPTRUPD" : \
44     (event == NRF_I2S_EVENT_TXPTRUPD ? "NRF_I2S_EVENT_TXPTRUPD" : \
45     (event == NRF_I2S_EVENT_STOPPED  ? "NRF_I2S_EVENT_STOPPED"  : \
46                                        "UNKNOWN EVENT")))
47 
48 #if !defined(USE_WORKAROUND_FOR_ANOMALY_194) &&          \
49     (defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \
50      defined(NRF52840_XXAA))
51 // Enable workaround for nRF52832 and nRF52840 anomaly 194 (STOP task does not
52 // switch off all resources).
53 #define USE_WORKAROUND_FOR_ANOMALY_194 1
54 #else
55 #define USE_WORKAROUND_FOR_ANOMALY_194 0
56 #endif
57 
58 // Control block - driver instance local data.
59 typedef struct
60 {
61     nrfx_i2s_data_handler_t handler;
62     nrfx_drv_state_t        state;
63 
64     bool use_rx         : 1;
65     bool use_tx         : 1;
66     bool rx_ready       : 1;
67     bool tx_ready       : 1;
68     bool buffers_needed : 1;
69     bool buffers_reused : 1;
70 
71     uint16_t            buffer_size;
72     nrfx_i2s_buffers_t  next_buffers;
73     nrfx_i2s_buffers_t  current_buffers;
74 } i2s_control_block_t;
75 static i2s_control_block_t m_cb;
76 
77 
configure_pins(nrfx_i2s_config_t const * p_config)78 static void configure_pins(nrfx_i2s_config_t const * p_config)
79 {
80     uint32_t mck_pin, sdout_pin, sdin_pin;
81 
82     // Configure pins used by the peripheral:
83 
84     // - SCK and LRCK (required) - depending on the mode of operation these
85     //   pins are configured as outputs (in Master mode) or inputs (in Slave
86     //   mode).
87     if (p_config->mode == NRF_I2S_MODE_MASTER)
88     {
89         nrf_gpio_cfg_output(p_config->sck_pin);
90         nrf_gpio_cfg_output(p_config->lrck_pin);
91     }
92     else
93     {
94         nrf_gpio_cfg_input(p_config->sck_pin,  NRF_GPIO_PIN_NOPULL);
95         nrf_gpio_cfg_input(p_config->lrck_pin, NRF_GPIO_PIN_NOPULL);
96     }
97 
98     // - MCK (optional) - always output,
99     if (p_config->mck_pin != NRFX_I2S_PIN_NOT_USED)
100     {
101         mck_pin = p_config->mck_pin;
102         nrf_gpio_cfg_output(mck_pin);
103     }
104     else
105     {
106         mck_pin = NRF_I2S_PIN_NOT_CONNECTED;
107     }
108 
109     // - SDOUT (optional) - always output,
110     if (p_config->sdout_pin != NRFX_I2S_PIN_NOT_USED)
111     {
112         sdout_pin = p_config->sdout_pin;
113         nrf_gpio_cfg_output(sdout_pin);
114     }
115     else
116     {
117         sdout_pin = NRF_I2S_PIN_NOT_CONNECTED;
118     }
119 
120     // - SDIN (optional) - always input.
121     if (p_config->sdin_pin != NRFX_I2S_PIN_NOT_USED)
122     {
123         sdin_pin = p_config->sdin_pin;
124         nrf_gpio_cfg_input(sdin_pin, NRF_GPIO_PIN_NOPULL);
125     }
126     else
127     {
128         sdin_pin = NRF_I2S_PIN_NOT_CONNECTED;
129     }
130 
131     nrf_i2s_pins_set(NRF_I2S,
132                      p_config->sck_pin,
133                      p_config->lrck_pin,
134                      mck_pin,
135                      sdout_pin,
136                      sdin_pin);
137 }
138 
139 
nrfx_i2s_init(nrfx_i2s_config_t const * p_config,nrfx_i2s_data_handler_t handler)140 nrfx_err_t nrfx_i2s_init(nrfx_i2s_config_t const * p_config,
141                          nrfx_i2s_data_handler_t   handler)
142 {
143     NRFX_ASSERT(p_config);
144     NRFX_ASSERT(handler);
145 
146     nrfx_err_t err_code;
147 
148     if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
149     {
150         err_code = NRFX_ERROR_INVALID_STATE;
151         NRFX_LOG_WARNING("Function: %s, error code: %s.",
152                          __func__,
153                          NRFX_LOG_ERROR_STRING_GET(err_code));
154         return err_code;
155     }
156 
157     if (!nrf_i2s_configure(NRF_I2S,
158                            p_config->mode,
159                            p_config->format,
160                            p_config->alignment,
161                            p_config->sample_width,
162                            p_config->channels,
163                            p_config->mck_setup,
164                            p_config->ratio))
165     {
166         err_code = NRFX_ERROR_INVALID_PARAM;
167         NRFX_LOG_WARNING("Function: %s, error code: %s.",
168                          __func__,
169                          NRFX_LOG_ERROR_STRING_GET(err_code));
170         return err_code;
171     }
172     configure_pins(p_config);
173 
174     m_cb.handler = handler;
175 
176     NRFX_IRQ_PRIORITY_SET(I2S_IRQn, p_config->irq_priority);
177     NRFX_IRQ_ENABLE(I2S_IRQn);
178 
179     m_cb.state = NRFX_DRV_STATE_INITIALIZED;
180 
181     NRFX_LOG_INFO("Initialized.");
182     return NRFX_SUCCESS;
183 }
184 
185 
nrfx_i2s_uninit(void)186 void nrfx_i2s_uninit(void)
187 {
188     NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
189 
190     nrfx_i2s_stop();
191 
192     NRFX_IRQ_DISABLE(I2S_IRQn);
193 
194     nrf_i2s_pins_set(NRF_I2S,
195                      NRF_I2S_PIN_NOT_CONNECTED,
196                      NRF_I2S_PIN_NOT_CONNECTED,
197                      NRF_I2S_PIN_NOT_CONNECTED,
198                      NRF_I2S_PIN_NOT_CONNECTED,
199                      NRF_I2S_PIN_NOT_CONNECTED);
200 
201     m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
202     NRFX_LOG_INFO("Uninitialized.");
203 }
204 
205 
nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,uint16_t buffer_size,uint8_t flags)206 nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
207                           uint16_t                   buffer_size,
208                           uint8_t                    flags)
209 {
210     NRFX_ASSERT(p_initial_buffers != NULL);
211     NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
212                 p_initial_buffers->p_tx_buffer != NULL);
213     NRFX_ASSERT((p_initial_buffers->p_rx_buffer == NULL) ||
214                 (nrfx_is_in_ram(p_initial_buffers->p_rx_buffer) &&
215                  nrfx_is_word_aligned(p_initial_buffers->p_rx_buffer)));
216     NRFX_ASSERT((p_initial_buffers->p_tx_buffer == NULL) ||
217                 (nrfx_is_in_ram(p_initial_buffers->p_tx_buffer) &&
218                  nrfx_is_word_aligned(p_initial_buffers->p_tx_buffer)));
219     NRFX_ASSERT(buffer_size != 0);
220     (void)(flags);
221 
222     nrfx_err_t err_code;
223 
224     if (m_cb.state != NRFX_DRV_STATE_INITIALIZED)
225     {
226         err_code = NRFX_ERROR_INVALID_STATE;
227         NRFX_LOG_WARNING("Function: %s, error code: %s.",
228                          __func__,
229                          NRFX_LOG_ERROR_STRING_GET(err_code));
230         return err_code;
231     }
232 
233     if (((p_initial_buffers->p_rx_buffer != NULL)
234          && !nrfx_is_in_ram(p_initial_buffers->p_rx_buffer))
235         ||
236         ((p_initial_buffers->p_tx_buffer != NULL)
237          && !nrfx_is_in_ram(p_initial_buffers->p_tx_buffer)))
238     {
239         err_code = NRFX_ERROR_INVALID_ADDR;
240         NRFX_LOG_WARNING("Function: %s, error code: %s.",
241                          __func__,
242                          NRFX_LOG_ERROR_STRING_GET(err_code));
243         return err_code;
244     }
245 
246     m_cb.use_rx         = (p_initial_buffers->p_rx_buffer != NULL);
247     m_cb.use_tx         = (p_initial_buffers->p_tx_buffer != NULL);
248     m_cb.rx_ready       = false;
249     m_cb.tx_ready       = false;
250     m_cb.buffers_needed = false;
251     m_cb.buffer_size    = buffer_size;
252 
253     // Set the provided initial buffers as next, they will become the current
254     // ones after the IRQ handler is called for the first time, what will occur
255     // right after the START task is triggered.
256     m_cb.next_buffers = *p_initial_buffers;
257     m_cb.current_buffers.p_rx_buffer = NULL;
258     m_cb.current_buffers.p_tx_buffer = NULL;
259 
260     nrf_i2s_transfer_set(NRF_I2S,
261                          m_cb.buffer_size,
262                          m_cb.next_buffers.p_rx_buffer,
263                          m_cb.next_buffers.p_tx_buffer);
264 
265     nrf_i2s_enable(NRF_I2S);
266 
267     m_cb.state = NRFX_DRV_STATE_POWERED_ON;
268 
269     nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
270     nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
271     nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
272     nrf_i2s_int_enable(NRF_I2S, (m_cb.use_rx ? NRF_I2S_INT_RXPTRUPD_MASK : 0) |
273                                 (m_cb.use_tx ? NRF_I2S_INT_TXPTRUPD_MASK : 0) |
274                                 NRF_I2S_INT_STOPPED_MASK);
275     nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_START);
276 
277     NRFX_LOG_INFO("Started.");
278     return NRFX_SUCCESS;
279 }
280 
281 
nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)282 nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)
283 {
284     NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_POWERED_ON);
285     NRFX_ASSERT(p_buffers);
286     NRFX_ASSERT((p_buffers->p_rx_buffer == NULL) ||
287                 (nrfx_is_in_ram(p_buffers->p_rx_buffer) &&
288                  nrfx_is_word_aligned(p_buffers->p_rx_buffer)));
289     NRFX_ASSERT((p_buffers->p_tx_buffer == NULL) ||
290                 (nrfx_is_in_ram(p_buffers->p_tx_buffer) &&
291                  nrfx_is_word_aligned(p_buffers->p_tx_buffer)));
292 
293     nrfx_err_t err_code;
294 
295     if (!m_cb.buffers_needed)
296     {
297         err_code = NRFX_ERROR_INVALID_STATE;
298         NRFX_LOG_WARNING("Function: %s, error code: %s.",
299                          __func__,
300                          NRFX_LOG_ERROR_STRING_GET(err_code));
301         return err_code;
302     }
303 
304     if (((p_buffers->p_rx_buffer != NULL)
305          && !nrfx_is_in_ram(p_buffers->p_rx_buffer))
306         ||
307         ((p_buffers->p_tx_buffer != NULL)
308          && !nrfx_is_in_ram(p_buffers->p_tx_buffer)))
309     {
310         err_code = NRFX_ERROR_INVALID_ADDR;
311         NRFX_LOG_WARNING("Function: %s, error code: %s.",
312                          __func__,
313                          NRFX_LOG_ERROR_STRING_GET(err_code));
314         return err_code;
315     }
316 
317     if (m_cb.use_tx)
318     {
319         NRFX_ASSERT(p_buffers->p_tx_buffer != NULL);
320         nrf_i2s_tx_buffer_set(NRF_I2S, p_buffers->p_tx_buffer);
321     }
322     if (m_cb.use_rx)
323     {
324         NRFX_ASSERT(p_buffers->p_rx_buffer != NULL);
325         nrf_i2s_rx_buffer_set(NRF_I2S, p_buffers->p_rx_buffer);
326     }
327 
328     m_cb.next_buffers   = *p_buffers;
329     m_cb.buffers_needed = false;
330 
331     return NRFX_SUCCESS;
332 }
333 
334 
nrfx_i2s_stop(void)335 void nrfx_i2s_stop(void)
336 {
337     NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
338 
339     m_cb.buffers_needed = false;
340 
341     // First disable interrupts, then trigger the STOP task, so no spurious
342     // RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55) are processed.
343     nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_RXPTRUPD_MASK |
344                                  NRF_I2S_INT_TXPTRUPD_MASK);
345     nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_STOP);
346 
347 #if USE_WORKAROUND_FOR_ANOMALY_194
348     *((volatile uint32_t *)0x40025038) = 1;
349     *((volatile uint32_t *)0x4002503C) = 1;
350 #endif
351 }
352 
353 
nrfx_i2s_irq_handler(void)354 void nrfx_i2s_irq_handler(void)
355 {
356     if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD))
357     {
358         nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
359         m_cb.tx_ready = true;
360         if (m_cb.use_tx && m_cb.buffers_needed)
361         {
362             m_cb.buffers_reused = true;
363         }
364     }
365     if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD))
366     {
367         nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
368         m_cb.rx_ready = true;
369         if (m_cb.use_rx && m_cb.buffers_needed)
370         {
371             m_cb.buffers_reused = true;
372         }
373     }
374 
375     if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_STOPPED))
376     {
377         nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
378         nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_STOPPED_MASK);
379         nrf_i2s_disable(NRF_I2S);
380 
381         // When stopped, release all buffers, including these scheduled for
382         // the next transfer.
383         m_cb.handler(&m_cb.current_buffers, 0);
384         m_cb.handler(&m_cb.next_buffers, 0);
385 
386         m_cb.state = NRFX_DRV_STATE_INITIALIZED;
387         NRFX_LOG_INFO("Stopped.");
388     }
389     else
390     {
391         // Check if the requested transfer has been completed:
392         // - full-duplex mode
393         if ((m_cb.use_tx && m_cb.use_rx && m_cb.tx_ready && m_cb.rx_ready) ||
394             // - TX only mode
395             (!m_cb.use_rx && m_cb.tx_ready) ||
396             // - RX only mode
397             (!m_cb.use_tx && m_cb.rx_ready))
398         {
399             m_cb.tx_ready = false;
400             m_cb.rx_ready = false;
401 
402             // If the application did not supply the buffers for the next
403             // part of the transfer until this moment, the current buffers
404             // cannot be released, since the I2S peripheral already started
405             // using them. Signal this situation to the application by
406             // passing NULL instead of the structure with released buffers.
407             if (m_cb.buffers_reused)
408             {
409                 m_cb.buffers_reused = false;
410                 // This will most likely be set at this point. However, there is
411                 // a small time window between TXPTRUPD and RXPTRUPD events,
412                 // and it is theoretically possible that next buffers will be
413                 // set in this window, so to be sure this flag is set to true,
414                 // set it explicitly.
415                 m_cb.buffers_needed = true;
416                 m_cb.handler(NULL,
417                              NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
418             }
419             else
420             {
421                 // Buffers that have been used by the I2S peripheral (current)
422                 // are now released and will be returned to the application,
423                 // and the ones scheduled to be used as next become the current
424                 // ones.
425                 nrfx_i2s_buffers_t released_buffers = m_cb.current_buffers;
426                 m_cb.current_buffers = m_cb.next_buffers;
427                 m_cb.next_buffers.p_rx_buffer = NULL;
428                 m_cb.next_buffers.p_tx_buffer = NULL;
429                 m_cb.buffers_needed = true;
430                 m_cb.handler(&released_buffers,
431                              NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
432             }
433 
434         }
435     }
436 }
437 
438 #endif // NRFX_CHECK(NRFX_I2S_ENABLED)
439