Commit | Line | Data |
---|---|---|
b2df26c1 ID |
1 | /* exynos_drm_dmabuf.c |
2 | * | |
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | |
4 | * Author: Inki Dae <inki.dae@samsung.com> | |
5 | * | |
d81aecb5 ID |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. | |
b2df26c1 ID |
10 | */ |
11 | ||
760285e7 DH |
12 | #include <drm/drmP.h> |
13 | #include <drm/exynos_drm.h> | |
b2df26c1 ID |
14 | #include "exynos_drm_drv.h" |
15 | #include "exynos_drm_gem.h" | |
16 | ||
17 | #include <linux/dma-buf.h> | |
18 | ||
a7b362fb ID |
19 | struct exynos_drm_dmabuf_attachment { |
20 | struct sg_table sgt; | |
21 | enum dma_data_direction dir; | |
b8b5c139 | 22 | bool is_mapped; |
a7b362fb ID |
23 | }; |
24 | ||
25 | static int exynos_gem_attach_dma_buf(struct dma_buf *dmabuf, | |
26 | struct device *dev, | |
27 | struct dma_buf_attachment *attach) | |
b2df26c1 | 28 | { |
a7b362fb | 29 | struct exynos_drm_dmabuf_attachment *exynos_attach; |
b2df26c1 | 30 | |
a7b362fb ID |
31 | exynos_attach = kzalloc(sizeof(*exynos_attach), GFP_KERNEL); |
32 | if (!exynos_attach) | |
33 | return -ENOMEM; | |
b2df26c1 | 34 | |
a7b362fb ID |
35 | exynos_attach->dir = DMA_NONE; |
36 | attach->priv = exynos_attach; | |
b2df26c1 | 37 | |
a7b362fb ID |
38 | return 0; |
39 | } | |
b2df26c1 | 40 | |
a7b362fb ID |
41 | static void exynos_gem_detach_dma_buf(struct dma_buf *dmabuf, |
42 | struct dma_buf_attachment *attach) | |
43 | { | |
44 | struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; | |
45 | struct sg_table *sgt; | |
46 | ||
47 | if (!exynos_attach) | |
48 | return; | |
49 | ||
50 | sgt = &exynos_attach->sgt; | |
51 | ||
52 | if (exynos_attach->dir != DMA_NONE) | |
53 | dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, | |
54 | exynos_attach->dir); | |
55 | ||
56 | sg_free_table(sgt); | |
57 | kfree(exynos_attach); | |
58 | attach->priv = NULL; | |
b2df26c1 ID |
59 | } |
60 | ||
61 | static struct sg_table * | |
62 | exynos_gem_map_dma_buf(struct dma_buf_attachment *attach, | |
63 | enum dma_data_direction dir) | |
64 | { | |
a7b362fb | 65 | struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; |
b2df26c1 ID |
66 | struct exynos_drm_gem_obj *gem_obj = attach->dmabuf->priv; |
67 | struct drm_device *dev = gem_obj->base.dev; | |
68 | struct exynos_drm_gem_buf *buf; | |
a7b362fb | 69 | struct scatterlist *rd, *wr; |
b2df26c1 | 70 | struct sg_table *sgt = NULL; |
a7b362fb ID |
71 | unsigned int i; |
72 | int nents, ret; | |
b2df26c1 | 73 | |
a7b362fb | 74 | /* just return current sgt if already requested. */ |
b8b5c139 | 75 | if (exynos_attach->dir == dir && exynos_attach->is_mapped) |
a7b362fb ID |
76 | return &exynos_attach->sgt; |
77 | ||
b2df26c1 | 78 | buf = gem_obj->buffer; |
0519f9a1 ID |
79 | if (!buf) { |
80 | DRM_ERROR("buffer is null.\n"); | |
a7b362fb ID |
81 | return ERR_PTR(-ENOMEM); |
82 | } | |
83 | ||
84 | sgt = &exynos_attach->sgt; | |
85 | ||
86 | ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL); | |
87 | if (ret) { | |
88 | DRM_ERROR("failed to alloc sgt.\n"); | |
89 | return ERR_PTR(-ENOMEM); | |
b2df26c1 ID |
90 | } |
91 | ||
0519f9a1 | 92 | mutex_lock(&dev->struct_mutex); |
b2df26c1 | 93 | |
a7b362fb ID |
94 | rd = buf->sgt->sgl; |
95 | wr = sgt->sgl; | |
96 | for (i = 0; i < sgt->orig_nents; ++i) { | |
97 | sg_set_page(wr, sg_page(rd), rd->length, rd->offset); | |
98 | rd = sg_next(rd); | |
99 | wr = sg_next(wr); | |
100 | } | |
0519f9a1 | 101 | |
b8b5c139 ID |
102 | if (dir != DMA_NONE) { |
103 | nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); | |
104 | if (!nents) { | |
105 | DRM_ERROR("failed to map sgl with iommu.\n"); | |
106 | sg_free_table(sgt); | |
107 | sgt = ERR_PTR(-EIO); | |
108 | goto err_unlock; | |
109 | } | |
0519f9a1 | 110 | } |
b2df26c1 | 111 | |
b8b5c139 | 112 | exynos_attach->is_mapped = true; |
a7b362fb ID |
113 | exynos_attach->dir = dir; |
114 | attach->priv = exynos_attach; | |
115 | ||
465ed660 | 116 | DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size); |
b2df26c1 ID |
117 | |
118 | err_unlock: | |
119 | mutex_unlock(&dev->struct_mutex); | |
120 | return sgt; | |
121 | } | |
122 | ||
123 | static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, | |
124 | struct sg_table *sgt, | |
125 | enum dma_data_direction dir) | |
126 | { | |
a7b362fb | 127 | /* Nothing to do. */ |
b2df26c1 ID |
128 | } |
129 | ||
130 | static void exynos_dmabuf_release(struct dma_buf *dmabuf) | |
131 | { | |
132 | struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv; | |
133 | ||
b2df26c1 ID |
134 | /* |
135 | * exynos_dmabuf_release() call means that file object's | |
136 | * f_count is 0 and it calls drm_gem_object_handle_unreference() | |
137 | * to drop the references that these values had been increased | |
138 | * at drm_prime_handle_to_fd() | |
139 | */ | |
140 | if (exynos_gem_obj->base.export_dma_buf == dmabuf) { | |
141 | exynos_gem_obj->base.export_dma_buf = NULL; | |
142 | ||
143 | /* | |
144 | * drop this gem object refcount to release allocated buffer | |
145 | * and resources. | |
146 | */ | |
147 | drm_gem_object_unreference_unlocked(&exynos_gem_obj->base); | |
148 | } | |
149 | } | |
150 | ||
151 | static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, | |
152 | unsigned long page_num) | |
153 | { | |
154 | /* TODO */ | |
155 | ||
156 | return NULL; | |
157 | } | |
158 | ||
159 | static void exynos_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, | |
160 | unsigned long page_num, | |
161 | void *addr) | |
162 | { | |
163 | /* TODO */ | |
164 | } | |
165 | ||
166 | static void *exynos_gem_dmabuf_kmap(struct dma_buf *dma_buf, | |
167 | unsigned long page_num) | |
168 | { | |
169 | /* TODO */ | |
170 | ||
171 | return NULL; | |
172 | } | |
173 | ||
174 | static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf, | |
175 | unsigned long page_num, void *addr) | |
176 | { | |
177 | /* TODO */ | |
178 | } | |
179 | ||
b716d46e TS |
180 | static int exynos_gem_dmabuf_mmap(struct dma_buf *dma_buf, |
181 | struct vm_area_struct *vma) | |
182 | { | |
183 | return -ENOTTY; | |
184 | } | |
185 | ||
b2df26c1 | 186 | static struct dma_buf_ops exynos_dmabuf_ops = { |
a7b362fb ID |
187 | .attach = exynos_gem_attach_dma_buf, |
188 | .detach = exynos_gem_detach_dma_buf, | |
b2df26c1 ID |
189 | .map_dma_buf = exynos_gem_map_dma_buf, |
190 | .unmap_dma_buf = exynos_gem_unmap_dma_buf, | |
191 | .kmap = exynos_gem_dmabuf_kmap, | |
192 | .kmap_atomic = exynos_gem_dmabuf_kmap_atomic, | |
193 | .kunmap = exynos_gem_dmabuf_kunmap, | |
194 | .kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic, | |
b716d46e | 195 | .mmap = exynos_gem_dmabuf_mmap, |
b2df26c1 ID |
196 | .release = exynos_dmabuf_release, |
197 | }; | |
198 | ||
199 | struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, | |
200 | struct drm_gem_object *obj, int flags) | |
201 | { | |
202 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
203 | ||
204 | return dma_buf_export(exynos_gem_obj, &exynos_dmabuf_ops, | |
f4fd9bd4 | 205 | exynos_gem_obj->base.size, flags); |
b2df26c1 ID |
206 | } |
207 | ||
208 | struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, | |
209 | struct dma_buf *dma_buf) | |
210 | { | |
211 | struct dma_buf_attachment *attach; | |
212 | struct sg_table *sgt; | |
213 | struct scatterlist *sgl; | |
214 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
215 | struct exynos_drm_gem_buf *buffer; | |
47fcdce2 | 216 | int ret; |
b2df26c1 | 217 | |
b2df26c1 ID |
218 | /* is this one of own objects? */ |
219 | if (dma_buf->ops == &exynos_dmabuf_ops) { | |
220 | struct drm_gem_object *obj; | |
221 | ||
222 | exynos_gem_obj = dma_buf->priv; | |
223 | obj = &exynos_gem_obj->base; | |
224 | ||
225 | /* is it from our device? */ | |
226 | if (obj->dev == drm_dev) { | |
be8a42ae SWK |
227 | /* |
228 | * Importing dmabuf exported from out own gem increases | |
229 | * refcount on gem itself instead of f_count of dmabuf. | |
230 | */ | |
b2df26c1 ID |
231 | drm_gem_object_reference(obj); |
232 | return obj; | |
233 | } | |
234 | } | |
235 | ||
236 | attach = dma_buf_attach(dma_buf, drm_dev->dev); | |
237 | if (IS_ERR(attach)) | |
238 | return ERR_PTR(-EINVAL); | |
239 | ||
011c2282 | 240 | get_dma_buf(dma_buf); |
b2df26c1 ID |
241 | |
242 | sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); | |
0dd3b72c | 243 | if (IS_ERR_OR_NULL(sgt)) { |
b2df26c1 ID |
244 | ret = PTR_ERR(sgt); |
245 | goto err_buf_detach; | |
246 | } | |
247 | ||
248 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | |
249 | if (!buffer) { | |
250 | DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); | |
251 | ret = -ENOMEM; | |
252 | goto err_unmap_attach; | |
253 | } | |
254 | ||
b2df26c1 ID |
255 | exynos_gem_obj = exynos_drm_gem_init(drm_dev, dma_buf->size); |
256 | if (!exynos_gem_obj) { | |
257 | ret = -ENOMEM; | |
0519f9a1 | 258 | goto err_free_buffer; |
b2df26c1 ID |
259 | } |
260 | ||
261 | sgl = sgt->sgl; | |
b2df26c1 | 262 | |
0519f9a1 ID |
263 | buffer->size = dma_buf->size; |
264 | buffer->dma_addr = sg_dma_address(sgl); | |
47fcdce2 | 265 | |
0519f9a1 | 266 | if (sgt->nents == 1) { |
47fcdce2 ID |
267 | /* always physically continuous memory if sgt->nents is 1. */ |
268 | exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; | |
269 | } else { | |
0519f9a1 ID |
270 | /* |
271 | * this case could be CONTIG or NONCONTIG type but for now | |
272 | * sets NONCONTIG. | |
273 | * TODO. we have to find a way that exporter can notify | |
274 | * the type of its own buffer to importer. | |
275 | */ | |
47fcdce2 | 276 | exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; |
b2df26c1 ID |
277 | } |
278 | ||
279 | exynos_gem_obj->buffer = buffer; | |
280 | buffer->sgt = sgt; | |
281 | exynos_gem_obj->base.import_attach = attach; | |
282 | ||
283 | DRM_DEBUG_PRIME("dma_addr = 0x%x, size = 0x%lx\n", buffer->dma_addr, | |
284 | buffer->size); | |
285 | ||
286 | return &exynos_gem_obj->base; | |
287 | ||
b2df26c1 ID |
288 | err_free_buffer: |
289 | kfree(buffer); | |
290 | buffer = NULL; | |
291 | err_unmap_attach: | |
292 | dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); | |
293 | err_buf_detach: | |
294 | dma_buf_detach(dma_buf, attach); | |
011c2282 ID |
295 | dma_buf_put(dma_buf); |
296 | ||
b2df26c1 ID |
297 | return ERR_PTR(ret); |
298 | } | |
299 | ||
300 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | |
301 | MODULE_DESCRIPTION("Samsung SoC DRM DMABUF Module"); | |
302 | MODULE_LICENSE("GPL"); |