Commit | Line | Data |
---|---|---|
443c1228 ST |
1 | /* |
2 | * Driver for the NXP SAA7164 PCIe bridge | |
3 | * | |
9b8b0199 | 4 | * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com> |
443c1228 ST |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | ||
5a0e3ad6 TH |
22 | #include <linux/slab.h> |
23 | ||
443c1228 ST |
24 | #include "saa7164.h" |
25 | ||
26 | /* The PCI address space for buffer handling looks like this: | |
bc250684 ST |
27 | * |
28 | * +-u32 wide-------------+ | |
29 | * | + | |
30 | * +-u64 wide------------------------------------+ | |
31 | * + + | |
32 | * +----------------------+ | |
33 | * | CurrentBufferPtr + Pointer to current PCI buffer >-+ | |
34 | * +----------------------+ | | |
35 | * | Unused + | | |
36 | * +----------------------+ | | |
37 | * | Pitch + = 188 (bytes) | | |
38 | * +----------------------+ | | |
39 | * | PCI buffer size + = pitch * number of lines (312) | | |
40 | * +----------------------+ | | |
41 | * |0| Buf0 Write Offset + | | |
42 | * +----------------------+ v | |
43 | * |1| Buf1 Write Offset + | | |
44 | * +----------------------+ | | |
45 | * |2| Buf2 Write Offset + | | |
46 | * +----------------------+ | | |
47 | * |3| Buf3 Write Offset + | | |
48 | * +----------------------+ | | |
49 | * ... More write offsets | | |
50 | * +---------------------------------------------+ | | |
51 | * +0| set of ptrs to PCI pagetables + | | |
52 | * +---------------------------------------------+ | | |
53 | * +1| set of ptrs to PCI pagetables + <--------+ | |
54 | * +---------------------------------------------+ | |
55 | * +2| set of ptrs to PCI pagetables + | |
56 | * +---------------------------------------------+ | |
57 | * +3| set of ptrs to PCI pagetables + >--+ | |
58 | * +---------------------------------------------+ | | |
59 | * ... More buffer pointers | +----------------+ | |
60 | * +->| pt[0] TS data | | |
61 | * | +----------------+ | |
62 | * | | |
63 | * | +----------------+ | |
64 | * +->| pt[1] TS data | | |
65 | * | +----------------+ | |
66 | * | etc | |
443c1228 ST |
67 | */ |
68 | ||
add3f580 ST |
69 | void saa7164_buffer_display(struct saa7164_buffer *buf) |
70 | { | |
71 | struct saa7164_dev *dev = buf->port->dev; | |
72 | int i; | |
73 | ||
74 | dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n", | |
75 | __func__, buf, buf->idx); | |
5cecdc81 MCC |
76 | dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n", |
77 | buf->cpu, (long long)buf->dma, buf->pci_size); | |
78 | dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n", | |
79 | buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size); | |
add3f580 ST |
80 | |
81 | /* Format the Page Table Entries to point into the data buffer */ | |
82 | for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { | |
83 | ||
84 | dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", | |
85 | i, buf->pt_cpu, (u64)*(buf->pt_cpu)); | |
86 | ||
87 | } | |
88 | } | |
443c1228 ST |
89 | /* Allocate a new buffer structure and associated PCI space in bytes. |
90 | * len must be a multiple of sizeof(u64) | |
91 | */ | |
add3f580 | 92 | struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, |
443c1228 ST |
93 | u32 len) |
94 | { | |
4d270cfb | 95 | struct tmHWStreamParameters *params = &port->hw_streamingparams; |
61ca1500 | 96 | struct saa7164_buffer *buf = NULL; |
443c1228 ST |
97 | struct saa7164_dev *dev = port->dev; |
98 | int i; | |
99 | ||
100 | if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { | |
101 | log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); | |
102 | goto ret; | |
103 | } | |
104 | ||
105 | buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); | |
61ca1500 | 106 | if (!buf) { |
443c1228 ST |
107 | log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); |
108 | goto ret; | |
109 | } | |
110 | ||
add3f580 | 111 | buf->idx = -1; |
443c1228 ST |
112 | buf->port = port; |
113 | buf->flags = SAA7164_BUFFER_FREE; | |
add3f580 ST |
114 | buf->pos = 0; |
115 | buf->actual_size = params->pitch * params->numberoflines; | |
12d3203e | 116 | buf->crc = 0; |
443c1228 ST |
117 | /* TODO: arg len is being ignored */ |
118 | buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; | |
119 | buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; | |
120 | ||
121 | /* Allocate contiguous memory */ | |
122 | buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, | |
123 | &buf->dma); | |
124 | if (!buf->cpu) | |
125 | goto fail1; | |
126 | ||
127 | buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, | |
128 | &buf->pt_dma); | |
129 | if (!buf->pt_cpu) | |
130 | goto fail2; | |
131 | ||
132 | /* init the buffers to a known pattern, easier during debugging */ | |
12d3203e ST |
133 | memset_io(buf->cpu, 0xff, buf->pci_size); |
134 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | |
135 | memset_io(buf->pt_cpu, 0xff, buf->pt_size); | |
443c1228 | 136 | |
1b0e8e46 ST |
137 | dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n", |
138 | __func__, buf, params->numpagetables); | |
ab205857 MCC |
139 | dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", |
140 | buf->cpu, (long)buf->dma, buf->pci_size); | |
141 | dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", | |
142 | buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); | |
443c1228 ST |
143 | |
144 | /* Format the Page Table Entries to point into the data buffer */ | |
1b0e8e46 | 145 | for (i = 0 ; i < params->numpagetables; i++) { |
443c1228 ST |
146 | |
147 | *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ | |
1b0e8e46 ST |
148 | dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", |
149 | i, buf->pt_cpu, (u64)*(buf->pt_cpu)); | |
443c1228 | 150 | |
443c1228 ST |
151 | } |
152 | ||
153 | goto ret; | |
154 | ||
155 | fail2: | |
156 | pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); | |
157 | fail1: | |
158 | kfree(buf); | |
159 | ||
61ca1500 | 160 | buf = NULL; |
443c1228 ST |
161 | ret: |
162 | return buf; | |
163 | } | |
164 | ||
add3f580 | 165 | int saa7164_buffer_dealloc(struct saa7164_buffer *buf) |
443c1228 | 166 | { |
23e64d55 | 167 | struct saa7164_dev *dev; |
443c1228 | 168 | |
add3f580 | 169 | if (!buf || !buf->port) |
443c1228 | 170 | return SAA_ERR_BAD_PARAMETER; |
add3f580 | 171 | dev = buf->port->dev; |
443c1228 | 172 | |
add3f580 ST |
173 | dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", |
174 | __func__, buf); | |
443c1228 ST |
175 | |
176 | if (buf->flags != SAA7164_BUFFER_FREE) | |
177 | log_warn(" freeing a non-free buffer\n"); | |
178 | ||
add3f580 ST |
179 | pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma); |
180 | pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma); | |
443c1228 ST |
181 | |
182 | kfree(buf); | |
183 | ||
184 | return SAA_OK; | |
185 | } | |
186 | ||
9230acaa ST |
187 | int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i) |
188 | { | |
189 | struct saa7164_dev *dev = port->dev; | |
190 | ||
191 | if ((i < 0) || (i >= port->hwcfg.buffercount)) | |
192 | return -EINVAL; | |
193 | ||
194 | dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); | |
195 | ||
196 | saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
add3f580 ST |
201 | /* Write a buffer into the hardware */ |
202 | int saa7164_buffer_activate(struct saa7164_buffer *buf, int i) | |
203 | { | |
204 | struct saa7164_port *port = buf->port; | |
205 | struct saa7164_dev *dev = port->dev; | |
206 | ||
207 | if ((i < 0) || (i >= port->hwcfg.buffercount)) | |
208 | return -EINVAL; | |
209 | ||
210 | dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); | |
211 | ||
212 | buf->idx = i; /* Note of which buffer list index position we occupy */ | |
213 | buf->flags = SAA7164_BUFFER_BUSY; | |
214 | buf->pos = 0; | |
215 | ||
216 | /* TODO: Review this in light of 32v64 assignments */ | |
217 | saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); | |
218 | saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma); | |
219 | saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); | |
220 | ||
221 | dprintk(DBGLVL_BUF, " buf[%d] offset 0x%llx (0x%x) " | |
222 | "buf 0x%llx/%llx (0x%x/%x) nr=%d\n", | |
223 | buf->idx, | |
224 | (u64)port->bufoffset + (i * sizeof(u32)), | |
225 | saa7164_readl(port->bufoffset + (sizeof(u32) * i)), | |
226 | (u64)port->bufptr32h + ((sizeof(u32) * 2) * i), | |
227 | (u64)port->bufptr32l + ((sizeof(u32) * 2) * i), | |
228 | saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)), | |
229 | saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)), | |
230 | buf->idx); | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | int saa7164_buffer_cfg_port(struct saa7164_port *port) | |
236 | { | |
4d270cfb | 237 | struct tmHWStreamParameters *params = &port->hw_streamingparams; |
add3f580 ST |
238 | struct saa7164_dev *dev = port->dev; |
239 | struct saa7164_buffer *buf; | |
240 | struct list_head *c, *n; | |
241 | int i = 0; | |
242 | ||
243 | dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr); | |
244 | ||
245 | saa7164_writel(port->bufcounter, 0); | |
246 | saa7164_writel(port->pitch, params->pitch); | |
247 | saa7164_writel(port->bufsize, params->pitch * params->numberoflines); | |
248 | ||
249 | dprintk(DBGLVL_BUF, " configured:\n"); | |
250 | dprintk(DBGLVL_BUF, " lmmio 0x%p\n", dev->lmmio); | |
251 | dprintk(DBGLVL_BUF, " bufcounter 0x%x = 0x%x\n", port->bufcounter, | |
252 | saa7164_readl(port->bufcounter)); | |
253 | ||
254 | dprintk(DBGLVL_BUF, " pitch 0x%x = %d\n", port->pitch, | |
255 | saa7164_readl(port->pitch)); | |
256 | ||
257 | dprintk(DBGLVL_BUF, " bufsize 0x%x = %d\n", port->bufsize, | |
258 | saa7164_readl(port->bufsize)); | |
259 | ||
260 | dprintk(DBGLVL_BUF, " buffercount = %d\n", port->hwcfg.buffercount); | |
261 | dprintk(DBGLVL_BUF, " bufoffset = 0x%x\n", port->bufoffset); | |
262 | dprintk(DBGLVL_BUF, " bufptr32h = 0x%x\n", port->bufptr32h); | |
263 | dprintk(DBGLVL_BUF, " bufptr32l = 0x%x\n", port->bufptr32l); | |
264 | ||
265 | /* Poke the buffers and offsets into PCI space */ | |
266 | mutex_lock(&port->dmaqueue_lock); | |
267 | list_for_each_safe(c, n, &port->dmaqueue.list) { | |
268 | buf = list_entry(c, struct saa7164_buffer, list); | |
269 | ||
270 | if (buf->flags != SAA7164_BUFFER_FREE) | |
271 | BUG(); | |
272 | ||
273 | /* Place the buffer in the h/w queue */ | |
274 | saa7164_buffer_activate(buf, i); | |
275 | ||
276 | /* Don't exceed the device maximum # bufs */ | |
277 | if (i++ > port->hwcfg.buffercount) | |
278 | BUG(); | |
279 | ||
280 | } | |
281 | mutex_unlock(&port->dmaqueue_lock); | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
bc250684 ST |
286 | struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, |
287 | u32 len) | |
7615e434 ST |
288 | { |
289 | struct saa7164_user_buffer *buf; | |
290 | ||
291 | buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL); | |
61ca1500 PH |
292 | if (!buf) |
293 | return NULL; | |
7615e434 ST |
294 | |
295 | buf->data = kzalloc(len, GFP_KERNEL); | |
296 | ||
61ca1500 | 297 | if (!buf->data) { |
7615e434 | 298 | kfree(buf); |
61ca1500 | 299 | return NULL; |
7615e434 ST |
300 | } |
301 | ||
302 | buf->actual_size = len; | |
303 | buf->pos = 0; | |
12d3203e | 304 | buf->crc = 0; |
7615e434 ST |
305 | |
306 | dprintk(DBGLVL_BUF, "%s() allocated user buffer @ 0x%p\n", | |
307 | __func__, buf); | |
308 | ||
309 | return buf; | |
310 | } | |
311 | ||
312 | void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) | |
313 | { | |
314 | if (!buf) | |
315 | return; | |
316 | ||
bc250684 | 317 | kfree(buf->data); |
61ca1500 | 318 | buf->data = NULL; |
7615e434 | 319 | |
bc250684 | 320 | kfree(buf); |
7615e434 ST |
321 | } |
322 |