1 /*
2 * Copyright (c) 2015 - 2020, 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_I2S_STOP_ANOMALY) && \
49 (defined(NRF52_SERIES) || defined(NRF9160_XXAA))
50 // Enable workaround for nRF52 Series anomaly 194 / nRF9160 anomaly 1
51 // (STOP task does not switch off all resources).
52 #define USE_WORKAROUND_FOR_I2S_STOP_ANOMALY 1
53 #endif
54
55 #if !defined(USE_WORKAROUND_FOR_ANOMALY_170) && defined(NRF52_SERIES)
56 // Enable workaround for nRF52 Series anomaly 170
57 // (when reading the value of PSEL registers, the CONNECT field might not
58 // return the same value that has been written to it).
59 #define USE_WORKAROUND_FOR_ANOMALY_170 1
60 #endif
61
62 #if !defined(USE_WORKAROUND_FOR_ANOMALY_196) && defined(NRF52_SERIES)
63 // Enable workaround for nRF52 Series anomaly 196
64 // (PSEL acquires GPIO regardless of ENABLE).
65 #define USE_WORKAROUND_FOR_ANOMALY_196 1
66 #endif
67
68 // Control block - driver instance local data.
69 typedef struct
70 {
71 nrfx_i2s_data_handler_t handler;
72 nrfx_drv_state_t state;
73
74 bool use_rx : 1;
75 bool use_tx : 1;
76 bool rx_ready : 1;
77 bool tx_ready : 1;
78 bool buffers_needed : 1;
79 bool buffers_reused : 1;
80
81 uint16_t buffer_size;
82 nrfx_i2s_buffers_t next_buffers;
83 nrfx_i2s_buffers_t current_buffers;
84 } i2s_control_block_t;
85 static i2s_control_block_t m_cb;
86
87
configure_pins(nrfx_i2s_config_t const * p_config)88 static void configure_pins(nrfx_i2s_config_t const * p_config)
89 {
90 uint32_t mck_pin, sdout_pin, sdin_pin;
91
92 // Configure pins used by the peripheral:
93
94 // - SCK and LRCK (required) - depending on the mode of operation these
95 // pins are configured as outputs (in Master mode) or inputs (in Slave
96 // mode).
97 if (p_config->mode == NRF_I2S_MODE_MASTER)
98 {
99 nrf_gpio_cfg_output(p_config->sck_pin);
100 nrf_gpio_cfg_output(p_config->lrck_pin);
101 }
102 else
103 {
104 nrf_gpio_cfg_input(p_config->sck_pin, NRF_GPIO_PIN_NOPULL);
105 nrf_gpio_cfg_input(p_config->lrck_pin, NRF_GPIO_PIN_NOPULL);
106 }
107
108 // - MCK (optional) - always output,
109 if (p_config->mck_pin != NRFX_I2S_PIN_NOT_USED)
110 {
111 mck_pin = p_config->mck_pin;
112 nrf_gpio_cfg_output(mck_pin);
113 }
114 else
115 {
116 mck_pin = NRF_I2S_PIN_NOT_CONNECTED;
117 }
118
119 // - SDOUT (optional) - always output,
120 if (p_config->sdout_pin != NRFX_I2S_PIN_NOT_USED)
121 {
122 sdout_pin = p_config->sdout_pin;
123 nrf_gpio_cfg_output(sdout_pin);
124 }
125 else
126 {
127 sdout_pin = NRF_I2S_PIN_NOT_CONNECTED;
128 }
129
130 // - SDIN (optional) - always input.
131 if (p_config->sdin_pin != NRFX_I2S_PIN_NOT_USED)
132 {
133 sdin_pin = p_config->sdin_pin;
134 nrf_gpio_cfg_input(sdin_pin, NRF_GPIO_PIN_NOPULL);
135 }
136 else
137 {
138 sdin_pin = NRF_I2S_PIN_NOT_CONNECTED;
139 }
140
141 nrf_i2s_pins_set(NRF_I2S0,
142 p_config->sck_pin,
143 p_config->lrck_pin,
144 mck_pin,
145 sdout_pin,
146 sdin_pin);
147 }
148
deconfigure_pins(void)149 static void deconfigure_pins(void)
150 {
151 uint32_t sck_pin = nrf_i2s_sck_pin_get(NRF_I2S0);
152 uint32_t lrck_pin = nrf_i2s_lrck_pin_get(NRF_I2S0);
153 uint32_t mck_pin = nrf_i2s_mck_pin_get(NRF_I2S0);
154 uint32_t sdout_pin = nrf_i2s_sdout_pin_get(NRF_I2S0);
155 uint32_t sdin_pin = nrf_i2s_sdin_pin_get(NRF_I2S0);
156
157 #if USE_WORKAROUND_FOR_ANOMALY_170
158 // Create bitmask for extracting pin number from PSEL register.
159 uint32_t pin_mask = I2S_PSEL_SCK_PIN_Msk;
160 #if defined(I2S_PSEL_SCK_PORT_Msk)
161 // If device supports more than one GPIO port, take port number into account as well.
162 pin_mask |= I2S_PSEL_SCK_PORT_Msk;
163 #endif
164 #else
165 uint32_t pin_mask = 0xFFFFFFFF;
166 #endif // USE_WORKAROUND_FOR_ANOMALY_170
167
168 nrf_gpio_cfg_default(sck_pin & pin_mask);
169 nrf_gpio_cfg_default(lrck_pin & pin_mask);
170
171 if (mck_pin != NRF_I2S_PIN_NOT_CONNECTED)
172 {
173 nrf_gpio_cfg_default(mck_pin & pin_mask);
174 }
175
176 if (sdout_pin != NRF_I2S_PIN_NOT_CONNECTED)
177 {
178 nrf_gpio_cfg_default(sdout_pin & pin_mask);
179 }
180
181 if (sdin_pin != NRF_I2S_PIN_NOT_CONNECTED)
182 {
183 nrf_gpio_cfg_default(sdin_pin & pin_mask);
184 }
185 }
186
nrfx_i2s_init(nrfx_i2s_config_t const * p_config,nrfx_i2s_data_handler_t handler)187 nrfx_err_t nrfx_i2s_init(nrfx_i2s_config_t const * p_config,
188 nrfx_i2s_data_handler_t handler)
189 {
190 NRFX_ASSERT(p_config);
191 NRFX_ASSERT(handler);
192
193 nrfx_err_t err_code;
194
195 if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
196 {
197 err_code = NRFX_ERROR_INVALID_STATE;
198 NRFX_LOG_WARNING("Function: %s, error code: %s.",
199 __func__,
200 NRFX_LOG_ERROR_STRING_GET(err_code));
201 return err_code;
202 }
203
204 if (!nrf_i2s_configure(NRF_I2S0,
205 p_config->mode,
206 p_config->format,
207 p_config->alignment,
208 p_config->sample_width,
209 p_config->channels,
210 p_config->mck_setup,
211 p_config->ratio))
212 {
213 err_code = NRFX_ERROR_INVALID_PARAM;
214 NRFX_LOG_WARNING("Function: %s, error code: %s.",
215 __func__,
216 NRFX_LOG_ERROR_STRING_GET(err_code));
217 return err_code;
218 }
219
220 #if NRF_I2S_HAS_CLKCONFIG
221 nrf_i2s_clk_configure(NRF_I2S0, p_config->clksrc, p_config->enable_bypass);
222 #endif
223 configure_pins(p_config);
224
225 m_cb.handler = handler;
226
227 NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_I2S0), p_config->irq_priority);
228 NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_I2S0));
229
230 m_cb.state = NRFX_DRV_STATE_INITIALIZED;
231
232 NRFX_LOG_INFO("Initialized.");
233 return NRFX_SUCCESS;
234 }
235
236
nrfx_i2s_uninit(void)237 void nrfx_i2s_uninit(void)
238 {
239 NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
240
241 nrfx_i2s_stop();
242
243 NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_I2S0));
244
245 nrf_i2s_disable(NRF_I2S0);
246
247 deconfigure_pins();
248
249 #if USE_WORKAROUND_FOR_ANOMALY_196
250 // Disabling I2S is insufficient to release pins acquired by the peripheral.
251 // Explicit disconnect is needed.
252 nrf_i2s_pins_set(NRF_I2S0,
253 NRF_I2S_PIN_NOT_CONNECTED,
254 NRF_I2S_PIN_NOT_CONNECTED,
255 NRF_I2S_PIN_NOT_CONNECTED,
256 NRF_I2S_PIN_NOT_CONNECTED,
257 NRF_I2S_PIN_NOT_CONNECTED);
258 #endif
259
260 m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
261 NRFX_LOG_INFO("Uninitialized.");
262 }
263
264
nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,uint16_t buffer_size,uint8_t flags)265 nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
266 uint16_t buffer_size,
267 uint8_t flags)
268 {
269 NRFX_ASSERT(p_initial_buffers != NULL);
270 NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
271 p_initial_buffers->p_tx_buffer != NULL);
272 NRFX_ASSERT((p_initial_buffers->p_rx_buffer == NULL) ||
273 (nrfx_is_in_ram(p_initial_buffers->p_rx_buffer) &&
274 nrfx_is_word_aligned(p_initial_buffers->p_rx_buffer)));
275 NRFX_ASSERT((p_initial_buffers->p_tx_buffer == NULL) ||
276 (nrfx_is_in_ram(p_initial_buffers->p_tx_buffer) &&
277 nrfx_is_word_aligned(p_initial_buffers->p_tx_buffer)));
278 NRFX_ASSERT(buffer_size != 0);
279 (void)(flags);
280
281 nrfx_err_t err_code;
282
283 if (m_cb.state != NRFX_DRV_STATE_INITIALIZED)
284 {
285 err_code = NRFX_ERROR_INVALID_STATE;
286 NRFX_LOG_WARNING("Function: %s, error code: %s.",
287 __func__,
288 NRFX_LOG_ERROR_STRING_GET(err_code));
289 return err_code;
290 }
291
292 if (((p_initial_buffers->p_rx_buffer != NULL)
293 && !nrfx_is_in_ram(p_initial_buffers->p_rx_buffer))
294 ||
295 ((p_initial_buffers->p_tx_buffer != NULL)
296 && !nrfx_is_in_ram(p_initial_buffers->p_tx_buffer)))
297 {
298 err_code = NRFX_ERROR_INVALID_ADDR;
299 NRFX_LOG_WARNING("Function: %s, error code: %s.",
300 __func__,
301 NRFX_LOG_ERROR_STRING_GET(err_code));
302 return err_code;
303 }
304
305 m_cb.use_rx = (p_initial_buffers->p_rx_buffer != NULL);
306 m_cb.use_tx = (p_initial_buffers->p_tx_buffer != NULL);
307 m_cb.rx_ready = false;
308 m_cb.tx_ready = false;
309 m_cb.buffers_needed = false;
310 m_cb.buffer_size = buffer_size;
311
312 // Set the provided initial buffers as next, they will become the current
313 // ones after the IRQ handler is called for the first time, what will occur
314 // right after the START task is triggered.
315 m_cb.next_buffers = *p_initial_buffers;
316 m_cb.current_buffers.p_rx_buffer = NULL;
317 m_cb.current_buffers.p_tx_buffer = NULL;
318
319 nrf_i2s_transfer_set(NRF_I2S0,
320 m_cb.buffer_size,
321 m_cb.next_buffers.p_rx_buffer,
322 m_cb.next_buffers.p_tx_buffer);
323
324 nrf_i2s_enable(NRF_I2S0);
325
326 m_cb.state = NRFX_DRV_STATE_POWERED_ON;
327
328 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_RXPTRUPD);
329 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_TXPTRUPD);
330 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_STOPPED);
331 nrf_i2s_int_enable(NRF_I2S0, (m_cb.use_rx ? NRF_I2S_INT_RXPTRUPD_MASK : 0) |
332 (m_cb.use_tx ? NRF_I2S_INT_TXPTRUPD_MASK : 0) |
333 NRF_I2S_INT_STOPPED_MASK);
334 nrf_i2s_task_trigger(NRF_I2S0, NRF_I2S_TASK_START);
335
336 NRFX_LOG_INFO("Started.");
337 return NRFX_SUCCESS;
338 }
339
340
nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)341 nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)
342 {
343 NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_POWERED_ON);
344 NRFX_ASSERT(p_buffers);
345 NRFX_ASSERT((p_buffers->p_rx_buffer == NULL) ||
346 (nrfx_is_in_ram(p_buffers->p_rx_buffer) &&
347 nrfx_is_word_aligned(p_buffers->p_rx_buffer)));
348 NRFX_ASSERT((p_buffers->p_tx_buffer == NULL) ||
349 (nrfx_is_in_ram(p_buffers->p_tx_buffer) &&
350 nrfx_is_word_aligned(p_buffers->p_tx_buffer)));
351
352 nrfx_err_t err_code;
353
354 if (!m_cb.buffers_needed)
355 {
356 err_code = NRFX_ERROR_INVALID_STATE;
357 NRFX_LOG_WARNING("Function: %s, error code: %s.",
358 __func__,
359 NRFX_LOG_ERROR_STRING_GET(err_code));
360 return err_code;
361 }
362
363 if (((p_buffers->p_rx_buffer != NULL)
364 && !nrfx_is_in_ram(p_buffers->p_rx_buffer))
365 ||
366 ((p_buffers->p_tx_buffer != NULL)
367 && !nrfx_is_in_ram(p_buffers->p_tx_buffer)))
368 {
369 err_code = NRFX_ERROR_INVALID_ADDR;
370 NRFX_LOG_WARNING("Function: %s, error code: %s.",
371 __func__,
372 NRFX_LOG_ERROR_STRING_GET(err_code));
373 return err_code;
374 }
375
376 if (m_cb.use_tx)
377 {
378 NRFX_ASSERT(p_buffers->p_tx_buffer != NULL);
379 nrf_i2s_tx_buffer_set(NRF_I2S0, p_buffers->p_tx_buffer);
380 }
381 if (m_cb.use_rx)
382 {
383 NRFX_ASSERT(p_buffers->p_rx_buffer != NULL);
384 nrf_i2s_rx_buffer_set(NRF_I2S0, p_buffers->p_rx_buffer);
385 }
386
387 m_cb.next_buffers = *p_buffers;
388 m_cb.buffers_needed = false;
389
390 return NRFX_SUCCESS;
391 }
392
393
nrfx_i2s_stop(void)394 void nrfx_i2s_stop(void)
395 {
396 NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
397
398 m_cb.buffers_needed = false;
399
400 // First disable interrupts, then trigger the STOP task, so no spurious
401 // RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55) are processed.
402 nrf_i2s_int_disable(NRF_I2S0, NRF_I2S_INT_RXPTRUPD_MASK |
403 NRF_I2S_INT_TXPTRUPD_MASK);
404 nrf_i2s_task_trigger(NRF_I2S0, NRF_I2S_TASK_STOP);
405
406 #if NRFX_CHECK(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY)
407 *((volatile uint32_t *)(((uint32_t)NRF_I2S0) + 0x38)) = 1;
408 *((volatile uint32_t *)(((uint32_t)NRF_I2S0) + 0x3C)) = 1;
409 #endif
410 }
411
412
nrfx_i2s_irq_handler(void)413 void nrfx_i2s_irq_handler(void)
414 {
415 if (nrf_i2s_event_check(NRF_I2S0, NRF_I2S_EVENT_TXPTRUPD))
416 {
417 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_TXPTRUPD);
418 m_cb.tx_ready = true;
419 if (m_cb.use_tx && m_cb.buffers_needed)
420 {
421 m_cb.buffers_reused = true;
422 }
423 }
424 if (nrf_i2s_event_check(NRF_I2S0, NRF_I2S_EVENT_RXPTRUPD))
425 {
426 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_RXPTRUPD);
427 m_cb.rx_ready = true;
428 if (m_cb.use_rx && m_cb.buffers_needed)
429 {
430 m_cb.buffers_reused = true;
431 }
432 }
433
434 if (nrf_i2s_event_check(NRF_I2S0, NRF_I2S_EVENT_STOPPED))
435 {
436 nrf_i2s_event_clear(NRF_I2S0, NRF_I2S_EVENT_STOPPED);
437 nrf_i2s_int_disable(NRF_I2S0, NRF_I2S_INT_STOPPED_MASK);
438 nrf_i2s_disable(NRF_I2S0);
439
440 // When stopped, release all buffers, including these scheduled for
441 // the next part of the transfer, and signal that the transfer has
442 // finished.
443
444 m_cb.handler(&m_cb.current_buffers, 0);
445
446 // Change the state of the driver before calling the handler with
447 // the flag signaling that the transfer has finished, so that it is
448 // possible to start a new transfer directly from the handler function.
449 m_cb.state = NRFX_DRV_STATE_INITIALIZED;
450 NRFX_LOG_INFO("Stopped.");
451
452 m_cb.handler(&m_cb.next_buffers, NRFX_I2S_STATUS_TRANSFER_STOPPED);
453 }
454 else
455 {
456 // Check if the requested transfer has been completed:
457 // - full-duplex mode
458 if ((m_cb.use_tx && m_cb.use_rx && m_cb.tx_ready && m_cb.rx_ready) ||
459 // - TX only mode
460 (!m_cb.use_rx && m_cb.tx_ready) ||
461 // - RX only mode
462 (!m_cb.use_tx && m_cb.rx_ready))
463 {
464 m_cb.tx_ready = false;
465 m_cb.rx_ready = false;
466
467 // If the application did not supply the buffers for the next
468 // part of the transfer until this moment, the current buffers
469 // cannot be released, since the I2S peripheral already started
470 // using them. Signal this situation to the application by
471 // passing NULL instead of the structure with released buffers.
472 if (m_cb.buffers_reused)
473 {
474 m_cb.buffers_reused = false;
475 // This will most likely be set at this point. However, there is
476 // a small time window between TXPTRUPD and RXPTRUPD events,
477 // and it is theoretically possible that next buffers will be
478 // set in this window, so to be sure this flag is set to true,
479 // set it explicitly.
480 m_cb.buffers_needed = true;
481 m_cb.handler(NULL,
482 NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
483 }
484 else
485 {
486 // Buffers that have been used by the I2S peripheral (current)
487 // are now released and will be returned to the application,
488 // and the ones scheduled to be used as next become the current
489 // ones.
490 nrfx_i2s_buffers_t released_buffers = m_cb.current_buffers;
491 m_cb.current_buffers = m_cb.next_buffers;
492 m_cb.next_buffers.p_rx_buffer = NULL;
493 m_cb.next_buffers.p_tx_buffer = NULL;
494 m_cb.buffers_needed = true;
495 m_cb.handler(&released_buffers,
496 NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
497 }
498
499 }
500 }
501 }
502
503 #endif // NRFX_CHECK(NRFX_I2S_ENABLED)
504