Linux Audio

Check our new training course

Loading...
/* dma.c - DMA test source file */

/*
 * Copyright (c) 2016 Intel Corporation.
 * Copyright (c) 2021 Linaro Limited.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Verify zephyr dma memory to memory transfer loops
 * @details
 * - Test Steps
 *   -# Set dma channel configuration including source/dest addr, burstlen
 *   -# Set direction memory-to-memory
 *   -# Start transfer
 *   -# Move to next dest addr
 *   -# Back to first step
 * - Expected Results
 *   -# Data is transferred correctly from src to dest, for each loop
 */

#include <zephyr.h>

#include <device.h>
#include <drivers/dma.h>
#include <ztest.h>

/* in millisecond */
#define SLEEPTIME 1000

#define TRANSFER_LOOPS (5)
#define RX_BUFF_SIZE (64)

#if CONFIG_NOCACHE_MEMORY
static const char TX_DATA[] = "The quick brown fox jumps over the lazy dog";
static __aligned(16) char tx_data[64] __used
	__attribute__((__section__(".nocache")));
static __aligned(16) char rx_data[TRANSFER_LOOPS][RX_BUFF_SIZE] __used
	__attribute__((__section__(".nocache.dma")));
#else
/* this src memory shall be in RAM to support usingas a DMA source pointer.*/
static char tx_data[] =
	"The quick brown fox jumps over the lazy dog";
static __aligned(16) char rx_data[TRANSFER_LOOPS][RX_BUFF_SIZE] = { { 0 } };
#endif

#define DMA_DEVICE_NAME CONFIG_DMA_LOOP_TRANSFER_DRV_NAME

volatile uint8_t transfer_count;
static struct dma_config dma_cfg = {0};
static struct dma_block_config dma_block_cfg = {0};

static void test_transfer(const struct device *dev, uint32_t id)
{
	transfer_count++;
	if (transfer_count < TRANSFER_LOOPS) {
		dma_block_cfg.block_size = strlen(tx_data);
		dma_block_cfg.source_address = (uint32_t)tx_data;
		dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count];

		zassert_false(dma_config(dev, id, &dma_cfg),
					"Not able to config transfer %d",
					transfer_count + 1);
		zassert_false(dma_start(dev, id),
					"Not able to start next transfer %d",
					transfer_count + 1);
	}
}

static void dma_user_callback(const struct device *dma_dev, void *arg,
			      uint32_t id, int error_code)
{
	zassert_false(error_code, "DMA could not proceed, an error occurred\n");

#ifdef CONFIG_DMAMUX_STM32
	/* the channel is the DMAMUX's one
	 * the device is the DMAMUX, given through
	 * the stream->user_data by the dma_stm32_irq_handler
	 */
	test_transfer((struct device *)arg, id);
#else
	test_transfer(dma_dev, id);
#endif /* CONFIG_DMAMUX_STM32 */
}

static int test_loop(void)
{
	const struct device *dma;
	static int chan_id;

	TC_PRINT("DMA memory to memory transfer started on %s\n",
	       DMA_DEVICE_NAME);
	TC_PRINT("Preparing DMA Controller\n");

#if CONFIG_NOCACHE_MEMORY
	memset(tx_data, 0, sizeof(tx_data));
	memcpy(tx_data, TX_DATA, sizeof(TX_DATA));
#endif

	memset(rx_data, 0, sizeof(rx_data));

	dma = device_get_binding(DMA_DEVICE_NAME);
	if (!dma) {
		TC_PRINT("Cannot get dma controller\n");
		return TC_FAIL;
	}

	dma_cfg.channel_direction = MEMORY_TO_MEMORY;
	dma_cfg.source_data_size = 1U;
	dma_cfg.dest_data_size = 1U;
	dma_cfg.source_burst_length = 1U;
	dma_cfg.dest_burst_length = 1U;
#ifdef CONFIG_DMAMUX_STM32
	dma_cfg.user_data = (struct device *)dma;
#else
	dma_cfg.user_data = NULL;
#endif /* CONFIG_DMAMUX_STM32 */
	dma_cfg.dma_callback = dma_user_callback;
	dma_cfg.block_count = 1U;
	dma_cfg.head_block = &dma_block_cfg;

#ifdef CONFIG_DMA_MCUX_TEST_SLOT_START
	dma_cfg.dma_slot = CONFIG_DMA_MCUX_TEST_SLOT_START;
#endif

	chan_id = dma_request_channel(dma, NULL);
	if (chan_id < 0) {
		TC_PRINT("this platform do not support the dma channel\n");
		chan_id = CONFIG_DMA_LOOP_TRANSFER_CHANNEL_NR;
	}
	transfer_count = 0;
	TC_PRINT("Starting the transfer on channel %d and waiting for 1 second\n", chan_id);
	dma_block_cfg.block_size = strlen(tx_data);
	dma_block_cfg.source_address = (uint32_t)tx_data;
	dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count];

	if (dma_config(dma, chan_id, &dma_cfg)) {
		TC_PRINT("ERROR: transfer config (%d)\n", chan_id);
		return TC_FAIL;
	}

	if (dma_start(dma, chan_id)) {
		TC_PRINT("ERROR: transfer start (%d)\n", chan_id);
		return TC_FAIL;
	}

	k_sleep(K_MSEC(SLEEPTIME));

	if (transfer_count < TRANSFER_LOOPS) {
		transfer_count = TRANSFER_LOOPS;
		TC_PRINT("ERROR: unfinished transfer\n");
		if (dma_stop(dma, chan_id)) {
			TC_PRINT("ERROR: transfer stop\n");
		}
		return TC_FAIL;
	}

	TC_PRINT("Each RX buffer should contain the full TX buffer string.\n");

	for (int i = 0; i < TRANSFER_LOOPS; i++) {
		TC_PRINT("RX data Loop %d: %s\n", i, rx_data[i]);
		if (strncmp(tx_data, rx_data[i], sizeof(rx_data[i])) != 0) {
			return TC_FAIL;
		}
	}

	TC_PRINT("Finished: DMA\n");
	return TC_PASS;
}

/* export test cases */
void test_dma_m2m_loop(void)
{
	zassert_true((test_loop() == TC_PASS), NULL);
}