Blame view

drivers/media/video/videobuf-dma-sg.c 18 KB
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
1
  /*
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
2
   * helper functions for SG DMA video4linux capture buffers
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
3
   *
5d6aaf50e   Magnus Damm   V4L/DVB (8340): v...
4
   * The functions expect the hardware being able to scatter gather
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   * (i.e. the buffers are not linear in physical memory, but fragmented
   * into PAGE_SIZE chunks).  They also assume the driver does not need
   * to touch the video data.
   *
   * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
   *
   * Highly based on video-buf written originally by:
   * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
   * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
   * (c) 2006 Ted Walther and John Sokol
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
26
  #include <linux/dma-mapping.h>
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
27
28
  #include <linux/vmalloc.h>
  #include <linux/pagemap.h>
117636092   Ralf Baechle   [PATCH] Fix break...
29
  #include <linux/scatterlist.h>
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
30
31
32
33
34
35
36
37
38
39
40
  #include <asm/page.h>
  #include <asm/pgtable.h>
  
  #include <media/videobuf-dma-sg.h>
  
  #define MAGIC_DMABUF 0x19721112
  #define MAGIC_SG_MEM 0x17890714
  
  #define MAGIC_CHECK(is,should)	if (unlikely((is) != (should))) \
  	{ printk(KERN_ERR "magic mismatch: %x (expected %x)
  ",is,should); BUG(); }
ff699e6bd   Douglas Schilling Landgraf   V4L/DVB (7094): ...
41
  static int debug;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
42
  module_param(debug, int, 0644);
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
43
  MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
  MODULE_LICENSE("GPL");
  
  #define dprintk(level, fmt, arg...)	if (debug >= level) \
  	printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
  
  /* --------------------------------------------------------------------- */
  
  struct scatterlist*
  videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages)
  {
  	struct scatterlist *sglist;
  	struct page *pg;
  	int i;
  
  	sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
  	if (NULL == sglist)
  		return NULL;
45711f1af   Jens Axboe   [SG] Update drive...
62
  	sg_init_table(sglist, nr_pages);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
63
64
65
66
67
  	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
  		pg = vmalloc_to_page(virt);
  		if (NULL == pg)
  			goto err;
  		BUG_ON(PageHighMem(pg));
642f14903   Jens Axboe   SG: Change sg_set...
68
  		sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
69
70
71
72
73
74
75
76
77
78
79
80
  	}
  	return sglist;
  
   err:
  	kfree(sglist);
  	return NULL;
  }
  
  struct scatterlist*
  videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset)
  {
  	struct scatterlist *sglist;
a47cacbd8   Christophe Jaillet   V4L/DVB (8252): b...
81
  	int i;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
82
83
84
  
  	if (NULL == pages[0])
  		return NULL;
a47cacbd8   Christophe Jaillet   V4L/DVB (8252): b...
85
  	sglist = kmalloc(nr_pages * sizeof(*sglist), GFP_KERNEL);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
86
87
  	if (NULL == sglist)
  		return NULL;
45711f1af   Jens Axboe   [SG] Update drive...
88
  	sg_init_table(sglist, nr_pages);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
89

7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
90
91
92
  	if (PageHighMem(pages[0]))
  		/* DMA to highmem pages might not work */
  		goto highmem;
642f14903   Jens Axboe   SG: Change sg_set...
93
  	sg_set_page(&sglist[0], pages[0], PAGE_SIZE - offset, offset);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
94
95
96
97
98
  	for (i = 1; i < nr_pages; i++) {
  		if (NULL == pages[i])
  			goto nopage;
  		if (PageHighMem(pages[i]))
  			goto highmem;
642f14903   Jens Axboe   SG: Change sg_set...
99
  		sg_set_page(&sglist[i], pages[i], PAGE_SIZE, 0);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  	}
  	return sglist;
  
   nopage:
  	dprintk(2,"sgl: oops - no page
  ");
  	kfree(sglist);
  	return NULL;
  
   highmem:
  	dprintk(2,"sgl: oops - highmem page
  ");
  	kfree(sglist);
  	return NULL;
  }
  
  /* --------------------------------------------------------------------- */
  
  struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf)
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
120
121
  	struct videobuf_dma_sg_memory *mem = buf->priv;
  	BUG_ON(!mem);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
122

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
123
  	MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
124
125
126
127
128
129
130
131
132
  
  	return &mem->dma;
  }
  
  void videobuf_dma_init(struct videobuf_dmabuf *dma)
  {
  	memset(dma,0,sizeof(*dma));
  	dma->magic = MAGIC_DMABUF;
  }
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
133
134
  static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
  			int direction, unsigned long data, unsigned long size)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
135
136
137
138
139
140
  {
  	unsigned long first,last;
  	int err, rw = 0;
  
  	dma->direction = direction;
  	switch (dma->direction) {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
141
142
143
144
145
146
147
148
  	case DMA_FROM_DEVICE:
  		rw = READ;
  		break;
  	case DMA_TO_DEVICE:
  		rw = WRITE;
  		break;
  	default:
  		BUG();
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
149
150
151
152
153
154
155
156
157
158
159
160
161
  	}
  
  	first = (data          & PAGE_MASK) >> PAGE_SHIFT;
  	last  = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
  	dma->offset   = data & ~PAGE_MASK;
  	dma->nr_pages = last-first+1;
  	dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*),
  			     GFP_KERNEL);
  	if (NULL == dma->pages)
  		return -ENOMEM;
  	dprintk(1,"init user [0x%lx+0x%lx => %d pages]
  ",
  		data,size,dma->nr_pages);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
162
163
164
165
  	err = get_user_pages(current,current->mm,
  			     data & PAGE_MASK, dma->nr_pages,
  			     rw == READ, 1, /* force */
  			     dma->pages, NULL);
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
166

7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
167
168
169
170
171
172
173
174
  	if (err != dma->nr_pages) {
  		dma->nr_pages = (err >= 0) ? err : 0;
  		dprintk(1,"get_user_pages: err=%d [%d]
  ",err,dma->nr_pages);
  		return err < 0 ? err : -EINVAL;
  	}
  	return 0;
  }
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
175
176
177
178
179
180
181
182
183
184
  int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
  			   unsigned long data, unsigned long size)
  {
  	int ret;
  	down_read(&current->mm->mmap_sem);
  	ret = videobuf_dma_init_user_locked(dma, direction, data, size);
  	up_read(&current->mm->mmap_sem);
  
  	return ret;
  }
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
  			     int nr_pages)
  {
  	dprintk(1,"init kernel [%d pages]
  ",nr_pages);
  	dma->direction = direction;
  	dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT);
  	if (NULL == dma->vmalloc) {
  		dprintk(1,"vmalloc_32(%d pages) failed
  ",nr_pages);
  		return -ENOMEM;
  	}
  	dprintk(1,"vmalloc is at addr 0x%08lx, size=%d
  ",
  				(unsigned long)dma->vmalloc,
  				nr_pages << PAGE_SHIFT);
  	memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT);
  	dma->nr_pages = nr_pages;
  	return 0;
  }
  
  int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
  			      dma_addr_t addr, int nr_pages)
  {
  	dprintk(1,"init overlay [%d pages @ bus 0x%lx]
  ",
  		nr_pages,(unsigned long)addr);
  	dma->direction = direction;
  	if (0 == addr)
  		return -EINVAL;
  
  	dma->bus_addr = addr;
  	dma->nr_pages = nr_pages;
  	return 0;
  }
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
220
  int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
221
  {
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
  	BUG_ON(0 == dma->nr_pages);
  
  	if (dma->pages) {
  		dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
  						   dma->offset);
  	}
  	if (dma->vmalloc) {
  		dma->sglist = videobuf_vmalloc_to_sg
  						(dma->vmalloc,dma->nr_pages);
  	}
  	if (dma->bus_addr) {
  		dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
  		if (NULL != dma->sglist) {
  			dma->sglen  = 1;
  			sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK;
  			dma->sglist[0].offset           = dma->bus_addr & ~PAGE_MASK;
  			sg_dma_len(&dma->sglist[0])     = dma->nr_pages * PAGE_SIZE;
  		}
  	}
  	if (NULL == dma->sglist) {
  		dprintk(1,"scatterlist is NULL
  ");
  		return -ENOMEM;
  	}
  	if (!dma->bus_addr) {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
248
  		dma->sglen = dma_map_sg(q->dev, dma->sglist,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
249
  					dma->nr_pages, dma->direction);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
250
251
  		if (0 == dma->sglen) {
  			printk(KERN_WARNING
7e28adb24   Harvey Harrison   V4L/DVB (7518): m...
252
253
  			       "%s: videobuf_map_sg failed
  ",__func__);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
254
255
256
257
258
259
260
261
  			kfree(dma->sglist);
  			dma->sglist = NULL;
  			dma->sglen = 0;
  			return -EIO;
  		}
  	}
  	return 0;
  }
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
262
  int videobuf_dma_sync(struct videobuf_queue *q, struct videobuf_dmabuf *dma)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
263
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
264
  	MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
265
  	BUG_ON(!dma->sglen);
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
266
  	dma_sync_sg_for_cpu(q->dev, dma->sglist, dma->nr_pages, dma->direction);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
267
268
269
270
271
  	return 0;
  }
  
  int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma)
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
272
  	MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
273
274
  	if (!dma->sglen)
  		return 0;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
275
  	dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction);
5ddff4343   Mauro Carvalho Chehab   V4L/DVB (6290): r...
276

7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  	kfree(dma->sglist);
  	dma->sglist = NULL;
  	dma->sglen = 0;
  	return 0;
  }
  
  int videobuf_dma_free(struct videobuf_dmabuf *dma)
  {
  	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
  	BUG_ON(dma->sglen);
  
  	if (dma->pages) {
  		int i;
  		for (i=0; i < dma->nr_pages; i++)
  			page_cache_release(dma->pages[i]);
  		kfree(dma->pages);
  		dma->pages = NULL;
  	}
  
  	vfree(dma->vmalloc);
  	dma->vmalloc = NULL;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
298
299
300
301
  
  	if (dma->bus_addr) {
  		dma->bus_addr = 0;
  	}
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
302
  	dma->direction = DMA_NONE;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
303
304
305
306
  	return 0;
  }
  
  /* --------------------------------------------------------------------- */
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
307
  int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
308
309
  {
  	struct videobuf_queue q;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
310

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
311
  	q.dev = dev;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
312

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
313
  	return videobuf_dma_map(&q, dma);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
314
  }
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
315
  int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
316
317
  {
  	struct videobuf_queue q;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
318

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
319
  	q.dev = dev;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
320

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
321
  	return videobuf_dma_unmap(&q, dma);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  }
  
  /* --------------------------------------------------------------------- */
  
  static void
  videobuf_vm_open(struct vm_area_struct *vma)
  {
  	struct videobuf_mapping *map = vma->vm_private_data;
  
  	dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]
  ",map,
  		map->count,vma->vm_start,vma->vm_end);
  	map->count++;
  }
  
  static void
  videobuf_vm_close(struct vm_area_struct *vma)
  {
  	struct videobuf_mapping *map = vma->vm_private_data;
  	struct videobuf_queue *q = map->q;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
342
  	struct videobuf_dma_sg_memory *mem;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
343
344
345
346
347
348
349
350
351
352
  	int i;
  
  	dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]
  ",map,
  		map->count,vma->vm_start,vma->vm_end);
  
  	map->count--;
  	if (0 == map->count) {
  		dprintk(1,"munmap %p q=%p
  ",map,q);
64f9477f9   Mauro Carvalho Chehab   V4L/DVB (7121): R...
353
  		mutex_lock(&q->vb_lock);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
354
355
356
357
358
359
360
361
362
  		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  			if (NULL == q->bufs[i])
  				continue;
  			mem=q->bufs[i]->priv;
  
  			if (!mem)
  				continue;
  
  			MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);
851c0c96b   Mauro Carvalho Chehab   V4L/DVB (6266): v...
363
  			if (q->bufs[i]->map != map)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
364
  				continue;
851c0c96b   Mauro Carvalho Chehab   V4L/DVB (6266): v...
365
  			q->bufs[i]->map   = NULL;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
366
367
368
  			q->bufs[i]->baddr = 0;
  			q->ops->buf_release(q,q->bufs[i]);
  		}
64f9477f9   Mauro Carvalho Chehab   V4L/DVB (7121): R...
369
  		mutex_unlock(&q->vb_lock);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
370
371
372
373
374
375
376
377
378
379
380
  		kfree(map);
  	}
  	return;
  }
  
  /*
   * Get a anonymous page for the mapping.  Make sure we can DMA to that
   * memory location with 32bit PCI devices (i.e. don't use highmem for
   * now ...).  Bounce buffers don't work very well for the data rates
   * video capture has.
   */
105354a0f   Nick Piggin   V4L/DVB (6748): S...
381
382
  static int
  videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
383
384
  {
  	struct page *page;
105354a0f   Nick Piggin   V4L/DVB (6748): S...
385
386
387
  	dprintk(3,"fault: fault @ %08lx [vma %08lx-%08lx]
  ",
  		(unsigned long)vmf->virtual_address,vma->vm_start,vma->vm_end);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
388
389
  	page = alloc_page(GFP_USER | __GFP_DMA32);
  	if (!page)
105354a0f   Nick Piggin   V4L/DVB (6748): S...
390
  		return VM_FAULT_OOM;
c0cd5010e   Guennadi Liakhovetski   V4L/DVB (10176a):...
391
  	clear_user_highpage(page, (unsigned long)vmf->virtual_address);
105354a0f   Nick Piggin   V4L/DVB (6748): S...
392
393
  	vmf->page = page;
  	return 0;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
394
395
396
397
398
399
  }
  
  static struct vm_operations_struct videobuf_vm_ops =
  {
  	.open     = videobuf_vm_open,
  	.close    = videobuf_vm_close,
105354a0f   Nick Piggin   V4L/DVB (6748): S...
400
  	.fault    = videobuf_vm_fault,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
401
402
403
  };
  
  /* ---------------------------------------------------------------------
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
404
   * SG handlers for the generic methods
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
405
406
407
408
409
   */
  
  /* Allocated area consists on 3 parts:
  	struct video_buffer
  	struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
410
  	struct videobuf_dma_sg_memory
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
411
412
413
414
   */
  
  static void *__videobuf_alloc(size_t size)
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
415
  	struct videobuf_dma_sg_memory *mem;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
416
417
418
419
420
421
422
423
424
425
426
  	struct videobuf_buffer *vb;
  
  	vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);
  
  	mem = vb->priv = ((char *)vb)+size;
  	mem->magic=MAGIC_SG_MEM;
  
  	videobuf_dma_init(&mem->dma);
  
  	dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)
  ",
7e28adb24   Harvey Harrison   V4L/DVB (7518): m...
427
  		__func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
428
429
430
431
  		mem,(long)sizeof(*mem));
  
  	return vb;
  }
59d344899   Mauro Carvalho Chehab   V4L/DVB (7566): v...
432
433
434
435
436
437
438
439
440
  static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf)
  {
  	struct videobuf_dma_sg_memory *mem = buf->priv;
  	BUG_ON(!mem);
  
  	MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
  
  	return mem->dma.vmalloc;
  }
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
441
442
443
444
445
446
  static int __videobuf_iolock (struct videobuf_queue* q,
  			      struct videobuf_buffer *vb,
  			      struct v4l2_framebuffer *fbuf)
  {
  	int err,pages;
  	dma_addr_t bus;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
447
  	struct videobuf_dma_sg_memory *mem = vb->priv;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
448
  	BUG_ON(!mem);
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
449
  	MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
450
451
452
453
454
455
456
457
  
  	switch (vb->memory) {
  	case V4L2_MEMORY_MMAP:
  	case V4L2_MEMORY_USERPTR:
  		if (0 == vb->baddr) {
  			/* no userspace addr -- kernel bounce buffer */
  			pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
  			err = videobuf_dma_init_kernel( &mem->dma,
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
458
  							DMA_FROM_DEVICE,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
459
460
461
  							pages );
  			if (0 != err)
  				return err;
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
462
  		} else if (vb->memory == V4L2_MEMORY_USERPTR) {
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
463
464
  			/* dma directly to userspace */
  			err = videobuf_dma_init_user( &mem->dma,
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
465
  						      DMA_FROM_DEVICE,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
466
467
468
  						      vb->baddr,vb->bsize );
  			if (0 != err)
  				return err;
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
469
470
471
472
473
474
475
  		} else {
  			/* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
  			buffers can only be called from videobuf_qbuf
  			we take current->mm->mmap_sem there, to prevent
  			locking inversion, so don't take it here */
  
  			err = videobuf_dma_init_user_locked(&mem->dma,
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
476
  						      DMA_FROM_DEVICE,
9900132f3   Maxim Levitsky   V4L/DVB (6268): V...
477
478
479
  						      vb->baddr, vb->bsize);
  			if (0 != err)
  				return err;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
480
481
482
483
484
485
486
487
488
489
490
491
492
  		}
  		break;
  	case V4L2_MEMORY_OVERLAY:
  		if (NULL == fbuf)
  			return -EINVAL;
  		/* FIXME: need sanity checks for vb->boff */
  		/*
  		 * Using a double cast to avoid compiler warnings when
  		 * building for PAE. Compiler doesn't like direct casting
  		 * of a 32 bit ptr to 64 bit integer.
  		 */
  		bus   = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
  		pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
493
  		err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
494
495
496
497
498
499
500
  						bus, pages);
  		if (0 != err)
  			return err;
  		break;
  	default:
  		BUG();
  	}
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
501
  	err = videobuf_dma_map(q, &mem->dma);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
502
503
504
505
506
507
508
509
510
  	if (0 != err)
  		return err;
  
  	return 0;
  }
  
  static int __videobuf_sync(struct videobuf_queue *q,
  			   struct videobuf_buffer *buf)
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
511
512
  	struct videobuf_dma_sg_memory *mem = buf->priv;
  	BUG_ON(!mem);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
513
514
515
516
517
518
519
520
521
522
523
  	MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);
  
  	return	videobuf_dma_sync(q,&mem->dma);
  }
  
  static int __videobuf_mmap_free(struct videobuf_queue *q)
  {
  	int i;
  
  	for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  		if (q->bufs[i]) {
851c0c96b   Mauro Carvalho Chehab   V4L/DVB (6266): v...
524
  			if (q->bufs[i]->map)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
525
526
527
528
529
530
531
532
533
534
  				return -EBUSY;
  		}
  	}
  
  	return 0;
  }
  
  static int __videobuf_mmap_mapper(struct videobuf_queue *q,
  			 struct vm_area_struct *vma)
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
535
  	struct videobuf_dma_sg_memory *mem;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  	struct videobuf_mapping *map;
  	unsigned int first,last,size,i;
  	int retval;
  
  	retval = -EINVAL;
  	if (!(vma->vm_flags & VM_WRITE)) {
  		dprintk(1,"mmap app bug: PROT_WRITE please
  ");
  		goto done;
  	}
  	if (!(vma->vm_flags & VM_SHARED)) {
  		dprintk(1,"mmap app bug: MAP_SHARED please
  ");
  		goto done;
  	}
ce54093ce   Brandon Philips   V4L/DVB (7494): v...
551
552
553
554
555
556
557
  	/* This function maintains backwards compatibility with V4L1 and will
  	 * map more than one buffer if the vma length is equal to the combined
  	 * size of multiple buffers than it will map them together.  See
  	 * VIDIOCGMBUF in the v4l spec
  	 *
  	 * TODO: Allow drivers to specify if they support this mode
  	 */
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
558
559
560
561
562
  	/* look for first buffer to map */
  	for (first = 0; first < VIDEO_MAX_FRAME; first++) {
  		if (NULL == q->bufs[first])
  			continue;
  		mem=q->bufs[first]->priv;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
563
  		BUG_ON(!mem);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  		MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);
  
  		if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
  			continue;
  		if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
  			break;
  	}
  	if (VIDEO_MAX_FRAME == first) {
  		dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]
  ",
  			(vma->vm_pgoff << PAGE_SHIFT));
  		goto done;
  	}
  
  	/* look for last buffer to map */
  	for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
  		if (NULL == q->bufs[last])
  			continue;
  		if (V4L2_MEMORY_MMAP != q->bufs[last]->memory)
  			continue;
851c0c96b   Mauro Carvalho Chehab   V4L/DVB (6266): v...
584
  		if (q->bufs[last]->map) {
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  			retval = -EBUSY;
  			goto done;
  		}
  		size += q->bufs[last]->bsize;
  		if (size == (vma->vm_end - vma->vm_start))
  			break;
  	}
  	if (VIDEO_MAX_FRAME == last) {
  		dprintk(1,"mmap app bug: size invalid [size=0x%lx]
  ",
  			(vma->vm_end - vma->vm_start));
  		goto done;
  	}
  
  	/* create mapping + update buffer list */
  	retval = -ENOMEM;
  	map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL);
  	if (NULL == map)
  		goto done;
ce54093ce   Brandon Philips   V4L/DVB (7494): v...
604
605
606
607
608
  
  	size = 0;
  	for (i = first; i <= last; i++) {
  		if (NULL == q->bufs[i])
  			continue;
851c0c96b   Mauro Carvalho Chehab   V4L/DVB (6266): v...
609
  		q->bufs[i]->map   = map;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
610
  		q->bufs[i]->baddr = vma->vm_start + size;
ce54093ce   Brandon Philips   V4L/DVB (7494): v...
611
  		size += q->bufs[i]->bsize;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
612
  	}
ce54093ce   Brandon Philips   V4L/DVB (7494): v...
613

7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  	map->count    = 1;
  	map->start    = vma->vm_start;
  	map->end      = vma->vm_end;
  	map->q        = q;
  	vma->vm_ops   = &videobuf_vm_ops;
  	vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
  	vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
  	vma->vm_private_data = map;
  	dprintk(1,"mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d
  ",
  		map,q,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last);
  	retval = 0;
  
   done:
  	return retval;
  }
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
630
631
632
633
  static int __videobuf_copy_to_user ( struct videobuf_queue *q,
  				char __user *data, size_t count,
  				int nonblocking )
  {
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
634
635
  	struct videobuf_dma_sg_memory *mem = q->read_buf->priv;
  	BUG_ON(!mem);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  	MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);
  
  	/* copy to userspace */
  	if (count > q->read_buf->size - q->read_off)
  		count = q->read_buf->size - q->read_off;
  
  	if (copy_to_user(data, mem->dma.vmalloc+q->read_off, count))
  		return -EFAULT;
  
  	return count;
  }
  
  static int __videobuf_copy_stream ( struct videobuf_queue *q,
  				char __user *data, size_t count, size_t pos,
  				int vbihack, int nonblocking )
  {
  	unsigned int  *fc;
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
653
654
  	struct videobuf_dma_sg_memory *mem = q->read_buf->priv;
  	BUG_ON(!mem);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
  	MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);
  
  	if (vbihack) {
  		/* dirty, undocumented hack -- pass the frame counter
  			* within the last four bytes of each vbi data block.
  			* We need that one to maintain backward compatibility
  			* to all vbi decoding software out there ... */
  		fc  = (unsigned int*)mem->dma.vmalloc;
  		fc += (q->read_buf->size>>2) -1;
  		*fc = q->read_buf->field_count >> 1;
  		dprintk(1,"vbihack: %d
  ",*fc);
  	}
  
  	/* copy stuff using the common method */
  	count = __videobuf_copy_to_user (q,data,count,nonblocking);
  
  	if ( (count==-EFAULT) && (0 == pos) )
  		return -EFAULT;
  
  	return count;
  }
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
677
  static struct videobuf_qtype_ops sg_ops = {
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
678
679
680
681
682
683
684
  	.magic        = MAGIC_QTYPE_OPS,
  
  	.alloc        = __videobuf_alloc,
  	.iolock       = __videobuf_iolock,
  	.sync         = __videobuf_sync,
  	.mmap_free    = __videobuf_mmap_free,
  	.mmap_mapper  = __videobuf_mmap_mapper,
13bcd5d0e   Al Viro   v4l: copy_to_user...
685
  	.video_copy_to_user = __videobuf_copy_to_user,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
686
  	.copy_stream  = __videobuf_copy_stream,
59d344899   Mauro Carvalho Chehab   V4L/DVB (7566): v...
687
  	.vmalloc      = __videobuf_to_vmalloc,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
688
  };
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
689
  void *videobuf_sg_alloc(size_t size)
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
690
691
692
693
  {
  	struct videobuf_queue q;
  
  	/* Required to make generic handler to call __videobuf_alloc */
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
694
  	q.int_ops = &sg_ops;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
695

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
696
  	q.msize = size;
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
697

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
698
  	return videobuf_alloc(&q);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
699
  }
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
700
  void videobuf_queue_sg_init(struct videobuf_queue* q,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
701
  			 struct videobuf_queue_ops *ops,
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
702
  			 struct device *dev,
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
703
704
705
706
707
708
  			 spinlock_t *irqlock,
  			 enum v4l2_buf_type type,
  			 enum v4l2_field field,
  			 unsigned int msize,
  			 void *priv)
  {
d4cae5a50   Mauro Carvalho Chehab   V4L/DVB (6292): v...
709
  	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
710
  				 priv, &sg_ops);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
711
  }
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
712
713
714
715
716
717
718
719
720
721
722
723
724
  /* --------------------------------------------------------------------- */
  
  EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg);
  
  EXPORT_SYMBOL_GPL(videobuf_to_dma);
  EXPORT_SYMBOL_GPL(videobuf_dma_init);
  EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
  EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
  EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
  EXPORT_SYMBOL_GPL(videobuf_dma_map);
  EXPORT_SYMBOL_GPL(videobuf_dma_sync);
  EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
  EXPORT_SYMBOL_GPL(videobuf_dma_free);
0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
725
726
727
  EXPORT_SYMBOL_GPL(videobuf_sg_dma_map);
  EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap);
  EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
728

0705135e5   Guennadi Liakhovetski   V4L/DVB (7237): C...
729
  EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
7a7d9a89d   Mauro Carvalho Chehab   V4L/DVB (6251): R...
730
731
732
733
734
735
  
  /*
   * Local variables:
   * c-basic-offset: 8
   * End:
   */