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 : 0 : static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
15 : : {
16 : 0 : return container_of(tx, struct virt_dma_desc, tx);
17 : : }
18 : :
19 : 0 : dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
20 : : {
21 : 0 : struct virt_dma_chan *vc = to_virt_chan(tx->chan);
22 : 0 : struct virt_dma_desc *vd = to_virt_desc(tx);
23 : 0 : unsigned long flags;
24 : 0 : dma_cookie_t cookie;
25 : :
26 : 0 : spin_lock_irqsave(&vc->lock, flags);
27 : 0 : cookie = dma_cookie_assign(tx);
28 : :
29 : 0 : list_move_tail(&vd->node, &vc->desc_submitted);
30 : 0 : spin_unlock_irqrestore(&vc->lock, flags);
31 : :
32 : 0 : dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
33 : : vc, vd, cookie);
34 : :
35 : 0 : 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 : 0 : struct virt_dma_desc *vd = to_virt_desc(tx);
53 : 0 : unsigned long flags;
54 : :
55 : 0 : spin_lock_irqsave(&vc->lock, flags);
56 : 0 : list_del(&vd->node);
57 : 0 : spin_unlock_irqrestore(&vc->lock, flags);
58 : :
59 : 0 : 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 : 0 : 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 : 0 : static void vchan_complete(unsigned long arg)
84 : : {
85 : 0 : struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
86 : 0 : struct virt_dma_desc *vd, *_vd;
87 : 0 : struct dmaengine_desc_callback cb;
88 : 0 : LIST_HEAD(head);
89 : :
90 : 0 : spin_lock_irq(&vc->lock);
91 [ # # ]: 0 : list_splice_tail_init(&vc->desc_completed, &head);
92 : 0 : vd = vc->cyclic;
93 [ # # ]: 0 : if (vd) {
94 : 0 : vc->cyclic = NULL;
95 : 0 : dmaengine_desc_get_callback(&vd->tx, &cb);
96 : : } else {
97 : 0 : memset(&cb, 0, sizeof(cb));
98 : : }
99 : 0 : spin_unlock_irq(&vc->lock);
100 : :
101 : 0 : dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
102 : :
103 [ # # ]: 0 : list_for_each_entry_safe(vd, _vd, &head, node) {
104 : 0 : dmaengine_desc_get_callback(&vd->tx, &cb);
105 : :
106 : 0 : list_del(&vd->node);
107 : 0 : dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
108 : 0 : vchan_vdesc_fini(vd);
109 : : }
110 : 0 : }
111 : :
112 : 0 : void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
113 : : {
114 : 0 : struct virt_dma_desc *vd, *_vd;
115 : :
116 [ # # ]: 0 : list_for_each_entry_safe(vd, _vd, head, node) {
117 : 0 : list_del(&vd->node);
118 : 0 : vchan_vdesc_fini(vd);
119 : : }
120 : 0 : }
121 : : EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
122 : :
123 : 0 : void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
124 : : {
125 : 0 : dma_cookie_init(&vc->chan);
126 : :
127 : 0 : spin_lock_init(&vc->lock);
128 : 0 : INIT_LIST_HEAD(&vc->desc_allocated);
129 : 0 : INIT_LIST_HEAD(&vc->desc_submitted);
130 : 0 : INIT_LIST_HEAD(&vc->desc_issued);
131 : 0 : INIT_LIST_HEAD(&vc->desc_completed);
132 : 0 : INIT_LIST_HEAD(&vc->desc_terminated);
133 : :
134 : 0 : tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
135 : :
136 : 0 : vc->chan.device = dmadev;
137 : 0 : list_add_tail(&vc->chan.device_node, &dmadev->channels);
138 : 0 : }
139 : : EXPORT_SYMBOL_GPL(vchan_init);
140 : :
141 : : MODULE_AUTHOR("Russell King");
142 : : MODULE_LICENSE("GPL");
|