diff options
Diffstat (limited to 'src/android/mm')
-rw-r--r-- | src/android/mm/cros_camera_buffer.cpp | 184 | ||||
-rw-r--r-- | src/android/mm/cros_frame_buffer_allocator.cpp | 88 | ||||
-rw-r--r-- | src/android/mm/generic_camera_buffer.cpp | 199 | ||||
-rw-r--r-- | src/android/mm/generic_frame_buffer_allocator.cpp | 150 | ||||
-rw-r--r-- | src/android/mm/libhardware_stub.c | 17 | ||||
-rw-r--r-- | src/android/mm/meson.build | 19 |
6 files changed, 657 insertions, 0 deletions
diff --git a/src/android/mm/cros_camera_buffer.cpp b/src/android/mm/cros_camera_buffer.cpp new file mode 100644 index 00000000..e2a44a2a --- /dev/null +++ b/src/android/mm/cros_camera_buffer.cpp @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Chromium OS buffer backend using CameraBufferManager + */ + +#include "../camera_buffer.h" + +#include <libcamera/base/log.h> + +#include "cros-camera/camera_buffer_manager.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(HAL) + +class CameraBuffer::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(CameraBuffer) + +public: + Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer, + PixelFormat pixelFormat, const Size &size, + int flags); + ~Private(); + + bool isValid() const { return registered_; } + + unsigned int numPlanes() const; + + Span<uint8_t> plane(unsigned int plane); + + unsigned int stride(unsigned int plane) const; + unsigned int offset(unsigned int plane) const; + unsigned int size(unsigned int plane) const; + + size_t jpegBufferSize(size_t maxJpegBufferSize) const; + +private: + void map(); + + cros::CameraBufferManager *bufferManager_; + buffer_handle_t handle_; + unsigned int numPlanes_; + bool mapped_; + bool registered_; + union { + void *addr; + android_ycbcr ycbcr; + } mem; +}; + +CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer, + buffer_handle_t camera3Buffer, + [[maybe_unused]] PixelFormat pixelFormat, + [[maybe_unused]] const Size &size, + [[maybe_unused]] int flags) + : handle_(camera3Buffer), numPlanes_(0), mapped_(false), + registered_(false) +{ + bufferManager_ = cros::CameraBufferManager::GetInstance(); + if (!bufferManager_) { + LOG(HAL, Fatal) + << "Failed to get cros CameraBufferManager instance"; + return; + } + + int ret = bufferManager_->Register(camera3Buffer); + if (ret) { + LOG(HAL, Error) << "Failed registering a buffer: " << ret; + return; + } + + registered_ = true; + numPlanes_ = bufferManager_->GetNumPlanes(camera3Buffer); +} + +CameraBuffer::Private::~Private() +{ + int ret; + if (mapped_) { + ret = bufferManager_->Unlock(handle_); + if (ret != 0) + LOG(HAL, Error) << "Failed to unlock buffer: " + << strerror(-ret); + } + + if (registered_) { + ret = bufferManager_->Deregister(handle_); + if (ret != 0) + LOG(HAL, Error) << "Failed to deregister buffer: " + << strerror(-ret); + } +} + +unsigned int CameraBuffer::Private::numPlanes() const +{ + return bufferManager_->GetNumPlanes(handle_); +} + +Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane) +{ + if (!mapped_) + map(); + if (!mapped_) + return {}; + + void *addr; + + switch (numPlanes()) { + case 1: + addr = mem.addr; + break; + default: + switch (plane) { + case 0: + addr = mem.ycbcr.y; + break; + case 1: + addr = mem.ycbcr.cb; + break; + case 2: + addr = mem.ycbcr.cr; + break; + } + } + + return { static_cast<uint8_t *>(addr), + bufferManager_->GetPlaneSize(handle_, plane) }; +} + +unsigned int CameraBuffer::Private::stride(unsigned int plane) const +{ + return cros::CameraBufferManager::GetPlaneStride(handle_, plane); +} + +unsigned int CameraBuffer::Private::offset(unsigned int plane) const +{ + return cros::CameraBufferManager::GetPlaneOffset(handle_, plane); +} + +unsigned int CameraBuffer::Private::size(unsigned int plane) const +{ + return cros::CameraBufferManager::GetPlaneSize(handle_, plane); +} + +size_t CameraBuffer::Private::jpegBufferSize([[maybe_unused]] size_t maxJpegBufferSize) const +{ + return bufferManager_->GetPlaneSize(handle_, 0); +} + +void CameraBuffer::Private::map() +{ + int ret; + switch (numPlanes_) { + case 1: { + ret = bufferManager_->Lock(handle_, 0, 0, 0, 0, 0, &mem.addr); + if (ret) { + LOG(HAL, Error) << "Single plane buffer mapping failed"; + return; + } + break; + } + case 2: + case 3: { + ret = bufferManager_->LockYCbCr(handle_, 0, 0, 0, 0, 0, + &mem.ycbcr); + if (ret) { + LOG(HAL, Error) << "YCbCr buffer mapping failed"; + return; + } + break; + } + default: + LOG(HAL, Error) << "Invalid number of planes: " << numPlanes_; + return; + } + + mapped_ = true; + return; +} + +PUBLIC_CAMERA_BUFFER_IMPLEMENTATION diff --git a/src/android/mm/cros_frame_buffer_allocator.cpp b/src/android/mm/cros_frame_buffer_allocator.cpp new file mode 100644 index 00000000..264c0d48 --- /dev/null +++ b/src/android/mm/cros_frame_buffer_allocator.cpp @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Allocate FrameBuffer for Chromium OS using CameraBufferManager + */ + +#include <memory> +#include <vector> + +#include <libcamera/base/log.h> +#include <libcamera/base/shared_fd.h> + +#include "libcamera/internal/framebuffer.h" + +#include "../camera_device.h" +#include "../frame_buffer_allocator.h" +#include "../hal_framebuffer.h" +#include "cros-camera/camera_buffer_manager.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(HAL) + +namespace { +class CrosFrameBufferData : public FrameBuffer::Private +{ + LIBCAMERA_DECLARE_PUBLIC(FrameBuffer) + +public: + CrosFrameBufferData(cros::ScopedBufferHandle scopedHandle, + const std::vector<FrameBuffer::Plane> &planes) + : FrameBuffer::Private(planes), scopedHandle_(std::move(scopedHandle)) + { + } + +private: + cros::ScopedBufferHandle scopedHandle_; +}; +} /* namespace */ + +class PlatformFrameBufferAllocator::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(PlatformFrameBufferAllocator) + +public: + Private([[maybe_unused]] CameraDevice *const cameraDevice) + { + } + + std::unique_ptr<HALFrameBuffer> + allocate(int halPixelFormat, const libcamera::Size &size, uint32_t usage); +}; + +std::unique_ptr<HALFrameBuffer> +PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat, + const libcamera::Size &size, + uint32_t usage) +{ + cros::ScopedBufferHandle scopedHandle = + cros::CameraBufferManager::AllocateScopedBuffer( + size.width, size.height, halPixelFormat, usage); + if (!scopedHandle) { + LOG(HAL, Error) << "Failed to allocate buffer handle"; + return nullptr; + } + + buffer_handle_t handle = *scopedHandle; + SharedFD fd{ handle->data[0] }; + if (!fd.isValid()) { + LOG(HAL, Fatal) << "Invalid fd"; + return nullptr; + } + + /* This code assumes all the planes are located in the same buffer. */ + const size_t numPlanes = cros::CameraBufferManager::GetNumPlanes(handle); + std::vector<FrameBuffer::Plane> planes(numPlanes); + for (auto [i, plane] : utils::enumerate(planes)) { + plane.fd = fd; + plane.offset = cros::CameraBufferManager::GetPlaneOffset(handle, i); + plane.length = cros::CameraBufferManager::GetPlaneSize(handle, i); + } + + return std::make_unique<HALFrameBuffer>( + std::make_unique<CrosFrameBufferData>(std::move(scopedHandle), planes), handle); +} + +PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp new file mode 100644 index 00000000..0ffcb445 --- /dev/null +++ b/src/android/mm/generic_camera_buffer.cpp @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Generic Android frame buffer backend + */ + +#include "../camera_buffer.h" + +#include <sys/mman.h> +#include <unistd.h> + +#include <libcamera/base/log.h> + +#include "libcamera/internal/formats.h" +#include "libcamera/internal/mapped_framebuffer.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(HAL) + +class CameraBuffer::Private : public Extensible::Private, + public MappedBuffer +{ + LIBCAMERA_DECLARE_PUBLIC(CameraBuffer) + +public: + Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer, + PixelFormat pixelFormat, const Size &size, int flags); + ~Private(); + + unsigned int numPlanes() const; + + Span<uint8_t> plane(unsigned int plane); + + unsigned int stride(unsigned int plane) const; + unsigned int offset(unsigned int plane) const; + unsigned int size(unsigned int plane) const; + + size_t jpegBufferSize(size_t maxJpegBufferSize) const; + +private: + struct PlaneInfo { + unsigned int stride; + unsigned int offset; + unsigned int size; + }; + + void map(); + + int fd_; + int flags_; + off_t bufferLength_; + bool mapped_; + std::vector<PlaneInfo> planeInfo_; +}; + +CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer, + buffer_handle_t camera3Buffer, + PixelFormat pixelFormat, + const Size &size, int flags) + : fd_(-1), flags_(flags), bufferLength_(-1), mapped_(false) +{ + error_ = 0; + + const auto &info = PixelFormatInfo::info(pixelFormat); + if (!info.isValid()) { + error_ = -EINVAL; + LOG(HAL, Error) << "Invalid pixel format: " << pixelFormat; + return; + } + + /* + * As Android doesn't offer an API to query buffer layouts, assume for + * now that the buffer is backed by a single dmabuf, with planes being + * stored contiguously. + */ + for (int i = 0; i < camera3Buffer->numFds; i++) { + if (camera3Buffer->data[i] == -1 || camera3Buffer->data[i] == fd_) + continue; + + if (fd_ != -1) { + error_ = -EINVAL; + LOG(HAL, Error) << "Discontiguous planes are not supported"; + return; + } + + fd_ = camera3Buffer->data[i]; + } + + if (fd_ == -1) { + error_ = -EINVAL; + LOG(HAL, Error) << "No valid file descriptor"; + return; + } + + bufferLength_ = lseek(fd_, 0, SEEK_END); + if (bufferLength_ < 0) { + error_ = -errno; + LOG(HAL, Error) << "Failed to get buffer length"; + return; + } + + const unsigned int numPlanes = info.numPlanes(); + planeInfo_.resize(numPlanes); + + unsigned int offset = 0; + for (unsigned int i = 0; i < numPlanes; ++i) { + const unsigned int planeSize = info.planeSize(size, i); + + planeInfo_[i].stride = info.stride(size.width, i, 1u); + planeInfo_[i].offset = offset; + planeInfo_[i].size = planeSize; + + if (bufferLength_ < offset + planeSize) { + LOG(HAL, Error) << "Plane " << i << " is out of buffer:" + << " plane offset=" << offset + << ", plane size=" << planeSize + << ", buffer length=" << bufferLength_; + return; + } + + offset += planeSize; + } +} + +CameraBuffer::Private::~Private() +{ +} + +unsigned int CameraBuffer::Private::numPlanes() const +{ + return planeInfo_.size(); +} + +Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane) +{ + if (!mapped_) + map(); + if (!mapped_) + return {}; + + return planes_[plane]; +} + +unsigned int CameraBuffer::Private::stride(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].stride; +} + +unsigned int CameraBuffer::Private::offset(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].offset; +} + +unsigned int CameraBuffer::Private::size(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].size; +} + +size_t CameraBuffer::Private::jpegBufferSize(size_t maxJpegBufferSize) const +{ + ASSERT(bufferLength_ >= 0); + + return std::min<unsigned int>(bufferLength_, maxJpegBufferSize); +} + +void CameraBuffer::Private::map() +{ + ASSERT(fd_ != -1); + ASSERT(bufferLength_ >= 0); + + void *address = mmap(nullptr, bufferLength_, flags_, MAP_SHARED, fd_, 0); + if (address == MAP_FAILED) { + error_ = -errno; + LOG(HAL, Error) << "Failed to mmap plane"; + return; + } + maps_.emplace_back(static_cast<uint8_t *>(address), bufferLength_); + + planes_.reserve(planeInfo_.size()); + for (const auto &info : planeInfo_) { + planes_.emplace_back( + static_cast<uint8_t *>(address) + info.offset, info.size); + } + + mapped_ = true; +} + +PUBLIC_CAMERA_BUFFER_IMPLEMENTATION diff --git a/src/android/mm/generic_frame_buffer_allocator.cpp b/src/android/mm/generic_frame_buffer_allocator.cpp new file mode 100644 index 00000000..79625a9a --- /dev/null +++ b/src/android/mm/generic_frame_buffer_allocator.cpp @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Allocate FrameBuffer using gralloc API + */ + +#include <dlfcn.h> +#include <memory> +#include <vector> + +#include <libcamera/base/log.h> +#include <libcamera/base/shared_fd.h> + +#include "libcamera/internal/formats.h" +#include "libcamera/internal/framebuffer.h" + +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <hardware/hardware.h> + +#include "../camera_device.h" +#include "../frame_buffer_allocator.h" +#include "../hal_framebuffer.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(HAL) + +namespace { +class GenericFrameBufferData : public FrameBuffer::Private +{ + LIBCAMERA_DECLARE_PUBLIC(FrameBuffer) + +public: + GenericFrameBufferData(struct alloc_device_t *allocDevice, + buffer_handle_t handle, + const std::vector<FrameBuffer::Plane> &planes) + : FrameBuffer::Private(planes), allocDevice_(allocDevice), + handle_(handle) + { + ASSERT(allocDevice_); + ASSERT(handle_); + } + + ~GenericFrameBufferData() override + { + /* + * allocDevice_ is used to destroy handle_. allocDevice_ is + * owned by PlatformFrameBufferAllocator::Private. + * GenericFrameBufferData must be destroyed before it is + * destroyed. + * + * \todo Consider managing alloc_device_t with std::shared_ptr + * if this is difficult to maintain. + * + * \todo Thread safety against alloc_device_t is not documented. + * Is it no problem to call alloc/free in parallel? + */ + allocDevice_->free(allocDevice_, handle_); + } + +private: + struct alloc_device_t *allocDevice_; + const buffer_handle_t handle_; +}; +} /* namespace */ + +class PlatformFrameBufferAllocator::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(PlatformFrameBufferAllocator) + +public: + Private(CameraDevice *const cameraDevice) + : cameraDevice_(cameraDevice), + hardwareModule_(nullptr), + allocDevice_(nullptr) + { + hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &hardwareModule_); + ASSERT(hardwareModule_); + } + + ~Private() override; + + std::unique_ptr<HALFrameBuffer> + allocate(int halPixelFormat, const libcamera::Size &size, uint32_t usage); + +private: + const CameraDevice *const cameraDevice_; + const struct hw_module_t *hardwareModule_; + struct alloc_device_t *allocDevice_; +}; + +PlatformFrameBufferAllocator::Private::~Private() +{ + if (allocDevice_) + gralloc_close(allocDevice_); + dlclose(hardwareModule_->dso); +} + +std::unique_ptr<HALFrameBuffer> +PlatformFrameBufferAllocator::Private::allocate(int halPixelFormat, + const libcamera::Size &size, + uint32_t usage) +{ + if (!allocDevice_) { + int ret = gralloc_open(hardwareModule_, &allocDevice_); + if (ret) { + LOG(HAL, Fatal) << "gralloc_open() failed: " << ret; + return nullptr; + } + } + + int stride = 0; + buffer_handle_t handle = nullptr; + int ret = allocDevice_->alloc(allocDevice_, size.width, size.height, + halPixelFormat, usage, &handle, &stride); + if (ret) { + LOG(HAL, Error) << "failed buffer allocation: " << ret; + return nullptr; + } + if (!handle) { + LOG(HAL, Fatal) << "invalid buffer_handle_t"; + return nullptr; + } + + /* This code assumes the planes are mapped consecutively. */ + const libcamera::PixelFormat pixelFormat = + cameraDevice_->capabilities()->toPixelFormat(halPixelFormat); + const auto &info = PixelFormatInfo::info(pixelFormat); + std::vector<FrameBuffer::Plane> planes(info.numPlanes()); + + SharedFD fd{ handle->data[0] }; + size_t offset = 0; + for (auto [i, plane] : utils::enumerate(planes)) { + const size_t planeSize = info.planeSize(size.height, i, stride); + + plane.fd = fd; + plane.offset = offset; + plane.length = planeSize; + offset += planeSize; + } + + return std::make_unique<HALFrameBuffer>( + std::make_unique<GenericFrameBufferData>( + allocDevice_, handle, planes), + handle); +} + +PUBLIC_FRAME_BUFFER_ALLOCATOR_IMPLEMENTATION diff --git a/src/android/mm/libhardware_stub.c b/src/android/mm/libhardware_stub.c new file mode 100644 index 00000000..28faa638 --- /dev/null +++ b/src/android/mm/libhardware_stub.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Copyright (C) 2023, Ideas on Board + * + * Android libhardware stub for test compilation + */ + +#include <errno.h> + +#include <hardware/hardware.h> + +int hw_get_module(const char *id __attribute__((__unused__)), + const struct hw_module_t **module) +{ + *module = NULL; + return -ENOTSUP; +} diff --git a/src/android/mm/meson.build b/src/android/mm/meson.build new file mode 100644 index 00000000..e3e0484c --- /dev/null +++ b/src/android/mm/meson.build @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: CC0-1.0 + +platform = get_option('android_platform') +if platform == 'generic' + android_hal_sources += files(['generic_camera_buffer.cpp', + 'generic_frame_buffer_allocator.cpp']) + android_deps += [libdl] + + libhardware = dependency('libhardware', required : false) + if libhardware.found() + android_deps += [libhardware] + else + android_hal_sources += files(['libhardware_stub.c']) + endif +elif platform == 'cros' + android_hal_sources += files(['cros_camera_buffer.cpp', + 'cros_frame_buffer_allocator.cpp']) + android_deps += [dependency('libcros_camera')] +endif |