Fix formatting issues
[platform/core/uifw/vulkan-wsi-tizen.git] / util / wsialloc / wsialloc_ion.c
1 /*
2  * Copyright (c) 2017, 2019, 2021 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include "wsialloc.h"
26 #include "format_table.h"
27
28 #include <assert.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <ion.h>
32 #include <stdbool.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <sys/ioctl.h>
36 #include <sys/stat.h>
37 #include <sys/mman.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <string.h>
41
42 /** Default alignment */
43 #define WSIALLOCP_MIN_ALIGN_SZ (64u)
44 /** Maximum image size allowed for each dimension */
45 #define MAX_IMAGE_SIZE 128000
46
47 struct wsialloc_allocator
48 {
49    /* File descriptor of /dev/ion. */
50    int fd;
51    /* Allocator heap id. */
52    uint32_t alloc_heap_id;
53    /* Protected allocator heap id */
54    uint32_t protected_alloc_heap_id;
55    bool protected_heap_exists;
56 };
57
58 typedef struct wsialloc_format_descriptor
59 {
60    wsialloc_format format;
61    fmt_spec format_spec;
62 } wsialloc_format_descriptor;
63
64 static int find_alloc_heap_id(int fd)
65 {
66    assert(fd != -1);
67
68    struct ion_heap_data heaps[ION_NUM_HEAP_IDS];
69    struct ion_heap_query query = {
70       .cnt = ION_NUM_HEAP_IDS, .heaps = (uint64_t)(uintptr_t)heaps,
71    };
72
73    int ret = ioctl(fd, ION_IOC_HEAP_QUERY, &query);
74    if (ret < 0)
75    {
76       return ret;
77    }
78
79    int alloc_heap_id = -1;
80    for (uint32_t i = 0; i < query.cnt; ++i)
81    {
82       if (ION_HEAP_TYPE_DMA == heaps[i].type)
83       {
84          alloc_heap_id = heaps[i].heap_id;
85          break;
86       }
87    }
88
89    return alloc_heap_id;
90 }
91
92 static int allocate(int fd, uint64_t size, uint32_t heap_id)
93 {
94    assert(size > 0);
95    assert(fd != -1);
96
97    struct ion_allocation_data alloc = {
98       .len = size, .heap_id_mask = 1u << heap_id, .flags = 0,
99    };
100    int ret = ioctl(fd, ION_IOC_ALLOC, &alloc);
101    if (ret < 0)
102    {
103       return ret;
104    }
105
106    return alloc.fd;
107 }
108
109 static uint64_t round_size_up_to_align(uint64_t size)
110 {
111    return (size + WSIALLOCP_MIN_ALIGN_SZ - 1) & ~(WSIALLOCP_MIN_ALIGN_SZ - 1);
112 }
113
114 wsialloc_error wsialloc_new(wsialloc_allocator **allocator)
115 {
116    assert(allocator != NULL);
117    wsialloc_error ret = WSIALLOC_ERROR_NONE;
118
119    wsialloc_allocator *ion = malloc(sizeof(wsialloc_allocator));
120    if (NULL == ion)
121    {
122       ret = WSIALLOC_ERROR_NO_RESOURCE;
123       goto fail;
124    }
125
126    ion->fd = open("/dev/ion", O_RDONLY);
127    if (ion->fd < 0)
128    {
129       ret = WSIALLOC_ERROR_NO_RESOURCE;
130       goto fail;
131    }
132
133    ion->alloc_heap_id = find_alloc_heap_id(ion->fd);
134    if (ion->alloc_heap_id < 0)
135    {
136       ret = WSIALLOC_ERROR_NO_RESOURCE;
137       goto fail;
138    }
139
140    ion->protected_heap_exists = false;
141    *allocator = ion;
142    return ret;
143 fail:
144    wsialloc_delete(ion);
145    return ret;
146 }
147
148 void wsialloc_delete(wsialloc_allocator *allocator)
149 {
150    if (NULL == allocator)
151    {
152       return;
153    }
154
155    if (allocator->fd >= 0)
156    {
157       close(allocator->fd);
158    }
159
160    free(allocator);
161 }
162
163 static wsialloc_error calculate_format_properties(const wsialloc_format_descriptor *descriptor,
164                                                   const wsialloc_allocate_info *info, int *strides, uint32_t *offsets)
165 {
166    assert(descriptor != NULL);
167    assert(info != NULL);
168    assert(strides != NULL);
169    assert(offsets != NULL);
170
171    const uint8_t *bits_per_pixel = descriptor->format_spec.bpp;
172    const uint64_t flags = descriptor->format.flags;
173    const uint64_t modifier = descriptor->format.modifier;
174    const uint32_t num_planes = descriptor->format_spec.nr_planes;
175
176    /* We currently don't support any kind of custom modifiers */
177    if (modifier != DRM_FORMAT_MOD_LINEAR)
178    {
179       return WSIALLOC_ERROR_NOT_SUPPORTED;
180    }
181    /* No multi-plane format support */
182    if (num_planes > 1)
183    {
184       return WSIALLOC_ERROR_NOT_SUPPORTED;
185    }
186
187    size_t size = 0;
188    for (size_t plane = 0; plane < num_planes; plane++)
189    {
190       /* Assumes multiple of 8--rework otherwise. */
191       const uint32_t plane_bytes_per_pixel = bits_per_pixel[plane] / 8;
192       assert(plane_bytes_per_pixel * 8 == bits_per_pixel[plane]);
193
194       /* With large enough width, this can overflow as strides are signed. In practice, this shouldn't happen */
195       strides[plane] = round_size_up_to_align(info->width * plane_bytes_per_pixel);
196
197       offsets[plane] = size;
198
199       size += strides[plane] * info->height;
200    }
201
202    return WSIALLOC_ERROR_NONE;
203 }
204
205 static wsialloc_error allocate_format(const wsialloc_allocator *allocator, const wsialloc_format_descriptor *descriptor,
206                                       const wsialloc_allocate_info *info, const int *strides, const uint32_t *offsets,
207                                       int *buffer_fds)
208 {
209    assert(allocator != NULL);
210    assert(descriptor != NULL);
211    assert(info != NULL);
212    assert(offsets != NULL);
213    assert(strides != NULL);
214    assert(buffer_fds != NULL);
215
216    const uint64_t flags = descriptor->format.flags;
217    const uint32_t num_planes = descriptor->format_spec.nr_planes;
218
219    /* The only error that can be encountered on allocations is lack of resources. Other parameter validation and
220     * support checks are done on format selection. */
221    assert(num_planes == 1);
222    uint32_t alloc_heap_id = allocator->alloc_heap_id;
223    if (info->flags & WSIALLOC_ALLOCATE_PROTECTED)
224    {
225       /* Exit if we don't support allocating protected memory */
226       if (!allocator->protected_heap_exists)
227       {
228          return WSIALLOC_ERROR_NO_RESOURCE;
229       }
230       alloc_heap_id = allocator->protected_alloc_heap_id;
231    }
232
233    size_t total_size = offsets[0] + (strides[0] * info->height);
234
235    buffer_fds[0] = allocate(allocator->fd, total_size, alloc_heap_id);
236    if (buffer_fds[0] < 0)
237    {
238       return WSIALLOC_ERROR_NO_RESOURCE;
239    }
240
241    return WSIALLOC_ERROR_NONE;
242 }
243
244 static const fmt_spec *find_format(uint32_t fourcc)
245 {
246    /* Mask off any bits not necessary for allocation size */
247    fourcc = fourcc & (~(uint32_t)DRM_FORMAT_BIG_ENDIAN);
248
249    /* Search table for the format*/
250    for (size_t i = 0; i < fourcc_format_table_len; i++)
251    {
252       if (fourcc == fourcc_format_table[i].drm_format)
253       {
254          const fmt_spec *found_fmt = &fourcc_format_table[i];
255          assert(found_fmt->nr_planes <= WSIALLOCP_MAX_PLANES);
256
257          return found_fmt;
258       }
259    }
260
261    return NULL;
262 }
263
264 static bool validate_parameters(const wsialloc_allocator *allocator, const wsialloc_allocate_info *info,
265                                 const wsialloc_format *format, const int *strides, const uint32_t *offsets)
266 {
267    if (allocator == NULL)
268    {
269       return false;
270    }
271    else if (!strides || !offsets)
272    {
273       return false;
274    }
275    else if (info->format_count == 0 || info->formats == NULL)
276    {
277       return false;
278    }
279    else if (info->width < 1 || info->height < 1 || info->width > MAX_IMAGE_SIZE || info->height > MAX_IMAGE_SIZE)
280    {
281       return false;
282    }
283
284    return true;
285 }
286
287 wsialloc_error wsialloc_alloc(wsialloc_allocator *allocator, const wsialloc_allocate_info *info,
288                               wsialloc_format *format, int *strides, int *buffer_fds, uint32_t *offsets)
289 {
290    assert(allocator != NULL);
291    assert(info != NULL);
292    assert(format != NULL);
293    assert(strides != NULL);
294    assert(offsets != NULL);
295
296    if (!validate_parameters(allocator, info, format, strides, offsets))
297    {
298       return WSIALLOC_ERROR_INVALID;
299    }
300
301    int local_strides[WSIALLOCP_MAX_PLANES];
302    int local_fds[WSIALLOCP_MAX_PLANES] = { -1 };
303    int local_offsets[WSIALLOCP_MAX_PLANES];
304    wsialloc_error err = WSIALLOC_ERROR_NONE;
305    wsialloc_format_descriptor selected_format_desc = {};
306
307    for (size_t i = 0; i < info->format_count; i++)
308    {
309       const wsialloc_format *current_format = &info->formats[i];
310       const fmt_spec *format_spec = find_format(current_format->fourcc);
311       if (!format_spec)
312       {
313          err = WSIALLOC_ERROR_NOT_SUPPORTED;
314          continue;
315       }
316
317       wsialloc_format_descriptor current_format_desc = { *current_format, *format_spec };
318       err = calculate_format_properties(&current_format_desc, info, local_strides, local_offsets);
319       if (err != WSIALLOC_ERROR_NONE)
320       {
321          continue;
322       }
323
324       /* A compatible format was found */
325       selected_format_desc = current_format_desc;
326       break;
327    }
328
329    if (err == WSIALLOC_ERROR_NONE)
330    {
331       if (!(info->flags & WSIALLOC_ALLOCATE_NO_MEMORY))
332       {
333          err = allocate_format(allocator, &selected_format_desc, info, local_strides, local_offsets, local_fds);
334       }
335    }
336
337    if (err == WSIALLOC_ERROR_NONE)
338    {
339       *format = selected_format_desc.format;
340       *strides = local_strides[0];
341       *offsets = local_offsets[0];
342       if (!(info->flags & WSIALLOC_ALLOCATE_NO_MEMORY))
343       {
344          *buffer_fds = local_fds[0];
345       }
346    }
347    return err;
348 }