]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1a0adaf3 HV |
2 | /* |
3 | User DMA | |
4 | ||
5 | Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> | |
6 | Copyright (C) 2004 Chris Kennedy <c@groovy.org> | |
7 | Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> | |
8 | ||
1a0adaf3 HV |
9 | */ |
10 | ||
11 | #include "ivtv-driver.h" | |
1a0adaf3 HV |
12 | #include "ivtv-udma.h" |
13 | ||
14 | void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) | |
15 | { | |
16 | dma_page->uaddr = first & PAGE_MASK; | |
17 | dma_page->offset = first & ~PAGE_MASK; | |
18 | dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK); | |
19 | dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT; | |
20 | dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT; | |
21 | dma_page->page_count = dma_page->last - dma_page->first + 1; | |
22 | if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset; | |
23 | } | |
24 | ||
25 | int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) | |
26 | { | |
27 | int i, offset; | |
0989fd2c | 28 | unsigned long flags; |
1a0adaf3 | 29 | |
8beb058f HV |
30 | if (map_offset < 0) |
31 | return map_offset; | |
32 | ||
1a0adaf3 HV |
33 | offset = dma_page->offset; |
34 | ||
35 | /* Fill SG Array with new values */ | |
36 | for (i = 0; i < dma_page->page_count; i++) { | |
0989fd2c HV |
37 | unsigned int len = (i == dma_page->page_count - 1) ? |
38 | dma_page->tail : PAGE_SIZE - offset; | |
39 | ||
0989fd2c HV |
40 | if (PageHighMem(dma->map[map_offset])) { |
41 | void *src; | |
42 | ||
43 | if (dma->bouncemap[map_offset] == NULL) | |
44 | dma->bouncemap[map_offset] = alloc_page(GFP_KERNEL); | |
45 | if (dma->bouncemap[map_offset] == NULL) | |
8beb058f | 46 | return -1; |
0989fd2c | 47 | local_irq_save(flags); |
b0b38c76 | 48 | src = kmap_atomic(dma->map[map_offset]) + offset; |
0989fd2c | 49 | memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len); |
b0b38c76 | 50 | kunmap_atomic(src); |
0989fd2c | 51 | local_irq_restore(flags); |
642f1490 | 52 | sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset); |
1a0adaf3 HV |
53 | } |
54 | else { | |
642f1490 | 55 | sg_set_page(&dma->SGlist[map_offset], dma->map[map_offset], len, offset); |
1a0adaf3 | 56 | } |
1a0adaf3 HV |
57 | offset = 0; |
58 | map_offset++; | |
59 | } | |
8beb058f | 60 | return map_offset; |
1a0adaf3 HV |
61 | } |
62 | ||
63 | void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) { | |
64 | int i; | |
65 | struct scatterlist *sg; | |
66 | ||
acf0e531 | 67 | for_each_sg(dma->SGlist, sg, dma->SG_length, i) { |
1a0adaf3 HV |
68 | dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg)); |
69 | dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg)); | |
70 | dma->SGarray[i].dst = cpu_to_le32(buffer_offset); | |
71 | buffer_offset += sg_dma_len(sg); | |
72 | ||
73 | split -= sg_dma_len(sg); | |
74 | if (split == 0) | |
75 | buffer_offset = buffer_offset_2; | |
76 | } | |
77 | } | |
78 | ||
79 | /* User DMA Buffers */ | |
80 | void ivtv_udma_alloc(struct ivtv *itv) | |
81 | { | |
82 | if (itv->udma.SG_handle == 0) { | |
83 | /* Map DMA Page Array Buffer */ | |
8ac05ae3 | 84 | itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray, |
1a0adaf3 HV |
85 | sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); |
86 | ivtv_udma_sync_for_cpu(itv); | |
87 | } | |
88 | } | |
89 | ||
90 | int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, | |
91 | void __user *userbuf, int size_in_bytes) | |
92 | { | |
93 | struct ivtv_dma_page_info user_dma; | |
94 | struct ivtv_user_dma *dma = &itv->udma; | |
e7920310 | 95 | int err; |
1a0adaf3 HV |
96 | |
97 | IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); | |
98 | ||
99 | /* Still in USE */ | |
100 | if (dma->SG_length || dma->page_count) { | |
101 | IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n", | |
102 | dma->SG_length, dma->page_count); | |
103 | return -EBUSY; | |
104 | } | |
105 | ||
106 | ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes); | |
107 | ||
108 | if (user_dma.page_count <= 0) { | |
109 | IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n", | |
110 | user_dma.page_count, size_in_bytes, user_dma.offset); | |
111 | return -EINVAL; | |
112 | } | |
113 | ||
e7920310 JH |
114 | /* Pin user pages for DMA Xfer */ |
115 | err = pin_user_pages_unlocked(user_dma.uaddr, user_dma.page_count, | |
c164154f | 116 | dma->map, FOLL_FORCE); |
1a0adaf3 HV |
117 | |
118 | if (user_dma.page_count != err) { | |
119 | IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", | |
120 | err, user_dma.page_count); | |
793e71b6 | 121 | if (err >= 0) { |
e7920310 | 122 | unpin_user_pages(dma->map, err); |
793e71b6 PC |
123 | return -EINVAL; |
124 | } | |
125 | return err; | |
1a0adaf3 HV |
126 | } |
127 | ||
128 | dma->page_count = user_dma.page_count; | |
129 | ||
130 | /* Fill SG List with new values */ | |
8beb058f | 131 | if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) { |
e7920310 | 132 | unpin_user_pages(dma->map, dma->page_count); |
0989fd2c | 133 | dma->page_count = 0; |
8beb058f | 134 | return -ENOMEM; |
0989fd2c | 135 | } |
1a0adaf3 HV |
136 | |
137 | /* Map SG List */ | |
8ac05ae3 | 138 | dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); |
1a0adaf3 HV |
139 | |
140 | /* Fill SG Array with new values */ | |
141 | ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); | |
142 | ||
143 | /* Tag SG Array with Interrupt Bit */ | |
144 | dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); | |
145 | ||
146 | ivtv_udma_sync_for_device(itv); | |
147 | return dma->page_count; | |
148 | } | |
149 | ||
150 | void ivtv_udma_unmap(struct ivtv *itv) | |
151 | { | |
152 | struct ivtv_user_dma *dma = &itv->udma; | |
1a0adaf3 HV |
153 | |
154 | IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n"); | |
155 | ||
156 | /* Nothing to free */ | |
157 | if (dma->page_count == 0) | |
158 | return; | |
159 | ||
160 | /* Unmap Scatterlist */ | |
161 | if (dma->SG_length) { | |
8ac05ae3 | 162 | pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); |
1a0adaf3 HV |
163 | dma->SG_length = 0; |
164 | } | |
165 | /* sync DMA */ | |
166 | ivtv_udma_sync_for_cpu(itv); | |
167 | ||
e7920310 | 168 | unpin_user_pages(dma->map, dma->page_count); |
1a0adaf3 HV |
169 | dma->page_count = 0; |
170 | } | |
171 | ||
172 | void ivtv_udma_free(struct ivtv *itv) | |
173 | { | |
0989fd2c HV |
174 | int i; |
175 | ||
1a0adaf3 HV |
176 | /* Unmap SG Array */ |
177 | if (itv->udma.SG_handle) { | |
8ac05ae3 | 178 | pci_unmap_single(itv->pdev, itv->udma.SG_handle, |
1a0adaf3 HV |
179 | sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); |
180 | } | |
181 | ||
182 | /* Unmap Scatterlist */ | |
183 | if (itv->udma.SG_length) { | |
8ac05ae3 | 184 | pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); |
1a0adaf3 | 185 | } |
0989fd2c HV |
186 | |
187 | for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) { | |
188 | if (itv->udma.bouncemap[i]) | |
189 | __free_page(itv->udma.bouncemap[i]); | |
190 | } | |
1a0adaf3 HV |
191 | } |
192 | ||
193 | void ivtv_udma_start(struct ivtv *itv) | |
194 | { | |
195 | IVTV_DEBUG_DMA("start UDMA\n"); | |
196 | write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR); | |
197 | write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); | |
198 | set_bit(IVTV_F_I_DMA, &itv->i_flags); | |
199 | set_bit(IVTV_F_I_UDMA, &itv->i_flags); | |
b6e436b2 | 200 | clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); |
1a0adaf3 HV |
201 | } |
202 | ||
203 | void ivtv_udma_prepare(struct ivtv *itv) | |
204 | { | |
205 | unsigned long flags; | |
206 | ||
207 | spin_lock_irqsave(&itv->dma_reg_lock, flags); | |
208 | if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) | |
209 | ivtv_udma_start(itv); | |
210 | else | |
211 | set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); | |
212 | spin_unlock_irqrestore(&itv->dma_reg_lock, flags); | |
213 | } |