/* * 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 "cros_gralloc_driver.h" #include "../util.h" #include #include #include cros_gralloc_driver::cros_gralloc_driver() : drv_(nullptr) { } cros_gralloc_driver::~cros_gralloc_driver() { buffers_.clear(); handles_.clear(); if (drv_) { drv_destroy(drv_); drv_ = nullptr; } } int32_t cros_gralloc_driver::init() { /* * Create a driver from rendernode while filtering out * the specified undesired driver. * * TODO(gsingh): Enable render nodes on udl/evdi. */ int fd; drmVersionPtr version; char const *str = "%s/renderD%d"; const char *undesired[2] = { "vgem", nullptr }; uint32_t num_nodes = 63; uint32_t min_node = 128; uint32_t max_node = (min_node + num_nodes); for (uint32_t i = 0; i < ARRAY_SIZE(undesired); i++) { for (uint32_t j = min_node; j < max_node; j++) { char *node; if (asprintf(&node, str, DRM_DIR_NAME, j) < 0) continue; fd = open(node, O_RDWR, 0); free(node); if (fd < 0) continue; version = drmGetVersion(fd); if (!version) continue; if (undesired[i] && !strcmp(version->name, undesired[i])) { drmFreeVersion(version); continue; } drmFreeVersion(version); drv_ = drv_create(fd); if (drv_) return 0; } } return -ENODEV; } bool cros_gralloc_driver::is_supported(const struct cros_gralloc_buffer_descriptor *descriptor) { struct combination *combo; uint32_t resolved_format; resolved_format = drv_resolve_format(drv_, descriptor->drm_format, descriptor->use_flags); combo = drv_get_combination(drv_, resolved_format, descriptor->use_flags); return (combo != nullptr); } int32_t cros_gralloc_driver::allocate(const struct cros_gralloc_buffer_descriptor *descriptor, buffer_handle_t *out_handle) { uint32_t id; uint64_t mod; size_t num_planes; uint32_t resolved_format; uint32_t bytes_per_pixel; uint64_t use_flags; struct bo *bo; struct cros_gralloc_handle *hnd; resolved_format = drv_resolve_format(drv_, descriptor->drm_format, descriptor->use_flags); use_flags = descriptor->use_flags; /* * TODO(b/79682290): ARC++ assumes NV12 is always linear and doesn't * send modifiers across Wayland protocol, so we or in the * BO_USE_LINEAR flag here. We need to fix ARC++ to allocate and work * with tiled buffers. */ if (resolved_format == DRM_FORMAT_NV12) use_flags |= BO_USE_LINEAR; bo = drv_bo_create(drv_, descriptor->width, descriptor->height, resolved_format, use_flags); if (!bo) { drv_log("Failed to create bo.\n"); return -ENOMEM; } /* * If there is a desire for more than one kernel buffer, this can be * removed once the ArcCodec and Wayland service have the ability to * send more than one fd. GL/Vulkan drivers may also have to modified. */ if (drv_num_buffers_per_bo(bo) != 1) { drv_bo_destroy(bo); drv_log("Can only support one buffer per bo.\n"); return -EINVAL; } hnd = new cros_gralloc_handle(); num_planes = drv_bo_get_num_planes(bo); hnd->base.version = sizeof(hnd->base); hnd->base.numFds = num_planes; hnd->base.numInts = handle_data_size - num_planes; for (size_t plane = 0; plane < num_planes; plane++) { hnd->fds[plane] = drv_bo_get_plane_fd(bo, plane); hnd->strides[plane] = drv_bo_get_plane_stride(bo, plane); hnd->offsets[plane] = drv_bo_get_plane_offset(bo, plane); mod = drv_bo_get_plane_format_modifier(bo, plane); hnd->format_modifiers[2 * plane] = static_cast(mod >> 32); hnd->format_modifiers[2 * plane + 1] = static_cast(mod); } hnd->width = drv_bo_get_width(bo); hnd->height = drv_bo_get_height(bo); hnd->format = drv_bo_get_format(bo); hnd->use_flags[0] = static_cast(descriptor->use_flags >> 32); hnd->use_flags[1] = static_cast(descriptor->use_flags); bytes_per_pixel = drv_bytes_per_pixel_from_format(hnd->format, 0); hnd->pixel_stride = DIV_ROUND_UP(hnd->strides[0], bytes_per_pixel); hnd->magic = cros_gralloc_magic; hnd->droid_format = descriptor->droid_format; hnd->usage = descriptor->producer_usage; id = drv_bo_get_plane_handle(bo, 0).u32; auto buffer = new cros_gralloc_buffer(id, bo, hnd); std::lock_guard lock(mutex_); buffers_.emplace(id, buffer); handles_.emplace(hnd, std::make_pair(buffer, 1)); *out_handle = &hnd->base; return 0; } int32_t cros_gralloc_driver::retain(buffer_handle_t handle) { uint32_t id; std::lock_guard lock(mutex_); auto hnd = cros_gralloc_convert_handle(handle); if (!hnd) { drv_log("Invalid handle.\n"); return -EINVAL; } auto buffer = get_buffer(hnd); if (buffer) { handles_[hnd].second++; buffer->increase_refcount(); return 0; } if (drmPrimeFDToHandle(drv_get_fd(drv_), hnd->fds[0], &id)) { drv_log("drmPrimeFDToHandle failed.\n"); return -errno; } if (buffers_.count(id)) { buffer = buffers_[id]; buffer->increase_refcount(); } else { struct bo *bo; struct drv_import_fd_data data; data.format = hnd->format; data.width = hnd->width; data.height = hnd->height; data.use_flags = static_cast(hnd->use_flags[0]) << 32; data.use_flags |= hnd->use_flags[1]; memcpy(data.fds, hnd->fds, sizeof(data.fds)); memcpy(data.strides, hnd->strides, sizeof(data.strides)); memcpy(data.offsets, hnd->offsets, sizeof(data.offsets)); for (uint32_t plane = 0; plane < DRV_MAX_PLANES; plane++) { data.format_modifiers[plane] = static_cast(hnd->format_modifiers[2 * plane]) << 32; data.format_modifiers[plane] |= hnd->format_modifiers[2 * plane + 1]; } bo = drv_bo_import(drv_, &data); if (!bo) return -EFAULT; id = drv_bo_get_plane_handle(bo, 0).u32; buffer = new cros_gralloc_buffer(id, bo, nullptr); buffers_.emplace(id, buffer); } handles_.emplace(hnd, std::make_pair(buffer, 1)); return 0; } int32_t cros_gralloc_driver::release(buffer_handle_t handle) { std::lock_guard lock(mutex_); auto hnd = cros_gralloc_convert_handle(handle); if (!hnd) { drv_log("Invalid handle.\n"); return -EINVAL; } auto buffer = get_buffer(hnd); if (!buffer) { drv_log("Invalid Reference.\n"); return -EINVAL; } if (!--handles_[hnd].second) handles_.erase(hnd); if (buffer->decrease_refcount() == 0) { buffers_.erase(buffer->get_id()); delete buffer; } return 0; } int32_t cros_gralloc_driver::lock(buffer_handle_t handle, int32_t acquire_fence, const struct rectangle *rect, uint32_t map_flags, uint8_t *addr[DRV_MAX_PLANES]) { int32_t ret = cros_gralloc_sync_wait(acquire_fence); if (ret) return ret; std::lock_guard lock(mutex_); auto hnd = cros_gralloc_convert_handle(handle); if (!hnd) { drv_log("Invalid handle.\n"); return -EINVAL; } auto buffer = get_buffer(hnd); if (!buffer) { drv_log("Invalid Reference.\n"); return -EINVAL; } return buffer->lock(rect, map_flags, addr); } int32_t cros_gralloc_driver::unlock(buffer_handle_t handle, int32_t *release_fence) { std::lock_guard lock(mutex_); auto hnd = cros_gralloc_convert_handle(handle); if (!hnd) { drv_log("Invalid handle.\n"); return -EINVAL; } auto buffer = get_buffer(hnd); if (!buffer) { drv_log("Invalid Reference.\n"); return -EINVAL; } /* * From the ANativeWindow::dequeueBuffer documentation: * * "A value of -1 indicates that the caller may access the buffer immediately without * waiting on a fence." */ *release_fence = -1; return buffer->unlock(); } int32_t cros_gralloc_driver::get_backing_store(buffer_handle_t handle, uint64_t *out_store) { std::lock_guard lock(mutex_); auto hnd = cros_gralloc_convert_handle(handle); if (!hnd) { drv_log("Invalid handle.\n"); return -EINVAL; } auto buffer = get_buffer(hnd); if (!buffer) { drv_log("Invalid Reference.\n"); return -EINVAL; } *out_store = static_cast(buffer->get_id()); return 0; } cros_gralloc_buffer *cros_gralloc_driver::get_buffer(cros_gralloc_handle_t hnd) { /* Assumes driver mutex is held. */ if (handles_.count(hnd)) return handles_[hnd].first; return nullptr; }