aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/ivtv/ivtv-udma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/ivtv/ivtv-udma.c')
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
new file mode 100644
index 000000000000..bd642e1aafc3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -0,0 +1,200 @@
1/*
2 User DMA
3
4 Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
5 Copyright (C) 2004 Chris Kennedy <c@groovy.org>
6 Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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 as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "ivtv-driver.h"
24#include "ivtv-streams.h"
25#include "ivtv-udma.h"
26
27void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
28{
29 dma_page->uaddr = first & PAGE_MASK;
30 dma_page->offset = first & ~PAGE_MASK;
31 dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
32 dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
33 dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
34 dma_page->page_count = dma_page->last - dma_page->first + 1;
35 if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
36}
37
38int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
39{
40 int i, offset;
41
42 offset = dma_page->offset;
43
44 /* Fill SG Array with new values */
45 for (i = 0; i < dma_page->page_count; i++) {
46 if (i == dma_page->page_count - 1) {
47 dma->SGlist[map_offset].length = dma_page->tail;
48 }
49 else {
50 dma->SGlist[map_offset].length = PAGE_SIZE - offset;
51 }
52 dma->SGlist[map_offset].offset = offset;
53 dma->SGlist[map_offset].page = dma->map[map_offset];
54 offset = 0;
55 map_offset++;
56 }
57 return map_offset;
58}
59
60void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
61 int i;
62 struct scatterlist *sg;
63
64 for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
65 dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
66 dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
67 dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
68 buffer_offset += sg_dma_len(sg);
69
70 split -= sg_dma_len(sg);
71 if (split == 0)
72 buffer_offset = buffer_offset_2;
73 }
74}
75
76/* User DMA Buffers */
77void ivtv_udma_alloc(struct ivtv *itv)
78{
79 if (itv->udma.SG_handle == 0) {
80 /* Map DMA Page Array Buffer */
81 itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
82 sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
83 ivtv_udma_sync_for_cpu(itv);
84 }
85}
86
87int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
88 void __user *userbuf, int size_in_bytes)
89{
90 struct ivtv_dma_page_info user_dma;
91 struct ivtv_user_dma *dma = &itv->udma;
92 int err;
93
94 IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
95
96 /* Still in USE */
97 if (dma->SG_length || dma->page_count) {
98 IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
99 dma->SG_length, dma->page_count);
100 return -EBUSY;
101 }
102
103 ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
104
105 if (user_dma.page_count <= 0) {
106 IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
107 user_dma.page_count, size_in_bytes, user_dma.offset);
108 return -EINVAL;
109 }
110
111 /* Get user pages for DMA Xfer */
112 down_read(&current->mm->mmap_sem);
113 err = get_user_pages(current, current->mm,
114 user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
115 up_read(&current->mm->mmap_sem);
116
117 if (user_dma.page_count != err) {
118 IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
119 err, user_dma.page_count);
120 return -EINVAL;
121 }
122
123 dma->page_count = user_dma.page_count;
124
125 /* Fill SG List with new values */
126 ivtv_udma_fill_sg_list(dma, &user_dma, 0);
127
128 /* Map SG List */
129 dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
130
131 /* Fill SG Array with new values */
132 ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
133
134 /* Tag SG Array with Interrupt Bit */
135 dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
136
137 ivtv_udma_sync_for_device(itv);
138 return dma->page_count;
139}
140
141void ivtv_udma_unmap(struct ivtv *itv)
142{
143 struct ivtv_user_dma *dma = &itv->udma;
144 int i;
145
146 IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
147
148 /* Nothing to free */
149 if (dma->page_count == 0)
150 return;
151
152 /* Unmap Scatterlist */
153 if (dma->SG_length) {
154 pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
155 dma->SG_length = 0;
156 }
157 /* sync DMA */
158 ivtv_udma_sync_for_cpu(itv);
159
160 /* Release User Pages */
161 for (i = 0; i < dma->page_count; i++) {
162 put_page(dma->map[i]);
163 }
164 dma->page_count = 0;
165}
166
167void ivtv_udma_free(struct ivtv *itv)
168{
169 /* Unmap SG Array */
170 if (itv->udma.SG_handle) {
171 pci_unmap_single(itv->dev, itv->udma.SG_handle,
172 sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
173 }
174
175 /* Unmap Scatterlist */
176 if (itv->udma.SG_length) {
177 pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
178 }
179}
180
181void ivtv_udma_start(struct ivtv *itv)
182{
183 IVTV_DEBUG_DMA("start UDMA\n");
184 write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
185 write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
186 set_bit(IVTV_F_I_DMA, &itv->i_flags);
187 set_bit(IVTV_F_I_UDMA, &itv->i_flags);
188}
189
190void ivtv_udma_prepare(struct ivtv *itv)
191{
192 unsigned long flags;
193
194 spin_lock_irqsave(&itv->dma_reg_lock, flags);
195 if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
196 ivtv_udma_start(itv);
197 else
198 set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
199 spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
200}