dma in arm - MarekBykowski/readme GitHub Wiki

probe()
    ↓
dma_request_chan()
    ↓
axxia driver provides channel
    ↓
kmalloc(src, dst)
    ↓
dma_map_single(src, DMA_TO_DEVICE)
dma_map_single(dst, DMA_FROM_DEVICE)
    ↓
prep_dma_memcpy()
    ↓
gpdma_prep_memcpy()
    ↓
dmaengine_submit()
    ↓
dma_async_issue_pending()
    ↓
gpdma_issue_pending()
    ↓
Hardware copies RAM → RAM
    ↓
Interrupt
    ↓
vchan_cookie_complete()
    ↓
dma_complete() callback
    ↓
dma_unmap_single()
// gpdma_memcpy_demo.c

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>

struct demo_dev {
	struct device *dev;
	struct dma_chan *chan;
	struct completion cmp;
	void *src;
	void *dst;
	dma_addr_t src_dma;
	dma_addr_t dst_dma;
	size_t len;
};

static void dma_complete(void *arg)
{
	struct demo_dev *d = arg;
	complete(&d->cmp);
}

static int demo_probe(struct platform_device *pdev)
{
	struct demo_dev *d;
	struct dma_async_tx_descriptor *tx;
	dma_cookie_t cookie;
	int ret;

	d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
	if (!d)
		return -ENOMEM;

	d->dev = &pdev->dev;
	d->len = 4096;

	init_completion(&d->cmp);

	/* Request DMA channel (must match DT dma-names) */
	d->chan = dma_request_chan(&pdev->dev, "memcpy");
	if (IS_ERR(d->chan))
		return PTR_ERR(d->chan);

	/* Allocate normal memory */
	d->src = kmalloc(d->len, GFP_KERNEL);
	d->dst = kmalloc(d->len, GFP_KERNEL);
	if (!d->src || !d->dst)
		return -ENOMEM;

	memset(d->src, 0xAB, d->len);
	memset(d->dst, 0x00, d->len);

	/* Map buffers for DMA (streaming) */
	d->src_dma = dma_map_single(d->dev, d->src,
				    d->len, DMA_TO_DEVICE);
	d->dst_dma = dma_map_single(d->dev, d->dst,
				    d->len, DMA_FROM_DEVICE);

	if (dma_mapping_error(d->dev, d->src_dma) ||
	    dma_mapping_error(d->dev, d->dst_dma))
		return -EIO;

	/* Prepare DMA memcpy */
	tx = dmaengine_prep_dma_memcpy(d->chan,
				       d->dst_dma,
				       d->src_dma,
				       d->len,
				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
	if (!tx)
		return -ENOMEM;

	tx->callback = dma_complete;
	tx->callback_param = d;

	cookie = dmaengine_submit(tx);
	if (dma_submit_error(cookie))
		return -EIO;

	dma_async_issue_pending(d->chan);

	/* Wait for completion */
	wait_for_completion(&d->cmp);

	/* Sync back for CPU */
	dma_unmap_single(d->dev, d->src_dma,
			 d->len, DMA_TO_DEVICE);
	dma_unmap_single(d->dev, d->dst_dma,
			 d->len, DMA_FROM_DEVICE);

	/* Verify */
	if (memcmp(d->src, d->dst, d->len) == 0)
		dev_info(d->dev, "DMA memcpy successful\n");
	else
		dev_err(d->dev, "DMA memcpy FAILED\n");

	return 0;
}

static int demo_remove(struct platform_device *pdev)
{
	return 0;
}

static const struct of_device_id demo_of_match[] = {
	{ .compatible = "demo,gpdma-memcpy", },
	{ }
};
MODULE_DEVICE_TABLE(of, demo_of_match);

static struct platform_driver demo_driver = {
	.probe = demo_probe,
	.remove = demo_remove,
	.driver = {
		.name = "gpdma-memcpy-demo",
		.of_match_table = demo_of_match,
	},
};

module_platform_driver(demo_driver);

MODULE_LICENSE("GPL");