Merge branch 'fix/psram-ecc-mspi-dma-align' into 'master'

fix(mspi): handle PSRAM ECC DMA alignment across MSPI users

Closes IDF-15850, IDF-15910, and IDF-15700

See merge request espressif/esp-idf!49987
This commit is contained in:
morris
2026-08-20 11:51:46 +08:00
60 changed files with 829 additions and 467 deletions
@@ -10,6 +10,8 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "hal/dma2d_types.h"
@@ -269,6 +271,47 @@ typedef struct {
*/
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability);
/**
* @brief Get DMA2D buffer alignment constraint for a specific buffer
*
* @note On invalid arguments, returns an impossible alignment (BIT(31)).
*
* @param[in] buffer Buffer address
* @return Alignment requirement in bytes
*/
size_t dma2d_get_buffer_alignment_constraint(const void *buffer);
/**
* @brief Get alignment required when allocating a buffer for DMA2D access
*
* Use this before the buffer exists (e.g. `heap_caps_aligned_calloc`).
* Unlike `dma2d_get_buffer_alignment_constraint`, this returns the worst-case
* DMA2D/MSPI alignment rather than treating a NULL pointer as invalid.
*
* @return Alignment requirement in bytes (1 if no strict alignment is needed)
*/
size_t dma2d_get_alloc_alignment(void);
/**
* @brief Check whether a 2D DMA transaction window satisfies DMA2D/MSPI alignment
*
* Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both
* address and size. For a 2D transfer that means:
* - buffer base address aligned to N bytes
* - bytes-per-line (`pic_width * bpp/8`) aligned, so every next line starts on an N-byte boundary
* - transfer width (`blk_width * bpp/8`) aligned
* - horizontal window offset (`offset_x * bpp/8`) aligned
*
* @param[in] buf Buffer base address
* @param[in] pic_width Picture / stride width in pixels
* @param[in] blk_width Transfer block width in pixels
* @param[in] offset_x Horizontal offset of the block in pixels
* @param[in] bit_depth Bits per pixel
* @return true if the transaction satisfies the alignment constraints
*/
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
uint32_t offset_x, uint32_t bit_depth);
/**
* @brief A collection of color space conversion (CSC) items that each 2D-DMA channel could apply
*/
@@ -7,6 +7,7 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "esp_etm.h"
#include "hal/gdma_types.h"
#include "esp_err.h"
@@ -221,23 +222,50 @@ typedef struct {
esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transfer_config_t *config);
/**
* @brief Get the alignment constraints for internal and external memory
* @brief Alignment constraints of a configured GDMA channel
*
* @note You should call this function after `gdma_config_transfer`, the later one can
* adjust the alignment constraints based on various conditions, e.g. burst size, memory encryption, etc.
* @note You can use returned alignment value to validate if a DMA buffer provided by the upper layer meets the constraints.
* @note The returned alignment doesn't take the cache line size into account, if you want to do aligned memory allocation,
* you should align the buffer size to the cache line size by yourself if the DMA buffer is behind a cache.
* @note Prefer this when allocating DMA buffers. Once a concrete buffer address is available,
* use `gdma_get_buffer_alignment_constraint` for the effective runtime constraint of that region.
* @note For allocation from external memory:
* - Use `ext_enc_mem_alignment` as the safe default (worst-case MSPI encryption/ECC).
* - Use `ext_no_enc_mem_alignment` when intentionally targeting no-encryption external memory (e.g. no-enc PSRAM).
* @note The returned alignment doesn't take the cache line size into account. If the DMA buffer is behind a cache,
* align the buffer size to the cache line size yourself when needed.
*/
typedef struct {
size_t int_mem_alignment; /*!< Alignment for internal memory */
size_t ext_enc_mem_alignment; /*!< Alignment for external memory including MSPI encryption/ECC constraints */
size_t ext_no_enc_mem_alignment; /*!< Alignment for external memory without MSPI region-specific constraints */
} gdma_channel_alignment_info_t;
/**
* @brief Get the alignment constraints for a configured GDMA channel
*
* @note Call this function after `gdma_config_transfer`.
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
* @param[out] int_mem_alignment Internal memory alignment
* @param[out] ext_mem_alignment External memory alignment
* @param[out] info Alignment constraints of the channel
* @return
* - ESP_OK: Get alignment constraints successfully
* - ESP_ERR_INVALID_ARG: Get alignment constraints failed because of invalid argument
* - ESP_FAIL: Get alignment constraints failed because of other error
*/
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment);
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info);
/**
* @brief Get the effective alignment constraint for a specific DMA buffer
*
* @note Call this function after `gdma_config_transfer`.
* @note Combines GDMA channel constraints with MSPI constraints of the actual buffer region.
* External no-encryption PSRAM buffers can therefore use their real runtime constraint
* instead of a generic worst-case MSPI alignment.
* @note The returned alignment doesn't take the cache line size into account.
* @note On invalid arguments, returns an impossible value (BIT(31)).
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
* @param[in] buffer DMA buffer address
* @return Effective buffer alignment in bytes
*/
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer);
/**
* @brief Apply channel strategy for GDMA channel
@@ -82,7 +82,9 @@ typedef struct {
gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount.
For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect.
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
uint32_t bypass_buffer_addr_align_check: 1; /*!< Whether to bypass the buffer address alignment check.
Only enable it when you know what you are doing. */
uint32_t bypass_buffer_size_align_check: 1; /*!< Whether to bypass the buffer size alignment check.
Only enable it when you know what you are doing. */
} flags; //!< Flags for buffer mount configurations
} gdma_buffer_mount_config_t;