/*
 * 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 "camera_buffer_manager_impl.h"

#include <cerrno>
#include <drm_fourcc.h>
#include <gbm.h>
#include <map>
#include <sys/mman.h>
#include <utility>
#include <vector>

#include <linux/videodev2.h>

#include <libcamera/base/log.h>

#include <hardware/gralloc.h>
#include <system/graphics.h>

#include "allocator.h"
#include "camera_buffer_handle.h"
#include "minigbm_allocator.h"

using namespace libcamera;

LOG_DECLARE_CATEGORY(HAL)

// A private gralloc usage flag to force allocation of YUV420 buffer.  This
// usage flag is only valid when allocating HAL_PIXEL_FORMAT_YCbCr_420_888
// flexible YUV buffers.
const uint32_t GRALLOC_USAGE_FORCE_I420 = 0x10000000U;

template<typename T>
class NoDestructor
{
public:
	static_assert(!(std::is_trivially_constructible_v<T> &&
			std::is_trivially_destructible_v<T>),
		      "T is trivially constructible and destructible; please use a "
		      "constinit object of type T directly instead");

	static_assert(
		!std::is_trivially_destructible_v<T>,
		"T is trivially destructible; please use a function-local static "
		"of type T directly instead");

	// Not constexpr; just write static constexpr T x = ...; if the value should
	// be a constexpr.
	template<typename... Args>
	explicit NoDestructor(Args &&...args)
	{
		new (storage_) T(std::forward<Args>(args)...);
	}

	// Allows copy and move construction of the contained type, to allow
	// construction from an initializer list, e.g. for std::vector.
	explicit NoDestructor(const T &x) { new (storage_) T(x); }
	explicit NoDestructor(T &&x) { new (storage_) T(std::move(x)); }

	NoDestructor(const NoDestructor &) = delete;
	NoDestructor &operator=(const NoDestructor &) = delete;

	~NoDestructor() = default;

	const T &operator*() const { return *get(); }
	T &operator*() { return *get(); }

	const T *operator->() const { return get(); }
	T *operator->() { return get(); }

	const T *get() const { return reinterpret_cast<const T *>(storage_); }
	T *get() { return reinterpret_cast<T *>(storage_); }

private:
	alignas(T) char storage_[sizeof(T)];

#if defined(LEAK_SANITIZER)
	// TODO(crbug.com/40562930): This is a hack to work around the fact
	// that LSan doesn't seem to treat NoDestructor as a root for reachability
	// analysis. This means that code like this:
	//   static base::NoDestructor<std::vector<int>> v({1, 2, 3});
	// is considered a leak. Using the standard leak sanitizer annotations to
	// suppress leaks doesn't work: std::vector is implicitly constructed before
	// calling the base::NoDestructor constructor.
	//
	// Unfortunately, I haven't been able to demonstrate this issue in simpler
	// reproductions: until that's resolved, hold an explicit pointer to the
	// placement-new'd object in leak sanitizer mode to help LSan realize that
	// objects allocated by the contained type are still reachable.
	T *storage_ptr_ = reinterpret_cast<T *>(storage_);
#endif // defined(LEAK_SANITIZER)
};

namespace crosLibcamera {

namespace {

inline std::string FormatToString(int32_t format)
{
	return std::string(reinterpret_cast<char *>(&format), 4);
}

std::map<uint32_t, std::vector<uint32_t>> kSupportedHalFormats{
	{ HAL_PIXEL_FORMAT_BLOB, { DRM_FORMAT_R8 } },
	{ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
	  { DRM_FORMAT_NV12, DRM_FORMAT_XBGR8888, DRM_FORMAT_MTISP_SXYZW10 } },
	{ HAL_PIXEL_FORMAT_RGBX_8888, { DRM_FORMAT_XBGR8888 } },
	{ HAL_PIXEL_FORMAT_RGBA_8888, { DRM_FORMAT_ABGR8888 } },
	{ HAL_PIXEL_FORMAT_YCbCr_420_888, { DRM_FORMAT_NV12 } },
	// Map to DRM_FORMAT_ABGR8888 because DRM_FORMAT_VYUY or DRM_FORMAT_YUYV is
	// not generally supported by minigbm.
	{ HAL_PIXEL_FORMAT_YCbCr_422_I, { DRM_FORMAT_ABGR8888 } },
	{ HAL_PIXEL_FORMAT_YCBCR_P010, { DRM_FORMAT_P010 } },
	{ HAL_PIXEL_FORMAT_Y8, { DRM_FORMAT_R8 } },
};

uint32_t GetGbmUseFlags(uint32_t hal_format, uint32_t hal_usage)
{
	uint32_t gbm_flags = 0;
	if (hal_format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED ||
	    !(hal_usage & GRALLOC_USAGE_HW_CAMERA_READ)) {
		// The default GBM flags for non-private-reprocessing camera buffers.
		gbm_flags = GBM_BO_USE_SW_READ_OFTEN | GBM_BO_USE_SW_WRITE_OFTEN;
	}

	if (hal_usage & GRALLOC_USAGE_HW_CAMERA_READ) {
		gbm_flags |= GBM_BO_USE_CAMERA_READ;
	}
	if (hal_usage & GRALLOC_USAGE_HW_CAMERA_WRITE) {
		gbm_flags |= GBM_BO_USE_CAMERA_WRITE;
	}
	if (hal_usage & GRALLOC_USAGE_HW_TEXTURE) {
		gbm_flags |= GBM_BO_USE_TEXTURING;
	}
	if (hal_usage & GRALLOC_USAGE_HW_RENDER) {
		gbm_flags |= GBM_BO_USE_RENDERING;
	}
	if (hal_usage & GRALLOC_USAGE_HW_COMPOSER) {
		gbm_flags |= GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING;
	}
	if (hal_usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) {
		gbm_flags |= GBM_BO_USE_HW_VIDEO_ENCODER;
	}
	return gbm_flags;
}

bool IsMatchingFormat(int32_t hal_pixel_format, uint32_t drm_format)
{
	switch (hal_pixel_format) {
	case HAL_PIXEL_FORMAT_RGBA_8888:
		return drm_format == DRM_FORMAT_ABGR8888;
	case HAL_PIXEL_FORMAT_RGBX_8888:
		return drm_format == DRM_FORMAT_XBGR8888;
	case HAL_PIXEL_FORMAT_BGRA_8888:
		return drm_format == DRM_FORMAT_ARGB8888;
	case HAL_PIXEL_FORMAT_YCrCb_420_SP:
		return drm_format == DRM_FORMAT_NV21;
	case HAL_PIXEL_FORMAT_YCbCr_422_I:
		return drm_format == DRM_FORMAT_ABGR8888;
	case HAL_PIXEL_FORMAT_BLOB:
		return drm_format == DRM_FORMAT_R8;
	case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
		// We can't really check implementation defined formats.
		return true;
	case HAL_PIXEL_FORMAT_YCbCr_420_888:
		return (drm_format == DRM_FORMAT_YUV420 ||
			drm_format == DRM_FORMAT_YVU420 ||
			drm_format == DRM_FORMAT_NV21 || drm_format == DRM_FORMAT_NV12);
	case HAL_PIXEL_FORMAT_YV12:
		return drm_format == DRM_FORMAT_YVU420;
	}
	return false;
}

size_t GetChromaStep(uint32_t drm_format)
{
	switch (drm_format) {
	case DRM_FORMAT_P010:
		return 4;
	case DRM_FORMAT_NV12:
	case DRM_FORMAT_NV21:
		return 2;
	case DRM_FORMAT_YUV420:
	case DRM_FORMAT_YVU420:
		return 1;
	}
	return 0;
}

uint8_t *GetPlaneAddr(const android_ycbcr &ycbcr,
		      uint32_t drm_format,
		      size_t plane)
{
	void *result = nullptr;
	if (plane == 0) {
		result = ycbcr.y;
	} else if (plane == 1) {
		switch (drm_format) {
		case DRM_FORMAT_NV12:
		case DRM_FORMAT_P010:
		case DRM_FORMAT_YUV420:
			result = ycbcr.cb;
			break;

		case DRM_FORMAT_NV21:
		case DRM_FORMAT_YVU420:
			result = ycbcr.cr;
			break;
		}
	} else if (plane == 2) {
		switch (drm_format) {
		case DRM_FORMAT_YUV420:
			result = ycbcr.cr;
			break;

		case DRM_FORMAT_YVU420:
			result = ycbcr.cb;
			break;
		}
	}
	if (result == nullptr) {
		LOG(HAL, Error) << "Unsupported DRM pixel format: "
				<< FormatToString(drm_format);
	}
	return reinterpret_cast<uint8_t *>(result);
}

} // namespace

void BufferHandleDeleter::operator()(buffer_handle_t *handle)
{
	if (handle) {
		auto *buf_mgr = crosLibcamera::CameraBufferManager::GetInstance();
		if (buf_mgr && *handle != nullptr) {
			buf_mgr->Free(*handle);
		}
		delete handle;
	}
}

//
// ScopedMapping implementations.
//

ScopedMapping::ScopedMapping(buffer_handle_t buffer)
	: buf_(buffer)
{
	for (size_t i = 0; i < num_planes(); ++i) {
		planes_[i] = {
			.stride = CameraBufferManager::GetPlaneStride(buf_, i),
			.size = CameraBufferManager::GetPlaneSize(buf_, i),
		};
	}
	auto *buf_mgr = CameraBufferManager::GetInstance();
	if (buf_mgr->Register(buf_) != 0) {
		LOG(HAL, Error) << "Cannot register buffer";
		Invalidate();
		return;
	}
	if (num_planes() == 1) {
		void **addr_to_void = reinterpret_cast<void **>(&planes_[0].addr);
		if (buf_mgr->Lock(buf_, 0, 0, 0, width(), height(), addr_to_void) != 0) {
			LOG(HAL, Error) << "Cannot Lock buffer";
			Invalidate();
			return;
		}
	} else {
		android_ycbcr ycbcr = {};
		if (buf_mgr->LockYCbCr(buf_, 0, 0, 0, width(), height(), &ycbcr) != 0) {
			LOG(HAL, Error) << "Cannot Lock buffer";
			Invalidate();
			return;
		}
		for (size_t i = 0; i < num_planes(); ++i) {
			planes_[i].addr = GetPlaneAddr(ycbcr, drm_format(), i);
		}
	}
}

ScopedMapping::~ScopedMapping()
{
	Invalidate();
}

ScopedMapping::ScopedMapping(ScopedMapping &&other)
{
	*this = std::move(other);
}

ScopedMapping &ScopedMapping::operator=(ScopedMapping &&other)
{
	if (this != &other) {
		Invalidate();
		planes_ = other.planes_;
		buf_ = other.buf_;
		other.planes_.fill(Plane());
		other.buf_ = nullptr;
	}
	return *this;
}

uint32_t ScopedMapping::width() const
{
	return CameraBufferManager::GetWidth(buf_);
}

uint32_t ScopedMapping::height() const
{
	return CameraBufferManager::GetHeight(buf_);
}

uint32_t ScopedMapping::drm_format() const
{
	return CameraBufferManager::GetDrmPixelFormat(buf_);
}

uint32_t ScopedMapping::v4l2_format() const
{
	return CameraBufferManager::GetV4L2PixelFormat(buf_);
}

uint32_t ScopedMapping::hal_pixel_format() const
{
	return CameraBufferManager::GetHalPixelFormat(buf_);
}

uint32_t ScopedMapping::num_planes() const
{
	return CameraBufferManager::GetNumPlanes(buf_);
}

ScopedMapping::Plane ScopedMapping::plane(int plane) const
{
	if (plane > planes_.size()) {
		return Plane();
	}
	return planes_[plane];
}

bool ScopedMapping::is_valid() const
{
	return buf_ != nullptr;
}

void ScopedMapping::Invalidate()
{
	if (buf_ == nullptr) {
		return;
	}
	auto *buf_mgr = CameraBufferManager::GetInstance();
	buf_mgr->Unlock(buf_);
	buf_mgr->Deregister(buf_);
	planes_.fill(Plane());
	buf_ = nullptr;
}

//
// CameraBufferManagerImpl implementations.
//

// static
CameraBufferManager *CameraBufferManager::GetInstance()
{
	static NoDestructor<CameraBufferManagerImpl> instance(
		crosLibcamera::CreateMinigbmAllocator());
	if (!instance->allocator_) {
		LOG(HAL, Error)
			<< "Failed to create the buffer allocator for CameraBufferManager";
		return nullptr;
	}
	return instance.get();
}

// static
bool CameraBufferManager::IsValidBuffer(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return false;
	}
	if (!IsMatchingFormat(handle->hal_pixel_format, handle->drm_format)) {
		LOG(HAL, Error) << "HAL pixel format " << handle->hal_pixel_format
				<< " does not match DRM format "
				<< FormatToString(handle->drm_format);
		return false;
	}
	return true;
}

// static
uint32_t CameraBufferManager::GetWidth(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}

	return handle->width;
}

// static
uint32_t CameraBufferManager::GetHeight(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}

	return handle->height;
}

// static
uint32_t CameraBufferManager::GetNumPlanes(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}

	switch (handle->drm_format) {
	case DRM_FORMAT_ABGR1555:
	case DRM_FORMAT_ABGR2101010:
	case DRM_FORMAT_ABGR4444:
	case DRM_FORMAT_ABGR8888:
	case DRM_FORMAT_ARGB1555:
	case DRM_FORMAT_ARGB2101010:
	case DRM_FORMAT_ARGB4444:
	case DRM_FORMAT_ARGB8888:
	case DRM_FORMAT_AYUV:
	case DRM_FORMAT_BGR233:
	case DRM_FORMAT_BGR565:
	case DRM_FORMAT_BGR888:
	case DRM_FORMAT_BGRA1010102:
	case DRM_FORMAT_BGRA4444:
	case DRM_FORMAT_BGRA5551:
	case DRM_FORMAT_BGRA8888:
	case DRM_FORMAT_BGRX1010102:
	case DRM_FORMAT_BGRX4444:
	case DRM_FORMAT_BGRX5551:
	case DRM_FORMAT_BGRX8888:
	case DRM_FORMAT_C8:
	case DRM_FORMAT_GR88:
	case DRM_FORMAT_R8:
	case DRM_FORMAT_RG88:
	case DRM_FORMAT_RGB332:
	case DRM_FORMAT_RGB565:
	case DRM_FORMAT_RGB888:
	case DRM_FORMAT_RGBA1010102:
	case DRM_FORMAT_RGBA4444:
	case DRM_FORMAT_RGBA5551:
	case DRM_FORMAT_RGBA8888:
	case DRM_FORMAT_RGBX1010102:
	case DRM_FORMAT_RGBX4444:
	case DRM_FORMAT_RGBX5551:
	case DRM_FORMAT_RGBX8888:
	case DRM_FORMAT_UYVY:
	case DRM_FORMAT_VYUY:
	case DRM_FORMAT_XBGR1555:
	case DRM_FORMAT_XBGR2101010:
	case DRM_FORMAT_XBGR4444:
	case DRM_FORMAT_XBGR8888:
	case DRM_FORMAT_XRGB1555:
	case DRM_FORMAT_XRGB2101010:
	case DRM_FORMAT_XRGB4444:
	case DRM_FORMAT_XRGB8888:
	case DRM_FORMAT_YUYV:
	case DRM_FORMAT_YVYU:
	case DRM_FORMAT_MTISP_SXYZW10:
		return 1;
	case DRM_FORMAT_NV12:
	case DRM_FORMAT_NV21:
	case DRM_FORMAT_P010:
		return 2;
	case DRM_FORMAT_YUV420:
	case DRM_FORMAT_YVU420:
		return 3;
	}

	LOG(HAL, Error) << "Unknown format: " << FormatToString(handle->drm_format);
	return 0;
}

// static
uint32_t CameraBufferManager::GetV4L2PixelFormat(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}

	uint32_t num_planes = GetNumPlanes(buffer);
	if (!num_planes) {
		return 0;
	}

	bool is_mplane = false;
	if (num_planes > 1) {
		// Check if the buffer has multiple physical planes by checking the offsets
		// of each plane.  If any of the offsets is zero, then we assume the buffer
		// is of multi-planar format.
		for (size_t i = 1; i < num_planes; ++i) {
			if (!handle->offsets[i]) {
				is_mplane = true;
			}
		}
	}

	switch (handle->drm_format) {
	case DRM_FORMAT_ARGB8888:
		return V4L2_PIX_FMT_ABGR32;

	// There is no standard V4L2 pixel format corresponding to
	// DRM_FORMAT_xBGR8888.  We use our own V4L2 format extension
	// V4L2_PIX_FMT_RGBX32 here.
	case DRM_FORMAT_ABGR8888:
		return V4L2_PIX_FMT_RGBX32;
	case DRM_FORMAT_XBGR8888:
		return V4L2_PIX_FMT_RGBX32;

	// DRM_FORMAT_R8 is used as the underlying buffer format for
	// HAL_PIXEL_FORMAT_BLOB which corresponds to JPEG buffer.
	case DRM_FORMAT_R8:
		return V4L2_PIX_FMT_JPEG;

	// Semi-planar formats.
	case DRM_FORMAT_NV12:
		return is_mplane ? V4L2_PIX_FMT_NV12M : V4L2_PIX_FMT_NV12;
	case DRM_FORMAT_NV21:
		return is_mplane ? V4L2_PIX_FMT_NV21M : V4L2_PIX_FMT_NV21;
	case DRM_FORMAT_P010:
		return is_mplane ? V4L2_PIX_FMT_P010M : V4L2_PIX_FMT_P010;

	// Multi-planar formats.
	case DRM_FORMAT_YUV420:
		return is_mplane ? V4L2_PIX_FMT_YUV420M : V4L2_PIX_FMT_YUV420;
	case DRM_FORMAT_YVU420:
		return is_mplane ? V4L2_PIX_FMT_YVU420M : V4L2_PIX_FMT_YVU420;
	}

	LOG(HAL, Error) << "Could not convert format "
			<< FormatToString(handle->drm_format) << " to V4L2 pixel format";
	return 0;
}

// static
size_t CameraBufferManager::GetPlaneStride(buffer_handle_t buffer,
					   size_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}
	if (plane >= GetNumPlanes(buffer)) {
		LOG(HAL, Error) << "Invalid plane: " << plane;
		return 0;
	}
	return handle->strides[plane];
}

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))

// static
size_t CameraBufferManager::GetPlaneSize(buffer_handle_t buffer, size_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}
	if (plane >= GetNumPlanes(buffer)) {
		LOG(HAL, Error) << "Invalid plane: " << plane;
		return 0;
	}
	uint32_t vertical_subsampling;
	switch (handle->drm_format) {
	case DRM_FORMAT_NV12:
	case DRM_FORMAT_NV21:
	case DRM_FORMAT_P010:
	case DRM_FORMAT_YUV420:
	case DRM_FORMAT_YVU420:
		vertical_subsampling = (plane == 0) ? 1 : 2;
		break;
	default:
		vertical_subsampling = 1;
	}
	return (handle->strides[plane] *
		DIV_ROUND_UP(handle->height, vertical_subsampling));
}

// static
off_t CameraBufferManager::GetPlaneOffset(buffer_handle_t buffer,
					  size_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -1;
	}
	if (plane >= GetNumPlanes(buffer)) {
		LOG(HAL, Error) << "Invalid plane: " << plane;
		return -1;
	}
	return handle->offsets[plane];
}

// static
uint64_t CameraBufferManager::GetModifier(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return DRM_FORMAT_MOD_INVALID;
	}

	return handle->modifier;
}

// static
int CameraBufferManager::GetPlaneFd(buffer_handle_t buffer, size_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -1;
	}
	if (plane >= GetNumPlanes(buffer)) {
		LOG(HAL, Error) << "Invalid plane: " << plane;
		return -1;
	}
	return handle->fds[plane];
}

// static
uint32_t CameraBufferManager::GetHalPixelFormat(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}
	return handle->hal_pixel_format;
}

// static
uint32_t CameraBufferManager::GetDrmPixelFormat(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return 0;
	}
	return handle->drm_format;
}

CameraBufferManagerImpl::CameraBufferManagerImpl(
	std::unique_ptr<Allocator> gbm_allocator)
	: allocator_(std::move(gbm_allocator))
{
}

int CameraBufferManagerImpl::Allocate(size_t width,
				      size_t height,
				      uint32_t hal_format,
				      uint32_t hal_usage,
				      buffer_handle_t *out_buffer,
				      uint32_t *out_stride)
{
	std::lock_guard l(lock_);

	uint32_t gbm_flags = 0;
	uint32_t drm_format = ResolveFormat(hal_format, hal_usage, &gbm_flags);
	if (!drm_format) {
		return -EINVAL;
	}

	BufferContext context = {
		.bo = allocator_->CreateBo(width, height, drm_format, gbm_flags)
	};
	if (!context.bo) {
		LOG(HAL, Error) << "Failed to create GBM bo";
		return -ENOMEM;
	}

	auto handle = std::make_unique<camera_buffer_handle_t>();
	handle->base.version = sizeof(handle->base);
	handle->base.numInts = kCameraBufferHandleNumInts;
	handle->base.numFds = kCameraBufferHandleNumFds;
	handle->magic = kCameraBufferMagic;
	handle->buffer_id = context.bo->GetId();
	handle->drm_format = drm_format;
	handle->hal_pixel_format = hal_format;
	handle->hal_usage_flags = hal_usage;
	handle->width = width;
	handle->height = height;
	auto desc = context.bo->Describe();
	for (size_t i = 0; i < desc.num_planes; ++i) {
		handle->fds[i] = context.bo->GetPlaneFd(i);
		handle->strides[i] = desc.planes[i].row_stride;
		handle->offsets[i] = desc.planes[i].offset;
	}
	handle->modifier = desc.format_modifier;

	if (desc.num_planes == 1) {
		*out_stride = handle->strides[0];
	} else {
		*out_stride = 0;
	}
	*out_buffer = reinterpret_cast<buffer_handle_t>(handle.release());

	context.refcount = 1;
	buffers_[*out_buffer] = std::move(context);
	return 0;
}

// static
ScopedBufferHandle CameraBufferManager::AllocateScopedBuffer(
	size_t width, size_t height, uint32_t hal_format, uint32_t hal_usage)
{
	auto *buf_mgr = CameraBufferManager::GetInstance();
	ScopedBufferHandle buffer(new buffer_handle_t(nullptr));
	uint32_t stride;
	if (buf_mgr->Allocate(width, height, hal_format, hal_usage, buffer.get(),
			      &stride) != 0) {
		LOG(HAL, Error) << "Failed to allocate buffer";
		return nullptr;
	}
	LOG(HAL, Info) << "Buffer allocated -";
	LOG(HAL, Info) << "\tplanes: " << CameraBufferManager::GetNumPlanes(*buffer);
	LOG(HAL, Info) << "\twidth: " << CameraBufferManager::GetWidth(*buffer);
	LOG(HAL, Info) << "\theight: " << CameraBufferManager::GetHeight(*buffer);
	LOG(HAL, Info) << "\tformat: "
		       << FormatToString(CameraBufferManager::GetDrmPixelFormat(*buffer));
	for (size_t i = 0; i < CameraBufferManager::GetNumPlanes(*buffer); ++i) {
		LOG(HAL, Info) << "\tplane" << i
			       << " fd: " << CameraBufferManager::GetPlaneFd(*buffer, i);
		LOG(HAL, Info) << "\tplane" << i
			       << " offset: " << CameraBufferManager::GetPlaneOffset(*buffer, i);
		LOG(HAL, Info) << "\tplane" << i
			       << " stride: " << CameraBufferManager::GetPlaneStride(*buffer, i);
	}
	return buffer;
}

int CameraBufferManagerImpl::Free(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}

	Deregister(buffer);
	delete handle;
	return 0;
}

int CameraBufferManagerImpl::Register(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}

	std::lock_guard l(lock_);

	auto context_it = buffers_.find(buffer);
	if (context_it != buffers_.end()) {
		++context_it->second.refcount;
		return 0;
	}

	// Import the buffer if we haven't done so.
	uint32_t num_planes = GetNumPlanes(buffer);
	if (num_planes == 0) {
		return -EINVAL;
	}
	ImportData data = {
		.desc = {
			.drm_format = handle->drm_format,
			.width = static_cast<int>(handle->width),
			.height = static_cast<int>(handle->height),
			.gbm_flags = GBM_BO_USE_CAMERA_READ | GBM_BO_USE_CAMERA_WRITE |
				     GBM_BO_USE_SW_READ_OFTEN | GBM_BO_USE_SW_WRITE_OFTEN,
			.num_planes = static_cast<int>(num_planes),
			.format_modifier = handle->modifier,
		}
	};
	for (size_t i = 0; i < num_planes; ++i) {
		// GBM doesn't need plane size and pixel_stride to import a BO.
		data.desc.planes[i] = {
			.offset = static_cast<int>(handle->offsets[i]),
			.row_stride = static_cast<int>(handle->strides[i]),
		};
		data.plane_fd[i] = handle->fds[i];
	}

	BufferContext context = { .bo = allocator_->ImportBo(data), .refcount = 1 };
	if (!context.bo) {
		LOG(HAL, Error) << "Failed to import buffer 0x" << std::hex
				<< handle->buffer_id;
		return -EIO;
	}

	buffers_[buffer] = std::move(context);
	return 0;
}

int CameraBufferManagerImpl::Deregister(buffer_handle_t buffer)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}

	std::lock_guard l(lock_);

	auto context_it = buffers_.find(buffer);
	if (context_it == buffers_.end()) {
		LOG(HAL, Error) << "Unknown buffer 0x" << std::hex << handle->buffer_id;
		return -EINVAL;
	}
	--context_it->second.refcount;
	if (context_it->second.refcount == 0) {
		buffers_.erase(context_it);
	}
	return 0;
}

int CameraBufferManagerImpl::Lock(buffer_handle_t buffer,
				  uint32_t hal_usage,
				  uint32_t x,
				  uint32_t y,
				  uint32_t width,
				  uint32_t height,
				  void **out_addr)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}
	uint32_t num_planes = GetNumPlanes(buffer);
	if (!num_planes) {
		return -EINVAL;
	}
	if (num_planes > 1) {
		LOG(HAL, Error) << "Lock called on multi-planar buffer 0x" << std::hex
				<< handle->buffer_id;
		return -EINVAL;
	}
	if ((hal_usage & handle->hal_usage_flags) != hal_usage) {
		LOG(HAL, Error) << "Incompatible usage flags: " << hal_usage
				<< " (original usage flags: " << handle->hal_usage_flags << ")";
		return -EINVAL;
	}

	*out_addr = Map(buffer, hal_usage, 0);
	if (*out_addr == MAP_FAILED) {
		return -EINVAL;
	}
	return 0;
}

int CameraBufferManagerImpl::LockYCbCr(buffer_handle_t buffer,
				       uint32_t hal_usage,
				       uint32_t x,
				       uint32_t y,
				       uint32_t width,
				       uint32_t height,
				       struct android_ycbcr *out_ycbcr)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}
	uint32_t num_planes = GetNumPlanes(buffer);
	if (!num_planes) {
		return -EINVAL;
	}
	if (num_planes < 2) {
		LOG(HAL, Error) << "LockYCbCr called on single-planar buffer 0x" << std::hex
				<< handle->buffer_id;
		return -EINVAL;
	}
	if ((hal_usage & handle->hal_usage_flags) != hal_usage) {
		LOG(HAL, Error) << "Incompatible usage flags: " << hal_usage
				<< " (original usage flags: " << handle->hal_usage_flags << ")";
		return -EINVAL;
	}

	//DCHECK_LE(num_planes, 3u);
	std::vector<uint8_t *> addr(num_planes);
	for (size_t i = 0; i < num_planes; ++i) {
		void *a = Map(buffer, hal_usage, i);
		if (a == MAP_FAILED) {
			return -EINVAL;
		}
		addr[i] = reinterpret_cast<uint8_t *>(a);
	}
	out_ycbcr->y = addr[0];
	out_ycbcr->ystride = handle->strides[0];
	out_ycbcr->cstride = handle->strides[1];
	out_ycbcr->chroma_step = GetChromaStep(handle->drm_format);
	//CHECK_GT(out_ycbcr->chroma_step, 0);

	if (num_planes == 2) {
		switch (handle->drm_format) {
		case DRM_FORMAT_NV12:
		case DRM_FORMAT_P010:
			out_ycbcr->cb = addr[1];
			out_ycbcr->cr = addr[1] + (out_ycbcr->chroma_step / 2);
			break;

		case DRM_FORMAT_NV21:
			out_ycbcr->cb = addr[1] + (out_ycbcr->chroma_step / 2);
			out_ycbcr->cr = addr[1];
			break;

		default:
			LOG(HAL, Error) << "Unsupported semi-planar format: "
					<< FormatToString(handle->drm_format);
			return -EINVAL;
		}
	} else { // num_planes == 3
		switch (handle->drm_format) {
		case DRM_FORMAT_YUV420:
			out_ycbcr->cb = addr[1];
			out_ycbcr->cr = addr[2];
			break;

		case DRM_FORMAT_YVU420:
			out_ycbcr->cb = addr[2];
			out_ycbcr->cr = addr[1];
			break;

		default:
			LOG(HAL, Error) << "Unsupported planar format: "
					<< FormatToString(handle->drm_format);
			return -EINVAL;
		}
	}
	return 0;
}

int CameraBufferManagerImpl::Unlock(buffer_handle_t buffer)
{
	for (size_t i = 0; i < GetNumPlanes(buffer); ++i) {
		int ret = Unmap(buffer, i);
		if (ret) {
			return ret;
		}
	}
	return 0;
}

uint32_t CameraBufferManagerImpl::ResolveDrmFormat(uint32_t hal_format,
						   uint32_t hal_usage)
{
	uint32_t unused_gbm_flags;
	return ResolveFormat(hal_format, hal_usage, &unused_gbm_flags);
}

uint32_t CameraBufferManagerImpl::ResolveFormat(uint32_t hal_format,
						uint32_t hal_usage,
						uint32_t *gbm_flags)
{
	uint32_t gbm_usage = GetGbmUseFlags(hal_format, hal_usage);
	uint32_t drm_format = 0;
	if (hal_usage & GRALLOC_USAGE_FORCE_I420) {
		//CHECK_EQ(hal_format, HAL_PIXEL_FORMAT_YCbCr_420_888);
		*gbm_flags = gbm_usage;
		return DRM_FORMAT_YUV420;
	}

	if (hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
	    (hal_usage & GRALLOC_USAGE_HW_CAMERA_READ)) {
		// Check which private format the graphics backend support.
		if (allocator_->IsFormatSupported(DRM_FORMAT_MTISP_SXYZW10, gbm_usage)) {
			*gbm_flags = gbm_usage;
			return DRM_FORMAT_MTISP_SXYZW10;
		}
		// TODO(lnishan): Check other private formats when we have private formats
		// from other platforms.
	}

	if (kSupportedHalFormats.find(hal_format) == kSupportedHalFormats.end()) {
		LOG(HAL, Error) << "Unsupported HAL pixel format";
		return 0;
	}

	for (uint32_t format : kSupportedHalFormats[hal_format]) {
		if (allocator_->IsFormatSupported(format, gbm_usage)) {
			drm_format = format;
			break;
		}
	}

	if (drm_format == 0 && hal_usage & GRALLOC_USAGE_HW_COMPOSER) {
		gbm_usage &= ~GBM_BO_USE_SCANOUT;
		for (uint32_t format : kSupportedHalFormats[hal_format]) {
			if (allocator_->IsFormatSupported(format, gbm_usage)) {
				drm_format = format;
				break;
			}
		}
	}

	if (drm_format == 0) {
		LOG(HAL, Error) << "Cannot resolve the actual format of HAL pixel format "
				<< hal_format;
		return 0;
	}

	*gbm_flags = gbm_usage;
	return drm_format;
}

void *CameraBufferManagerImpl::Map(buffer_handle_t buffer,
				   uint32_t hal_usage,
				   uint32_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return MAP_FAILED;
	}

	uint32_t num_planes = GetNumPlanes(buffer);
	if (!num_planes) {
		return MAP_FAILED;
	}
	if (!(plane < kMaxPlanes && plane < num_planes)) {
		LOG(HAL, Error) << "Invalid plane: " << plane;
		return MAP_FAILED;
	}

	LOG(HAL, Info) << "buffer info:";
	LOG(HAL, Info) << "\tfd: " << handle->fds[plane];
	LOG(HAL, Info) << "\tbuffer_id: 0x" << std::hex << handle->buffer_id;
	LOG(HAL, Info) << "\tformat: " << FormatToString(handle->drm_format);
	LOG(HAL, Info) << "\twidth: " << handle->width;
	LOG(HAL, Info) << "\theight: " << handle->height;
	LOG(HAL, Info) << "\tstride: " << handle->strides[plane];
	LOG(HAL, Info) << "\toffset: " << handle->offsets[plane];

	std::lock_guard l(lock_);

	auto context_it = buffers_.find(buffer);
	if (context_it == buffers_.end()) {
		LOG(HAL, Error) << "Buffer 0x" << std::hex << handle->buffer_id
				<< " is not registered";
		return MAP_FAILED;
	}

	// Always map the whole buffer.
	auto &bo = context_it->second.bo;
	if (!bo->BeginCpuAccess(SyncType::kSyncReadWrite, plane)) {
		LOG(HAL, Error) << "Failed to sync buffer for CPU access";
		return MAP_FAILED;
	}
	if (!bo->Map(plane)) {
		LOG(HAL, Error) << "Failed to map buffer";
		return MAP_FAILED;
	}
	void *addr = bo->GetPlaneAddr(plane);
	LOG(HAL, Info) << "Plane " << plane << " of DMA-buf 0x" << std::hex
		       << handle->buffer_id << " mapped to " << addr;
	return addr;
}

int CameraBufferManagerImpl::Unmap(buffer_handle_t buffer, uint32_t plane)
{
	auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
	if (!handle) {
		return -EINVAL;
	}

	std::lock_guard l(lock_);

	auto context_it = buffers_.find(buffer);
	if (context_it == buffers_.end()) {
		LOG(HAL, Error) << "Buffer 0x" << std::hex << handle->buffer_id
				<< " is not registered";
		return -EINVAL;
	}

	auto &bo = context_it->second.bo;
	bo->Unmap(plane);
	if (!bo->EndCpuAccess(SyncType::kSyncReadWrite, plane)) {
		LOG(HAL, Error) << "Failed to sync buffer after CPU access";
		return -EINVAL;
	}
	return 0;
}

} // namespace crosLibcamera
