150437bff7f7374f86837986f66e15e73a364f894Russell King/*
250437bff7f7374f86837986f66e15e73a364f894Russell King * Virtual DMA channel support for DMAengine
350437bff7f7374f86837986f66e15e73a364f894Russell King *
450437bff7f7374f86837986f66e15e73a364f894Russell King * Copyright (C) 2012 Russell King
550437bff7f7374f86837986f66e15e73a364f894Russell King *
650437bff7f7374f86837986f66e15e73a364f894Russell King * This program is free software; you can redistribute it and/or modify
750437bff7f7374f86837986f66e15e73a364f894Russell King * it under the terms of the GNU General Public License version 2 as
850437bff7f7374f86837986f66e15e73a364f894Russell King * published by the Free Software Foundation.
950437bff7f7374f86837986f66e15e73a364f894Russell King */
1050437bff7f7374f86837986f66e15e73a364f894Russell King#include <linux/device.h>
1150437bff7f7374f86837986f66e15e73a364f894Russell King#include <linux/dmaengine.h>
1250437bff7f7374f86837986f66e15e73a364f894Russell King#include <linux/module.h>
1350437bff7f7374f86837986f66e15e73a364f894Russell King#include <linux/spinlock.h>
1450437bff7f7374f86837986f66e15e73a364f894Russell King
1550437bff7f7374f86837986f66e15e73a364f894Russell King#include "virt-dma.h"
1650437bff7f7374f86837986f66e15e73a364f894Russell King
1750437bff7f7374f86837986f66e15e73a364f894Russell Kingstatic struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
1850437bff7f7374f86837986f66e15e73a364f894Russell King{
1950437bff7f7374f86837986f66e15e73a364f894Russell King	return container_of(tx, struct virt_dma_desc, tx);
2050437bff7f7374f86837986f66e15e73a364f894Russell King}
2150437bff7f7374f86837986f66e15e73a364f894Russell King
2250437bff7f7374f86837986f66e15e73a364f894Russell Kingdma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
2350437bff7f7374f86837986f66e15e73a364f894Russell King{
2450437bff7f7374f86837986f66e15e73a364f894Russell King	struct virt_dma_chan *vc = to_virt_chan(tx->chan);
2550437bff7f7374f86837986f66e15e73a364f894Russell King	struct virt_dma_desc *vd = to_virt_desc(tx);
2650437bff7f7374f86837986f66e15e73a364f894Russell King	unsigned long flags;
2750437bff7f7374f86837986f66e15e73a364f894Russell King	dma_cookie_t cookie;
2850437bff7f7374f86837986f66e15e73a364f894Russell King
2950437bff7f7374f86837986f66e15e73a364f894Russell King	spin_lock_irqsave(&vc->lock, flags);
3050437bff7f7374f86837986f66e15e73a364f894Russell King	cookie = dma_cookie_assign(tx);
3150437bff7f7374f86837986f66e15e73a364f894Russell King
3250437bff7f7374f86837986f66e15e73a364f894Russell King	list_add_tail(&vd->node, &vc->desc_submitted);
3350437bff7f7374f86837986f66e15e73a364f894Russell King	spin_unlock_irqrestore(&vc->lock, flags);
3450437bff7f7374f86837986f66e15e73a364f894Russell King
3550437bff7f7374f86837986f66e15e73a364f894Russell King	dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
3650437bff7f7374f86837986f66e15e73a364f894Russell King		vc, vd, cookie);
3750437bff7f7374f86837986f66e15e73a364f894Russell King
3850437bff7f7374f86837986f66e15e73a364f894Russell King	return cookie;
3950437bff7f7374f86837986f66e15e73a364f894Russell King}
4050437bff7f7374f86837986f66e15e73a364f894Russell KingEXPORT_SYMBOL_GPL(vchan_tx_submit);
4150437bff7f7374f86837986f66e15e73a364f894Russell King
42fe045874aaf4480386c65baf1acae82af4c5e21fRussell Kingstruct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
43fe045874aaf4480386c65baf1acae82af4c5e21fRussell King	dma_cookie_t cookie)
44fe045874aaf4480386c65baf1acae82af4c5e21fRussell King{
45fe045874aaf4480386c65baf1acae82af4c5e21fRussell King	struct virt_dma_desc *vd;
46fe045874aaf4480386c65baf1acae82af4c5e21fRussell King
47fe045874aaf4480386c65baf1acae82af4c5e21fRussell King	list_for_each_entry(vd, &vc->desc_issued, node)
48fe045874aaf4480386c65baf1acae82af4c5e21fRussell King		if (vd->tx.cookie == cookie)
49fe045874aaf4480386c65baf1acae82af4c5e21fRussell King			return vd;
50fe045874aaf4480386c65baf1acae82af4c5e21fRussell King
51fe045874aaf4480386c65baf1acae82af4c5e21fRussell King	return NULL;
52fe045874aaf4480386c65baf1acae82af4c5e21fRussell King}
53fe045874aaf4480386c65baf1acae82af4c5e21fRussell KingEXPORT_SYMBOL_GPL(vchan_find_desc);
54fe045874aaf4480386c65baf1acae82af4c5e21fRussell King
5550437bff7f7374f86837986f66e15e73a364f894Russell King/*
5650437bff7f7374f86837986f66e15e73a364f894Russell King * This tasklet handles the completion of a DMA descriptor by
5750437bff7f7374f86837986f66e15e73a364f894Russell King * calling its callback and freeing it.
5850437bff7f7374f86837986f66e15e73a364f894Russell King */
5950437bff7f7374f86837986f66e15e73a364f894Russell Kingstatic void vchan_complete(unsigned long arg)
6050437bff7f7374f86837986f66e15e73a364f894Russell King{
6150437bff7f7374f86837986f66e15e73a364f894Russell King	struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
62571fa74034701391b1be2ad193f684acfdeb75d1Russell King	struct virt_dma_desc *vd;
63571fa74034701391b1be2ad193f684acfdeb75d1Russell King	dma_async_tx_callback cb = NULL;
64571fa74034701391b1be2ad193f684acfdeb75d1Russell King	void *cb_data = NULL;
6550437bff7f7374f86837986f66e15e73a364f894Russell King	LIST_HEAD(head);
6650437bff7f7374f86837986f66e15e73a364f894Russell King
6750437bff7f7374f86837986f66e15e73a364f894Russell King	spin_lock_irq(&vc->lock);
6850437bff7f7374f86837986f66e15e73a364f894Russell King	list_splice_tail_init(&vc->desc_completed, &head);
69571fa74034701391b1be2ad193f684acfdeb75d1Russell King	vd = vc->cyclic;
70571fa74034701391b1be2ad193f684acfdeb75d1Russell King	if (vd) {
71571fa74034701391b1be2ad193f684acfdeb75d1Russell King		vc->cyclic = NULL;
72571fa74034701391b1be2ad193f684acfdeb75d1Russell King		cb = vd->tx.callback;
73571fa74034701391b1be2ad193f684acfdeb75d1Russell King		cb_data = vd->tx.callback_param;
74571fa74034701391b1be2ad193f684acfdeb75d1Russell King	}
7550437bff7f7374f86837986f66e15e73a364f894Russell King	spin_unlock_irq(&vc->lock);
7650437bff7f7374f86837986f66e15e73a364f894Russell King
77571fa74034701391b1be2ad193f684acfdeb75d1Russell King	if (cb)
78571fa74034701391b1be2ad193f684acfdeb75d1Russell King		cb(cb_data);
79571fa74034701391b1be2ad193f684acfdeb75d1Russell King
8050437bff7f7374f86837986f66e15e73a364f894Russell King	while (!list_empty(&head)) {
81571fa74034701391b1be2ad193f684acfdeb75d1Russell King		vd = list_first_entry(&head, struct virt_dma_desc, node);
82571fa74034701391b1be2ad193f684acfdeb75d1Russell King		cb = vd->tx.callback;
83571fa74034701391b1be2ad193f684acfdeb75d1Russell King		cb_data = vd->tx.callback_param;
8450437bff7f7374f86837986f66e15e73a364f894Russell King
8550437bff7f7374f86837986f66e15e73a364f894Russell King		list_del(&vd->node);
8650437bff7f7374f86837986f66e15e73a364f894Russell King
8750437bff7f7374f86837986f66e15e73a364f894Russell King		vc->desc_free(vd);
8850437bff7f7374f86837986f66e15e73a364f894Russell King
8950437bff7f7374f86837986f66e15e73a364f894Russell King		if (cb)
9050437bff7f7374f86837986f66e15e73a364f894Russell King			cb(cb_data);
9150437bff7f7374f86837986f66e15e73a364f894Russell King	}
9250437bff7f7374f86837986f66e15e73a364f894Russell King}
9350437bff7f7374f86837986f66e15e73a364f894Russell King
9450437bff7f7374f86837986f66e15e73a364f894Russell Kingvoid vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
9550437bff7f7374f86837986f66e15e73a364f894Russell King{
9650437bff7f7374f86837986f66e15e73a364f894Russell King	while (!list_empty(head)) {
9750437bff7f7374f86837986f66e15e73a364f894Russell King		struct virt_dma_desc *vd = list_first_entry(head,
9850437bff7f7374f86837986f66e15e73a364f894Russell King			struct virt_dma_desc, node);
9950437bff7f7374f86837986f66e15e73a364f894Russell King		list_del(&vd->node);
10050437bff7f7374f86837986f66e15e73a364f894Russell King		dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
10150437bff7f7374f86837986f66e15e73a364f894Russell King		vc->desc_free(vd);
10250437bff7f7374f86837986f66e15e73a364f894Russell King	}
10350437bff7f7374f86837986f66e15e73a364f894Russell King}
10450437bff7f7374f86837986f66e15e73a364f894Russell KingEXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
10550437bff7f7374f86837986f66e15e73a364f894Russell King
10650437bff7f7374f86837986f66e15e73a364f894Russell Kingvoid vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
10750437bff7f7374f86837986f66e15e73a364f894Russell King{
10850437bff7f7374f86837986f66e15e73a364f894Russell King	dma_cookie_init(&vc->chan);
10950437bff7f7374f86837986f66e15e73a364f894Russell King
11050437bff7f7374f86837986f66e15e73a364f894Russell King	spin_lock_init(&vc->lock);
11150437bff7f7374f86837986f66e15e73a364f894Russell King	INIT_LIST_HEAD(&vc->desc_submitted);
11250437bff7f7374f86837986f66e15e73a364f894Russell King	INIT_LIST_HEAD(&vc->desc_issued);
11350437bff7f7374f86837986f66e15e73a364f894Russell King	INIT_LIST_HEAD(&vc->desc_completed);
11450437bff7f7374f86837986f66e15e73a364f894Russell King
11550437bff7f7374f86837986f66e15e73a364f894Russell King	tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
11650437bff7f7374f86837986f66e15e73a364f894Russell King
11750437bff7f7374f86837986f66e15e73a364f894Russell King	vc->chan.device = dmadev;
11850437bff7f7374f86837986f66e15e73a364f894Russell King	list_add_tail(&vc->chan.device_node, &dmadev->channels);
11950437bff7f7374f86837986f66e15e73a364f894Russell King}
12050437bff7f7374f86837986f66e15e73a364f894Russell KingEXPORT_SYMBOL_GPL(vchan_init);
12150437bff7f7374f86837986f66e15e73a364f894Russell King
12250437bff7f7374f86837986f66e15e73a364f894Russell KingMODULE_AUTHOR("Russell King");
12350437bff7f7374f86837986f66e15e73a364f894Russell KingMODULE_LICENSE("GPL");
124