Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2019 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 *
 * @brief Sample app to illustrate dma transfer on Intel S1000 CRB.
 *
 * Intel S1000 CRB - Xtensa
 * --------------------
 *
 * The dma_cavs driver is being used.
 *
 * In this sample app, multi-block dma is tested in the following manner
 *   - Define 2 strings which will serve as 2 blocks of source data.
 *   - Define 2 empty buffers to receive the data from the DMA operation.
 *   - Set dma channel configuration including source/dest addr, burstlen etc.
 *   - Set direction memory-to-memory
 *   - Start transfer
 *
 * Expected Results
 *   - Data is transferred correctly from src to dest. The DMAed string should
 *     be printed on to the console. No error should be seen.
 */

#include <zephyr.h>
#include <misc/printk.h>

#include <device.h>
#include <dma.h>

#include <string.h>
#include <xtensa/hal.h>

/* size of stack area used by each thread */
#define STACKSIZE               1024

/* scheduling priority used by each thread */
#define PRIORITY                7

/* delay between greetings (in ms) */
#define SLEEPTIME               500

/* max time to be waited for dma to complete (in ms) */
#define WAITTIME                1000

#define MAX_TRANSFERS		4

/* This semaphore is used as a signal from the dma isr to the app
 * to let it know the DMA is complete. The app should wait till
 * this event comes indicating the completion of DMA.
 */
K_SEM_DEFINE(dma_sem, 0, 1);

extern struct k_sem thread_sem;

#define DMA_DEVICE_NAME		CONFIG_DMA_0_NAME
#define RX_BUFF_SIZE		(48)

struct transfers {
	const char *source;
	char *destination;
	size_t size;
};

static const char tx_data[] = "It is harder to be kind than to be wise";
static const char tx_data2[] = "India have a good cricket team";
static const char tx_data3[] = "Virat: the best ever?";
static const char tx_data4[] = "Phenomenon";
static char rx_data[RX_BUFF_SIZE] = { 0 };
static char rx_data2[RX_BUFF_SIZE] = { 0 };
static char rx_data3[RX_BUFF_SIZE] = { 0 };
static char rx_data4[RX_BUFF_SIZE] = { 0 };

static struct transfers transfer_blocks[MAX_TRANSFERS] = {
	{
		.source = tx_data,
		.destination = rx_data,
		.size = sizeof(tx_data),
	},
	{
		.source = tx_data2,
		.destination = rx_data2,
		.size = sizeof(tx_data2),
	},
	{
		.source = tx_data3,
		.destination = rx_data3,
		.size = sizeof(tx_data3),
	},
	{
		.source = tx_data4,
		.destination = rx_data4,
		.size = sizeof(tx_data4),
	},
};
static struct device *dma_device;
static u32_t current_block_count, total_block_count;

static void test_done(void *arg, u32_t channel, int error_code)
{
	u32_t src, dst;
	size_t size;

	current_block_count++;

	if (error_code != 0) {
		printk("DMA transfer met an error = 0x%x\n", error_code);
		k_sem_give(&dma_sem);
	} else if (current_block_count < total_block_count) {
		src = (u32_t)transfer_blocks[current_block_count].source;
		dst = (u32_t)transfer_blocks[current_block_count].destination;
		size = transfer_blocks[current_block_count].size;
		dma_reload(dma_device, channel, src, dst, size);
		dma_start(dma_device, channel);
	} else {
		printk("DMA transfer done\n");
		k_sem_give(&dma_sem);
	}
}

static int test_task(u32_t chan_id, u32_t blen, u32_t block_count)
{
	struct dma_config dma_cfg = {0};

	struct dma_block_config dma_block_cfg = {
		.block_size = sizeof(tx_data),
		.source_address = (u32_t)tx_data,
		.dest_address = (u32_t)rx_data,
	};

	if (block_count > ARRAY_SIZE(transfer_blocks)) {
		printk("block_count %u is greater than %ld\n", block_count,
			ARRAY_SIZE(transfer_blocks));
		return -1;
	}

	dma_device = device_get_binding(DMA_DEVICE_NAME);

	if (!dma_device) {
		printk("Cannot get dma controller\n");
		return -1;
	}

	dma_cfg.channel_direction = MEMORY_TO_MEMORY;
	dma_cfg.source_data_size = 1U;
	dma_cfg.dest_data_size = 1U;
	dma_cfg.source_burst_length = blen;
	dma_cfg.dest_burst_length = blen;
	dma_cfg.dma_callback = test_done;
	dma_cfg.complete_callback_en = 0U;
	dma_cfg.error_callback_en = 1U;
	dma_cfg.block_count = 1U;
	dma_cfg.head_block = &dma_block_cfg;

	printk("Preparing DMA Controller: Chan_ID=%u, BURST_LEN=%u\n",
			chan_id, blen);

	(void)memset(rx_data, 0, sizeof(rx_data));
	(void)memset(rx_data2, 0, sizeof(rx_data2));
	(void)memset(rx_data3, 0, sizeof(rx_data3));
	(void)memset(rx_data4, 0, sizeof(rx_data4));

	/*
	 * dma_block_cfg4 is assigned to 0 by default. Hence if callback_arg is
	 * not assigned, it will be NULL implying there are no more blocks to
	 * transfer
	 */

	if (dma_config(dma_device, chan_id, &dma_cfg)) {
		printk("ERROR: configuring\n");
		return -1;
	}

	printk("Starting the transfer\n");

	current_block_count = 0U;
	total_block_count = block_count;

	if (dma_start(dma_device, chan_id)) {
		printk("ERROR: transfer\n");
		return -1;
	}

	/* Wait a while for the dma to complete */
	if (k_sem_take(&dma_sem, WAITTIME)) {
		printk("*** timed out waiting for dma to complete ***\n");
	}

	xthal_dcache_region_invalidate(rx_data, RX_BUFF_SIZE);
	xthal_dcache_region_invalidate(rx_data2, RX_BUFF_SIZE);
	xthal_dcache_region_invalidate(rx_data3, RX_BUFF_SIZE);
	xthal_dcache_region_invalidate(rx_data4, RX_BUFF_SIZE);

	/* Intentionally break has been omitted (fall-through) */
	switch (block_count) {
	case 4:
		if (strcmp(tx_data4, rx_data4) != 0)
			return -1;
		printk("%s\n", rx_data4);

	case 3:
		if (strcmp(tx_data3, rx_data3) != 0)
			return -1;
		printk("%s\n", rx_data3);

	case 2:
		if (strcmp(tx_data2, rx_data2) != 0)
			return -1;
		printk("%s\n", rx_data2);

	case 1:
		if (strcmp(tx_data, rx_data) != 0)
			return -1;
		printk("%s\n", rx_data);
		break;

	default:
		printk("Invalid block count %d\n", block_count);
		return -1;
	}

	return 0;
}

/* export test cases */
void dma_thread(void)
{
	while (1) {
		k_sem_take(&thread_sem, K_FOREVER);
		if (test_task(0, 8, 2) == 0) {
			printk("DMA Passed\n");
		} else {
			printk("DMA Failed\n");
		}
		k_sem_give(&thread_sem);
		k_sleep(SLEEPTIME);

		k_sem_take(&thread_sem, K_FOREVER);
		if (test_task(1, 8, 3) == 0) {
			printk("DMA Passed\n");
		} else {
			printk("DMA Failed\n");
		}
		k_sem_give(&thread_sem);
		k_sleep(SLEEPTIME);

		k_sem_take(&thread_sem, K_FOREVER);
		if (test_task(0, 16, 4) == 0) {
			printk("DMA Passed\n");
		} else {
			printk("DMA Failed\n");
		}
		k_sem_give(&thread_sem);
		k_sleep(SLEEPTIME);

		k_sem_take(&thread_sem, K_FOREVER);
		if (test_task(1, 16, 1) == 0) {
			printk("DMA Passed\n");
		} else {
			printk("DMA Failed\n");
		}
		k_sem_give(&thread_sem);
		k_sleep(SLEEPTIME);
	}
}

K_THREAD_DEFINE(dma_thread_id, STACKSIZE, dma_thread, NULL, NULL, NULL,
		PRIORITY, 0, K_NO_WAIT);