Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.5-rc3 222 lines 5.9 kB view raw
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#ifndef VIRT_DMA_H 8#define VIRT_DMA_H 9 10#include <linux/dmaengine.h> 11#include <linux/interrupt.h> 12 13#include "dmaengine.h" 14 15struct virt_dma_desc { 16 struct dma_async_tx_descriptor tx; 17 struct dmaengine_result tx_result; 18 /* protected by vc.lock */ 19 struct list_head node; 20}; 21 22struct virt_dma_chan { 23 struct dma_chan chan; 24 struct tasklet_struct task; 25 void (*desc_free)(struct virt_dma_desc *); 26 27 spinlock_t lock; 28 29 /* protected by vc.lock */ 30 struct list_head desc_allocated; 31 struct list_head desc_submitted; 32 struct list_head desc_issued; 33 struct list_head desc_completed; 34 35 struct virt_dma_desc *cyclic; 36 struct virt_dma_desc *vd_terminated; 37}; 38 39static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan) 40{ 41 return container_of(chan, struct virt_dma_chan, chan); 42} 43 44void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head); 45void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev); 46struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t); 47extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *); 48extern int vchan_tx_desc_free(struct dma_async_tx_descriptor *); 49 50/** 51 * vchan_tx_prep - prepare a descriptor 52 * @vc: virtual channel allocating this descriptor 53 * @vd: virtual descriptor to prepare 54 * @tx_flags: flags argument passed in to prepare function 55 */ 56static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc, 57 struct virt_dma_desc *vd, unsigned long tx_flags) 58{ 59 unsigned long flags; 60 61 dma_async_tx_descriptor_init(&vd->tx, &vc->chan); 62 vd->tx.flags = tx_flags; 63 vd->tx.tx_submit = vchan_tx_submit; 64 vd->tx.desc_free = vchan_tx_desc_free; 65 66 vd->tx_result.result = DMA_TRANS_NOERROR; 67 vd->tx_result.residue = 0; 68 69 spin_lock_irqsave(&vc->lock, flags); 70 list_add_tail(&vd->node, &vc->desc_allocated); 71 spin_unlock_irqrestore(&vc->lock, flags); 72 73 return &vd->tx; 74} 75 76/** 77 * vchan_issue_pending - move submitted descriptors to issued list 78 * @vc: virtual channel to update 79 * 80 * vc.lock must be held by caller 81 */ 82static inline bool vchan_issue_pending(struct virt_dma_chan *vc) 83{ 84 list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued); 85 return !list_empty(&vc->desc_issued); 86} 87 88/** 89 * vchan_cookie_complete - report completion of a descriptor 90 * @vd: virtual descriptor to update 91 * 92 * vc.lock must be held by caller 93 */ 94static inline void vchan_cookie_complete(struct virt_dma_desc *vd) 95{ 96 struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); 97 dma_cookie_t cookie; 98 99 cookie = vd->tx.cookie; 100 dma_cookie_complete(&vd->tx); 101 dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n", 102 vd, cookie); 103 list_add_tail(&vd->node, &vc->desc_completed); 104 105 tasklet_schedule(&vc->task); 106} 107 108/** 109 * vchan_vdesc_fini - Free or reuse a descriptor 110 * @vd: virtual descriptor to free/reuse 111 */ 112static inline void vchan_vdesc_fini(struct virt_dma_desc *vd) 113{ 114 struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); 115 116 if (dmaengine_desc_test_reuse(&vd->tx)) 117 list_add(&vd->node, &vc->desc_allocated); 118 else 119 vc->desc_free(vd); 120} 121 122/** 123 * vchan_cyclic_callback - report the completion of a period 124 * @vd: virtual descriptor 125 */ 126static inline void vchan_cyclic_callback(struct virt_dma_desc *vd) 127{ 128 struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); 129 130 vc->cyclic = vd; 131 tasklet_schedule(&vc->task); 132} 133 134/** 135 * vchan_terminate_vdesc - Disable pending cyclic callback 136 * @vd: virtual descriptor to be terminated 137 * 138 * vc.lock must be held by caller 139 */ 140static inline void vchan_terminate_vdesc(struct virt_dma_desc *vd) 141{ 142 struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); 143 144 /* free up stuck descriptor */ 145 if (vc->vd_terminated) 146 vchan_vdesc_fini(vc->vd_terminated); 147 148 vc->vd_terminated = vd; 149 if (vc->cyclic == vd) 150 vc->cyclic = NULL; 151} 152 153/** 154 * vchan_next_desc - peek at the next descriptor to be processed 155 * @vc: virtual channel to obtain descriptor from 156 * 157 * vc.lock must be held by caller 158 */ 159static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc) 160{ 161 return list_first_entry_or_null(&vc->desc_issued, 162 struct virt_dma_desc, node); 163} 164 165/** 166 * vchan_get_all_descriptors - obtain all submitted and issued descriptors 167 * @vc: virtual channel to get descriptors from 168 * @head: list of descriptors found 169 * 170 * vc.lock must be held by caller 171 * 172 * Removes all submitted and issued descriptors from internal lists, and 173 * provides a list of all descriptors found 174 */ 175static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc, 176 struct list_head *head) 177{ 178 list_splice_tail_init(&vc->desc_allocated, head); 179 list_splice_tail_init(&vc->desc_submitted, head); 180 list_splice_tail_init(&vc->desc_issued, head); 181 list_splice_tail_init(&vc->desc_completed, head); 182} 183 184static inline void vchan_free_chan_resources(struct virt_dma_chan *vc) 185{ 186 struct virt_dma_desc *vd; 187 unsigned long flags; 188 LIST_HEAD(head); 189 190 spin_lock_irqsave(&vc->lock, flags); 191 vchan_get_all_descriptors(vc, &head); 192 list_for_each_entry(vd, &head, node) 193 dmaengine_desc_clear_reuse(&vd->tx); 194 spin_unlock_irqrestore(&vc->lock, flags); 195 196 vchan_dma_desc_free_list(vc, &head); 197} 198 199/** 200 * vchan_synchronize() - synchronize callback execution to the current context 201 * @vc: virtual channel to synchronize 202 * 203 * Makes sure that all scheduled or active callbacks have finished running. For 204 * proper operation the caller has to ensure that no new callbacks are scheduled 205 * after the invocation of this function started. 206 * Free up the terminated cyclic descriptor to prevent memory leakage. 207 */ 208static inline void vchan_synchronize(struct virt_dma_chan *vc) 209{ 210 unsigned long flags; 211 212 tasklet_kill(&vc->task); 213 214 spin_lock_irqsave(&vc->lock, flags); 215 if (vc->vd_terminated) { 216 vchan_vdesc_fini(vc->vd_terminated); 217 vc->vd_terminated = NULL; 218 } 219 spin_unlock_irqrestore(&vc->lock, flags); 220} 221 222#endif