| // Copyright 2024 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "minigbm_allocator.h" |
| |
| #include <gbm.h> |
| #include <unistd.h> |
| |
| #include <libcamera/base/log.h> |
| |
| #if HAVE_ANDROID_OS |
| #include <minigbm_helpers.h> |
| #else |
| #include <minigbm/minigbm_helpers.h> |
| #endif |
| #include <memory> |
| #include <sys/mman.h> |
| #include <utility> |
| |
| #include "allocator.h" |
| |
| #define CHECK(...) |
| #define CHECK_NE(...) |
| #define CHECK_LT(...) |
| |
| using namespace libcamera; |
| |
| LOG_DECLARE_CATEGORY(HAL) |
| |
| namespace crosLibcamera { |
| |
| namespace { |
| |
| uint32_t SyncTypeToGbmTransferFlag(SyncType sync_type) |
| { |
| switch (sync_type) { |
| case SyncType::kSyncRead: |
| return GBM_BO_TRANSFER_READ; |
| case SyncType::kSyncWrite: |
| return GBM_BO_TRANSFER_WRITE; |
| case SyncType::kSyncReadWrite: |
| return GBM_BO_TRANSFER_READ_WRITE; |
| } |
| } |
| |
| } // namespace |
| |
| MinigbmAllocator::BufferObject::BufferObject(struct gbm_bo *bo, |
| uint32_t gbm_flags) |
| : bo_(bo) |
| { |
| CHECK_NE(bo_, nullptr); |
| |
| desc_ = { |
| .drm_format = gbm_bo_get_format(bo_), |
| .width = static_cast<int>(gbm_bo_get_width(bo_)), |
| .height = static_cast<int>(gbm_bo_get_height(bo_)), |
| .gbm_flags = gbm_flags, |
| .num_planes = gbm_bo_get_plane_count(bo_), |
| .format_modifier = gbm_bo_get_modifier(bo_), |
| }; |
| for (int i = 0; i < desc_.num_planes; ++i) { |
| desc_.planes[i] = { |
| .size = static_cast<int>(gbm_bo_get_plane_size(bo_, i)), |
| .offset = static_cast<int>(gbm_bo_get_offset(bo_, i)), |
| // Should this be per-plane? |
| .pixel_stride = static_cast<int>(gbm_bo_get_bpp(bo_)), |
| .row_stride = static_cast<int>(gbm_bo_get_stride_for_plane(bo_, i)), |
| }; |
| } |
| } |
| |
| MinigbmAllocator::BufferObject::BufferObject(BufferObject &&other) |
| { |
| (*this) = std::move(other); |
| } |
| |
| MinigbmAllocator::BufferObject &MinigbmAllocator::BufferObject::operator=( |
| BufferObject &&other) |
| { |
| if (this != &other) { |
| Invalidate(); |
| this->bo_ = other.bo_; |
| this->plane_data_ = other.plane_data_; |
| this->desc_ = other.desc_; |
| other.bo_ = nullptr; |
| for (int i = 0; i < kMaxPlanes; ++i) { |
| other.plane_data_[i] = { .map_data = nullptr, .addr = nullptr }; |
| } |
| other.desc_ = BufferDescriptor(); |
| } |
| return *this; |
| } |
| |
| MinigbmAllocator::BufferObject::~BufferObject() |
| { |
| Invalidate(); |
| } |
| |
| BufferDescriptor MinigbmAllocator::BufferObject::Describe() const |
| { |
| return desc_; |
| } |
| |
| bool MinigbmAllocator::BufferObject::BeginCpuAccess(SyncType sync_type, |
| int plane) |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| if (IsMapped(plane)) { |
| Unmap(plane); |
| } |
| return MapInternal(sync_type, plane); |
| } |
| |
| bool MinigbmAllocator::BufferObject::EndCpuAccess(SyncType sync_type, |
| int plane) |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| if (IsMapped(plane)) { |
| Unmap(plane); |
| return MapInternal(sync_type, plane); |
| } |
| return true; |
| } |
| |
| bool MinigbmAllocator::BufferObject::Map(int plane) |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| return MapInternal(SyncType::kSyncReadWrite, plane); |
| } |
| |
| void MinigbmAllocator::BufferObject::Unmap(int plane) |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| UnmapInternal(plane); |
| } |
| |
| int MinigbmAllocator::BufferObject::GetPlaneFd(int plane) const |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| return gbm_bo_get_plane_fd(bo_, plane); |
| } |
| |
| void *MinigbmAllocator::BufferObject::GetPlaneAddr(int plane) const |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| if (!IsMapped(plane)) { |
| LOG(HAL, Error) << "Buffer 0x" << std::hex << GetId() << " is not mapped"; |
| return nullptr; |
| } |
| return plane_data_[plane].addr; |
| } |
| |
| uint64_t MinigbmAllocator::BufferObject::GetId() const |
| { |
| CHECK_NE(bo_, nullptr); |
| return reinterpret_cast<uint64_t>(bo_); |
| } |
| |
| bool MinigbmAllocator::BufferObject::MapInternal(SyncType sync_type, |
| int plane) |
| { |
| if (IsMapped(plane)) { |
| return true; |
| } |
| uint32_t stride = 0; |
| void *addr = |
| gbm_bo_map2(bo_, 0, 0, gbm_bo_get_width(bo_), gbm_bo_get_height(bo_), |
| SyncTypeToGbmTransferFlag(sync_type), &stride, |
| &plane_data_[plane].map_data, plane); |
| if (addr == MAP_FAILED) { |
| LOG(HAL, Error) << "Failed to map buffer"; |
| plane_data_[plane].map_data = nullptr; |
| return false; |
| } |
| plane_data_[plane].addr = addr; |
| return true; |
| } |
| |
| void MinigbmAllocator::BufferObject::UnmapInternal(int plane) |
| { |
| if (!IsMapped(plane)) { |
| return; |
| } |
| if (plane_data_[plane].map_data) { |
| gbm_bo_unmap(bo_, plane_data_[plane].map_data); |
| } |
| plane_data_[plane].map_data = nullptr; |
| plane_data_[plane].addr = nullptr; |
| } |
| |
| bool MinigbmAllocator::BufferObject::IsMapped(int plane) const |
| { |
| CHECK_NE(bo_, nullptr); |
| CHECK_LT(plane, desc_.num_planes); |
| return plane_data_[plane].map_data != nullptr && |
| plane_data_[plane].addr != nullptr; |
| } |
| |
| void MinigbmAllocator::BufferObject::Invalidate() |
| { |
| if (bo_) { |
| for (size_t i = 0; i < desc_.num_planes; ++i) { |
| Unmap(i); |
| } |
| gbm_bo_destroy(bo_); |
| bo_ = nullptr; |
| } |
| } |
| |
| MinigbmAllocator::MinigbmAllocator(struct gbm_device *gbm_device) |
| : gbm_device_(gbm_device) |
| { |
| CHECK(gbm_device_); |
| } |
| |
| MinigbmAllocator::~MinigbmAllocator() |
| { |
| close(gbm_device_get_fd(gbm_device_)); |
| gbm_device_destroy(gbm_device_); |
| } |
| |
| std::unique_ptr<Allocator::BufferObject> MinigbmAllocator::CreateBo( |
| int width, int height, uint32_t drm_format, uint32_t gbm_flags) |
| { |
| return std::make_unique<BufferObject>( |
| gbm_bo_create(gbm_device_, width, height, drm_format, gbm_flags), |
| gbm_flags); |
| } |
| |
| std::unique_ptr<Allocator::BufferObject> MinigbmAllocator::ImportBo( |
| const ImportData &data) |
| { |
| auto &desc = data.desc; |
| struct gbm_import_fd_modifier_data import_data = { |
| .width = static_cast<uint32_t>(desc.width), |
| .height = static_cast<uint32_t>(desc.height), |
| .format = desc.drm_format, |
| .num_fds = static_cast<uint32_t>(desc.num_planes), |
| .modifier = desc.format_modifier, |
| }; |
| for (size_t i = 0; i < desc.num_planes; ++i) { |
| import_data.fds[i] = data.plane_fd[i]; |
| import_data.strides[i] = desc.planes[i].row_stride; |
| import_data.offsets[i] = desc.planes[i].offset; |
| } |
| return std::make_unique<BufferObject>( |
| gbm_bo_import(gbm_device_, GBM_BO_IMPORT_FD_MODIFIER, &import_data, |
| desc.gbm_flags), |
| desc.gbm_flags); |
| } |
| |
| bool MinigbmAllocator::IsFormatSupported(uint32_t drm_format, |
| uint32_t gbm_flags) |
| { |
| return gbm_device_is_format_supported(gbm_device_, drm_format, gbm_flags); |
| } |
| |
| std::unique_ptr<Allocator> CreateMinigbmAllocator() |
| { |
| int unused_fd = -1; |
| struct gbm_device *gbm_device = minigbm_create_default_device(&unused_fd); |
| if (!gbm_device) { |
| LOG(HAL, Error) << "Minigbm not supported"; |
| return nullptr; |
| } |
| return std::make_unique<MinigbmAllocator>(gbm_device); |
| } |
| |
| } // namespace crosLibcamera |