Commit | Line | Data |
---|---|---|
1c248b7d ID |
1 | /* exynos_drm_buf.c |
2 | * | |
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
4 | * Author: Inki Dae <inki.dae@samsung.com> | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a | |
7 | * copy of this software and associated documentation files (the "Software"), | |
8 | * to deal in the Software without restriction, including without limitation | |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
10 | * and/or sell copies of the Software, and to permit persons to whom the | |
11 | * Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice (including the next | |
14 | * paragraph) shall be included in all copies or substantial portions of the | |
15 | * Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
23 | * OTHER DEALINGS IN THE SOFTWARE. | |
24 | */ | |
25 | ||
26 | #include "drmP.h" | |
27 | #include "drm.h" | |
2b35892e | 28 | #include "exynos_drm.h" |
1c248b7d ID |
29 | |
30 | #include "exynos_drm_drv.h" | |
2c871127 | 31 | #include "exynos_drm_gem.h" |
1c248b7d ID |
32 | #include "exynos_drm_buf.h" |
33 | ||
1c248b7d | 34 | static int lowlevel_buffer_allocate(struct drm_device *dev, |
2b35892e | 35 | unsigned int flags, struct exynos_drm_gem_buf *buf) |
1c248b7d | 36 | { |
61db75d8 | 37 | dma_addr_t start_addr; |
b2df26c1 | 38 | unsigned int npages, i = 0; |
2b35892e ID |
39 | struct scatterlist *sgl; |
40 | int ret = 0; | |
41 | ||
1c248b7d ID |
42 | DRM_DEBUG_KMS("%s\n", __FILE__); |
43 | ||
dcf9af82 | 44 | if (IS_NONCONTIG_BUFFER(flags)) { |
2b35892e ID |
45 | DRM_DEBUG_KMS("not support allocation type.\n"); |
46 | return -EINVAL; | |
47 | } | |
48 | ||
49 | if (buf->dma_addr) { | |
50 | DRM_DEBUG_KMS("already allocated.\n"); | |
51 | return 0; | |
52 | } | |
53 | ||
54 | if (buf->size >= SZ_1M) { | |
dcf9af82 | 55 | npages = buf->size >> SECTION_SHIFT; |
b2df26c1 | 56 | buf->page_size = SECTION_SIZE; |
2b35892e | 57 | } else if (buf->size >= SZ_64K) { |
dcf9af82 | 58 | npages = buf->size >> 16; |
b2df26c1 | 59 | buf->page_size = SZ_64K; |
2b35892e | 60 | } else { |
dcf9af82 | 61 | npages = buf->size >> PAGE_SHIFT; |
b2df26c1 | 62 | buf->page_size = PAGE_SIZE; |
2b35892e ID |
63 | } |
64 | ||
65 | buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); | |
66 | if (!buf->sgt) { | |
67 | DRM_ERROR("failed to allocate sg table.\n"); | |
1c248b7d ID |
68 | return -ENOMEM; |
69 | } | |
70 | ||
2b35892e ID |
71 | ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); |
72 | if (ret < 0) { | |
73 | DRM_ERROR("failed to initialize sg table.\n"); | |
74 | kfree(buf->sgt); | |
75 | buf->sgt = NULL; | |
76 | return -ENOMEM; | |
77 | } | |
1c248b7d | 78 | |
61db75d8 ID |
79 | buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, |
80 | &buf->dma_addr, GFP_KERNEL); | |
81 | if (!buf->kvaddr) { | |
82 | DRM_ERROR("failed to allocate buffer.\n"); | |
83 | ret = -ENOMEM; | |
84 | goto err1; | |
85 | } | |
2b35892e ID |
86 | |
87 | buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); | |
88 | if (!buf->pages) { | |
89 | DRM_ERROR("failed to allocate pages.\n"); | |
90 | ret = -ENOMEM; | |
91 | goto err2; | |
92 | } | |
93 | ||
94 | sgl = buf->sgt->sgl; | |
61db75d8 | 95 | start_addr = buf->dma_addr; |
2b35892e ID |
96 | |
97 | while (i < npages) { | |
98 | buf->pages[i] = phys_to_page(start_addr); | |
b2df26c1 | 99 | sg_set_page(sgl, buf->pages[i], buf->page_size, 0); |
2b35892e | 100 | sg_dma_address(sgl) = start_addr; |
b2df26c1 | 101 | start_addr += buf->page_size; |
2b35892e ID |
102 | sgl = sg_next(sgl); |
103 | i++; | |
104 | } | |
105 | ||
2b35892e ID |
106 | DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", |
107 | (unsigned long)buf->kvaddr, | |
108 | (unsigned long)buf->dma_addr, | |
109 | buf->size); | |
110 | ||
111 | return ret; | |
112 | err2: | |
113 | dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, | |
114 | (dma_addr_t)buf->dma_addr); | |
115 | buf->dma_addr = (dma_addr_t)NULL; | |
116 | err1: | |
117 | sg_free_table(buf->sgt); | |
118 | kfree(buf->sgt); | |
119 | buf->sgt = NULL; | |
120 | ||
121 | return ret; | |
1c248b7d ID |
122 | } |
123 | ||
124 | static void lowlevel_buffer_deallocate(struct drm_device *dev, | |
2b35892e | 125 | unsigned int flags, struct exynos_drm_gem_buf *buf) |
1c248b7d ID |
126 | { |
127 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
128 | ||
2b35892e ID |
129 | /* |
130 | * release only physically continuous memory and | |
131 | * non-continuous memory would be released by exynos | |
132 | * gem framework. | |
133 | */ | |
dcf9af82 | 134 | if (IS_NONCONTIG_BUFFER(flags)) { |
2b35892e ID |
135 | DRM_DEBUG_KMS("not support allocation type.\n"); |
136 | return; | |
137 | } | |
138 | ||
139 | if (!buf->dma_addr) { | |
140 | DRM_DEBUG_KMS("dma_addr is invalid.\n"); | |
141 | return; | |
142 | } | |
143 | ||
144 | DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", | |
145 | (unsigned long)buf->kvaddr, | |
146 | (unsigned long)buf->dma_addr, | |
147 | buf->size); | |
148 | ||
149 | sg_free_table(buf->sgt); | |
150 | ||
151 | kfree(buf->sgt); | |
152 | buf->sgt = NULL; | |
153 | ||
154 | kfree(buf->pages); | |
155 | buf->pages = NULL; | |
156 | ||
157 | dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, | |
158 | (dma_addr_t)buf->dma_addr); | |
159 | buf->dma_addr = (dma_addr_t)NULL; | |
1c248b7d ID |
160 | } |
161 | ||
2b35892e ID |
162 | struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, |
163 | unsigned int size) | |
1c248b7d | 164 | { |
2c871127 | 165 | struct exynos_drm_gem_buf *buffer; |
1c248b7d ID |
166 | |
167 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
2c871127 | 168 | DRM_DEBUG_KMS("desired size = 0x%x\n", size); |
1c248b7d | 169 | |
2c871127 ID |
170 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); |
171 | if (!buffer) { | |
172 | DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); | |
ee5e770e | 173 | return NULL; |
1c248b7d ID |
174 | } |
175 | ||
2c871127 | 176 | buffer->size = size; |
2c871127 | 177 | return buffer; |
1c248b7d ID |
178 | } |
179 | ||
2b35892e ID |
180 | void exynos_drm_fini_buf(struct drm_device *dev, |
181 | struct exynos_drm_gem_buf *buffer) | |
1c248b7d ID |
182 | { |
183 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
184 | ||
2c871127 ID |
185 | if (!buffer) { |
186 | DRM_DEBUG_KMS("buffer is null.\n"); | |
1c248b7d ID |
187 | return; |
188 | } | |
189 | ||
2c871127 ID |
190 | kfree(buffer); |
191 | buffer = NULL; | |
1c248b7d ID |
192 | } |
193 | ||
2b35892e ID |
194 | int exynos_drm_alloc_buf(struct drm_device *dev, |
195 | struct exynos_drm_gem_buf *buf, unsigned int flags) | |
196 | { | |
197 | ||
198 | /* | |
199 | * allocate memory region and set the memory information | |
200 | * to vaddr and dma_addr of a buffer object. | |
201 | */ | |
202 | if (lowlevel_buffer_allocate(dev, flags, buf) < 0) | |
203 | return -ENOMEM; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | void exynos_drm_free_buf(struct drm_device *dev, | |
209 | unsigned int flags, struct exynos_drm_gem_buf *buffer) | |
210 | { | |
211 | ||
212 | lowlevel_buffer_deallocate(dev, flags, buffer); | |
213 | } |