blob: c50dbedb646d9ff6ee7f44046bc921fc963d8c69 [file] [log] [blame]
// 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