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