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 | * | |
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. | |
1c248b7d ID |
10 | */ |
11 | ||
760285e7 DH |
12 | #include <drm/drmP.h> |
13 | #include <drm/exynos_drm.h> | |
1c248b7d ID |
14 | |
15 | #include "exynos_drm_drv.h" | |
2c871127 | 16 | #include "exynos_drm_gem.h" |
1c248b7d | 17 | #include "exynos_drm_buf.h" |
694be458 | 18 | #include "exynos_drm_iommu.h" |
1c248b7d | 19 | |
1c248b7d | 20 | static int lowlevel_buffer_allocate(struct drm_device *dev, |
2b35892e | 21 | unsigned int flags, struct exynos_drm_gem_buf *buf) |
1c248b7d | 22 | { |
0519f9a1 | 23 | int ret = 0; |
1169af21 | 24 | enum dma_attr attr; |
4744ad24 | 25 | unsigned int nr_pages; |
2b35892e | 26 | |
1c248b7d ID |
27 | DRM_DEBUG_KMS("%s\n", __FILE__); |
28 | ||
2b35892e ID |
29 | if (buf->dma_addr) { |
30 | DRM_DEBUG_KMS("already allocated.\n"); | |
31 | return 0; | |
32 | } | |
33 | ||
0519f9a1 ID |
34 | init_dma_attrs(&buf->dma_attrs); |
35 | ||
1169af21 ID |
36 | /* |
37 | * if EXYNOS_BO_CONTIG, fully physically contiguous memory | |
38 | * region will be allocated else physically contiguous | |
39 | * as possible. | |
40 | */ | |
1dcfe238 | 41 | if (!(flags & EXYNOS_BO_NONCONTIG)) |
1169af21 ID |
42 | dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs); |
43 | ||
44 | /* | |
45 | * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping | |
46 | * else cachable mapping. | |
47 | */ | |
48 | if (flags & EXYNOS_BO_WC || !(flags & EXYNOS_BO_CACHABLE)) | |
0519f9a1 | 49 | attr = DMA_ATTR_WRITE_COMBINE; |
1169af21 ID |
50 | else |
51 | attr = DMA_ATTR_NON_CONSISTENT; | |
0519f9a1 ID |
52 | |
53 | dma_set_attr(attr, &buf->dma_attrs); | |
4744ad24 | 54 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs); |
0519f9a1 | 55 | |
694be458 ID |
56 | nr_pages = buf->size >> PAGE_SHIFT; |
57 | ||
58 | if (!is_drm_iommu_supported(dev)) { | |
59 | dma_addr_t start_addr; | |
60 | unsigned int i = 0; | |
61 | ||
62 | buf->pages = kzalloc(sizeof(struct page) * nr_pages, | |
63 | GFP_KERNEL); | |
64 | if (!buf->pages) { | |
65 | DRM_ERROR("failed to allocate pages.\n"); | |
66 | return -ENOMEM; | |
67 | } | |
68 | ||
69 | buf->kvaddr = dma_alloc_attrs(dev->dev, buf->size, | |
70 | &buf->dma_addr, GFP_KERNEL, | |
71 | &buf->dma_attrs); | |
72 | if (!buf->kvaddr) { | |
73 | DRM_ERROR("failed to allocate buffer.\n"); | |
74 | kfree(buf->pages); | |
75 | return -ENOMEM; | |
76 | } | |
77 | ||
78 | start_addr = buf->dma_addr; | |
79 | while (i < nr_pages) { | |
80 | buf->pages[i] = phys_to_page(start_addr); | |
81 | start_addr += PAGE_SIZE; | |
82 | i++; | |
83 | } | |
84 | } else { | |
85 | ||
86 | buf->pages = dma_alloc_attrs(dev->dev, buf->size, | |
87 | &buf->dma_addr, GFP_KERNEL, | |
88 | &buf->dma_attrs); | |
89 | if (!buf->pages) { | |
90 | DRM_ERROR("failed to allocate buffer.\n"); | |
91 | return -ENOMEM; | |
92 | } | |
2b35892e ID |
93 | } |
94 | ||
4744ad24 | 95 | buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages); |
2b35892e | 96 | if (!buf->sgt) { |
4744ad24 | 97 | DRM_ERROR("failed to get sg table.\n"); |
0519f9a1 ID |
98 | ret = -ENOMEM; |
99 | goto err_free_attrs; | |
1c248b7d ID |
100 | } |
101 | ||
4744ad24 | 102 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", |
2b35892e ID |
103 | (unsigned long)buf->dma_addr, |
104 | buf->size); | |
105 | ||
106 | return ret; | |
0519f9a1 | 107 | |
0519f9a1 | 108 | err_free_attrs: |
4744ad24 | 109 | dma_free_attrs(dev->dev, buf->size, buf->pages, |
0519f9a1 ID |
110 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); |
111 | buf->dma_addr = (dma_addr_t)NULL; | |
2b35892e | 112 | |
694be458 ID |
113 | if (!is_drm_iommu_supported(dev)) |
114 | kfree(buf->pages); | |
115 | ||
2b35892e | 116 | return ret; |
1c248b7d ID |
117 | } |
118 | ||
119 | static void lowlevel_buffer_deallocate(struct drm_device *dev, | |
2b35892e | 120 | unsigned int flags, struct exynos_drm_gem_buf *buf) |
1c248b7d ID |
121 | { |
122 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
123 | ||
2b35892e ID |
124 | if (!buf->dma_addr) { |
125 | DRM_DEBUG_KMS("dma_addr is invalid.\n"); | |
126 | return; | |
127 | } | |
128 | ||
4744ad24 | 129 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", |
2b35892e ID |
130 | (unsigned long)buf->dma_addr, |
131 | buf->size); | |
132 | ||
133 | sg_free_table(buf->sgt); | |
134 | ||
135 | kfree(buf->sgt); | |
136 | buf->sgt = NULL; | |
137 | ||
694be458 ID |
138 | if (!is_drm_iommu_supported(dev)) { |
139 | dma_free_attrs(dev->dev, buf->size, buf->kvaddr, | |
140 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); | |
141 | kfree(buf->pages); | |
142 | } else | |
143 | dma_free_attrs(dev->dev, buf->size, buf->pages, | |
0519f9a1 | 144 | (dma_addr_t)buf->dma_addr, &buf->dma_attrs); |
694be458 | 145 | |
2b35892e | 146 | buf->dma_addr = (dma_addr_t)NULL; |
1c248b7d ID |
147 | } |
148 | ||
2b35892e ID |
149 | struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, |
150 | unsigned int size) | |
1c248b7d | 151 | { |
2c871127 | 152 | struct exynos_drm_gem_buf *buffer; |
1c248b7d ID |
153 | |
154 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
2c871127 | 155 | DRM_DEBUG_KMS("desired size = 0x%x\n", size); |
1c248b7d | 156 | |
2c871127 ID |
157 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); |
158 | if (!buffer) { | |
159 | DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); | |
ee5e770e | 160 | return NULL; |
1c248b7d ID |
161 | } |
162 | ||
2c871127 | 163 | buffer->size = size; |
2c871127 | 164 | return buffer; |
1c248b7d ID |
165 | } |
166 | ||
2b35892e ID |
167 | void exynos_drm_fini_buf(struct drm_device *dev, |
168 | struct exynos_drm_gem_buf *buffer) | |
1c248b7d ID |
169 | { |
170 | DRM_DEBUG_KMS("%s.\n", __FILE__); | |
171 | ||
2c871127 ID |
172 | if (!buffer) { |
173 | DRM_DEBUG_KMS("buffer is null.\n"); | |
1c248b7d ID |
174 | return; |
175 | } | |
176 | ||
2c871127 ID |
177 | kfree(buffer); |
178 | buffer = NULL; | |
1c248b7d ID |
179 | } |
180 | ||
2b35892e ID |
181 | int exynos_drm_alloc_buf(struct drm_device *dev, |
182 | struct exynos_drm_gem_buf *buf, unsigned int flags) | |
183 | { | |
184 | ||
185 | /* | |
186 | * allocate memory region and set the memory information | |
187 | * to vaddr and dma_addr of a buffer object. | |
188 | */ | |
189 | if (lowlevel_buffer_allocate(dev, flags, buf) < 0) | |
190 | return -ENOMEM; | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | void exynos_drm_free_buf(struct drm_device *dev, | |
196 | unsigned int flags, struct exynos_drm_gem_buf *buffer) | |
197 | { | |
198 | ||
199 | lowlevel_buffer_deallocate(dev, flags, buffer); | |
200 | } |