Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Virtual DMA channel support for DMAengine
4 : : *
5 : : * Copyright (C) 2012 Russell King
6 : : */
7 : : #include <linux/device.h>
8 : : #include <linux/dmaengine.h>
9 : : #include <linux/module.h>
10 : : #include <linux/spinlock.h>
11 : :
12 : : #include "virt-dma.h"
13 : :
14 : : static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
15 : : {
16 : : return container_of(tx, struct virt_dma_desc, tx);
17 : : }
18 : :
19 : 1041277 : dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
20 : : {
21 : 1041277 : struct virt_dma_chan *vc = to_virt_chan(tx->chan);
22 : : struct virt_dma_desc *vd = to_virt_desc(tx);
23 : : unsigned long flags;
24 : : dma_cookie_t cookie;
25 : :
26 : 1041277 : spin_lock_irqsave(&vc->lock, flags);
27 : : cookie = dma_cookie_assign(tx);
28 : :
29 : 1041277 : list_move_tail(&vd->node, &vc->desc_submitted);
30 : : spin_unlock_irqrestore(&vc->lock, flags);
31 : :
32 : : dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
33 : : vc, vd, cookie);
34 : :
35 : 1041277 : return cookie;
36 : : }
37 : : EXPORT_SYMBOL_GPL(vchan_tx_submit);
38 : :
39 : : /**
40 : : * vchan_tx_desc_free - free a reusable descriptor
41 : : * @tx: the transfer
42 : : *
43 : : * This function frees a previously allocated reusable descriptor. The only
44 : : * other way is to clear the DMA_CTRL_REUSE flag and submit one last time the
45 : : * transfer.
46 : : *
47 : : * Returns 0 upon success
48 : : */
49 : 0 : int vchan_tx_desc_free(struct dma_async_tx_descriptor *tx)
50 : : {
51 : 0 : struct virt_dma_chan *vc = to_virt_chan(tx->chan);
52 : : struct virt_dma_desc *vd = to_virt_desc(tx);
53 : : unsigned long flags;
54 : :
55 : 0 : spin_lock_irqsave(&vc->lock, flags);
56 : : list_del(&vd->node);
57 : : spin_unlock_irqrestore(&vc->lock, flags);
58 : :
59 : : dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: freeing\n",
60 : : vc, vd, vd->tx.cookie);
61 : 0 : vc->desc_free(vd);
62 : 0 : return 0;
63 : : }
64 : : EXPORT_SYMBOL_GPL(vchan_tx_desc_free);
65 : :
66 : 0 : struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
67 : : dma_cookie_t cookie)
68 : : {
69 : : struct virt_dma_desc *vd;
70 : :
71 [ # # ]: 0 : list_for_each_entry(vd, &vc->desc_issued, node)
72 [ # # ]: 0 : if (vd->tx.cookie == cookie)
73 : 0 : return vd;
74 : :
75 : : return NULL;
76 : : }
77 : : EXPORT_SYMBOL_GPL(vchan_find_desc);
78 : :
79 : : /*
80 : : * This tasklet handles the completion of a DMA descriptor by
81 : : * calling its callback and freeing it.
82 : : */
83 : 1041276 : static void vchan_complete(unsigned long arg)
84 : : {
85 : 1041276 : struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
86 : : struct virt_dma_desc *vd, *_vd;
87 : : struct dmaengine_desc_callback cb;
88 : 1041276 : LIST_HEAD(head);
89 : :
90 : : spin_lock_irq(&vc->lock);
91 : 1041276 : list_splice_tail_init(&vc->desc_completed, &head);
92 : 1041276 : vd = vc->cyclic;
93 [ - + ]: 1041276 : if (vd) {
94 : 0 : vc->cyclic = NULL;
95 : : dmaengine_desc_get_callback(&vd->tx, &cb);
96 : : } else {
97 : 1041276 : memset(&cb, 0, sizeof(cb));
98 : : }
99 : : spin_unlock_irq(&vc->lock);
100 : :
101 : 1041276 : dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
102 : :
103 [ + + ]: 2082552 : list_for_each_entry_safe(vd, _vd, &head, node) {
104 : : dmaengine_desc_get_callback(&vd->tx, &cb);
105 : :
106 : : list_del(&vd->node);
107 : 1041276 : dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
108 : : vchan_vdesc_fini(vd);
109 : : }
110 : 1041276 : }
111 : :
112 : 207 : void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
113 : : {
114 : : struct virt_dma_desc *vd, *_vd;
115 : :
116 [ - + ]: 207 : list_for_each_entry_safe(vd, _vd, head, node) {
117 [ # # ]: 0 : if (dmaengine_desc_test_reuse(&vd->tx)) {
118 : 0 : list_move_tail(&vd->node, &vc->desc_allocated);
119 : : } else {
120 : : dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
121 : : list_del(&vd->node);
122 : 0 : vc->desc_free(vd);
123 : : }
124 : : }
125 : 207 : }
126 : : EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
127 : :
128 : 2070 : void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
129 : : {
130 : : dma_cookie_init(&vc->chan);
131 : :
132 : 2070 : spin_lock_init(&vc->lock);
133 : 2070 : INIT_LIST_HEAD(&vc->desc_allocated);
134 : 2070 : INIT_LIST_HEAD(&vc->desc_submitted);
135 : 2070 : INIT_LIST_HEAD(&vc->desc_issued);
136 : 2070 : INIT_LIST_HEAD(&vc->desc_completed);
137 : :
138 : 2070 : tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
139 : :
140 : 2070 : vc->chan.device = dmadev;
141 : 2070 : list_add_tail(&vc->chan.device_node, &dmadev->channels);
142 : 2070 : }
143 : : EXPORT_SYMBOL_GPL(vchan_init);
144 : :
145 : : MODULE_AUTHOR("Russell King");
146 : : MODULE_LICENSE("GPL");
|