android_external_minigbm/gbm_mesa_driver/gbm_mesa_internals.cpp
dvab-sarma 0367f79617 reverting
since these constraints are not applicable to Rockchip SoCs, removing them
2026-07-15 22:37:40 -04:00

527 lines
14 KiB
C++

/*
* Copyright (C) 2021-2022 Roman Stratiienko (r.stratiienko@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "GBM-MESA-GRALLOC"
#define NDEBUG 1
extern "C" {
#include "drv_helpers.h"
}
#include "gbm_mesa_wrapper.h"
#include "UniqueFd.h"
#include "drv_priv.h"
#include "util.h"
#include <algorithm>
#include <array>
#include <assert.h>
#include <cutils/properties.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <functional>
#include <gbm.h>
#include <glob.h>
#include <iterator>
#include <linux/dma-buf.h>
#include <log/log.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include <xf86drm.h>
#include <xf86drmMode.h>
#define GBM_WRAPPER_NAME "libgbm_mesa_wrapper.so"
#define GBM_GET_OPS_SYMBOL "get_gbm_ops"
void gbm_mesa_resolve_format_and_use_flags(struct driver *drv, uint32_t format, uint64_t use_flags,
uint32_t *out_format, uint64_t *out_use_flags)
{
*out_format = format;
*out_use_flags = use_flags;
switch (format) {
case DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED:
/* Camera subsystem requires NV12. */
if (use_flags & (BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE)) {
*out_format = DRM_FORMAT_NV12;
} else {
/*HACK: See b/28671744 */
*out_format = DRM_FORMAT_XBGR8888;
}
break;
case DRM_FORMAT_FLEX_YCbCr_420_888:
*out_format = DRM_FORMAT_NV12;
break;
case DRM_FORMAT_BGR565:
/* mesa3d doesn't support BGR565 */
*out_format = DRM_FORMAT_RGB565;
break;
}
}
static const uint32_t scanout_render_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888,
DRM_FORMAT_ABGR8888, DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB565 };
static const uint32_t texture_only_formats[] = { DRM_FORMAT_NV12, DRM_FORMAT_NV21,
DRM_FORMAT_YVU420, DRM_FORMAT_YVU420_ANDROID };
static struct format_metadata linear_metadata = { 1, 0, DRM_FORMAT_MOD_LINEAR };
int gbm_mesa_driver_init(struct driver *drv)
{
drv_add_combinations(drv, scanout_render_formats, ARRAY_SIZE(scanout_render_formats),
&linear_metadata, BO_USE_RENDER_MASK | BO_USE_SCANOUT);
drv_add_combinations(drv, texture_only_formats, ARRAY_SIZE(texture_only_formats),
&linear_metadata, BO_USE_TEXTURE_MASK | BO_USE_SCANOUT);
drv_add_combination(drv, DRM_FORMAT_R8, &linear_metadata, BO_USE_SW_MASK | BO_USE_LINEAR);
// Fixes android.hardware.cts.HardwareBufferTest#testCreate CTS test
drv_add_combination(drv, DRM_FORMAT_BGR888, &linear_metadata, BO_USE_SW_MASK);
drv_modify_combination(drv, DRM_FORMAT_NV12, &linear_metadata,
BO_USE_HW_VIDEO_ENCODER | BO_USE_HW_VIDEO_DECODER |
BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE);
drv_modify_combination(drv, DRM_FORMAT_NV21, &linear_metadata, BO_USE_HW_VIDEO_ENCODER);
/*
* R8 format is used for Android's HAL_PIXEL_FORMAT_BLOB and is used for JPEG snapshots
* from camera and input/output from hardware decoder/encoder.
*/
drv_modify_combination(drv, DRM_FORMAT_R8, &linear_metadata,
BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER |
BO_USE_HW_VIDEO_ENCODER);
/*
* Android also frequently requests YV12 formats for some camera implementations
* (including the external provider implmenetation).
*/
drv_modify_combination(drv, DRM_FORMAT_YVU420_ANDROID, &linear_metadata,
BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE);
return drv_modify_linear_combinations(drv);
}
struct GbmMesaDriver {
~GbmMesaDriver()
{
if (gbm_dev)
wrapper->dev_destroy(gbm_dev);
if (dl_handle)
dlclose(dl_handle);
}
struct gbm_ops *wrapper = nullptr;
struct gbm_device *gbm_dev = nullptr;
void *dl_handle = nullptr;
UniqueFd gbm_node_fd;
UniqueFd gpu_node_fd;
};
struct GbmMesaDriverPriv {
std::shared_ptr<GbmMesaDriver> gbm_mesa_drv;
};
/*
* Check if target device has KMS.
*/
int is_kms_dev(int fd)
{
auto res = drmModeGetResources(fd);
if (!res)
return false;
bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 && res->count_encoders > 0;
drmModeFreeResources(res);
return is_kms;
}
/*
* Search for a KMS device. Return opened file descriptor on success.
*/
int open_drm_dev(bool card_node, std::function<bool(int, bool, std::string)> found)
{
glob_t glob_buf;
memset(&glob_buf, 0, sizeof(glob_buf));
int fd;
const char *pattern = card_node ? "/dev/dri/card*" : "/dev/dri/renderD*";
int ret = glob(pattern, 0, NULL, &glob_buf);
if (ret) {
globfree(&glob_buf);
return -EINVAL;
}
for (size_t i = 0; i < glob_buf.gl_pathc; ++i) {
fd = open(glob_buf.gl_pathv[i], O_RDWR | O_CLOEXEC);
if (fd < 0) {
drv_loge("Unable to open %s with error %s", glob_buf.gl_pathv[i],
strerror(errno));
continue;
}
drmVersionPtr drm_version;
drm_version = drmGetVersion(fd);
std::string drm_name(drm_version->name);
drmFreeVersion(drm_version);
if (found(fd, is_kms_dev(fd), drm_name))
continue;
close(fd);
fd = 0;
}
globfree(&glob_buf);
return 0;
}
/*
* List of GPU which rely on separate display controller drivers,
* For this GPUs we have to find and open /dev/cardX KMS node
* Other GPUs can be accessed via renderD GPU node.
*/
static std::array<std::string, 6> separate_dc_gpu_list = { "v3d", "vc4", "etnaviv",
"panfrost", "lima", "freedreno" };
static bool is_separate_dc_gpu(UniqueFd *out_gpu_fd)
{
UniqueFd gpu_fd;
bool separate_dc = false;
std::string gpu_name;
open_drm_dev(false, [&](int fd, bool is_kms, std::string drm_name) -> bool {
if (separate_dc)
return false;
for (const auto &name : separate_dc_gpu_list) {
if (drm_name == std::string(name))
separate_dc = true;
}
gpu_fd = UniqueFd(fd);
gpu_name = drm_name;
return true;
});
*out_gpu_fd = std::move(gpu_fd);
drv_logi("Found GPU %s\n", gpu_name.c_str());
return separate_dc;
}
static std::shared_ptr<GbmMesaDriver> gbm_mesa_get_or_init_driver(struct driver *drv,
bool mapper_sphal)
{
std::shared_ptr<GbmMesaDriver> gbm_mesa_drv;
if (!drv->priv) {
gbm_mesa_drv = std::make_unique<GbmMesaDriver>();
bool look_for_kms = is_separate_dc_gpu(&gbm_mesa_drv->gpu_node_fd);
if (look_for_kms && !mapper_sphal) {
drv_logi("GPU require KMSRO entry, searching for separate KMS driver...\n");
open_drm_dev(true, [&](int fd, bool is_kms, std::string drm_name) -> bool {
if (!is_kms || gbm_mesa_drv->gbm_node_fd)
return false;
gbm_mesa_drv->gbm_node_fd = UniqueFd(fd);
drv_logi("Found KMS dev %s\n", drm_name.c_str());
return true;
});
/* cardX KMS node need this otherwise composer won't be able to configure
* KMS state */
if (gbm_mesa_drv->gbm_node_fd)
drmDropMaster(gbm_mesa_drv->gbm_node_fd.Get());
else
drv_loge(
"Unable to find/open /dev/card node with KMS capabilities.\n");
} else {
gbm_mesa_drv->gbm_node_fd = UniqueFd(dup(gbm_mesa_drv->gpu_node_fd.Get()));
}
if (!gbm_mesa_drv->gbm_node_fd) {
drv_loge("Unable to find or open DRM node");
return nullptr;
}
gbm_mesa_drv->dl_handle = dlopen(GBM_WRAPPER_NAME, RTLD_NOW);
if (gbm_mesa_drv->dl_handle == nullptr) {
drv_loge("%s", dlerror());
drv_loge("Unable to open '%s' shared library", GBM_WRAPPER_NAME);
return nullptr;
}
auto get_gbm_ops =
(struct gbm_ops * (*)(void)) dlsym(gbm_mesa_drv->dl_handle, GBM_GET_OPS_SYMBOL);
if (get_gbm_ops == nullptr) {
drv_loge("Unable to find '%s' symbol", GBM_GET_OPS_SYMBOL);
return nullptr;
}
gbm_mesa_drv->wrapper = get_gbm_ops();
if (gbm_mesa_drv->wrapper == nullptr) {
drv_loge("Unable to get wrapper ops");
return nullptr;
}
gbm_mesa_drv->gbm_dev =
gbm_mesa_drv->wrapper->dev_create(gbm_mesa_drv->gbm_node_fd.Get());
if (!gbm_mesa_drv->gbm_dev) {
drv_loge("Unable to create gbm_mesa driver");
return nullptr;
}
auto priv = new GbmMesaDriverPriv();
priv->gbm_mesa_drv = gbm_mesa_drv;
drv->priv = priv;
} else {
gbm_mesa_drv = ((GbmMesaDriverPriv *)drv->priv)->gbm_mesa_drv;
}
return gbm_mesa_drv;
}
void gbm_mesa_driver_close(struct driver *drv)
{
if (drv->priv) {
delete (GbmMesaDriverPriv *)(drv->priv);
drv->priv = nullptr;
}
}
struct GbmMesaBoPriv {
~GbmMesaBoPriv()
{
if (gbm_bo) {
auto wr = drv->wrapper;
wr->free(gbm_bo);
}
}
std::shared_ptr<GbmMesaDriver> drv;
uint32_t map_stride = 0;
UniqueFd fds[DRV_MAX_PLANES];
struct gbm_bo *gbm_bo = nullptr;
};
int gbm_mesa_bo_create(struct bo *bo, uint32_t width, uint32_t height, uint32_t format,
uint64_t use_flags)
{
/* For some ARM SOCs, if no more free CMA available, buffer can be allocated in VRAM but HWC
* won't be able to display it directly, using GPU for compositing */
bool scanout_strong = false;
bool bo_layout_ready = false;
uint32_t size_align = 1;
int err = 0;
auto drv = gbm_mesa_get_or_init_driver(bo->drv, false);
if (drv == nullptr) {
drv_loge("Failed to init gbm driver");
return -EINVAL;
}
auto wr = drv->wrapper;
struct alloc_args alloc_args = {
.gbm = drv->gbm_dev,
.width = width,
.height = height,
.drm_format = wr->get_gbm_format(format) ? format : 0,
.force_linear = (use_flags & BO_USE_SW_MASK) != 0,
.needs_map_stride = (use_flags & BO_USE_SW_MASK) != 0,
.use_scanout = (use_flags & (BO_USE_SCANOUT | BO_USE_CURSOR)) != 0,
};
/* Alignment for RPI4 CSI camera. Since we do not care about other cameras, keep this
* globally for now.
* TODO: Create/use constraints table for camera/codecs */
if (use_flags & (BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE)) {
scanout_strong = true;
alloc_args.use_scanout = true;
alloc_args.width = ALIGN(alloc_args.width, 32);
size_align = 4096;
}
if (alloc_args.drm_format == 0) {
/* Always use linear for spoofed format allocations. */
drv_bo_from_format(bo, alloc_args.width, 1, alloc_args.height, format);
bo_layout_ready = true;
bo->meta.total_size = ALIGN(bo->meta.total_size, size_align);
alloc_args.drm_format = DRM_FORMAT_R8;
alloc_args.width = bo->meta.total_size;
alloc_args.height = 1;
alloc_args.force_linear = true;
drv_logv("Unable to allocate 0x%08x format, allocate as 1D buffer", format);
}
if (alloc_args.drm_format == DRM_FORMAT_R8 && alloc_args.height == 1) {
/* Some mesa drivers may not support 1D allocations.
* Use 2D texture with 4096 width instead.
*/
alloc_args.needs_map_stride = false;
alloc_args.height = DIV_ROUND_UP(alloc_args.width, 4096);
alloc_args.width = 4096;
drv_logv("Allocate 1D buffer as %dx%d R8 2D texture", alloc_args.width,
alloc_args.height);
}
err = wr->alloc(&alloc_args);
if (err && !scanout_strong) {
drv_loge("Failed to allocate for scanout, trying non-scanout");
alloc_args.use_scanout = false;
err = wr->alloc(&alloc_args);
}
if (err) {
drv_loge("Failed to allocate buffer");
return err;
}
if (!bo_layout_ready)
drv_bo_from_format(bo, alloc_args.out_stride, 1, alloc_args.height, format);
drv_logv("Allocated: %dx%d, stride: %d, map_stride: %d", width, height,
alloc_args.out_stride, alloc_args.out_map_stride);
auto priv = new GbmMesaBoPriv();
bo->inode = drv_get_inode(alloc_args.out_fd);
for (size_t plane = 0; plane < bo->meta.num_planes; plane++) {
priv->fds[plane] = UniqueFd(alloc_args.out_fd);
}
priv->map_stride = alloc_args.out_map_stride;
bo->meta.format_modifier = alloc_args.out_modifier;
bo->priv = priv;
priv->drv = drv;
return 0;
}
int gbm_mesa_bo_import(struct bo *bo, struct drv_import_fd_data *data)
{
if (bo->priv) {
drv_loge("%s bo isn't empty", __func__);
return -EINVAL;
}
auto priv = new GbmMesaBoPriv();
for (size_t plane = 0; plane < bo->meta.num_planes; plane++) {
priv->fds[plane] = UniqueFd(dup(data->fds[plane]));
}
if (data->use_flags & BO_USE_SW_MASK) {
// Mapping require importing by gbm_mesa
auto drv = gbm_mesa_get_or_init_driver(bo->drv, true);
auto wr = drv->wrapper;
uint32_t s_format = data->format;
int s_height = data->height;
int s_width = data->width;
if (wr->get_gbm_format(s_format) == 0) {
s_width = bo->meta.total_size;
s_height = 1;
s_format = DRM_FORMAT_R8;
}
priv->drv = drv;
priv->gbm_bo = wr->import(drv->gbm_dev, data->fds[0], s_width, s_height,
data->strides[0], data->format_modifier, s_format);
}
bo->priv = priv;
return 0;
}
int gbm_mesa_bo_destroy(struct bo *bo)
{
if (bo->priv) {
delete (GbmMesaBoPriv *)(bo->priv);
bo->priv = nullptr;
}
return 0;
}
int gbm_mesa_bo_get_plane_fd(struct bo *bo, size_t plane)
{
return dup(((GbmMesaBoPriv *)bo->priv)->fds[plane].Get());
}
void *gbm_mesa_bo_map(struct bo *bo, struct vma *vma, uint32_t map_flags)
{
auto drv = gbm_mesa_get_or_init_driver(bo->drv, true);
auto wr = drv->wrapper;
vma->length = bo->meta.total_size;
auto priv = (GbmMesaBoPriv *)bo->priv;
assert(priv->gbm_bo != nullptr);
void *buf = MAP_FAILED;
uint32_t s_format = bo->meta.format;
int s_width = bo->meta.width;
int s_height = bo->meta.height;
if (wr->get_gbm_format(s_format) == 0) {
s_width = bo->meta.total_size;
s_height = 1;
}
wr->map(priv->gbm_bo, s_width, s_height, &buf, &vma->priv);
return buf;
}
int gbm_mesa_bo_unmap(struct bo *bo, struct vma *vma)
{
auto drv = gbm_mesa_get_or_init_driver(bo->drv, true);
auto wr = drv->wrapper;
auto priv = (GbmMesaBoPriv *)bo->priv;
assert(priv->gbm_bo != nullptr);
assert(vma->priv != nullptr);
wr->unmap(priv->gbm_bo, vma->priv);
vma->priv = nullptr;
return 0;
}
uint32_t gbm_mesa_bo_get_map_stride(struct bo *bo)
{
auto priv = (GbmMesaBoPriv *)bo->priv;
return priv->map_stride;
}