2 * Copyright (c) 2017, 2019, 2021 Arm Limited.
4 * SPDX-License-Identifier: MIT
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:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
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
26 #include "format_table.h"
35 #include <sys/ioctl.h>
38 #include <sys/types.h>
42 /** Default alignment */
43 #define WSIALLOCP_MIN_ALIGN_SZ (64u)
44 /** Maximum image size allowed for each dimension */
45 #define MAX_IMAGE_SIZE 128000
47 struct wsialloc_allocator
49 /* File descriptor of /dev/ion. */
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;
58 typedef struct wsialloc_format_descriptor
60 wsialloc_format format;
62 } wsialloc_format_descriptor;
64 static int find_alloc_heap_id(int fd)
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,
73 int ret = ioctl(fd, ION_IOC_HEAP_QUERY, &query);
79 int alloc_heap_id = -1;
80 for (uint32_t i = 0; i < query.cnt; ++i)
82 if (ION_HEAP_TYPE_DMA == heaps[i].type)
84 alloc_heap_id = heaps[i].heap_id;
92 static int allocate(int fd, uint64_t size, uint32_t heap_id)
97 struct ion_allocation_data alloc = {
98 .len = size, .heap_id_mask = 1u << heap_id, .flags = 0,
100 int ret = ioctl(fd, ION_IOC_ALLOC, &alloc);
109 static uint64_t round_size_up_to_align(uint64_t size)
111 return (size + WSIALLOCP_MIN_ALIGN_SZ - 1) & ~(WSIALLOCP_MIN_ALIGN_SZ - 1);
114 wsialloc_error wsialloc_new(wsialloc_allocator **allocator)
116 assert(allocator != NULL);
117 wsialloc_error ret = WSIALLOC_ERROR_NONE;
119 wsialloc_allocator *ion = malloc(sizeof(wsialloc_allocator));
122 ret = WSIALLOC_ERROR_NO_RESOURCE;
126 ion->fd = open("/dev/ion", O_RDONLY);
129 ret = WSIALLOC_ERROR_NO_RESOURCE;
133 ion->alloc_heap_id = find_alloc_heap_id(ion->fd);
134 if (ion->alloc_heap_id < 0)
136 ret = WSIALLOC_ERROR_NO_RESOURCE;
140 ion->protected_heap_exists = false;
144 wsialloc_delete(ion);
148 void wsialloc_delete(wsialloc_allocator *allocator)
150 if (NULL == allocator)
155 if (allocator->fd >= 0)
157 close(allocator->fd);
163 static wsialloc_error calculate_format_properties(const wsialloc_format_descriptor *descriptor,
164 const wsialloc_allocate_info *info, int *strides, uint32_t *offsets)
166 assert(descriptor != NULL);
167 assert(info != NULL);
168 assert(strides != NULL);
169 assert(offsets != NULL);
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;
176 /* We currently don't support any kind of custom modifiers */
177 if (modifier != DRM_FORMAT_MOD_LINEAR)
179 return WSIALLOC_ERROR_NOT_SUPPORTED;
181 /* No multi-plane format support */
184 return WSIALLOC_ERROR_NOT_SUPPORTED;
188 for (size_t plane = 0; plane < num_planes; plane++)
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]);
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);
197 offsets[plane] = size;
199 size += strides[plane] * info->height;
202 return WSIALLOC_ERROR_NONE;
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,
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);
216 const uint64_t flags = descriptor->format.flags;
217 const uint32_t num_planes = descriptor->format_spec.nr_planes;
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)
225 /* Exit if we don't support allocating protected memory */
226 if (!allocator->protected_heap_exists)
228 return WSIALLOC_ERROR_NO_RESOURCE;
230 alloc_heap_id = allocator->protected_alloc_heap_id;
233 size_t total_size = offsets[0] + (strides[0] * info->height);
235 buffer_fds[0] = allocate(allocator->fd, total_size, alloc_heap_id);
236 if (buffer_fds[0] < 0)
238 return WSIALLOC_ERROR_NO_RESOURCE;
241 return WSIALLOC_ERROR_NONE;
244 static const fmt_spec *find_format(uint32_t fourcc)
246 /* Mask off any bits not necessary for allocation size */
247 fourcc = fourcc & (~(uint32_t)DRM_FORMAT_BIG_ENDIAN);
249 /* Search table for the format*/
250 for (size_t i = 0; i < fourcc_format_table_len; i++)
252 if (fourcc == fourcc_format_table[i].drm_format)
254 const fmt_spec *found_fmt = &fourcc_format_table[i];
255 assert(found_fmt->nr_planes <= WSIALLOCP_MAX_PLANES);
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)
267 if (allocator == NULL)
271 else if (!strides || !offsets)
275 else if (info->format_count == 0 || info->formats == NULL)
279 else if (info->width < 1 || info->height < 1 || info->width > MAX_IMAGE_SIZE || info->height > MAX_IMAGE_SIZE)
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)
290 assert(allocator != NULL);
291 assert(info != NULL);
292 assert(format != NULL);
293 assert(strides != NULL);
294 assert(offsets != NULL);
296 if (!validate_parameters(allocator, info, format, strides, offsets))
298 return WSIALLOC_ERROR_INVALID;
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 = {};
307 for (size_t i = 0; i < info->format_count; i++)
309 const wsialloc_format *current_format = &info->formats[i];
310 const fmt_spec *format_spec = find_format(current_format->fourcc);
313 err = WSIALLOC_ERROR_NOT_SUPPORTED;
317 wsialloc_format_descriptor current_format_desc = { *current_format, *format_spec };
318 err = calculate_format_properties(¤t_format_desc, info, local_strides, local_offsets);
319 if (err != WSIALLOC_ERROR_NONE)
324 /* A compatible format was found */
325 selected_format_desc = current_format_desc;
329 if (err == WSIALLOC_ERROR_NONE)
331 if (!(info->flags & WSIALLOC_ALLOCATE_NO_MEMORY))
333 err = allocate_format(allocator, &selected_format_desc, info, local_strides, local_offsets, local_fds);
337 if (err == WSIALLOC_ERROR_NONE)
339 *format = selected_format_desc.format;
340 *strides = local_strides[0];
341 *offsets = local_offsets[0];
342 if (!(info->flags & WSIALLOC_ALLOCATE_NO_MEMORY))
344 *buffer_fds = local_fds[0];