blob: 75ad085858c2620b27bc0132411a4d77d451f75b [file] [log] [blame]
/*
* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "arc/camera_buffer_mapper.h"
#include <vector>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <drm_fourcc.h>
#include <gbm.h>
#include "arc/common.h"
#include "common/camera_buffer_handle.h"
#include "common/camera_buffer_mapper_internal.h"
#include "system/graphics.h"
namespace arc {
// static
CameraBufferMapper* CameraBufferMapper::GetInstance() {
static CameraBufferMapper instance;
if (!instance.gbm_device_) {
LOGF(ERROR) << "Failed to create GBM device for CameraBufferMapper";
return nullptr;
}
return &instance;
}
CameraBufferMapper::CameraBufferMapper()
: gbm_device_(internal::CreateGbmDevice()) {}
int CameraBufferMapper::Register(buffer_handle_t buffer) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return -EINVAL;
}
base::AutoLock l(lock_);
if (handle->type == GRALLOC) {
auto it = buffer_context_.find(buffer);
if (it == buffer_context_.end()) {
BufferContextUniquePtr buffer_context(new struct BufferContext);
// Import the buffer if we haven't done so.
struct gbm_import_fd_planar_data import_data;
memset(&import_data, 0, sizeof(import_data));
import_data.width = handle->width;
import_data.height = handle->height;
import_data.format = handle->drm_format;
uint32_t num_planes = GetNumPlanes(buffer);
if (num_planes <= 0) {
return -EINVAL;
}
for (size_t i = 0; i < num_planes; ++i) {
import_data.fds[i] = handle->fds[i].get();
import_data.strides[i] = handle->strides[i];
import_data.offsets[i] = handle->offsets[i];
}
uint32_t usage = GBM_BO_USE_RENDERING;
if (import_data.format == DRM_FORMAT_R8) {
usage = GBM_BO_USE_LINEAR;
}
buffer_context->bo = gbm_bo_import(
gbm_device_.get(), GBM_BO_IMPORT_FD_PLANAR, &import_data, usage);
if (!buffer_context->bo) {
LOGF(ERROR) << "Failed to import buffer 0x" << std::hex
<< handle->buffer_id;
return -EIO;
}
buffer_context->usage = 1;
buffer_context_[buffer] = std::move(buffer_context);
} else {
it->second->usage++;
}
return 0;
} else if (handle->type == SHM) {
// The shared memory buffer is a contiguous area of memory which is large
// enough to hold all the physical planes. We mmap the buffer on Register
// and munmap on Deregister.
auto it = buffer_context_.find(buffer);
if (it == buffer_context_.end()) {
BufferContextUniquePtr buffer_context(new struct BufferContext);
off_t size = lseek(handle->fds[0].get(), 0, SEEK_END);
if (size == -1) {
LOGF(ERROR) << "Failed to get shm buffer size through lseek: "
<< strerror(errno);
return -errno;
}
buffer_context->shm_buffer_size = static_cast<uint32_t>(size);
lseek(handle->fds[0].get(), 0, SEEK_SET);
buffer_context->mapped_addr =
mmap(nullptr, buffer_context->shm_buffer_size, PROT_READ | PROT_WRITE,
MAP_SHARED, handle->fds[0].get(), 0);
if (buffer_context->mapped_addr == MAP_FAILED) {
LOGF(ERROR) << "Failed to mmap shm buffer: " << strerror(errno);
return -errno;
}
buffer_context->usage = 1;
buffer_context_[buffer] = std::move(buffer_context);
} else {
it->second->usage++;
}
return 0;
} else {
NOTREACHED() << "Invalid buffer type: " << handle->type;
return -EINVAL;
}
}
int CameraBufferMapper::Deregister(buffer_handle_t buffer) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return -EINVAL;
}
base::AutoLock l(lock_);
auto it = buffer_context_.find(buffer);
if (it == buffer_context_.end()) {
LOGF(ERROR) << "Unknown buffer 0x" << std::hex << handle->buffer_id;
return -EINVAL;
}
auto buffer_context = it->second.get();
if (handle->type == GRALLOC) {
if (!--buffer_context->usage) {
// Unmap all the existing mapping of bo.
for (auto it = buffer_info_.begin(); it != buffer_info_.end();) {
if (it->second->bo == it->second->bo) {
it = buffer_info_.erase(it);
} else {
++it;
}
}
buffer_context_.erase(it);
}
return 0;
} else if (handle->type == SHM) {
if (!--buffer_context->usage) {
int ret =
munmap(buffer_context->mapped_addr, buffer_context->shm_buffer_size);
if (ret == -1) {
LOGF(ERROR) << "Failed to munmap shm buffer: " << strerror(errno);
}
buffer_context_.erase(it);
}
return 0;
} else {
NOTREACHED() << "Invalid buffer type: " << handle->type;
return -EINVAL;
}
}
int CameraBufferMapper::Lock(buffer_handle_t buffer,
uint32_t flags,
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) {
LOGF(ERROR) << "Lock called on multi-planar buffer 0x" << std::hex
<< handle->buffer_id;
return -EINVAL;
}
*out_addr = Map(buffer, flags, 0);
if (*out_addr == MAP_FAILED) {
return -EINVAL;
}
return 0;
}
int CameraBufferMapper::LockYCbCr(buffer_handle_t buffer,
uint32_t flags,
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) {
LOGF(ERROR) << "LockYCbCr called on single-planar buffer 0x" << std::hex
<< handle->buffer_id;
return -EINVAL;
}
DCHECK_LE(num_planes, 3u);
std::vector<uint8_t*> addr(3);
for (size_t i = 0; i < num_planes; ++i) {
void* a = Map(buffer, flags, 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];
if (num_planes == 2) {
out_ycbcr->chroma_step = 2;
switch (handle->drm_format) {
case DRM_FORMAT_NV12:
out_ycbcr->cb = addr[1] + handle->offsets[1];
out_ycbcr->cr = addr[1] + handle->offsets[1] + 1;
break;
case DRM_FORMAT_NV21:
out_ycbcr->cb = addr[1] + handle->offsets[1] + 1;
out_ycbcr->cr = addr[1] + handle->offsets[1];
break;
default:
LOGF(ERROR) << "Unsupported semi-planar format: "
<< FormatToString(handle->drm_format);
return -EINVAL;
}
} else { // num_planes == 3
out_ycbcr->chroma_step = 1;
switch (handle->drm_format) {
case DRM_FORMAT_YUV420:
out_ycbcr->cb = addr[1] + handle->offsets[1];
out_ycbcr->cr = addr[2] + handle->offsets[2];
break;
case DRM_FORMAT_YVU420:
out_ycbcr->cb = addr[2] + handle->offsets[2];
out_ycbcr->cr = addr[1] + handle->offsets[1];
break;
default:
LOGF(ERROR) << "Unsupported semi-planar format: "
<< FormatToString(handle->drm_format);
return -EINVAL;
}
}
return 0;
}
int CameraBufferMapper::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;
}
// static
uint32_t CameraBufferMapper::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:
return 1;
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
return 2;
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
return 3;
}
LOGF(ERROR) << "Unknown format: " << FormatToString(handle->drm_format);
return 0;
}
// static
uint32_t CameraBufferMapper::GetV4L2PixelFormat(buffer_handle_t buffer) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return 0;
}
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 V4L2_PIX_FMT_NV12M;
case DRM_FORMAT_NV21:
return V4L2_PIX_FMT_NV21M;
// Multi-planar formats.
case DRM_FORMAT_YUV420:
return V4L2_PIX_FMT_YUV420M;
case DRM_FORMAT_YVU420:
return V4L2_PIX_FMT_YVU420M;
}
LOGF(ERROR) << "Could not convert format "
<< FormatToString(handle->drm_format) << " to V4L2 pixel format";
return 0;
}
// static
size_t CameraBufferMapper::GetPlaneStride(buffer_handle_t buffer,
size_t plane) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return 0;
}
if (plane >= GetNumPlanes(buffer)) {
LOGF(ERROR) << "Invalid plane: " << plane;
return 0;
}
return handle->strides[plane];
}
#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
// static
size_t CameraBufferMapper::GetPlaneSize(buffer_handle_t buffer, size_t plane) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return 0;
}
if (plane >= GetNumPlanes(buffer)) {
LOGF(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_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));
}
void* CameraBufferMapper::Map(buffer_handle_t buffer,
uint32_t flags,
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)) {
LOGF(ERROR) << "Invalid plane: " << plane;
return MAP_FAILED;
}
VLOGF(2) << "buffer info:";
VLOGF(2) << "\tfd: " << handle->fds[plane].get();
VLOGF(2) << "\tbuffer_id: 0x" << std::hex << handle->buffer_id;
VLOGF(2) << "\ttype: " << handle->type;
VLOGF(2) << "\tformat: " << FormatToString(handle->drm_format);
VLOGF(2) << "\twidth: " << handle->width;
VLOGF(2) << "\theight: " << handle->height;
VLOGF(2) << "\tstride: " << handle->strides[plane];
VLOGF(2) << "\toffset: " << handle->offsets[plane];
base::AutoLock l(lock_);
if (handle->type == GRALLOC) {
struct MappedGrallocBufferInfo* info;
auto key = MappedGrallocBufferInfoCache::key_type(buffer, plane);
auto info_cache = buffer_info_.find(key);
if (info_cache == buffer_info_.end()) {
// We haven't mapped |plane| of |buffer| yet.
info = new struct MappedGrallocBufferInfo;
auto it = buffer_context_.find(buffer);
if (it == buffer_context_.end()) {
LOGF(ERROR) << "Buffer 0x" << std::hex << handle->buffer_id
<< " is not registered";
return MAP_FAILED;
}
info->bo = it->second->bo;
} else {
// We have mapped |plane| on |buffer| before: we can simply call
// gbm_bo_map() on the existing bo.
DCHECK(buffer_context_.find(buffer) != buffer_context_.end());
info = info_cache->second.get();
}
uint32_t stride;
void* out_addr = gbm_bo_map(info->bo, 0, 0, handle->width, handle->height,
flags, &stride, &info->map_data, plane);
if (out_addr == MAP_FAILED) {
LOGF(ERROR) << "Failed to map buffer: " << strerror(errno);
return MAP_FAILED;
}
// Only increase the usage count and insert |info| into the cache after the
// buffer is successfully mapped.
info->usage++;
if (info_cache == buffer_info_.end()) {
buffer_info_[key].reset(info);
}
VLOGF(2) << "Plane " << plane << " of gralloc buffer 0x" << std::hex
<< handle->buffer_id << " mapped";
return out_addr;
} else if (handle->type == SHM) {
// We can't call mmap() here because each mmap call may return different
// mapped virtual addresses and may lead to virtual memory address leak.
// Instead we call mmap() only once in Register.
auto it = buffer_context_.find(buffer);
if (it == buffer_context_.end()) {
LOGF(ERROR) << "Unknown buffer 0x" << std::hex << handle->buffer_id;
return MAP_FAILED;
}
auto buffer_context = it->second.get();
void* out_addr = reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(buffer_context->mapped_addr) +
handle->offsets[plane]);
VLOGF(2) << "Plane " << plane << " of shm buffer 0x" << std::hex
<< handle->buffer_id << " mapped";
return out_addr;
} else {
NOTREACHED() << "Invalid buffer type: " << handle->type;
return MAP_FAILED;
}
}
int CameraBufferMapper::Unmap(buffer_handle_t buffer, uint32_t plane) {
auto handle = camera_buffer_handle_t::FromBufferHandle(buffer);
if (!handle) {
return -EINVAL;
}
if (handle->type == GRALLOC) {
base::AutoLock l(lock_);
auto key = MappedGrallocBufferInfoCache::key_type(buffer, plane);
auto info_cache = buffer_info_.find(key);
if (info_cache == buffer_info_.end()) {
LOGF(ERROR) << "Plane " << plane << " of buffer 0x" << std::hex
<< handle->buffer_id << " was not mapped";
return -EINVAL;
}
auto& info = info_cache->second;
if (info->usage) {
// We rely on GBM's internal refcounting mechanism to unmap the bo when
// appropriate.
gbm_bo_unmap(info->bo, info->map_data);
if (!--info->usage) {
buffer_info_.erase(info_cache);
}
}
} else if (handle->type == SHM) {
// No-op for SHM buffers.
} else {
NOTREACHED() << "Invalid buffer type: " << handle->type;
return -EINVAL;
}
VLOGF(2) << "buffer 0x" << std::hex << handle->buffer_id << " unmapped";
return 0;
}
} // namespace arc