summaryrefslogtreecommitdiff
path: root/src/android/mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/mm')
-rw-r--r--src/android/mm/cros_camera_buffer.cpp184
-rw-r--r--src/android/mm/cros_frame_buffer_allocator.cpp88
-rw-r--r--src/android/mm/generic_camera_buffer.cpp199
-rw-r--r--src/android/mm/generic_frame_buffer_allocator.cpp150
-rw-r--r--src/android/mm/libhardware_stub.c17
-rw-r--r--src/android/mm/meson.build19
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