Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev add i2s stm32h5 #72990

Merged
merged 8 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions drivers/dma/dma_stm32u5.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,13 +543,7 @@ static int dma_stm32_reload(const struct device *dev, uint32_t id,
dma_stm32_id_to_stream(id),
src, dst);

if (stream->source_periph) {
LL_DMA_SetBlkDataLength(dma, dma_stm32_id_to_stream(id),
size / stream->src_size);
} else {
LL_DMA_SetBlkDataLength(dma, dma_stm32_id_to_stream(id),
size / stream->dst_size);
}
LL_DMA_SetBlkDataLength(dma, dma_stm32_id_to_stream(id), size);

/* When reloading the dma, the stream is busy again before enabling */
stream->busy = true;
Expand Down
97 changes: 80 additions & 17 deletions drivers/i2s/i2s_ll_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ static unsigned int div_round_closest(uint32_t dividend, uint32_t divisor)
return (dividend + (divisor / 2U)) / divisor;
}

static bool queue_is_empty(struct ring_buf *rb)
{
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
unsigned int key;

key = irq_lock();

if (rb->tail != rb->head) {
/* Ring buffer is not empty */
irq_unlock(key);
return false;
}

irq_unlock(key);

return true;
}

/*
* Get data from the queue
*/
Expand All @@ -39,8 +56,7 @@ static int queue_get(struct ring_buf *rb, void **mem_block, size_t *size)

key = irq_lock();

if (rb->tail == rb->head) {
/* Ring buffer is empty */
if (queue_is_empty(rb) == true) {
irq_unlock(key);
return -ENOMEM;
}
Expand Down Expand Up @@ -174,9 +190,6 @@ static int i2s_stm32_configure(const struct device *dev, enum i2s_dir dir,
int ret;

if (dir == I2S_DIR_RX) {
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_i2s)
return -ENOSYS;
#endif
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
stream = &dev_data->rx;
} else if (dir == I2S_DIR_TX) {
stream = &dev_data->tx;
Expand Down Expand Up @@ -293,6 +306,7 @@ static int i2s_stm32_trigger(const struct device *dev, enum i2s_dir dir,
enum i2s_trigger_cmd cmd)
{
struct i2s_stm32_data *const dev_data = dev->data;
const struct i2s_stm32_cfg *const cfg = dev->config;
struct stream *stream;
unsigned int key;
int ret;
Expand Down Expand Up @@ -335,11 +349,20 @@ static int i2s_stm32_trigger(const struct device *dev, enum i2s_dir dir,
LOG_ERR("STOP trigger: invalid state");
return -EIO;
}
do_trigger_stop:
if (ll_func_i2s_dma_busy(cfg->i2s)) {
stream->state = I2S_STATE_STOPPING;
/*
* Indicate that the transition to I2S_STATE_STOPPING
* is triggered by STOP command
*/
stream->tx_stop_for_drain = false;
} else {
stream->stream_disable(stream, dev);
stream->state = I2S_STATE_READY;
stream->last_block = true;
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
}
irq_unlock(key);
stream->stream_disable(stream, dev);
stream->queue_drop(stream);
stream->state = I2S_STATE_READY;
stream->last_block = true;
break;

case I2S_TRIGGER_DRAIN:
Expand All @@ -349,9 +372,26 @@ static int i2s_stm32_trigger(const struct device *dev, enum i2s_dir dir,
LOG_ERR("DRAIN trigger: invalid state");
return -EIO;
}
stream->stream_disable(stream, dev);
stream->queue_drop(stream);
stream->state = I2S_STATE_READY;

marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
if (dir == I2S_DIR_TX) {
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
if ((queue_is_empty(&stream->mem_block_queue) == false) ||
(ll_func_i2s_dma_busy(cfg->i2s))) {
stream->state = I2S_STATE_STOPPING;
/*
* Indicate that the transition to I2S_STATE_STOPPING
* is triggered by DRAIN command
*/
stream->tx_stop_for_drain = true;
} else {
stream->stream_disable(stream, dev);
stream->state = I2S_STATE_READY;
}
} else if (dir == I2S_DIR_RX) {
goto do_trigger_stop;
} else {
LOG_ERR("Unavailable direction");
return -EINVAL;
}
irq_unlock(key);
break;

Expand Down Expand Up @@ -603,6 +643,28 @@ static void dma_tx_callback(const struct device *dma_dev, void *arg,
goto tx_disable;
}

/* Check if we finished transferring one block and stopping is requested */
if ((stream->state == I2S_STATE_STOPPING) && (status == DMA_STATUS_COMPLETE)) {
/*
* Check if all tx samples have been completely handled
* as stated in zephyr i2s specification, in case of DRAIN command
* send all data in the transmit queue and stop the transmission.
*/
if (queue_is_empty(&stream->mem_block_queue) == true) {
stream->queue_drop(stream);
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
stream->state = I2S_STATE_READY;
goto tx_disable;
} else if (stream->tx_stop_for_drain == false) {
/*
* In case of STOP command, just stop the transmission
* at the current. The transmission can be resumed.
*/
stream->state = I2S_STATE_READY;
goto tx_disable;
}
/* else: DRAIN trigger -> continue TX normally until queue is empty */
}

/* Stop transmission if we were requested */
if (stream->last_block) {
stream->state = I2S_STATE_READY;
Expand Down Expand Up @@ -652,11 +714,6 @@ static uint32_t i2s_stm32_irq_udr_count;
static void i2s_stm32_isr(const struct device *dev)
{
const struct i2s_stm32_cfg *cfg = dev->config;
struct i2s_stm32_data *const dev_data = dev->data;
struct stream *stream = &dev_data->rx;

LOG_ERR("%s: err=%d", __func__, (int)LL_I2S_ReadReg(cfg->i2s, SR));
stream->state = I2S_STATE_ERROR;
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved

/* OVR error must be explicitly cleared */
if (LL_I2S_IsActiveFlag_OVR(cfg->i2s)) {
Expand All @@ -677,8 +734,12 @@ static int i2s_stm32_initialize(const struct device *dev)
{
const struct i2s_stm32_cfg *cfg = dev->config;
struct i2s_stm32_data *const dev_data = dev->data;
struct stream *stream = &dev_data->tx;
int ret, i;

/* Initialize the variable used to handle the TX */
stream->tx_stop_for_drain = false;

/* Enable I2S clock propagation */
ret = i2s_stm32_enable_clock(dev);
if (ret < 0) {
Expand Down Expand Up @@ -871,6 +932,8 @@ static void tx_stream_disable(struct stream *stream, const struct device *dev)
stream->mem_block = NULL;
}

/* Wait for TX queue to drain before disabling */
k_busy_wait(100);
LL_I2S_Disable(cfg->i2s);

active_dma_tx_channel[stream->dma_channel] = NULL;
Expand Down
13 changes: 13 additions & 0 deletions drivers/i2s/i2s_ll_stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct stream {
bool src_addr_increment;
bool dst_addr_increment;
uint8_t fifo_threshold;
bool tx_stop_for_drain;

struct i2s_config cfg;
struct ring_buf mem_block_queue;
Expand All @@ -58,4 +59,16 @@ struct i2s_stm32_data {
struct stream tx;
};

/* checks that DMA Tx packet is fully transmitted over the I2S */
static inline uint32_t ll_func_i2s_dma_busy(SPI_TypeDef *i2s)
erwango marked this conversation as resolved.
Show resolved Hide resolved
erwango marked this conversation as resolved.
Show resolved Hide resolved
{
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_i2s)
return LL_SPI_IsActiveFlag_TXC(i2s) == 0;
#else
/* the I2S Tx empty and busy flags are needed */
return (LL_SPI_IsActiveFlag_TXE(i2s) &&
!LL_SPI_IsActiveFlag_BSY(i2s));
#endif
}

#endif /* _STM32_I2S_H_ */
48 changes: 48 additions & 0 deletions dts/arm/st/h5/stm32h5.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,54 @@
status = "disabled";
};

i2s1: i2s@40013000 {
compatible = "st,stm32h7-i2s", "st,stm32-i2s";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40013000 0x400>;
clocks = <&rcc STM32_CLOCK_BUS_APB2 0x00001000>,
<&rcc STM32_SRC_PLL1_Q SPI1_SEL(0)>;
dmas = <&gpdma1 0 7 (STM32_DMA_PERIPH_TX |STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)
&gpdma1 1 6 (STM32_DMA_PERIPH_RX | STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)>;
marwaiehm-st marked this conversation as resolved.
Show resolved Hide resolved
dma-names = "tx", "rx";
interrupts = <55 3>;
status = "disabled";
};

i2s2: i2s@40003800 {
compatible = "st,stm32h7-i2s", "st,stm32-i2s";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40003800 0x400>;
clocks = <&rcc STM32_CLOCK_BUS_APB1 0x00004000>,
<&rcc STM32_SRC_PLL1_Q SPI2_SEL(0)>;
dmas = <&gpdma1 2 9 (STM32_DMA_PERIPH_TX | STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)
&gpdma1 3 8 (STM32_DMA_PERIPH_RX | STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)>;
dma-names = "tx", "rx";
interrupts = <56 3>;
status = "disabled";
};

i2s3: i2s@40003c00 {
compatible = "st,stm32h7-i2s", "st,stm32-i2s";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40003c00 0x400>;
clocks = <&rcc STM32_CLOCK_BUS_APB1 0x00008000>,
<&rcc STM32_SRC_PLL1_Q SPI3_SEL(0)>;
dmas = <&gpdma1 4 11 (STM32_DMA_PERIPH_TX | STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)
&gpdma1 5 10 (STM32_DMA_PERIPH_RX | STM32_DMA_16BITS | \
STM32_DMA_PRIORITY_HIGH)>;
dma-names = "tx", "rx";
interrupts = <57 3>;
status = "disabled";
};

usb: usb@40016000 {
compatible = "st,stm32-usb";
reg = <0x40016000 0x400>;
Expand Down
2 changes: 2 additions & 0 deletions include/zephyr/dt-bindings/dma/stm32_dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
#define STM32_DMA_PERIPH_TX (STM32_DMA_MEMORY_TO_PERIPH | STM32_DMA_MEM_INC)
#define STM32_DMA_PERIPH_RX (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_MEM_INC)

#define STM32_DMA_16BITS (STM32_DMA_PERIPH_16BITS | STM32_DMA_MEM_16BITS)

/** @} */

#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_STM32_DMA_H_ */
6 changes: 6 additions & 0 deletions tests/drivers/i2s/i2s_speed/boards/stm32h573i_dk.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# Copyright (c) 2024 STMicroelectronics
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_I2S_TEST_SEPARATE_DEVICES=y
20 changes: 20 additions & 0 deletions tests/drivers/i2s/i2s_speed/boards/stm32h573i_dk.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* i2s-node0 is the receiver */
/* i2s-node1 is the transmitter */
/ {
aliases {
i2s-node0 = &i2s1;
i2s-node1 = &i2s2;
};
};

&i2s1 {
pinctrl-0 = <&i2s1_ws_pa4 &i2s1_ck_pa5 &spi1_miso_pa6 &spi1_mosi_pb5>;
pinctrl-names = "default";
status = "okay";
};

&i2s2 {
pinctrl-0 = <&i2s2_ws_pa3 &i2s2_ck_pi1 &spi2_miso_pi2 &spi2_mosi_pb15>;
pinctrl-names = "default";
status = "okay";
};
Loading