]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/media/v4l2-core/videobuf2-dma-contig.c
20c95da7aa77cffd0d1500e862f504dacbf6fd74
[karo-tx-linux.git] / drivers / media / v4l2-core / 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 <pawel@osciak.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-dma-contig.h>
19 #include <media/videobuf2-memops.h>
20
21 struct vb2_dc_conf {
22         struct device           *dev;
23 };
24
25 struct vb2_dc_buf {
26         struct device                   *dev;
27         void                            *vaddr;
28         dma_addr_t                      dma_addr;
29         unsigned long                   size;
30         struct vm_area_struct           *vma;
31         atomic_t                        refcount;
32         struct vb2_vmarea_handler       handler;
33 };
34
35 static void vb2_dc_put(void *buf_priv);
36
37 static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size)
38 {
39         struct vb2_dc_conf *conf = alloc_ctx;
40         struct device *dev = conf->dev;
41         struct vb2_dc_buf *buf;
42
43         buf = kzalloc(sizeof *buf, GFP_KERNEL);
44         if (!buf)
45                 return ERR_PTR(-ENOMEM);
46
47         buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL);
48         if (!buf->vaddr) {
49                 dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
50                 kfree(buf);
51                 return ERR_PTR(-ENOMEM);
52         }
53
54         buf->dev = dev;
55         buf->size = size;
56
57         buf->handler.refcount = &buf->refcount;
58         buf->handler.put = vb2_dc_put;
59         buf->handler.arg = buf;
60
61         atomic_inc(&buf->refcount);
62
63         return buf;
64 }
65
66 static void vb2_dc_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->dev, buf->size, buf->vaddr,
72                                   buf->dma_addr);
73                 kfree(buf);
74         }
75 }
76
77 static void *vb2_dc_cookie(void *buf_priv)
78 {
79         struct vb2_dc_buf *buf = buf_priv;
80
81         return &buf->dma_addr;
82 }
83
84 static void *vb2_dc_vaddr(void *buf_priv)
85 {
86         struct vb2_dc_buf *buf = buf_priv;
87         if (!buf)
88                 return NULL;
89
90         return buf->vaddr;
91 }
92
93 static unsigned int vb2_dc_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_dc_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->dma_addr, buf->size,
110                                   &vb2_common_vm_ops, &buf->handler);
111 }
112
113 static void *vb2_dc_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 dma_addr = 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, &dma_addr);
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->dma_addr = dma_addr;
135         buf->vma = vma;
136
137         return buf;
138 }
139
140 static void vb2_dc_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_dc_alloc,
153         .put            = vb2_dc_put,
154         .cookie         = vb2_dc_cookie,
155         .vaddr          = vb2_dc_vaddr,
156         .mmap           = vb2_dc_mmap,
157         .get_userptr    = vb2_dc_get_userptr,
158         .put_userptr    = vb2_dc_put_userptr,
159         .num_users      = vb2_dc_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 <pawel@osciak.com>");
185 MODULE_LICENSE("GPL");