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");