Blame view

kernel/linux-imx6_3.14.28/drivers/dma/virt-dma.c 2.88 KB
6b13f685e   김민수   BSP 최초 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  /*
   * Virtual DMA channel support for DMAengine
   *
   * Copyright (C) 2012 Russell King
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  #include <linux/device.h>
  #include <linux/dmaengine.h>
  #include <linux/module.h>
  #include <linux/spinlock.h>
  
  #include "virt-dma.h"
  
  static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
  {
  	return container_of(tx, struct virt_dma_desc, tx);
  }
  
  dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
  {
  	struct virt_dma_chan *vc = to_virt_chan(tx->chan);
  	struct virt_dma_desc *vd = to_virt_desc(tx);
  	unsigned long flags;
  	dma_cookie_t cookie;
  
  	spin_lock_irqsave(&vc->lock, flags);
  	cookie = dma_cookie_assign(tx);
  
  	list_add_tail(&vd->node, &vc->desc_submitted);
  	spin_unlock_irqrestore(&vc->lock, flags);
  
  	dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted
  ",
  		vc, vd, cookie);
  
  	return cookie;
  }
  EXPORT_SYMBOL_GPL(vchan_tx_submit);
  
  struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
  	dma_cookie_t cookie)
  {
  	struct virt_dma_desc *vd;
  
  	list_for_each_entry(vd, &vc->desc_issued, node)
  		if (vd->tx.cookie == cookie)
  			return vd;
  
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(vchan_find_desc);
  
  /*
   * This tasklet handles the completion of a DMA descriptor by
   * calling its callback and freeing it.
   */
  static void vchan_complete(unsigned long arg)
  {
  	struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
  	struct virt_dma_desc *vd;
  	dma_async_tx_callback cb = NULL;
  	void *cb_data = NULL;
  	LIST_HEAD(head);
  
  	spin_lock_irq(&vc->lock);
  	list_splice_tail_init(&vc->desc_completed, &head);
  	vd = vc->cyclic;
  	if (vd) {
  		vc->cyclic = NULL;
  		cb = vd->tx.callback;
  		cb_data = vd->tx.callback_param;
  	}
  	spin_unlock_irq(&vc->lock);
  
  	if (cb)
  		cb(cb_data);
  
  	while (!list_empty(&head)) {
  		vd = list_first_entry(&head, struct virt_dma_desc, node);
  		cb = vd->tx.callback;
  		cb_data = vd->tx.callback_param;
  
  		list_del(&vd->node);
  
  		vc->desc_free(vd);
  
  		if (cb)
  			cb(cb_data);
  	}
  }
  
  void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
  {
  	while (!list_empty(head)) {
  		struct virt_dma_desc *vd = list_first_entry(head,
  			struct virt_dma_desc, node);
  		list_del(&vd->node);
  		dev_dbg(vc->chan.device->dev, "txd %p: freeing
  ", vd);
  		vc->desc_free(vd);
  	}
  }
  EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
  
  void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
  {
  	dma_cookie_init(&vc->chan);
  
  	spin_lock_init(&vc->lock);
  	INIT_LIST_HEAD(&vc->desc_submitted);
  	INIT_LIST_HEAD(&vc->desc_issued);
  	INIT_LIST_HEAD(&vc->desc_completed);
  
  	tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
  
  	vc->chan.device = dmadev;
  	list_add_tail(&vc->chan.device_node, &dmadev->channels);
  }
  EXPORT_SYMBOL_GPL(vchan_init);
  
  MODULE_AUTHOR("Russell King");
  MODULE_LICENSE("GPL");