[media] v4l: videobuf2: add DMA coherent allocator
[linux-2.6.git] / drivers / media / video / videobuf2-dma-contig.c
1 /*
2  * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
3  *
4  * Copyright (C) 2010 Samsung Electronics
5  *
6  * Author: Pawel Osciak <p.osciak@samsung.com>
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.
11  */
12
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/dma-mapping.h>
16
17 #include <media/videobuf2-core.h>
18 #include <media/videobuf2-memops.h>
19
20 struct vb2_dc_conf {
21         struct device           *dev;
22 };
23
24 struct vb2_dc_buf {
25         struct vb2_dc_conf              *conf;
26         void                            *vaddr;
27         dma_addr_t                      paddr;
28         unsigned long                   size;
29         struct vm_area_struct           *vma;
30         atomic_t                        refcount;
31         struct vb2_vmarea_handler       handler;
32 };
33
34 static void vb2_dma_contig_put(void *buf_priv);
35
36 static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
37 {
38         struct vb2_dc_conf *conf = alloc_ctx;
39         struct vb2_dc_buf *buf;
40
41         buf = kzalloc(sizeof *buf, GFP_KERNEL);
42         if (!buf)
43                 return ERR_PTR(-ENOMEM);
44
45         buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr,
46                                         GFP_KERNEL);
47         if (!buf->vaddr) {
48                 dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
49                         buf->size);
50                 kfree(buf);
51                 return ERR_PTR(-ENOMEM);
52         }
53
54         buf->conf = conf;
55         buf->size = size;
56
57         buf->handler.refcount = &buf->refcount;
58         buf->handler.put = vb2_dma_contig_put;
59         buf->handler.arg = buf;
60
61         atomic_inc(&buf->refcount);
62
63         return buf;
64 }
65
66 static void vb2_dma_contig_put(void *buf_priv)
67 {
68         struct vb2_dc_buf *buf = buf_priv;
69
70         if (atomic_dec_and_test(&buf->refcount)) {
71                 dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
72                                   buf->paddr);
73                 kfree(buf);
74         }
75 }
76
77 static void *vb2_dma_contig_cookie(void *buf_priv)
78 {
79         struct vb2_dc_buf *buf = buf_priv;
80
81         return (void *)buf->paddr;
82 }
83
84 static void *vb2_dma_contig_vaddr(void *buf_priv)
85 {
86         struct vb2_dc_buf *buf = buf_priv;
87         if (!buf)
88                 return 0;
89
90         return buf->vaddr;
91 }
92
93 static unsigned int vb2_dma_contig_num_users(void *buf_priv)
94 {
95         struct vb2_dc_buf *buf = buf_priv;
96
97         return atomic_read(&buf->refcount);
98 }
99
100 static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
101 {
102         struct vb2_dc_buf *buf = buf_priv;
103
104         if (!buf) {
105                 printk(KERN_ERR "No buffer to map\n");
106                 return -EINVAL;
107         }
108
109         return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
110                                   &vb2_common_vm_ops, &buf->handler);
111 }
112
113 static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
114                                         unsigned long size, int write)
115 {
116         struct vb2_dc_buf *buf;
117         struct vm_area_struct *vma;
118         dma_addr_t paddr = 0;
119         int ret;
120
121         buf = kzalloc(sizeof *buf, GFP_KERNEL);
122         if (!buf)
123                 return ERR_PTR(-ENOMEM);
124
125         ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
126         if (ret) {
127                 printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
128                                 vaddr);
129                 kfree(buf);
130                 return ERR_PTR(ret);
131         }
132
133         buf->size = size;
134         buf->paddr = paddr;
135         buf->vma = vma;
136
137         return buf;
138 }
139
140 static void vb2_dma_contig_put_userptr(void *mem_priv)
141 {
142         struct vb2_dc_buf *buf = mem_priv;
143
144         if (!buf)
145                 return;
146
147         vb2_put_vma(buf->vma);
148         kfree(buf);
149 }
150
151 const struct vb2_mem_ops vb2_dma_contig_memops = {
152         .alloc          = vb2_dma_contig_alloc,
153         .put            = vb2_dma_contig_put,
154         .cookie         = vb2_dma_contig_cookie,
155         .vaddr          = vb2_dma_contig_vaddr,
156         .mmap           = vb2_dma_contig_mmap,
157         .get_userptr    = vb2_dma_contig_get_userptr,
158         .put_userptr    = vb2_dma_contig_put_userptr,
159         .num_users      = vb2_dma_contig_num_users,
160 };
161 EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
162
163 void *vb2_dma_contig_init_ctx(struct device *dev)
164 {
165         struct vb2_dc_conf *conf;
166
167         conf = kzalloc(sizeof *conf, GFP_KERNEL);
168         if (!conf)
169                 return ERR_PTR(-ENOMEM);
170
171         conf->dev = dev;
172
173         return conf;
174 }
175 EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
176
177 void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
178 {
179         kfree(alloc_ctx);
180 }
181 EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
182
183 MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
184 MODULE_AUTHOR("Pawel Osciak");
185 MODULE_LICENSE("GPL");