| /* |
| * Copyright 2017 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <linux/dma-buf.h> |
| #include <sys/ioctl.h> |
| |
| #include "bs_drm.h" |
| |
| #define HANDLE_EINTR(x) \ |
| ({ \ |
| int eintr_wrapper_counter = 0; \ |
| int eintr_wrapper_result; \ |
| do { \ |
| eintr_wrapper_result = (x); \ |
| } while (eintr_wrapper_result == -1 && errno == EINTR && \ |
| eintr_wrapper_counter++ < 100); \ |
| eintr_wrapper_result; \ |
| }) |
| |
| struct bs_map_info { |
| size_t plane_index; |
| void *ptr; |
| void *map_data; |
| }; |
| |
| typedef void *(*bs_map_t)(struct bs_mapper *mapper, struct gbm_bo *bo, size_t plane, |
| struct bs_map_info *info, uint32_t *stride); |
| typedef void (*bs_unmap_t)(struct gbm_bo *bo, struct bs_map_info *info); |
| |
| struct bs_mapper { |
| bs_map_t map_plane_fn; |
| bs_unmap_t unmap_plane_fn; |
| int device_fd; |
| }; |
| |
| static void *dma_buf_map(struct bs_mapper *mapper, struct gbm_bo *bo, size_t plane, |
| struct bs_map_info *info, uint32_t *stride) |
| { |
| int drm_prime_fd = gbm_bo_get_fd_for_plane(bo, plane); |
| uint32_t handle = gbm_bo_get_handle_for_plane(bo, plane).u32; |
| size_t length = 0; |
| |
| for (size_t p = 0; p <= plane; p++) { |
| if (gbm_bo_get_handle_for_plane(bo, p).u32 == handle) |
| length += gbm_bo_get_plane_size(bo, p); |
| } |
| |
| void *ptr = mmap(NULL, length, (PROT_READ | PROT_WRITE), MAP_SHARED, drm_prime_fd, 0); |
| if (ptr == MAP_FAILED) { |
| bs_debug_error("dma-buf mmap returned MAP_FAILED: %d", errno); |
| ptr = MAP_FAILED; |
| } else { |
| ptr += gbm_bo_get_offset(bo, plane); |
| } |
| |
| struct dma_buf_sync sync_start = { 0 }; |
| sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; |
| int ret = HANDLE_EINTR(ioctl(drm_prime_fd, DMA_BUF_IOCTL_SYNC, &sync_start)); |
| if (ret) |
| bs_debug_error("DMA_BUF_IOCTL_SYNC failed"); |
| *stride = gbm_bo_get_stride_for_plane(bo, plane); |
| |
| close(drm_prime_fd); |
| return ptr; |
| } |
| |
| static void dma_buf_unmap(struct gbm_bo *bo, struct bs_map_info *info) |
| { |
| struct dma_buf_sync sync_end = { 0 }; |
| sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; |
| int drm_prime_fd = gbm_bo_get_fd_for_plane(bo, info->plane_index); |
| int ret = HANDLE_EINTR(ioctl(drm_prime_fd, DMA_BUF_IOCTL_SYNC, &sync_end)); |
| close(drm_prime_fd); |
| if (ret) |
| bs_debug_error("DMA_BUF_IOCTL_SYNC failed"); |
| |
| void *addr = info->ptr; |
| addr -= gbm_bo_get_offset(bo, info->plane_index); |
| ret = munmap(addr, gbm_bo_get_plane_size(bo, info->plane_index)); |
| if (ret) |
| bs_debug_error("dma-buf unmap failed."); |
| } |
| |
| static void *gem_map(struct bs_mapper *mapper, struct gbm_bo *bo, size_t plane, |
| struct bs_map_info *info, uint32_t *stride) |
| { |
| uint32_t w, h, horizontal_subsampling, vertical_subsampling; |
| uint32_t format = gbm_bo_get_format(bo); |
| |
| horizontal_subsampling = 1; |
| vertical_subsampling = 1; |
| if (plane > 0) { |
| switch (format) |
| { |
| case GBM_FORMAT_NV12: |
| case GBM_FORMAT_NV21: |
| case GBM_FORMAT_YVU420: |
| horizontal_subsampling = 2; |
| vertical_subsampling = 2; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| w = gbm_bo_get_width(bo) / horizontal_subsampling; |
| h = gbm_bo_get_height(bo) / vertical_subsampling; |
| |
| void *ptr = NULL; |
| // Use mmap() for planar formats and gbm_bo_map() for others. |
| if (gbm_bo_get_plane_count(bo) > 1) { |
| // TODO(mcasas, b/185492766): Use mmap() here. |
| ptr = gbm_bo_map2(bo, /*x=*/0, /*y=*/0, w, h, GBM_BO_TRANSFER_READ_WRITE, stride, |
| &info->map_data, plane); |
| } else { |
| assert(plane == 0); |
| ptr = gbm_bo_map(bo, /*x=*/0, /*y=*/0, w, h, GBM_BO_TRANSFER_READ_WRITE, stride, |
| &info->map_data); |
| } |
| assert(ptr); |
| return ptr; |
| } |
| |
| static void gem_unmap(struct gbm_bo *bo, struct bs_map_info *info) |
| { |
| gbm_bo_unmap(bo, info->map_data); |
| } |
| |
| static void *dumb_map(struct bs_mapper *mapper, struct gbm_bo *bo, size_t plane, |
| struct bs_map_info *info, uint32_t *stride) |
| { |
| if (plane) { |
| bs_debug_error("dump map supports only single plane buffer."); |
| return MAP_FAILED; |
| } |
| |
| int prime_fd = gbm_bo_get_fd(bo); |
| uint32_t size = gbm_bo_get_plane_size(bo, plane); |
| |
| uint32_t bo_handle; |
| int ret = drmPrimeFDToHandle(mapper->device_fd, prime_fd, &bo_handle); |
| if (ret) { |
| bs_debug_error("dump map failed."); |
| return MAP_FAILED; |
| } |
| |
| struct drm_mode_map_dumb mmap_arg = { 0 }; |
| mmap_arg.handle = bo_handle; |
| |
| ret = drmIoctl(mapper->device_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); |
| if (ret) { |
| bs_debug_error("failed DRM_IOCTL_MODE_MAP_DUMB: %d", ret); |
| return MAP_FAILED; |
| } |
| |
| if (mmap_arg.offset == 0) { |
| bs_debug_error("DRM_IOCTL_MODE_MAP_DUMB returned 0 offset"); |
| return MAP_FAILED; |
| } |
| |
| void *ptr = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, mapper->device_fd, |
| mmap_arg.offset); |
| |
| if (ptr == MAP_FAILED) { |
| bs_debug_error("mmap returned MAP_FAILED: %d", errno); |
| return MAP_FAILED; |
| } |
| *stride = gbm_bo_get_stride_for_plane(bo, plane); |
| |
| close(prime_fd); |
| return ptr; |
| } |
| |
| static void dumb_unmap(struct gbm_bo *bo, struct bs_map_info *info) |
| { |
| int ret = munmap(info->ptr, gbm_bo_get_plane_size(bo, info->plane_index)); |
| if (ret) |
| bs_debug_error("dump unmap failed."); |
| } |
| |
| struct bs_mapper *bs_mapper_dma_buf_new() |
| { |
| struct bs_mapper *mapper = calloc(1, sizeof(struct bs_mapper)); |
| assert(mapper); |
| mapper->map_plane_fn = dma_buf_map; |
| mapper->unmap_plane_fn = dma_buf_unmap; |
| mapper->device_fd = -1; |
| return mapper; |
| } |
| |
| struct bs_mapper *bs_mapper_gem_new() |
| { |
| struct bs_mapper *mapper = calloc(1, sizeof(struct bs_mapper)); |
| assert(mapper); |
| mapper->map_plane_fn = gem_map; |
| mapper->unmap_plane_fn = gem_unmap; |
| mapper->device_fd = -1; |
| return mapper; |
| } |
| |
| struct bs_mapper *bs_mapper_dumb_new(int device_fd) |
| { |
| assert(device_fd >= 0); |
| struct bs_mapper *mapper = calloc(1, sizeof(struct bs_mapper)); |
| assert(mapper); |
| mapper->map_plane_fn = dumb_map; |
| mapper->unmap_plane_fn = dumb_unmap; |
| mapper->device_fd = dup(device_fd); |
| assert(mapper->device_fd >= 0); |
| return mapper; |
| } |
| |
| void bs_mapper_destroy(struct bs_mapper *mapper) |
| { |
| assert(mapper); |
| if (mapper->device_fd >= 0) |
| close(mapper->device_fd); |
| |
| free(mapper); |
| } |
| |
| void *bs_mapper_map(struct bs_mapper *mapper, struct gbm_bo *bo, size_t plane, |
| void **map_data, uint32_t *stride) |
| { |
| assert(mapper); |
| assert(bo); |
| assert(map_data); |
| struct bs_map_info *info = calloc(1, sizeof(struct bs_map_info)); |
| info->plane_index = plane; |
| void *ptr = mapper->map_plane_fn(mapper, bo, plane, info, stride); |
| if (ptr == MAP_FAILED) { |
| free(info); |
| return MAP_FAILED; |
| } |
| |
| info->ptr = ptr; |
| *map_data = info; |
| return info->ptr; |
| } |
| |
| void bs_mapper_unmap(struct bs_mapper *mapper, struct gbm_bo *bo, void *map_data) |
| { |
| struct bs_map_info *info = map_data; |
| assert(info); |
| mapper->unmap_plane_fn(bo, info); |
| free(info); |
| } |