You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by je...@apache.org on 2020/04/15 05:55:04 UTC

[mynewt-core] branch master updated (40fb2d6 -> f54786e)

This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git.


    from 40fb2d6  stm32f4: Add I2SPLL configuration
     new bfe7d7f  hw/drivers/i2s: Add I2S API
     new 6eac7e5  hw/drivers/i2s: Add I2S device implementation
     new f54786e  hw/drivers/i2s: Add I2S driver for STM32F4 family

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 hw/drivers/i2s/README.md                           | 224 ++++++
 .../i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h  | 274 +++++++
 .../include/i2s_stm32f4/stm32_pin_cfg.h}           |  16 +-
 .../{pwm/pwm_da1469x => i2s/i2s_stm32f4}/pkg.yml   |   8 +-
 hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c       | 855 +++++++++++++++++++++
 .../drivers/i2s/i2s_stm32f4}/syscfg.yml            |   2 +-
 hw/drivers/i2s/include/i2s/i2s.h                   | 270 +++++++
 .../i2s/include/i2s/i2s_driver.h}                  |  36 +-
 hw/drivers/{hash => i2s}/pkg.yml                   |   7 +-
 hw/drivers/i2s/src/i2s.c                           | 378 +++++++++
 10 files changed, 2037 insertions(+), 33 deletions(-)
 create mode 100644 hw/drivers/i2s/README.md
 create mode 100644 hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h
 copy hw/{mcu/dialog/da1469x/src/da1469x_priv.h => drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/stm32_pin_cfg.h} (79%)
 mode change 100755 => 100644
 copy hw/drivers/{pwm/pwm_da1469x => i2s/i2s_stm32f4}/pkg.yml (86%)
 create mode 100644 hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c
 copy {compiler/arm-none-eabi-m3 => hw/drivers/i2s/i2s_stm32f4}/syscfg.yml (98%)
 create mode 100644 hw/drivers/i2s/include/i2s/i2s.h
 copy hw/{mcu/stm/stm32_common/include/stm32_common/stm32_hal.h => drivers/i2s/include/i2s/i2s_driver.h} (52%)
 copy hw/drivers/{hash => i2s}/pkg.yml (90%)
 create mode 100644 hw/drivers/i2s/src/i2s.c


[mynewt-core] 03/03: hw/drivers/i2s: Add I2S driver for STM32F4 family

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit f54786e60885eba55a34c4a4dc7a971bf2323ab7
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Fri Mar 27 13:21:12 2020 +0100

    hw/drivers/i2s: Add I2S driver for STM32F4 family
    
    Code implements I2S driver for STM32F4xx MCUs that have it.
---
 .../i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h  | 274 +++++++
 .../include/i2s_stm32f4/stm32_pin_cfg.h            |  41 +
 hw/drivers/i2s/i2s_stm32f4/pkg.yml                 |  29 +
 hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c       | 855 +++++++++++++++++++++
 hw/drivers/i2s/i2s_stm32f4/syscfg.yml              |  19 +
 5 files changed, 1218 insertions(+)

diff --git a/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h b/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h
new file mode 100644
index 0000000..8e1fc87
--- /dev/null
+++ b/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/i2s_stm32f4.h
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _I2S_STM32_H
+#define _I2S_STM32_H
+
+#include <stm32f4xx_hal.h>
+#include <i2s_stm32f4/stm32_pin_cfg.h>
+
+struct i2s;
+struct i2s_cfg;
+struct stm32_spi_cfg;
+
+struct stm32_i2s_pins {
+    stm32_pin_cfg_t ck_pin;
+    stm32_pin_cfg_t ws_pin;
+    stm32_pin_cfg_t sd_pin;
+    stm32_pin_cfg_t ext_sd_pin;
+};
+
+#define I2S_PIN(n, port, pin) &(I2S ## n ## _P ## port ## pin)
+#define I2S_CK_PIN(n, port, pin) &(I2S ## n ## _CK_P ## port ## pin)
+#define I2S_WS_PIN(n, port, pin) &(I2S ## n ## _WS_P ## port ## pin)
+#define I2S_SD_PIN(n, port, pin) &(I2S ## n ## _SD_P ## port ## pin)
+
+struct stm32_dma_cfg {
+    uint8_t dma_num;
+    IRQn_Type dma_stream_irq;
+    DMA_Stream_TypeDef *dma_stream;
+    uint32_t dma_channel;
+};
+
+struct i2s_cfg {
+    uint32_t mode;
+    uint32_t standard;
+    uint32_t data_format;
+    uint32_t sample_rate;
+
+    struct i2s_buffer_pool *pool;
+    const struct stm32_spi_cfg *spi_cfg;
+    const struct stm32_dma_cfg *dma_cfg;
+    const struct stm32_dma_cfg *dma_i2sext_cfg;
+    struct stm32_i2s_pins pins;
+};
+
+#define SPI_CFG(n) &(spi ## n ## _cfg)
+
+struct stm32_i2s {
+    I2S_HandleTypeDef hi2s;
+    DMA_HandleTypeDef *hdma_spi;
+    DMA_HandleTypeDef *hdma_i2sext;
+
+    struct i2s *i2s;
+    struct i2s_sample_buffer *active_buffer;
+};
+
+#define DMA_CFG(dma, ch, st, name) &(name ## _stream ## st ## _channel ## ch)
+
+#define DMA_STREAM_DECLARE(dma, ch, st, name) \
+    extern const struct stm32_dma_cfg name ## _stream ## st ## _channel ## ch
+
+DMA_STREAM_DECLARE(1, 0, 0, spi3_rx);
+DMA_STREAM_DECLARE(1, 0, 1, i2c1_tx);
+DMA_STREAM_DECLARE(1, 0, 2, spi3_rx);
+DMA_STREAM_DECLARE(1, 0, 3, spi2_rx);
+DMA_STREAM_DECLARE(1, 0, 4, spi2_tx);
+DMA_STREAM_DECLARE(1, 0, 5, spi3_tx);
+DMA_STREAM_DECLARE(1, 0, 7, spi3_tx);
+DMA_STREAM_DECLARE(1, 1, 0, i2c1_rx);
+DMA_STREAM_DECLARE(1, 1, 1, i2c3_rx);
+DMA_STREAM_DECLARE(1, 1, 2, tim7_up);
+DMA_STREAM_DECLARE(1, 1, 4, tim7_up);
+DMA_STREAM_DECLARE(1, 1, 5, i2c1_rx);
+DMA_STREAM_DECLARE(1, 1, 6, i2c1_tx);
+DMA_STREAM_DECLARE(1, 1, 7, i2c1_tx);
+DMA_STREAM_DECLARE(1, 2, 0, tim4_ch1);
+DMA_STREAM_DECLARE(1, 2, 2, i2s3_ext_rx);
+DMA_STREAM_DECLARE(1, 2, 3, tim4_ch2);
+DMA_STREAM_DECLARE(1, 2, 4, i2s2_ext_tx);
+DMA_STREAM_DECLARE(1, 2, 5, i2s3_ext_tx);
+DMA_STREAM_DECLARE(1, 2, 6, tim4_up);
+DMA_STREAM_DECLARE(1, 2, 7, tim4_ch3);
+DMA_STREAM_DECLARE(1, 3, 0, i2s3_ext_rx);
+DMA_STREAM_DECLARE(1, 3, 1, tim2_up);
+DMA_STREAM_DECLARE(1, 3, 1, tim2_ch3);
+DMA_STREAM_DECLARE(1, 3, 2, i2c3_rx);
+DMA_STREAM_DECLARE(1, 3, 3, i2s2_ext_rx);
+DMA_STREAM_DECLARE(1, 3, 4, i2c3_tx);
+DMA_STREAM_DECLARE(1, 3, 5, tim2_ch1);
+DMA_STREAM_DECLARE(1, 3, 6, tim2_ch2);
+DMA_STREAM_DECLARE(1, 3, 6, tim2_ch4);
+DMA_STREAM_DECLARE(1, 3, 7, tim2_up);
+DMA_STREAM_DECLARE(1, 3, 7, tim2_ch4);
+DMA_STREAM_DECLARE(1, 4, 0, uart5_rx);
+DMA_STREAM_DECLARE(1, 4, 1, usart3_rx);
+DMA_STREAM_DECLARE(1, 4, 2, uart4_rx);
+DMA_STREAM_DECLARE(1, 4, 3, usart3_tx);
+DMA_STREAM_DECLARE(1, 4, 4, uart4_tx);
+DMA_STREAM_DECLARE(1, 4, 5, usart2_rx);
+DMA_STREAM_DECLARE(1, 4, 6, usart2_tx);
+DMA_STREAM_DECLARE(1, 4, 7, uart5_tx);
+
+DMA_STREAM_DECLARE(2, 0, 0, adc1);
+DMA_STREAM_DECLARE(2, 0, 1, sai1_a);
+DMA_STREAM_DECLARE(2, 0, 2, tim8_ch1);
+DMA_STREAM_DECLARE(2, 0, 2, tim8_ch2);
+DMA_STREAM_DECLARE(2, 0, 2, tim8_ch3);
+DMA_STREAM_DECLARE(2, 0, 3, sai1_a);
+DMA_STREAM_DECLARE(2, 0, 4, adc1);
+DMA_STREAM_DECLARE(2, 0, 5, sai1_b);
+DMA_STREAM_DECLARE(2, 0, 6, tim1_ch1);
+DMA_STREAM_DECLARE(2, 0, 6, tim1_ch2);
+DMA_STREAM_DECLARE(2, 0, 6, tim1_ch3);
+DMA_STREAM_DECLARE(2, 1, 1, dcmi);
+DMA_STREAM_DECLARE(2, 1, 2, adc2);
+DMA_STREAM_DECLARE(2, 1, 3, adc2);
+DMA_STREAM_DECLARE(2, 1, 4, sai1_b);
+DMA_STREAM_DECLARE(2, 1, 5, spi6_tx);
+DMA_STREAM_DECLARE(2, 1, 6, spi6_rx);
+DMA_STREAM_DECLARE(2, 1, 7, dcmi);
+DMA_STREAM_DECLARE(2, 2, 0, adc3);
+DMA_STREAM_DECLARE(2, 2, 1, adc3);
+DMA_STREAM_DECLARE(2, 2, 2, spi1_tx);
+DMA_STREAM_DECLARE(2, 2, 3, spi5_rx);
+DMA_STREAM_DECLARE(2, 2, 4, spi5_tx);
+DMA_STREAM_DECLARE(2, 2, 5, cryp_out);
+DMA_STREAM_DECLARE(2, 2, 6, cryp_in);
+DMA_STREAM_DECLARE(2, 2, 7, hash_in);
+DMA_STREAM_DECLARE(2, 3, 0, spi1_rx);
+DMA_STREAM_DECLARE(2, 3, 2, spi1_rx);
+DMA_STREAM_DECLARE(2, 3, 3, spi1_tx);
+DMA_STREAM_DECLARE(2, 3, 5, spi1_tx);
+DMA_STREAM_DECLARE(2, 4, 0, spi4_rx);
+DMA_STREAM_DECLARE(2, 4, 1, spi4_tx);
+DMA_STREAM_DECLARE(2, 4, 2, usart1_rx);
+DMA_STREAM_DECLARE(2, 4, 3, sdio);
+DMA_STREAM_DECLARE(2, 4, 4, spi4_rx);
+DMA_STREAM_DECLARE(2, 4, 5, usart1_rx);
+DMA_STREAM_DECLARE(2, 4, 6, sdio);
+DMA_STREAM_DECLARE(2, 4, 7, usart1_tx);
+DMA_STREAM_DECLARE(2, 5, 1, usart6_rx);
+DMA_STREAM_DECLARE(2, 5, 2, usart6_rx);
+DMA_STREAM_DECLARE(2, 5, 3, spi4_rx);
+DMA_STREAM_DECLARE(2, 5, 4, spi4_tx);
+DMA_STREAM_DECLARE(2, 5, 5, spi5_tx);
+DMA_STREAM_DECLARE(2, 5, 6, usart6_tx);
+DMA_STREAM_DECLARE(2, 5, 7, usart6_tx);
+DMA_STREAM_DECLARE(2, 6, 0, tim1_trig);
+DMA_STREAM_DECLARE(2, 6, 1, tim1_ch1);
+DMA_STREAM_DECLARE(2, 6, 2, tim1_ch2);
+DMA_STREAM_DECLARE(2, 6, 3, tim1_ch1);
+DMA_STREAM_DECLARE(2, 6, 4, tim1_ch4);
+DMA_STREAM_DECLARE(2, 6, 4, tim1_trig);
+DMA_STREAM_DECLARE(2, 6, 4, tim1_com);
+DMA_STREAM_DECLARE(2, 6, 5, tim1_up);
+DMA_STREAM_DECLARE(2, 6, 6, tim1_ch3);
+DMA_STREAM_DECLARE(2, 7, 1, tim8_up);
+DMA_STREAM_DECLARE(2, 7, 2, tim8_ch1);
+DMA_STREAM_DECLARE(2, 7, 3, tim8_ch2);
+DMA_STREAM_DECLARE(2, 7, 4, tim8_ch3);
+DMA_STREAM_DECLARE(2, 7, 5, spi5_rx);
+DMA_STREAM_DECLARE(2, 7, 6, spi5_tx);
+DMA_STREAM_DECLARE(2, 7, 7, tim8_ch4);
+DMA_STREAM_DECLARE(2, 7, 7, tim8_trig);
+DMA_STREAM_DECLARE(2, 7, 7, tim8_com);
+
+#define SPI_CFG_DECLARE(n) \
+    extern struct stm32_spi_cfg spi ## n ## _cfg;
+
+SPI_CFG_DECLARE(1);
+SPI_CFG_DECLARE(2);
+SPI_CFG_DECLARE(3);
+SPI_CFG_DECLARE(4);
+SPI_CFG_DECLARE(5);
+
+#define I2S_PIN_DECLARE(n, port, pin) \
+    extern const struct stm32_pin_cfg I2S ## n ## _P ## port ## pin;
+#define I2S_CK_PIN_DECLARE(n, port, pin) \
+    extern const struct stm32_pin_cfg I2S ## n ## _CK_P ## port ## pin;
+#define I2S_WS_PIN_DECLARE(n, port, pin) \
+    extern const struct stm32_pin_cfg I2S ## n ## _WS_P ## port ## pin;
+#define I2S_SD_PIN_DECLARE(n, port, pin) \
+    extern const struct stm32_pin_cfg I2S ## n ## _SD_P ## port ## pin;
+
+/* I2S1 Possible CK pins */
+I2S_CK_PIN_DECLARE(1, A, 5)
+I2S_CK_PIN_DECLARE(1, B, 3)
+/* I2S1 possible WS pins */
+I2S_WS_PIN_DECLARE(1, A, 4)
+I2S_WS_PIN_DECLARE(1, A, 15)
+/* I2S1 possible SD pins */
+I2S_SD_PIN_DECLARE(1, B, 5)
+I2S_SD_PIN_DECLARE(1, A, 7)
+/* I2S2 Possible CKIN pins */
+I2S_PIN_DECLARE(2, A, 2)
+I2S_PIN_DECLARE(2, B, 11)
+I2S_PIN_DECLARE(2, C, 9)
+/* I2S2 Possible MCK pins */
+I2S_PIN_DECLARE(2, A, 3)
+I2S_PIN_DECLARE(2, A, 6)
+I2S_PIN_DECLARE(2, C, 6)
+/* I2S2 Possible CK pins */
+I2S_CK_PIN_DECLARE(2, B, 10)
+I2S_CK_PIN_DECLARE(2, B, 13)
+I2S_CK_PIN_DECLARE(2, C, 7)
+I2S_CK_PIN_DECLARE(2, D, 3)
+/* I2S2 possible WS pins */
+I2S_WS_PIN_DECLARE(2, B, 9)
+I2S_WS_PIN_DECLARE(2, B, 12)
+/* I2S2 possible SD pins */
+I2S_SD_PIN_DECLARE(2, B, 15)
+I2S_SD_PIN_DECLARE(2, C, 3)
+/* I2S2_ext possible SD pins */
+I2S_SD_PIN_DECLARE(2, B, 14)
+I2S_SD_PIN_DECLARE(2, C, 2)
+/* I2S3 possible CK pins */
+I2S_CK_PIN_DECLARE(3, B, 3)
+I2S_CK_PIN_DECLARE(3, C, 10)
+/* I2S3 possible WS pins */
+I2S_WS_PIN_DECLARE(3, A, 4)
+I2S_WS_PIN_DECLARE(3, A, 15)
+/* I2S3 possible SD pins */
+I2S_SD_PIN_DECLARE(3, B, 5)
+I2S_SD_PIN_DECLARE(3, C, 12)
+I2S_SD_PIN_DECLARE(3, D, 6)
+/* I2S3_ext possible SD pins */
+I2S_SD_PIN_DECLARE(3, B, 4)
+I2S_SD_PIN_DECLARE(3, C, 11)
+/* I2S3 possible MCK pins */
+I2S_PIN_DECLARE(3, B, 10)
+I2S_PIN_DECLARE(3, C, 7)
+/* I2S4 possible CK pins */
+I2S_CK_PIN_DECLARE(4, E, 2)
+I2S_CK_PIN_DECLARE(4, E, 12)
+I2S_CK_PIN_DECLARE(4, B, 13)
+/* I2S4 possible WS pins */
+I2S_WS_PIN_DECLARE(4, B, 12)
+I2S_WS_PIN_DECLARE(4, E, 4)
+I2S_WS_PIN_DECLARE(4, E, 11)
+/* I2S4 possible SD pins */
+I2S_SD_PIN_DECLARE(4, A, 1)
+I2S_SD_PIN_DECLARE(4, E, 6)
+I2S_SD_PIN_DECLARE(4, E, 14)
+/* I2S5 possible CK pins */
+I2S_CK_PIN_DECLARE(5, B, 0)
+I2S_CK_PIN_DECLARE(5, E, 2)
+I2S_CK_PIN_DECLARE(5, E, 12)
+/* I2S5 possible WS pins */
+I2S_WS_PIN_DECLARE(5, B, 1)
+I2S_WS_PIN_DECLARE(5, E, 4)
+I2S_WS_PIN_DECLARE(5, E, 11)
+/* I2S5 possible SD pins */
+I2S_SD_PIN_DECLARE(5, A, 10)
+I2S_SD_PIN_DECLARE(5, B, 8)
+I2S_SD_PIN_DECLARE(5, E, 6)
+I2S_SD_PIN_DECLARE(5, E, 14)
+
+#endif /* _I2S_STM32_H */
diff --git a/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/stm32_pin_cfg.h b/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/stm32_pin_cfg.h
new file mode 100644
index 0000000..10823f4
--- /dev/null
+++ b/hw/drivers/i2s/i2s_stm32f4/include/i2s_stm32f4/stm32_pin_cfg.h
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _STM32_PIN_CFG_H
+#define _STM32_PIN_CFG_H
+
+#include <stdint.h>
+#include <mcu/stm32_hal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct stm32_pin_cfg {
+    int8_t pin;
+    GPIO_InitTypeDef hal_init;
+};
+
+typedef const struct stm32_pin_cfg *stm32_pin_cfg_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STM32_PIN_CFG_H */
diff --git a/hw/drivers/i2s/i2s_stm32f4/pkg.yml b/hw/drivers/i2s/i2s_stm32f4/pkg.yml
new file mode 100644
index 0000000..4f2b437
--- /dev/null
+++ b/hw/drivers/i2s/i2s_stm32f4/pkg.yml
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/drivers/i2s/i2s_stm32f4
+pkg.description: I2S driver for STM32 F4xx devices
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.apis:
+    - I2S_HW_IMPL
+pkg.deps:
+    - "@apache-mynewt-core/hw/drivers/i2s"
diff --git a/hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c b/hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c
new file mode 100644
index 0000000..d6a17ad
--- /dev/null
+++ b/hw/drivers/i2s/i2s_stm32f4/src/i2s_stm32f4.c
@@ -0,0 +1,855 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <os/mynewt.h>
+#include <mcu/mcu.h>
+#include <mcu/stm32_hal.h>
+#include <bsp/bsp.h>
+#include <i2s/i2s.h>
+#include <i2s/i2s_driver.h>
+#include <i2s_stm32f4/i2s_stm32f4.h>
+
+#if defined(SPI_I2S_FULLDUPLEX_SUPPORT)
+#define I2SEXT_PRESENT  1
+#else
+#define I2SEXT_PRESENT  0
+#endif
+
+struct stm32_spi_cfg {
+    uint8_t spi_num;
+    SPI_TypeDef *spi;
+    IRQn_Type i2s_irq;
+    struct stm32_i2s *driver_data;
+    DMA_HandleTypeDef *hdma_spi;
+    void (*irq_handler)(void);
+    void (*i2s_dma_handler)(void);
+    void (*enable_clock)(bool enable);
+#if I2SEXT_PRESENT
+    DMA_HandleTypeDef *hdma_i2sext;
+    void (*i2sext_dma_handler)(void);
+#endif
+};
+
+static struct stm32_i2s stm32_i2s1;
+static struct stm32_i2s stm32_i2s2;
+static struct stm32_i2s stm32_i2s3;
+static struct stm32_i2s stm32_i2s4;
+static struct stm32_i2s stm32_i2s5;
+
+void
+i2s1_irq_handler(void)
+{
+    HAL_I2S_IRQHandler(&stm32_i2s1.hi2s);
+}
+
+void
+i2s2_irq_handler(void)
+{
+    HAL_I2S_IRQHandler(&stm32_i2s2.hi2s);
+}
+
+void
+i2s3_irq_handler(void)
+{
+    HAL_I2S_IRQHandler(&stm32_i2s3.hi2s);
+}
+
+void
+i2s4_irq_handler(void)
+{
+    HAL_I2S_IRQHandler(&stm32_i2s4.hi2s);
+}
+
+void
+i2s5_irq_handler(void)
+{
+    HAL_I2S_IRQHandler(&stm32_i2s5.hi2s);
+}
+
+static void
+i2s1_clock_enable(bool enable)
+{
+#ifdef SPI1
+    if (enable) {
+        __HAL_RCC_SPI1_CLK_ENABLE();
+    } else {
+        __HAL_RCC_SPI1_CLK_DISABLE();
+    }
+#endif
+}
+
+static void
+i2s2_clock_enable(bool enable)
+{
+#ifdef SPI2
+    if (enable) {
+        __HAL_RCC_SPI2_CLK_ENABLE();
+    } else {
+        __HAL_RCC_SPI2_CLK_DISABLE();
+    }
+#endif
+}
+
+static void
+i2s3_clock_enable(bool enable)
+{
+#ifdef SPI3
+    if (enable) {
+        __HAL_RCC_SPI3_CLK_ENABLE();
+    } else {
+        __HAL_RCC_SPI3_CLK_DISABLE();
+    }
+#endif
+}
+
+static void
+i2s4_clock_enable(bool enable)
+{
+#ifdef SPI4
+    if (enable) {
+        __HAL_RCC_SPI4_CLK_ENABLE();
+    } else {
+        __HAL_RCC_SPI4_CLK_DISABLE();
+    }
+#endif
+}
+
+static void
+i2s5_clock_enable(bool enable)
+{
+#ifdef SPI5
+    if (enable) {
+        __HAL_RCC_SPI5_CLK_ENABLE();
+    } else {
+        __HAL_RCC_SPI5_CLK_DISABLE();
+    }
+#endif
+}
+
+void
+HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)hi2s;
+    struct i2s *i2s = i2s_data->i2s;
+    struct i2s_sample_buffer *processed_buffer = i2s_data->active_buffer;
+
+    i2s_data->active_buffer = i2s_driver_buffer_get(i2s);
+    if (i2s_data->active_buffer) {
+        HAL_I2S_Transmit_DMA(&i2s_data->hi2s,
+                             i2s_data->active_buffer->sample_data,
+                             i2s_data->active_buffer->sample_count);
+    } else {
+        i2s_driver_state_changed(i2s, I2S_STATE_OUT_OF_BUFFERS);
+    }
+    i2s_driver_buffer_put(i2s, processed_buffer);
+}
+
+void
+HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)hi2s;
+    struct i2s *i2s = i2s_data->i2s;
+    struct i2s_sample_buffer *processed_buffer = i2s_data->active_buffer;
+
+    i2s_data->active_buffer = i2s_driver_buffer_get(i2s);
+    if (i2s_data->active_buffer) {
+        HAL_I2S_Receive_DMA(&i2s_data->hi2s,
+                            i2s_data->active_buffer->sample_data,
+                            i2s_data->active_buffer->capacity);
+    } else {
+        i2s_driver_state_changed(i2s, I2S_STATE_OUT_OF_BUFFERS);
+    }
+    processed_buffer->sample_count = processed_buffer->capacity;
+    i2s_driver_buffer_put(i2s, processed_buffer);
+}
+
+#if I2SEXT_PRESENT
+void
+HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)hi2s;
+    struct i2s *i2s = i2s_data->i2s;
+    struct i2s_sample_buffer *processed_buffer = i2s_data->active_buffer;
+
+    i2s_data->active_buffer = i2s_driver_buffer_get(i2s);
+    if (i2s_data->active_buffer) {
+        HAL_I2SEx_TransmitReceive_DMA(hi2s,
+                                      i2s_data->active_buffer->sample_data,
+                                      i2s_data->active_buffer->sample_data,
+                                      i2s_data->active_buffer->sample_count);
+    } else {
+        i2s_driver_state_changed(i2s, I2S_STATE_OUT_OF_BUFFERS);
+    }
+    i2s_driver_buffer_put(i2s, processed_buffer);
+}
+#endif
+
+static void
+i2s1_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s1.hdma_spi);
+}
+
+static void
+i2s2_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s2.hdma_spi);
+}
+
+static void
+i2s2ext_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s2.hdma_i2sext);
+}
+
+static void
+i2s3_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s3.hdma_spi);
+}
+
+static void
+i2s3ext_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s3.hdma_i2sext);
+}
+
+static void
+i2s4_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s4.hdma_spi);
+}
+
+static void
+i2s5_dma_stream_irq_handler(void)
+{
+    HAL_DMA_IRQHandler(stm32_i2s5.hdma_spi);
+}
+
+static void
+i2s_init_interrupts(const struct i2s_cfg *cfg)
+{
+    NVIC_SetVector(cfg->dma_cfg->dma_stream_irq, (uint32_t)cfg->spi_cfg->i2s_dma_handler);
+    HAL_NVIC_SetPriority(cfg->dma_cfg->dma_stream_irq, 5, 0);
+    HAL_NVIC_EnableIRQ(cfg->dma_cfg->dma_stream_irq);
+
+#if I2SEXT_PRESENT
+    if (cfg->dma_i2sext_cfg) {
+        NVIC_SetVector(cfg->dma_i2sext_cfg->dma_stream_irq, (uint32_t)cfg->spi_cfg->i2sext_dma_handler);
+        HAL_NVIC_SetPriority(cfg->dma_i2sext_cfg->dma_stream_irq, 5, 0);
+        HAL_NVIC_EnableIRQ(cfg->dma_i2sext_cfg->dma_stream_irq);
+    }
+#endif
+
+    /* I2S1 interrupt Init */
+    NVIC_SetVector(cfg->spi_cfg->i2s_irq, (uint32_t)cfg->spi_cfg->irq_handler);
+    HAL_NVIC_SetPriority(cfg->spi_cfg->i2s_irq, 5, 0);
+    HAL_NVIC_EnableIRQ(cfg->spi_cfg->i2s_irq);
+}
+
+static void
+i2s_init_pins(struct stm32_i2s_pins *pins)
+{
+    hal_gpio_init_stm(pins->ck_pin->pin, (GPIO_InitTypeDef *)&pins->ck_pin->hal_init);
+    hal_gpio_init_stm(pins->ws_pin->pin, (GPIO_InitTypeDef *)&pins->ws_pin->hal_init);
+    hal_gpio_init_stm(pins->sd_pin->pin, (GPIO_InitTypeDef *)&pins->sd_pin->hal_init);
+#if I2SEXT_PRESENT
+    if (pins->ext_sd_pin) {
+        hal_gpio_init_stm(pins->ext_sd_pin->pin, (GPIO_InitTypeDef *)&pins->ext_sd_pin->hal_init);
+    }
+#endif
+}
+
+static int
+stm32_i2s_init(struct i2s *i2s, const struct i2s_cfg *cfg)
+{
+    int rc = 0;
+    struct stm32_i2s *stm32_i2s;
+
+#if I2SEXT_PRESENT
+    if (cfg->pins.ext_sd_pin != NULL) {
+        i2s->direction = I2S_OUT_IN;
+    } else {
+        i2s->direction = ((cfg->mode == I2S_MODE_MASTER_TX) ||
+                          (cfg->mode == I2S_MODE_SLAVE_TX)) ? I2S_OUT : I2S_IN;
+    }
+#else
+    i2s->direction = ((cfg->mode == I2S_MODE_MASTER_TX) ||
+                      (cfg->mode == I2S_MODE_SLAVE_TX)) ? I2S_OUT : I2S_IN;
+#endif
+
+    if (cfg->data_format == I2S_DATAFORMAT_16B_EXTENDED ||
+        cfg->data_format == I2S_DATAFORMAT_16B) {
+        i2s->sample_size_in_bytes = 2;
+    } else {
+        i2s->sample_size_in_bytes = 4;
+    }
+
+    rc = i2s_init(i2s, cfg->pool);
+
+    if (rc != OS_OK) {
+        goto end;
+    }
+
+    stm32_i2s = cfg->spi_cfg->driver_data;
+    stm32_i2s->i2s = i2s;
+    stm32_i2s->hdma_spi = cfg->spi_cfg->hdma_spi;
+#if I2SEXT_PRESENT
+    stm32_i2s->hdma_i2sext = cfg->spi_cfg->hdma_i2sext;
+#endif
+
+    i2s->sample_rate = cfg->sample_rate;
+    i2s->driver_data = stm32_i2s;
+
+    i2s_init_pins((struct stm32_i2s_pins *)&cfg->pins);
+
+    cfg->spi_cfg->enable_clock(true);
+
+    stm32_i2s->hi2s.Instance = cfg->spi_cfg->spi;
+    stm32_i2s->hi2s.Init.Mode = cfg->mode;
+    stm32_i2s->hi2s.Init.Standard = cfg->standard;
+    stm32_i2s->hi2s.Init.DataFormat = cfg->data_format;
+    stm32_i2s->hi2s.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
+    stm32_i2s->hi2s.Init.AudioFreq = cfg->sample_rate;
+    stm32_i2s->hi2s.Init.CPOL = I2S_CPOL_LOW;
+#if I2SEXT_PRESENT
+    stm32_i2s->hi2s.Init.ClockSource = I2S_CLOCK_PLL;
+    if (i2s->direction == I2S_OUT_IN) {
+        stm32_i2s->hi2s.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
+    } else {
+        stm32_i2s->hi2s.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
+    }
+#endif
+
+    if (cfg->dma_cfg->dma_num == 1) {
+        __HAL_RCC_DMA1_CLK_ENABLE();
+    } else {
+#ifdef __HAL_RCC_DMA2_CLK_DISABLE
+        __HAL_RCC_DMA2_CLK_ENABLE();
+#endif
+    }
+
+    stm32_i2s->hdma_spi->Instance = cfg->dma_cfg->dma_stream;
+    stm32_i2s->hdma_spi->Init.Channel = cfg->dma_cfg->dma_channel;
+    if (cfg->mode == I2S_MODE_MASTER_TX || cfg->mode == I2S_MODE_SLAVE_TX) {
+        stm32_i2s->hdma_spi->Init.Direction = DMA_MEMORY_TO_PERIPH;
+    } else {
+        stm32_i2s->hdma_spi->Init.Direction = DMA_PERIPH_TO_MEMORY;
+    }
+    stm32_i2s->hdma_spi->Init.PeriphInc = DMA_PINC_DISABLE;
+    stm32_i2s->hdma_spi->Init.MemInc = DMA_MINC_ENABLE;
+    stm32_i2s->hdma_spi->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+    stm32_i2s->hdma_spi->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+    stm32_i2s->hdma_spi->Init.Mode = DMA_NORMAL;
+    stm32_i2s->hdma_spi->Init.Priority = DMA_PRIORITY_LOW;
+    stm32_i2s->hdma_spi->Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+
+    if (cfg->mode == I2S_MODE_MASTER_TX || cfg->mode == I2S_MODE_SLAVE_TX) {
+        __HAL_LINKDMA(&stm32_i2s->hi2s, hdmatx, *stm32_i2s->hdma_spi);
+    } else {
+        __HAL_LINKDMA(&stm32_i2s->hi2s, hdmarx, *stm32_i2s->hdma_spi);
+    }
+
+#if I2SEXT_PRESENT
+    if (i2s->direction == I2S_OUT_IN) {
+        stm32_i2s->hdma_i2sext->Instance = cfg->dma_i2sext_cfg->dma_stream;
+        stm32_i2s->hdma_i2sext->Init.Channel = cfg->dma_i2sext_cfg->dma_channel;
+        if (cfg->mode == I2S_MODE_MASTER_TX || cfg->mode == I2S_MODE_SLAVE_TX) {
+            stm32_i2s->hdma_i2sext->Init.Direction = DMA_PERIPH_TO_MEMORY;
+            __HAL_LINKDMA(&stm32_i2s->hi2s, hdmarx, *stm32_i2s->hdma_i2sext);
+        } else {
+            stm32_i2s->hdma_i2sext->Init.Direction = DMA_MEMORY_TO_PERIPH;
+            __HAL_LINKDMA(&stm32_i2s->hi2s, hdmatx, *stm32_i2s->hdma_i2sext);
+        }
+        stm32_i2s->hdma_i2sext->Init.PeriphInc = DMA_PINC_DISABLE;
+        stm32_i2s->hdma_i2sext->Init.MemInc = DMA_MINC_ENABLE;
+        stm32_i2s->hdma_i2sext->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+        stm32_i2s->hdma_i2sext->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+        stm32_i2s->hdma_i2sext->Init.Mode = DMA_NORMAL;
+        stm32_i2s->hdma_i2sext->Init.Priority = DMA_PRIORITY_LOW;
+        stm32_i2s->hdma_i2sext->Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+    }
+#endif
+
+    i2s_init_interrupts(cfg);
+end:
+    return rc;
+}
+
+int
+i2s_create(struct i2s *i2s, const char *name, const struct i2s_cfg *cfg)
+{
+    return os_dev_create(&i2s->dev, name, OS_DEV_INIT_PRIMARY,
+                         100, (os_dev_init_func_t)stm32_i2s_init, (void *)cfg);
+}
+
+int
+i2s_driver_stop(struct i2s *i2s)
+{
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)i2s->driver_data;
+    struct i2s_sample_buffer *buffer;
+
+    HAL_I2S_DMAStop(&i2s_data->hi2s);
+    if (i2s->state == I2S_STATE_RUNNING && i2s->direction == I2S_OUT) {
+        /*
+         * When DMA is stopped and then I2S peripheral is stopped, it
+         * may happen that DMA put some data already in SPI data buffer.
+         * In that case single sample may be in the I2S output buffer.
+         * If this happens next transmission will swap channels due to
+         * one extra sample already present.
+         * To avoid this just wait till all samples are gone.
+         */
+        if (0 == (i2s_data->hi2s.Instance->SR & SPI_SR_TXE_Msk)) {
+            __HAL_I2S_ENABLE(&i2s_data->hi2s);
+            while (0 == (i2s_data->hi2s.Instance->SR & SPI_SR_TXE_Msk))
+                ;
+            __HAL_I2S_DISABLE(&i2s_data->hi2s);
+        }
+    }
+
+    assert(i2s_data->hi2s.State == HAL_I2S_STATE_READY);
+    buffer = i2s_data->active_buffer;
+    if (buffer) {
+        i2s_data->active_buffer = NULL;
+        i2s_driver_buffer_put(i2s, buffer);
+    }
+    HAL_I2S_DeInit(&i2s_data->hi2s);
+    HAL_DMA_DeInit(i2s_data->hdma_spi);
+#if I2SEXT_PRESENT
+    if (i2s->direction == I2S_OUT_IN) {
+        HAL_DMA_DeInit(i2s_data->hdma_i2sext);
+    }
+#endif
+    return 0;
+}
+
+int
+i2s_driver_start(struct i2s *i2s)
+{
+    int rc = 0;
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)i2s->driver_data;
+
+    switch (i2s_data->hi2s.State) {
+    case HAL_I2S_STATE_RESET:
+        if (i2s->sample_rate) {
+            i2s_data->hi2s.Init.AudioFreq = i2s->sample_rate;
+        }
+        if (HAL_I2S_Init(&i2s_data->hi2s) != HAL_OK) {
+            rc = SYS_EUNKNOWN;
+            break;
+        }
+        if (HAL_DMA_Init(i2s_data->hdma_spi) != HAL_OK) {
+            (void)HAL_I2S_DeInit(&i2s_data->hi2s);
+            rc = SYS_EUNKNOWN;
+            break;
+        }
+#if I2SEXT_PRESENT
+        if (i2s->direction == I2S_OUT_IN) {
+            if (HAL_DMA_Init(i2s_data->hdma_i2sext) != HAL_OK) {
+                (void)HAL_DMA_DeInit(i2s_data->hdma_spi);
+                (void)HAL_I2S_DeInit(&i2s_data->hi2s);
+                rc = SYS_EUNKNOWN;
+                break;
+            }
+        }
+#endif
+    /* Fallthrough */
+    case HAL_I2S_STATE_READY:
+        assert(i2s_data->active_buffer == NULL);
+        i2s_data->active_buffer = i2s_driver_buffer_get(i2s);
+        assert(i2s_data->active_buffer);
+        if (i2s_data->active_buffer == NULL) {
+            i2s->state = I2S_STATE_OUT_OF_BUFFERS;
+            rc = I2S_ERR_NO_BUFFER;
+            break;
+        }
+        i2s->state = I2S_STATE_RUNNING;
+        if (i2s->direction == I2S_IN) {
+            i2s_data->active_buffer->sample_count = i2s_data->active_buffer->capacity;
+            HAL_I2S_Receive_DMA(&i2s_data->hi2s,
+                                i2s_data->active_buffer->sample_data,
+                                i2s_data->active_buffer->sample_count);
+        } else if (i2s->direction == I2S_OUT) {
+            HAL_I2S_Transmit_DMA(&i2s_data->hi2s,
+                                 i2s_data->active_buffer->sample_data,
+                                 i2s_data->active_buffer->sample_count);
+        } else {
+#if I2SEXT_PRESENT
+            HAL_I2SEx_TransmitReceive_DMA(&i2s_data->hi2s,
+                                          i2s_data->active_buffer->sample_data,
+                                          i2s_data->active_buffer->sample_data,
+                                          i2s_data->active_buffer->sample_count);
+#endif
+        }
+        break;
+    case HAL_I2S_STATE_BUSY:
+    case HAL_I2S_STATE_BUSY_RX:
+    case HAL_I2S_STATE_BUSY_TX:
+    case HAL_I2S_STATE_BUSY_TX_RX:
+        break;
+    default:
+        rc = I2S_ERR_INTERNAL;
+    }
+    return rc;
+}
+
+void
+i2s_driver_buffer_queued(struct i2s *i2s)
+{
+}
+
+int
+i2s_driver_suspend(struct i2s *i2s, os_time_t timeout, int arg)
+{
+    return OS_OK;
+}
+
+int
+i2s_driver_resume(struct i2s *i2s)
+{
+    return OS_OK;
+}
+
+bool
+i2s_out_is_active(struct i2s *i2s)
+{
+    struct stm32_i2s *i2s_data = (struct stm32_i2s *)i2s->driver_data;
+    return READ_BIT(i2s_data->hi2s.Instance->I2SCFGR, SPI_I2SCFGR_I2SE) != 0;
+}
+
+#define I2S_PIN_DEFINE(n, po, pi, af) \
+    const struct stm32_pin_cfg I2S ## n ## _P ## po ## pi = {    \
+        .pin = MCU_GPIO_PORT ## po(pi),                          \
+        .hal_init = {                                            \
+            .Pin = GPIO_PIN_ ## pi,                              \
+            .Mode = GPIO_MODE_AF_PP,                             \
+            .Pull = GPIO_NOPULL,                                 \
+            .Speed = GPIO_SPEED_FREQ_LOW,                        \
+            .Alternate = af,                                     \
+        }                                                        \
+    }
+
+#define I2S_CK_PIN_DEFINE(n, po, pi, af) \
+    const struct stm32_pin_cfg I2S ## n ## _CK_P ## po ## pi = { \
+        .pin = MCU_GPIO_PORT ## po(pi),                          \
+        .hal_init = {                                            \
+            .Pin = GPIO_PIN_ ## pi,                              \
+            .Mode = GPIO_MODE_AF_PP,                             \
+            .Pull = GPIO_NOPULL,                                 \
+            .Speed = GPIO_SPEED_FREQ_LOW,                        \
+            .Alternate = af,                                     \
+        }                                                        \
+    }
+
+#define I2S_WS_PIN_DEFINE(n, po, pi, af) \
+    const struct stm32_pin_cfg I2S ## n ## _WS_P ## po ## pi = { \
+        .pin = MCU_GPIO_PORT ## po(pi),                          \
+        .hal_init = {                                            \
+            .Pin = GPIO_PIN_ ## pi,                              \
+            .Mode = GPIO_MODE_AF_PP,                             \
+            .Pull = GPIO_NOPULL,                                 \
+            .Speed = GPIO_SPEED_FREQ_LOW,                        \
+            .Alternate = af,                                     \
+        }                                                        \
+    }
+
+#define I2S_SD_PIN_DEFINE(n, po, pi, af) \
+    const struct stm32_pin_cfg I2S ## n ## _SD_P ## po ## pi = { \
+        .pin = MCU_GPIO_PORT ## po(pi),                          \
+        .hal_init = {                                            \
+            .Pin = GPIO_PIN_ ## pi,                              \
+            .Mode = GPIO_MODE_AF_PP,                             \
+            .Pull = GPIO_NOPULL,                                 \
+            .Speed = GPIO_SPEED_FREQ_LOW,                        \
+            .Alternate = af,                                     \
+        }                                                        \
+    }
+
+/* I2S1 Possible CK pins */
+I2S_CK_PIN_DEFINE(1, A, 5, GPIO_AF5_SPI1);
+I2S_CK_PIN_DEFINE(1, B, 3, GPIO_AF5_SPI1);
+
+/* I2S1 possible WS pins */
+I2S_WS_PIN_DEFINE(1, A, 4, GPIO_AF5_SPI1);
+I2S_WS_PIN_DEFINE(1, A, 15, GPIO_AF5_SPI1);
+
+/* I2S1 possible SD pins */
+I2S_SD_PIN_DEFINE(1, B, 5, GPIO_AF5_SPI1);
+I2S_SD_PIN_DEFINE(1, A, 7, GPIO_AF5_SPI1);
+
+/* I2S2 Possible CKIN pins */
+I2S_PIN_DEFINE(2, A, 2, GPIO_AF5_SPI2);
+I2S_PIN_DEFINE(2, B, 11, GPIO_AF5_SPI2);
+I2S_PIN_DEFINE(2, C, 9, GPIO_AF5_SPI2);
+
+/* I2S2 Possible MCK pins */
+I2S_PIN_DEFINE(2, A, 3, GPIO_AF5_SPI2);
+I2S_PIN_DEFINE(2, A, 6, GPIO_AF6_SPI2);
+I2S_PIN_DEFINE(2, C, 6, GPIO_AF6_SPI2);
+
+/* I2S2 Possible CK pins */
+I2S_CK_PIN_DEFINE(2, B, 10, GPIO_AF5_SPI2);
+I2S_CK_PIN_DEFINE(2, B, 13, GPIO_AF5_SPI2);
+I2S_CK_PIN_DEFINE(2, C, 7, GPIO_AF5_SPI2);
+I2S_CK_PIN_DEFINE(2, D, 3, GPIO_AF5_SPI2);
+
+/* I2S2 possible WS pins */
+I2S_WS_PIN_DEFINE(2, B, 9, GPIO_AF5_SPI2);
+I2S_WS_PIN_DEFINE(2, B, 12, GPIO_AF5_SPI2);
+
+/* I2S2 possible SD pins */
+I2S_SD_PIN_DEFINE(2, B, 15, GPIO_AF5_SPI2);
+I2S_SD_PIN_DEFINE(2, C, 3, GPIO_AF5_SPI2);
+
+/* I2S2_ext possible SD pins */
+I2S_SD_PIN_DEFINE(2, B, 14, GPIO_AF6_SPI2);
+I2S_SD_PIN_DEFINE(2, C, 2, GPIO_AF6_SPI2);
+
+/* I2S3 possible CK pins */
+I2S_CK_PIN_DEFINE(3, B, 3, GPIO_AF6_SPI3);
+I2S_CK_PIN_DEFINE(3, C, 10, GPIO_AF6_SPI3);
+
+/* I2S3 possible WS pins */
+I2S_WS_PIN_DEFINE(3, A, 4, GPIO_AF6_SPI3);
+I2S_WS_PIN_DEFINE(3, A, 15, GPIO_AF6_SPI3);
+
+/* I2S3 possible SD pins */
+I2S_SD_PIN_DEFINE(3, B, 5, GPIO_AF6_SPI3);
+I2S_SD_PIN_DEFINE(3, C, 12, GPIO_AF6_SPI3);
+I2S_SD_PIN_DEFINE(3, D, 6, GPIO_AF5_SPI3);
+
+/* I2S3 possible MCK pins */
+I2S_PIN_DEFINE(3, B, 10, GPIO_AF6_SPI3);
+I2S_PIN_DEFINE(3, C, 7, GPIO_AF6_SPI3);
+
+/* I2S4 possible CK pins */
+I2S_CK_PIN_DEFINE(4, E, 2, GPIO_AF5_SPI4);
+I2S_CK_PIN_DEFINE(4, E, 12, GPIO_AF5_SPI4);
+I2S_CK_PIN_DEFINE(4, B, 13, GPIO_AF6_SPI4);
+
+/* I2S3_ext possible SD pins */
+I2S_SD_PIN_DEFINE(3, B, 4, GPIO_AF7_SPI3);
+I2S_SD_PIN_DEFINE(3, C, 11, GPIO_AF5_SPI3);
+
+/* I2S4 possible WS pins */
+I2S_WS_PIN_DEFINE(4, B, 12, GPIO_AF6_SPI4);
+I2S_WS_PIN_DEFINE(4, E, 4, GPIO_AF5_SPI4);
+I2S_WS_PIN_DEFINE(4, E, 11, GPIO_AF5_SPI4);
+
+/* I2S4 possible SD pins */
+I2S_SD_PIN_DEFINE(4, A, 1, GPIO_AF5_SPI4);
+I2S_SD_PIN_DEFINE(4, E, 6, GPIO_AF5_SPI4);
+I2S_SD_PIN_DEFINE(4, E, 14, GPIO_AF5_SPI4);
+
+/* I2S5 possible CK pins */
+I2S_CK_PIN_DEFINE(5, B, 0, GPIO_AF6_SPI5);
+I2S_CK_PIN_DEFINE(5, E, 2, GPIO_AF6_SPI5);
+I2S_CK_PIN_DEFINE(5, E, 12, GPIO_AF6_SPI5);
+
+/* I2S5 possible WS pins */
+I2S_WS_PIN_DEFINE(5, B, 1, GPIO_AF6_SPI5);
+I2S_WS_PIN_DEFINE(5, E, 4, GPIO_AF6_SPI5);
+I2S_WS_PIN_DEFINE(5, E, 11, GPIO_AF6_SPI5);
+
+/* I2S5 possible SD pins */
+I2S_SD_PIN_DEFINE(5, A, 10, GPIO_AF6_SPI5);
+I2S_SD_PIN_DEFINE(5, B, 8, GPIO_AF6_SPI5);
+I2S_SD_PIN_DEFINE(5, E, 6, GPIO_AF6_SPI5);
+I2S_SD_PIN_DEFINE(5, E, 14, GPIO_AF6_SPI5);
+
+#define DMA_STREAM_DEFINE(dma, ch, st, name) \
+    const struct stm32_dma_cfg name ## _stream ## st ## _channel ## ch = {  \
+        dma,                                                                \
+        DMA ## dma ## _Stream ## st ## _IRQn,                               \
+        DMA ## dma ## _Stream ## st,                                        \
+        DMA_CHANNEL_ ## ch,                                                 \
+    }
+
+DMA_STREAM_DEFINE(1, 0, 0, spi3_rx);
+DMA_STREAM_DEFINE(1, 0, 1, i2c1_tx);
+DMA_STREAM_DEFINE(1, 0, 2, spi3_rx);
+DMA_STREAM_DEFINE(1, 0, 3, spi2_rx);
+DMA_STREAM_DEFINE(1, 0, 4, spi2_tx);
+DMA_STREAM_DEFINE(1, 0, 5, spi3_tx);
+DMA_STREAM_DEFINE(1, 0, 7, spi3_tx);
+
+DMA_STREAM_DEFINE(1, 1, 0, i2c1_rx);
+DMA_STREAM_DEFINE(1, 1, 1, i2c3_rx);
+DMA_STREAM_DEFINE(1, 1, 2, tim7_up);
+DMA_STREAM_DEFINE(1, 1, 4, tim7_up);
+DMA_STREAM_DEFINE(1, 1, 5, i2c1_rx);
+DMA_STREAM_DEFINE(1, 1, 6, i2c1_tx);
+DMA_STREAM_DEFINE(1, 1, 7, i2c1_tx);
+
+DMA_STREAM_DEFINE(1, 2, 0, tim4_ch1);
+DMA_STREAM_DEFINE(1, 2, 2, i2s3_ext_rx);
+DMA_STREAM_DEFINE(1, 2, 3, tim4_ch2);
+DMA_STREAM_DEFINE(1, 2, 4, i2s2_ext_tx);
+DMA_STREAM_DEFINE(1, 2, 5, i2s3_ext_tx);
+DMA_STREAM_DEFINE(1, 2, 6, tim4_up);
+DMA_STREAM_DEFINE(1, 2, 7, tim4_ch3);
+
+DMA_STREAM_DEFINE(1, 3, 0, i2s3_ext_rx);
+DMA_STREAM_DEFINE(1, 3, 1, tim2_up);
+DMA_STREAM_DEFINE(1, 3, 1, tim2_ch3);
+DMA_STREAM_DEFINE(1, 3, 2, i2c3_rx);
+DMA_STREAM_DEFINE(1, 3, 3, i2s2_ext_rx);
+DMA_STREAM_DEFINE(1, 3, 4, i2c3_tx);
+DMA_STREAM_DEFINE(1, 3, 5, tim2_ch1);
+DMA_STREAM_DEFINE(1, 3, 6, tim2_ch2);
+DMA_STREAM_DEFINE(1, 3, 6, tim2_ch4);
+DMA_STREAM_DEFINE(1, 3, 7, tim2_up);
+DMA_STREAM_DEFINE(1, 3, 7, tim2_ch4);
+
+DMA_STREAM_DEFINE(1, 4, 0, uart5_rx);
+DMA_STREAM_DEFINE(1, 4, 1, usart3_rx);
+DMA_STREAM_DEFINE(1, 4, 2, uart4_rx);
+DMA_STREAM_DEFINE(1, 4, 3, usart3_tx);
+DMA_STREAM_DEFINE(1, 4, 4, uart4_tx);
+DMA_STREAM_DEFINE(1, 4, 5, usart2_rx);
+DMA_STREAM_DEFINE(1, 4, 6, usart2_tx);
+DMA_STREAM_DEFINE(1, 4, 7, uart5_tx);
+
+DMA_STREAM_DEFINE(2, 0, 0, adc1);
+DMA_STREAM_DEFINE(2, 0, 1, sai1_a);
+DMA_STREAM_DEFINE(2, 0, 2, tim8_ch1);
+DMA_STREAM_DEFINE(2, 0, 2, tim8_ch2);
+DMA_STREAM_DEFINE(2, 0, 2, tim8_ch3);
+DMA_STREAM_DEFINE(2, 0, 3, sai1_a);
+DMA_STREAM_DEFINE(2, 0, 4, adc1);
+DMA_STREAM_DEFINE(2, 0, 5, sai1_b);
+DMA_STREAM_DEFINE(2, 0, 6, tim1_ch1);
+DMA_STREAM_DEFINE(2, 0, 6, tim1_ch2);
+DMA_STREAM_DEFINE(2, 0, 6, tim1_ch3);
+
+DMA_STREAM_DEFINE(2, 1, 1, dcmi);
+DMA_STREAM_DEFINE(2, 1, 2, adc2);
+DMA_STREAM_DEFINE(2, 1, 3, adc2);
+DMA_STREAM_DEFINE(2, 1, 4, sai1_b);
+DMA_STREAM_DEFINE(2, 1, 5, spi6_tx);
+DMA_STREAM_DEFINE(2, 1, 6, spi6_rx);
+DMA_STREAM_DEFINE(2, 1, 7, dcmi);
+
+DMA_STREAM_DEFINE(2, 2, 0, adc3);
+DMA_STREAM_DEFINE(2, 2, 1, adc3);
+DMA_STREAM_DEFINE(2, 2, 2, spi1_tx);
+DMA_STREAM_DEFINE(2, 2, 3, spi5_rx);
+DMA_STREAM_DEFINE(2, 2, 4, spi5_tx);
+DMA_STREAM_DEFINE(2, 2, 5, cryp_out);
+DMA_STREAM_DEFINE(2, 2, 6, cryp_in);
+DMA_STREAM_DEFINE(2, 2, 7, hash_in);
+
+DMA_STREAM_DEFINE(2, 3, 0, spi1_rx);
+DMA_STREAM_DEFINE(2, 3, 2, spi1_rx);
+DMA_STREAM_DEFINE(2, 3, 3, spi1_tx);
+DMA_STREAM_DEFINE(2, 3, 5, spi1_tx);
+
+DMA_STREAM_DEFINE(2, 4, 0, spi4_rx);
+DMA_STREAM_DEFINE(2, 4, 1, spi4_tx);
+DMA_STREAM_DEFINE(2, 4, 2, usart1_rx);
+DMA_STREAM_DEFINE(2, 4, 3, sdio);
+DMA_STREAM_DEFINE(2, 4, 4, spi4_rx);
+DMA_STREAM_DEFINE(2, 4, 5, usart1_rx);
+DMA_STREAM_DEFINE(2, 4, 6, sdio);
+DMA_STREAM_DEFINE(2, 4, 7, usart1_tx);
+
+DMA_STREAM_DEFINE(2, 5, 1, usart6_rx);
+DMA_STREAM_DEFINE(2, 5, 2, usart6_rx);
+DMA_STREAM_DEFINE(2, 5, 3, spi4_rx);
+DMA_STREAM_DEFINE(2, 5, 4, spi4_tx);
+DMA_STREAM_DEFINE(2, 5, 5, spi5_tx);
+DMA_STREAM_DEFINE(2, 5, 6, usart6_tx);
+DMA_STREAM_DEFINE(2, 5, 7, usart6_tx);
+
+DMA_STREAM_DEFINE(2, 6, 0, tim1_trig);
+DMA_STREAM_DEFINE(2, 6, 1, tim1_ch1);
+DMA_STREAM_DEFINE(2, 6, 2, tim1_ch2);
+DMA_STREAM_DEFINE(2, 6, 3, tim1_ch1);
+DMA_STREAM_DEFINE(2, 6, 4, tim1_ch4);
+DMA_STREAM_DEFINE(2, 6, 4, tim1_trig);
+DMA_STREAM_DEFINE(2, 6, 4, tim1_com);
+DMA_STREAM_DEFINE(2, 6, 5, tim1_up);
+DMA_STREAM_DEFINE(2, 6, 6, tim1_ch3);
+
+DMA_STREAM_DEFINE(2, 7, 1, tim8_up);
+DMA_STREAM_DEFINE(2, 7, 2, tim8_ch1);
+DMA_STREAM_DEFINE(2, 7, 3, tim8_ch2);
+DMA_STREAM_DEFINE(2, 7, 4, tim8_ch3);
+DMA_STREAM_DEFINE(2, 7, 5, spi5_rx);
+DMA_STREAM_DEFINE(2, 7, 6, spi5_tx);
+DMA_STREAM_DEFINE(2, 7, 7, tim8_ch4);
+DMA_STREAM_DEFINE(2, 7, 7, tim8_trig);
+DMA_STREAM_DEFINE(2, 7, 7, tim8_com);
+
+#define SPI_CFG_DEFINE(n) \
+    struct stm32_spi_cfg spi ## n ## _cfg = {                   \
+        .spi_num = n,                                           \
+        .spi = SPI ## n,                                        \
+        .i2s_irq = SPI ## n ## _IRQn,                           \
+        .driver_data = &stm32_i2s ## n,                         \
+        .irq_handler = i2s ## n ## _irq_handler,                \
+        .i2s_dma_handler = i2s ## n ## _dma_stream_irq_handler, \
+        .hdma_spi = &hdma_spi ## n,                             \
+        .enable_clock = i2s ## n ## _clock_enable,              \
+    }
+
+#define I2S_CFG_DEFINE(n) \
+    struct stm32_spi_cfg spi ## n ## _cfg = {                         \
+        .spi_num = n,                                                 \
+        .spi = SPI ## n,                                              \
+        .i2s_irq = SPI ## n ## _IRQn,                                 \
+        .driver_data = &stm32_i2s ## n,                               \
+        .irq_handler = i2s ## n ## _irq_handler,                      \
+        .i2s_dma_handler = i2s ## n ## _dma_stream_irq_handler,       \
+        .i2sext_dma_handler = i2s ## n ## ext_dma_stream_irq_handler, \
+        .hdma_spi = &hdma_spi ## n,                                   \
+        .hdma_i2sext = &hdma_i2s ## n ## ext,                         \
+        .enable_clock = i2s ## n ## _clock_enable,                    \
+    }
+
+#ifdef SPI1
+static DMA_HandleTypeDef hdma_spi1;
+SPI_CFG_DEFINE(1);
+#endif
+#ifdef SPI2
+static DMA_HandleTypeDef hdma_spi2;
+#ifdef I2S2ext
+static DMA_HandleTypeDef hdma_i2s2ext;
+I2S_CFG_DEFINE(2);
+#else
+SPI_CFG_DEFINE(2);
+#endif
+#endif
+#ifdef SPI3
+static DMA_HandleTypeDef hdma_spi3;
+#ifdef I2S3ext
+static DMA_HandleTypeDef hdma_i2s3ext;
+I2S_CFG_DEFINE(3);
+#else
+SPI_CFG_DEFINE(3);
+#endif
+#endif
+#ifdef SPI4
+static DMA_HandleTypeDef hdma_spi4;
+SPI_CFG_DEFINE(4);
+#endif
+#ifdef SPI5
+static DMA_HandleTypeDef hdma_spi5;
+SPI_CFG_DEFINE(5);
+#endif
diff --git a/hw/drivers/i2s/i2s_stm32f4/syscfg.yml b/hw/drivers/i2s/i2s_stm32f4/syscfg.yml
new file mode 100644
index 0000000..2cdd574
--- /dev/null
+++ b/hw/drivers/i2s/i2s_stm32f4/syscfg.yml
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:


[mynewt-core] 01/03: hw/drivers/i2s: Add I2S API

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit bfe7d7f7527e656d9f4b448f15c14d71051fbc7f
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Fri Mar 27 10:24:53 2020 +0100

    hw/drivers/i2s: Add I2S API
    
    This adds API that can be used to stream
    data through I2S interface.
---
 hw/drivers/i2s/README.md                | 224 ++++++++++++++++++++++++++
 hw/drivers/i2s/include/i2s/i2s.h        | 270 ++++++++++++++++++++++++++++++++
 hw/drivers/i2s/include/i2s/i2s_driver.h |  43 +++++
 hw/drivers/i2s/pkg.yml                  |  27 ++++
 4 files changed, 564 insertions(+)

diff --git a/hw/drivers/i2s/README.md b/hw/drivers/i2s/README.md
new file mode 100644
index 0000000..492234e
--- /dev/null
+++ b/hw/drivers/i2s/README.md
@@ -0,0 +1,224 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+# I2S interface (Inter-IC Sound)
+
+# Overview
+
+Inter-IC Sound is interface for sending digital audio data.
+
+Data send to and received from I2S device is grouped in sample buffers.
+When user code wants to send audio data to I2S device (speaker), it first
+request buffer from I2S device, fills it with samples and send it back to the
+device. When data is transmitted out of the system to external IC, buffer
+become available to the user core and can be obtained again.
+
+For input devices like digital microphone, correctly configured i2s device has
+several buffers that are filled with incoming data.
+User code requests sample buffer and gets buffers filled with data, user code
+can process this data and must return buffer back to the i2s device.
+
+General flow is to get buffers from i2s device fill them for outgoing transmission
+(or interpret samples for incoming transmission) and send buffer back to the device. 
+
+# API
+
+#### Device creation
+
+```i2s_create(i2s, "name", cfg)```
+
+Function creates I2S device with specified name and configuration.
+Note that cfg argument is not defined in **api.h** but by driver package that must be present in build.
+#### Device access
+
+`i2s_open("dev_name", timout, client)`
+
+Opens I2S interface. It can be input (microphone) or output (speaker) interface.
+
+`i2s_close()`
+
+Closes I2S interface, opened with `i2s_open()`
+
+#### Start/stop device operation
+
+`i2s_start()`
+
+Starts I2S device operation, for input, device starts collecting samples to internal buffers.
+For output, samples are streamed out of device if they are already sent to device with `i2s_buffer_put()`.
+* NOTE: this function must be called explicitly for input i2s device.
+
+`i2s_stop()`
+
+Stops sending or receiving samples. 
+
+#### High level read and write
+It is possible to use simple blocking functions to read from I2S input device or write to I2S output device.
+
+`i2s_write(i2s, data, size)`
+
+This function write user provided data to the output I2S device. It returns number of bytes written.
+Return value will in most cases will be less then requested since data is written in internal buffer size chunks.
+
+`i2s_read(i2s, data_buffer, size)`
+Read data from microphone to user provided buffer. Function returns actual number of bytes read. Return value
+may be less then size because it will most likely be truncated to internal buffer size. 
+
+#### Sample buffers
+
+I2S software device needs at least 2 buffers for seamless sample streaming.
+
+`i2s_buffer_get()`
+
+Returns a buffer with samples taken from the I2S input device.
+For output device it will return a buffer that should be filled with samples by user code and then passed back to the device.
+
+`i2s_buffer_put()`
+
+Sends sample to output I2S device. For input device it simply returns buffer so it can be reused for next
+incoming samples.
+
+- For output device user code gets a buffers from the i2s device with `i2s_buffer_get()`, then fills the buffer with
+new samples, then sends the buffer with samples back to the device using `i2s_buffer_put()`.
+- For input device after user code starts sampling operation calling `i2s_start()` it must wait for collected samples.
+Simplest way to wait is to call `i2s_buffer_get(i2s, OS_WAIT_FOREVER)` that will block till samples are available
+and returns buffer full of data.
+Once application does something with the samples it should immediately call `i2s_buffer_put()` to return buffer back
+to the driver so next samples can be collected.
+- It is possible to have I2S created without internal buffers. In that case user code can create buffers and pass
+them to the driver using `i2s_buffer_put()`. Such buffers would then be available when processed by the driver and
+could be taken back with `i2s_buffer_get()`. If user provided callback function `sample_buffer_ready_cb` is called
+from interrupt context and return value other then 0, buffer will not be put in internal queue and can not be obtained
+with `i2s_buffer_get()`.
+
+Buffer pool can be created by `I2S_BUFFER_POOL_DEF()` macro
+
+
+Definition ```I2S_BUFFER_POOL_DEF(my_pool, 2, 512);``` would create following structure in memory:
+
+```
+
+my_pool_buffers: +----------------------------+
+                 | i2s_sample_buffer[0]       |<---------+
+                 |               sample_data  |------+   |
+                 + - - - - - - - - - - - - - -+      |   |
+                 | i2s_sample_buffer[1]       |      |   |
+            +----|               sample_data  |      |   |
+            |    +----------------------------+      |   |
+            |    | buffer0[0]                 |<-----+   |
+            |    | ....                       |          |
+            |    | buffer0[511]               |          |
+            |    +----------------------------|          |
+            +--->| buffer1[0]                 |          |
+                 | ....                       |          |
+                 | buffer1[511]               |          |
+                 +----------------------------|          |
+                                                         |
+my_pool:         +----------------------------+          |
+                 | buf_size: 512              |          |
+                 | buf_count: 2               |          |
+                 | buffers:                   |----------+
+                 +----------------------------+
+```
+# Usage examples
+
+#####1. Simple code that streams data to output I2S using own task
+
+````c
+int total_data_to_write = ....
+uint8_t *data_ptr = ....
+
+struct i2s *spkr;
+int data_written;
+
+spekr = i2s_open("speaker", OS_WAIT_FOREVER, NULL);
+
+while (total_data_to_write) {
+    data_written = i2s_write(spkr, data_ptr, total_data_to_write);
+    total_data_to_write -= data_written;
+    data_ptr += total_data_to_write;
+}
+i2s_close(spkr);
+````
+#####2. Blocking read from microphone example
+````c
+int total_data_to_read = ....
+uint8_t *data_ptr = ....
+
+struct i2s *mic;
+int data_read;
+
+spekr = i2s_open("mic", OS_WAIT_FOREVER, NULL);
+
+while (total_data_to_read) {
+    data_read = i2s_read(mic, data_ptr, total_data_to_read);
+    total_data_to_read -= data_read;
+    data_ptr += data_read;
+}
+i2s_close(spkr);
+````
+#####3. Reading without dedicated task
+Example shows how to read I2S data stream without blocking calls.
+````c
+
+struct i2s mic;
+
+void
+buffer_ready_event_cb(struct os_event *ev)
+{
+    struct i2s_sample_buffer *buffer;
+
+    /* This function is called so there is buffer ready. i2s_buffer_get() could be called
+     * with timeout 0 */
+    buffer = i2s_buffer_get(mic, OS_WAIT_FOREVER);
+    /* Handle microphone data */
+    ...
+    /* Microphone samples processed, send buffer back to i2s device */
+    i2s_buffer_put(mic, buffer);
+}
+
+struct os_event buffer_ready_event = {
+    .ev_cb = buffer_ready_event_cb,
+};
+
+/* Function called from interrupt telling client that new buffer with samples is available */
+static int
+more_data(struct i2s *i2s, struct i2s_sample_buffer *sample_buffer)
+{
+    /* Handle incoming data in main task queue */
+    os_eventq_put(os_eventq_dflt_get(), &buffer_ready_event);
+
+    return 0;
+}
+
+static void state_changed(struct i2s *i2s, enum i2s_state state) {}
+
+struct i2s_client client = {
+    .sample_buffer_ready_cb = more_data,
+    .state_changed_cb = state_changed,
+};
+
+void start_microphone(void)
+{
+    mic = i2s_open("mic", OS_WAIT_FOREVER, client);
+    /* I2S input device is not started when device is opened */
+    i2s_start(mic);
+}
+````
\ No newline at end of file
diff --git a/hw/drivers/i2s/include/i2s/i2s.h b/hw/drivers/i2s/include/i2s/i2s.h
new file mode 100644
index 0000000..aefc1f5
--- /dev/null
+++ b/hw/drivers/i2s/include/i2s/i2s.h
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _HW_DRIVERS_I2S_H
+#define _HW_DRIVERS_I2S_H
+
+#include <stdint.h>
+#include <os/os_dev.h>
+
+/*
+ * I2S API does not specify this structure. It is used by i2s_create() and is defined
+ * by specific driver.
+ */
+struct i2s_cfg;
+
+/**
+ * Buffer for passing data between user code and i2s driver
+ */
+struct i2s_sample_buffer {
+    /* Internal use */
+    STAILQ_ENTRY(i2s_sample_buffer) next_buffer;
+    /** Actual sample data pointer */
+    void *sample_data;
+    /**
+     * Number of samples that buffer can hold
+     * This value is used for input I2S by driver.
+     */
+    uint32_t capacity;
+    /**
+     * Actual number of samples in buffer.
+     * This value is used when buffer is filled with samples.
+     * For output i2s user code fills this value.
+     * For input i2s driver fills this value.
+     */
+    uint32_t sample_count;
+};
+
+struct i2s_buffer_pool {
+    uint16_t buffer_size;
+    uint16_t buffer_count;
+    struct i2s_sample_buffer *buffers;
+};
+
+/**
+ * I2S buffer pool definition.
+ * @param name   pool name to be used,
+ * @param count  number of buffers to initialize
+ * @param size   single buffer size in bytes
+ */
+#define I2S_BUFFER_POOL_DEF(name, count, size) \
+    static uint8_t _Alignas(struct i2s_buffer_pool) name ## _buffers[(sizeof(struct i2s_sample_buffer) + size) * \
+                                                                     count]; \
+    struct i2s_buffer_pool name = { \
+        .buffer_size = size, \
+        .buffer_count = count, \
+        .buffers = (struct i2s_sample_buffer *)name ## _buffers \
+    }
+#define I2S_BUFFER_POOL(name) ((struct i2s_buffer_pool *)(&name))
+
+enum i2s_state {
+    I2S_STATE_STOPPED,
+    I2S_STATE_OUT_OF_BUFFERS,
+    I2S_STATE_RUNNING,
+};
+
+enum i2s_direction {
+    I2S_INVALID,
+    I2S_OUT,
+    I2S_IN,
+    I2S_OUT_IN,
+};
+
+/**
+ * I2S device
+ */
+struct i2s {
+    struct os_dev dev;
+    void *driver_data;
+    struct i2s_buffer_pool *buffer_pool;
+
+    /* Internal queues not for user code */
+    STAILQ_HEAD(, i2s_sample_buffer) user_queue;
+    STAILQ_HEAD(, i2s_sample_buffer) driver_queue;
+    /* Semaphore holding number of elements in user queue */
+    struct os_sem user_queue_buffer_count;
+
+    struct i2s_client *client;
+    /* Samples per second. */
+    uint32_t sample_rate;
+    uint8_t sample_size_in_bytes;
+    enum i2s_direction direction;
+    enum i2s_state state;
+};
+
+#define I2S_OK                  0
+#define I2S_ERR_NO_BUFFER       -1
+#define I2S_ERR_INTERNAL        -2
+
+/**
+ * Function that will be called from interrupt after sample_buffer if processed by I2S driver.
+ * For output I2S it will be called when last sample was sent out.
+ * For input I2S it will be called when whole buffer is filled with samples.
+ *
+ * @return 0 - buffer should be queued in i2s device for further usage.
+ *         1 - buffer will not be stored in i2s. This is useful when buffers are provided for
+ *             data playback from FLASH and once they are processed sent out they are not be used
+ *             any more. That way output I2S device may work without any RAM buffer.
+ */
+typedef int (*i2s_sample_buffer_ready_t)(struct i2s *i2s, struct i2s_sample_buffer *sample_buffer);
+
+/**
+ * Function will be called (possibly from interrupt context) when driver state changes.
+ * For practical reason it can be useful to know when I2S stream was stopped because
+ * it run out of buffers (I2S_STATE_OUT_OF_BUFFERS).
+ */
+typedef void (*i2s_state_change_t)(struct i2s *i2s, enum i2s_state state);
+
+/**
+ * Client interface.
+ */
+struct i2s_client {
+    /** Requested sample rate */
+    uint32_t sample_rate;
+    /** Function called when I2S state changes */
+    i2s_state_change_t state_changed_cb;
+    /** Function called when buffer is ready and i2s_buffer_get() will succeed */
+    i2s_sample_buffer_ready_t sample_buffer_ready_cb;
+};
+
+/**
+ * Creates I2S device with given name and configuration.
+ *
+ * @param i2s   device to crate
+ * @param name  name of the i2s device that can be used in i2s_open()
+ * @param cfg   device specific configuration
+ *
+ * @return OS_OK when device was created successfully, non zero on failure.
+ */
+int i2s_create(struct i2s *i2s, const char *name, const struct i2s_cfg *cfg);
+
+/**
+ * Open i2s device
+ *
+ * @param name        device name to open
+ * @param timeout     timeout for open
+ * @param client      structure with user callbacks, can be NULL
+ *
+ * @return pointer ot i2s device on success, NULL on failure
+ */
+struct i2s *i2s_open(const char *name, uint32_t timeout, struct i2s_client *client);
+
+/**
+ * Convenience function for closing i2s device
+ *
+ * @param i2s        device to close
+ *
+ * @return 0 on success, non zero on failure
+ */
+int i2s_close(struct i2s *i2s);
+
+/**
+ * Start i2s device operation
+ *
+ * For i2s input device, it will start filling buffers that were queued.
+ *
+ * @param i2s   device to start
+ *
+ * @return 0 on success, I2S_STATE_OUT_OF_BUFFERS is device is paused due to missing buffers
+ */
+int i2s_start(struct i2s *i2s);
+
+/**
+ * Stop i2s device operation
+ *
+ * @param i2s   device to stop
+ *
+ * @return 0 on success, other values in the future
+ */
+int i2s_stop(struct i2s *i2s);
+
+/**
+ * High level function to write samples to I2S device.
+ * Function is intended to be used from tasks. It will block if sample buffers are
+ * not ready yet.
+ *
+ * @param i2s                 device to send samples to
+ * @param samples             sample buffer
+ * @param sample_buffer_size  size of sample buffers in bytes
+ *
+ * @return number of bytes consumed, it may be less then sample_buffer_size
+ */
+int i2s_write(struct i2s *i2s, void *samples, uint32_t sample_buffer_size);
+
+/**
+ * High level function to read samples from I2S device.
+ * Function is intended to be used from tasks. It will block if sample buffers are
+ * not ready yet.
+ *
+ * @param i2s                 device to read samples from
+ * @param samples             buffer to be filled with samples
+ * @param sample_buffer_size  size of sample buffers in bytes
+ *
+ * @return number of bytes read, it may be less then sample_buffer_size.
+ */
+int i2s_read(struct i2s *i2s, void *samples, uint32_t sample_buffer_size);
+
+/**
+ * Return number of buffers that can be acquired by application without blocking
+ *
+ * @param i2s   device to check number of buffers from
+ *
+ * @return number of buffers that can be get without blocking, negative value on error
+ */
+int i2s_available_buffers(struct i2s *i2s);
+
+/**
+ * Dequeue buffer from I2S
+ *
+ * For output I2S (speaker), function will return buffer that user code will
+ * fill with samples and then pass to same i2s to be played with i2s_buffer_put.
+ *
+ * For input I2S (microphone), function will block and receive samples that were
+ * recorded. Once samples are processed user code should enqueue buffer to receive
+ * more samples.
+ *
+ * @param i2s       interface to use (speaker of microphone)
+ * @param timeout   time to wait for buffer to be ready
+ *
+ * @return pointer  to sample buffer with data (for microphone), with no data (for
+ *                  speaker). NULL if there was buffer available in specified time.
+ */
+struct i2s_sample_buffer *i2s_buffer_get(struct i2s *i2s, os_time_t timeout);
+
+/**
+ * Add/return buffer to I2S
+ *
+ * For output I2S buffer contains sample that should be sent to I2S device.
+ * For input I2S buffer will be filled with incoming data
+ * samples.
+ *
+ * @param i2s     interface to use
+ * @param buffer buffer with samples to be played for output I2S,
+ *                buffer for incoming samples for input I2S.
+ * @return 0 on success, non 0 on failure
+ */
+int i2s_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer);
+
+static inline uint32_t
+i2s_get_sample_rate(struct i2s *i2s)
+{
+    return i2s->sample_rate;
+}
+
+#endif /* _HW_DRIVERS_I2S_H */
diff --git a/hw/drivers/i2s/include/i2s/i2s_driver.h b/hw/drivers/i2s/include/i2s/i2s_driver.h
new file mode 100644
index 0000000..e20ab2d
--- /dev/null
+++ b/hw/drivers/i2s/include/i2s/i2s_driver.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _HW_DRIVERS_I2S_DRIVER_H
+#define _HW_DRIVERS_I2S_DRIVER_H
+
+struct i2s;
+struct i2s_cfg;
+struct i2s_sample_buffer;
+
+/* Functions to be implemented by driver */
+
+int i2s_create(struct i2s *i2s, const char *name, const struct i2s_cfg *cfg);
+
+int i2s_driver_start(struct i2s *i2s);
+int i2s_driver_stop(struct i2s *i2s);
+void i2s_driver_buffer_queued(struct i2s *i2s);
+int i2s_driver_suspend(struct i2s *i2s, os_time_t timeout, int arg);
+int i2s_driver_resume(struct i2s *i2s);
+
+/* Functions to be called by driver code */
+int i2s_init(struct i2s *i2s, struct i2s_buffer_pool *pool);
+void i2s_driver_state_changed(struct i2s *i2s, enum i2s_state);
+void i2s_driver_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer);
+struct i2s_sample_buffer *i2s_driver_buffer_get(struct i2s *i2s);
+
+#endif /* _HW_DRIVERS_I2S_DRIVER_H */
diff --git a/hw/drivers/i2s/pkg.yml b/hw/drivers/i2s/pkg.yml
new file mode 100644
index 0000000..01b6ad6
--- /dev/null
+++ b/hw/drivers/i2s/pkg.yml
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/drivers/i2s
+pkg.description: I2S device insterace
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+pkg.deps:
+pkg.req_apis:
+    - I2S_HW_IMPL


[mynewt-core] 02/03: hw/drivers/i2s: Add I2S device implementation

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 6eac7e5002a49dcbfc695fba3471bee610a854f9
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Fri Mar 27 13:17:56 2020 +0100

    hw/drivers/i2s: Add I2S device implementation
    
    This adds common code for I2S device handling.
---
 hw/drivers/i2s/src/i2s.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 378 insertions(+)

diff --git a/hw/drivers/i2s/src/i2s.c b/hw/drivers/i2s/src/i2s.c
new file mode 100644
index 0000000..4d430bd
--- /dev/null
+++ b/hw/drivers/i2s/src/i2s.c
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+
+#include <os/os_eventq.h>
+#include <os/os_sem.h>
+
+#include <i2s/i2s.h>
+#include <i2s/i2s_driver.h>
+
+/* Function called from i2s_open/os_dev_open */
+static int
+i2s_open_handler(struct os_dev *dev, uint32_t timout, void *arg)
+{
+    struct i2s *i2s;
+    struct i2s_client *client = (struct i2s_client *)arg;
+    struct i2s_sample_buffer *buffer;
+
+    if (dev->od_flags & OS_DEV_F_STATUS_OPEN) {
+        return OS_EBUSY;
+    }
+
+    i2s = (struct i2s *)dev;
+
+    assert(client == NULL ||
+           (client->sample_buffer_ready_cb != NULL &&
+            client->state_changed_cb != NULL));
+    i2s->client = client;
+    if (client && client->sample_rate) {
+        i2s->sample_rate = client->sample_rate;
+    }
+
+    if (i2s->direction == I2S_IN) {
+        while (NULL != (buffer = i2s_buffer_get(i2s, 0))) {
+            i2s_buffer_put(i2s, buffer);
+        }
+    } else {
+        i2s_start(i2s);
+    }
+
+    return OS_OK;
+}
+
+/* Function called from i2s_close/os_dev_close */
+static int
+i2s_close_handler(struct os_dev *dev)
+{
+    struct i2s *i2s;
+
+    i2s = (struct i2s *)dev;
+    i2s_stop(i2s);
+    i2s->client = NULL;
+
+    return OS_OK;
+}
+
+static int
+i2s_suspend_handler(struct os_dev *dev, os_time_t timeout, int arg)
+{
+    return i2s_driver_suspend((struct i2s *)dev, timeout, arg);
+}
+
+static int
+i2s_resume_handler(struct os_dev *dev)
+{
+    return i2s_driver_resume((struct i2s *)dev);
+}
+
+static void
+i2s_add_to_user_queue(struct i2s *i2s, struct i2s_sample_buffer *buffer)
+{
+    STAILQ_INSERT_TAIL(&i2s->user_queue, buffer, next_buffer);
+    os_sem_release(&i2s->user_queue_buffer_count);
+}
+
+static void
+i2s_add_to_driver_queue(struct i2s *i2s, struct i2s_sample_buffer *buffer)
+{
+    STAILQ_INSERT_TAIL(&i2s->driver_queue, buffer, next_buffer);
+    if (i2s->state != I2S_STATE_STOPPED) {
+        i2s_driver_buffer_queued(i2s);
+    }
+}
+
+static void
+i2s_buffers_from_pool(struct i2s *i2s, struct i2s_buffer_pool *pool)
+{
+    int i;
+    int sr;
+    struct i2s_sample_buffer *buffers;
+    uintptr_t sample_data;
+    uint32_t samples_per_buffer;
+
+    if (i2s->direction != I2S_IN && pool != NULL) {
+        os_sem_init(&i2s->user_queue_buffer_count, pool->buffer_count);
+    } else {
+        os_sem_init(&i2s->user_queue_buffer_count, 0);
+    }
+
+    i2s->buffer_pool = pool;
+    if (pool == NULL) {
+        return;
+    }
+
+    buffers = (struct i2s_sample_buffer *)pool->buffers;
+
+    samples_per_buffer = pool->buffer_size / i2s->sample_size_in_bytes;
+    sample_data = (uintptr_t)&buffers[pool->buffer_count];
+
+    for (i = 0; i < pool->buffer_count; ++i) {
+        buffers[i].capacity = samples_per_buffer;
+        buffers[i].sample_data = (void *)sample_data;
+        buffers[i].sample_count = 0;
+        sample_data += pool->buffer_size;
+
+        OS_ENTER_CRITICAL(sr);
+        if (i2s->direction == I2S_IN) {
+            i2s_add_to_driver_queue(i2s, &buffers[i]);
+        } else {
+            STAILQ_INSERT_TAIL(&i2s->user_queue, &buffers[i], next_buffer);
+        }
+        OS_EXIT_CRITICAL(sr);
+    }
+}
+
+int
+i2s_init(struct i2s *i2s, struct i2s_buffer_pool *pool)
+{
+    STAILQ_INIT(&i2s->driver_queue);
+    STAILQ_INIT(&i2s->user_queue);
+
+    i2s->state = I2S_STATE_STOPPED;
+
+    i2s_buffers_from_pool(i2s, pool);
+
+    i2s->dev.od_handlers.od_open = i2s_open_handler;
+    i2s->dev.od_handlers.od_close = i2s_close_handler;
+    i2s->dev.od_handlers.od_suspend = i2s_suspend_handler;
+    i2s->dev.od_handlers.od_resume = i2s_resume_handler;
+
+    return 0;
+}
+
+struct i2s *
+i2s_open(const char *name, uint32_t timeout, struct i2s_client *client)
+{
+    return (struct i2s *)os_dev_open(name, timeout, client);
+}
+
+int
+i2s_close(struct i2s *i2s)
+{
+    return os_dev_close(&i2s->dev);
+}
+
+int
+i2s_write(struct i2s *i2s, void *samples, uint32_t sample_buffer_size)
+{
+    uint32_t sample_pair_size = (i2s->sample_size_in_bytes * 2);
+    size_t sample_count;
+    struct i2s_sample_buffer *buffer;
+
+    assert(i2s->direction == I2S_OUT);
+
+    buffer = i2s_buffer_get(i2s, OS_WAIT_FOREVER);
+
+    if (buffer == NULL) {
+        return -1;
+    }
+
+    /* Calculate buffer size */
+    sample_count = sample_buffer_size / sample_pair_size;
+    if (buffer->capacity < sample_count) {
+        sample_count = buffer->capacity;
+    }
+
+    sample_buffer_size = sample_count * sample_pair_size;
+
+    /* Move data to buffer */
+    memcpy(buffer->sample_data, samples, sample_buffer_size);
+
+    /* Pass buffer to output device */
+    i2s_buffer_put(i2s, buffer);
+
+    return sample_buffer_size;
+}
+
+int
+i2s_read(struct i2s *i2s, void *samples, uint32_t sample_buffer_size)
+{
+    int sr;
+    uint32_t sample_pair_size = (i2s->sample_size_in_bytes * 2);
+    size_t sample_capacity = sample_buffer_size / sample_pair_size;
+    struct i2s_sample_buffer *buffer;
+
+    assert(i2s->direction == I2S_IN);
+
+    if (i2s->state == I2S_STATE_STOPPED) {
+        i2s_start(i2s);
+    }
+
+    buffer = i2s_buffer_get(i2s, OS_WAIT_FOREVER);
+
+    if (buffer == NULL) {
+        return 0;
+    }
+
+    if (sample_capacity > buffer->sample_count) {
+        sample_capacity = buffer->sample_count;
+    }
+    sample_buffer_size = sample_capacity * sample_pair_size;
+    memcpy(samples, buffer->sample_data, sample_buffer_size);
+    if (sample_capacity < buffer->sample_count) {
+        /* Not all data consumed, modify buffer and put buffer at the front again */
+        memmove(buffer->sample_data, buffer->sample_data,
+                (buffer->sample_count - sample_capacity) * sample_pair_size);
+        OS_ENTER_CRITICAL(sr);
+        STAILQ_INSERT_HEAD(&i2s->user_queue, buffer, next_buffer);
+        OS_EXIT_CRITICAL(sr);
+    }
+
+    return sample_buffer_size;
+}
+
+int
+i2s_start(struct i2s *i2s)
+{
+    int rc = I2S_OK;
+
+    if (i2s->state != I2S_STATE_RUNNING) {
+        if (STAILQ_EMPTY(&i2s->driver_queue)) {
+            i2s->state = I2S_STATE_OUT_OF_BUFFERS;
+            rc = I2S_ERR_NO_BUFFER;
+        } else {
+            rc = i2s_driver_start(i2s);
+            if (rc == I2S_OK) {
+                i2s->state = I2S_STATE_RUNNING;
+                i2s->client->state_changed_cb(i2s, I2S_STATE_RUNNING);
+            }
+        }
+    }
+    return rc;
+}
+
+int
+i2s_stop(struct i2s *i2s)
+{
+    struct i2s_sample_buffer *buffer;
+
+    i2s_driver_stop(i2s);
+
+    i2s->state = I2S_STATE_STOPPED;
+    if (i2s->client) {
+        i2s->client->state_changed_cb(i2s, i2s->state);
+    }
+
+    if (i2s->direction == I2S_IN) {
+        while (NULL != (buffer = i2s_buffer_get(i2s, 0))) {
+            i2s_add_to_driver_queue(i2s, buffer);
+        }
+    } else {
+        while (!STAILQ_EMPTY(&i2s->driver_queue)) {
+            buffer = STAILQ_FIRST(&i2s->driver_queue);
+            STAILQ_REMOVE_HEAD(&i2s->driver_queue, next_buffer);
+            i2s_add_to_user_queue(i2s, buffer);
+        }
+    }
+    return 0;
+}
+
+int
+i2s_available_buffers(struct i2s *i2s)
+{
+    return os_sem_get_count(&i2s->user_queue_buffer_count);
+}
+
+struct i2s_sample_buffer *
+i2s_buffer_get(struct i2s *i2s, os_time_t timeout)
+{
+    int sr;
+    struct i2s_sample_buffer *buffer = NULL;
+
+    if (OS_OK == os_sem_pend(&i2s->user_queue_buffer_count, timeout)) {
+        OS_ENTER_CRITICAL(sr);
+        buffer = STAILQ_FIRST(&i2s->user_queue);
+        assert(buffer);
+        STAILQ_REMOVE_HEAD(&i2s->user_queue, next_buffer);
+        OS_EXIT_CRITICAL(sr);
+        assert(buffer->capacity > 0);
+    }
+
+    return buffer;
+}
+
+int
+i2s_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer)
+{
+    int sr;
+    int rc = I2S_OK;
+
+    /*
+     * Output sample buffer without samples?
+     * Don't bother driver, just put in client queue.
+     */
+    if (i2s->direction == I2S_OUT && buffer->sample_count == 0) {
+        i2s_driver_buffer_put(i2s, buffer);
+    } else {
+        OS_ENTER_CRITICAL(sr);
+        i2s_add_to_driver_queue(i2s, buffer);
+        OS_EXIT_CRITICAL(sr);
+
+        if (i2s->state == I2S_STATE_OUT_OF_BUFFERS) {
+            rc = i2s_driver_start(i2s);
+        }
+    }
+    return rc;
+}
+
+struct i2s_sample_buffer *
+i2s_driver_buffer_get(struct i2s *i2s)
+{
+    int sr;
+    struct i2s_sample_buffer *buffer;
+
+    OS_ENTER_CRITICAL(sr);
+    buffer = STAILQ_FIRST(&i2s->driver_queue);
+    if (buffer) {
+        STAILQ_REMOVE_HEAD(&i2s->driver_queue, next_buffer);
+    }
+    OS_EXIT_CRITICAL(sr);
+
+    return buffer;
+}
+
+void
+i2s_driver_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer)
+{
+    int sr;
+
+    assert(buffer != NULL && i2s != NULL);
+    if (i2s->client) {
+        /* If callback returns 1, buffer is not added to the pool */
+        if (i2s->client->sample_buffer_ready_cb(i2s, buffer) == 1) {
+            return;
+        }
+    }
+    OS_ENTER_CRITICAL(sr);
+    i2s_add_to_user_queue(i2s, buffer);
+    OS_EXIT_CRITICAL(sr);
+}
+
+void
+i2s_driver_state_changed(struct i2s *i2s, enum i2s_state state)
+{
+    if (i2s->state != state) {
+        i2s->state = state;
+        if (i2s->client) {
+            i2s->client->state_changed_cb(i2s, state);
+        }
+    }
+}