Commit | Line | Data |
---|---|---|
c4e16625 BK |
1 | /* linux/arch/arm/plat-samsung/dma-ops.c |
2 | * | |
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
4 | * http://www.samsung.com | |
5 | * | |
6 | * Samsung DMA Operations | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/errno.h> | |
15 | #include <linux/amba/pl330.h> | |
16 | #include <linux/scatterlist.h> | |
0c073e35 | 17 | #include <linux/export.h> |
c4e16625 BK |
18 | |
19 | #include <mach/dma.h> | |
20 | ||
c4e16625 | 21 | static unsigned samsung_dmadev_request(enum dma_ch dma_ch, |
fbb20e81 | 22 | struct samsung_dma_req *param) |
c4e16625 | 23 | { |
c4e16625 | 24 | dma_cap_mask_t mask; |
4972a80e | 25 | void *filter_param; |
c4e16625 BK |
26 | |
27 | dma_cap_zero(mask); | |
fbb20e81 | 28 | dma_cap_set(param->cap, mask); |
c4e16625 | 29 | |
4972a80e TA |
30 | /* |
31 | * If a dma channel property of a device node from device tree is | |
32 | * specified, use that as the fliter parameter. | |
33 | */ | |
fbb20e81 BK |
34 | filter_param = (dma_ch == DMACH_DT_PROP) ? |
35 | (void *)param->dt_dmach_prop : (void *)dma_ch; | |
36 | return (unsigned)dma_request_channel(mask, pl330_filter, filter_param); | |
37 | } | |
c4e16625 | 38 | |
3688be49 | 39 | static int samsung_dmadev_release(unsigned ch, void *param) |
fbb20e81 BK |
40 | { |
41 | dma_release_channel((struct dma_chan *)ch); | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | static int samsung_dmadev_config(unsigned ch, | |
47 | struct samsung_dma_config *param) | |
48 | { | |
49 | struct dma_chan *chan = (struct dma_chan *)ch; | |
50 | struct dma_slave_config slave_config; | |
51 | ||
52 | if (param->direction == DMA_DEV_TO_MEM) { | |
c4e16625 | 53 | memset(&slave_config, 0, sizeof(struct dma_slave_config)); |
fbb20e81 BK |
54 | slave_config.direction = param->direction; |
55 | slave_config.src_addr = param->fifo; | |
56 | slave_config.src_addr_width = param->width; | |
c4e16625 BK |
57 | slave_config.src_maxburst = 1; |
58 | dmaengine_slave_config(chan, &slave_config); | |
fbb20e81 | 59 | } else if (param->direction == DMA_MEM_TO_DEV) { |
c4e16625 | 60 | memset(&slave_config, 0, sizeof(struct dma_slave_config)); |
fbb20e81 BK |
61 | slave_config.direction = param->direction; |
62 | slave_config.dst_addr = param->fifo; | |
63 | slave_config.dst_addr_width = param->width; | |
c4e16625 BK |
64 | slave_config.dst_maxburst = 1; |
65 | dmaengine_slave_config(chan, &slave_config); | |
fbb20e81 BK |
66 | } else { |
67 | pr_warn("unsupported direction\n"); | |
68 | return -EINVAL; | |
c4e16625 BK |
69 | } |
70 | ||
c4e16625 BK |
71 | return 0; |
72 | } | |
73 | ||
74 | static int samsung_dmadev_prepare(unsigned ch, | |
fbb20e81 | 75 | struct samsung_dma_prep *param) |
c4e16625 BK |
76 | { |
77 | struct scatterlist sg; | |
78 | struct dma_chan *chan = (struct dma_chan *)ch; | |
79 | struct dma_async_tx_descriptor *desc; | |
80 | ||
fbb20e81 | 81 | switch (param->cap) { |
c4e16625 BK |
82 | case DMA_SLAVE: |
83 | sg_init_table(&sg, 1); | |
fbb20e81 BK |
84 | sg_dma_len(&sg) = param->len; |
85 | sg_set_page(&sg, pfn_to_page(PFN_DOWN(param->buf)), | |
86 | param->len, offset_in_page(param->buf)); | |
87 | sg_dma_address(&sg) = param->buf; | |
c4e16625 | 88 | |
16052827 | 89 | desc = dmaengine_prep_slave_sg(chan, |
fbb20e81 | 90 | &sg, 1, param->direction, DMA_PREP_INTERRUPT); |
c4e16625 BK |
91 | break; |
92 | case DMA_CYCLIC: | |
fbb20e81 | 93 | desc = dmaengine_prep_dma_cyclic(chan, param->buf, |
b7ef37d0 PU |
94 | param->len, param->period, param->direction, |
95 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | |
c4e16625 BK |
96 | break; |
97 | default: | |
98 | dev_err(&chan->dev->device, "unsupported format\n"); | |
99 | return -EFAULT; | |
100 | } | |
101 | ||
102 | if (!desc) { | |
103 | dev_err(&chan->dev->device, "cannot prepare cyclic dma\n"); | |
104 | return -EFAULT; | |
105 | } | |
106 | ||
fbb20e81 BK |
107 | desc->callback = param->fp; |
108 | desc->callback_param = param->fp_param; | |
c4e16625 BK |
109 | |
110 | dmaengine_submit((struct dma_async_tx_descriptor *)desc); | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static inline int samsung_dmadev_trigger(unsigned ch) | |
116 | { | |
117 | dma_async_issue_pending((struct dma_chan *)ch); | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | static inline int samsung_dmadev_flush(unsigned ch) | |
123 | { | |
124 | return dmaengine_terminate_all((struct dma_chan *)ch); | |
125 | } | |
126 | ||
6d259a25 | 127 | static struct samsung_dma_ops dmadev_ops = { |
c4e16625 BK |
128 | .request = samsung_dmadev_request, |
129 | .release = samsung_dmadev_release, | |
fbb20e81 | 130 | .config = samsung_dmadev_config, |
c4e16625 BK |
131 | .prepare = samsung_dmadev_prepare, |
132 | .trigger = samsung_dmadev_trigger, | |
133 | .started = NULL, | |
134 | .flush = samsung_dmadev_flush, | |
135 | .stop = samsung_dmadev_flush, | |
136 | }; | |
137 | ||
138 | void *samsung_dmadev_get_ops(void) | |
139 | { | |
140 | return &dmadev_ops; | |
141 | } | |
142 | EXPORT_SYMBOL(samsung_dmadev_get_ops); |