diff --git a/gbm_mesa_driver/UniqueFd.h b/gbm_mesa_driver/UniqueFd.h new file mode 100644 index 0000000..d2de5fb --- /dev/null +++ b/gbm_mesa_driver/UniqueFd.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * 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. + */ + +#ifndef UNIQUEFD_H_ +#define UNIQUEFD_H_ + +#include + +#include + +/* + * Using UniqueFd: + * 1. Create UniqueFd object: + * auto fd_obj = UniqueFd(open("SomeFile", xxx)); + * + * 2. Check whether the fd_obj is empty: + * if (!fd_obj) { return -errno; } + * + * 3. Accessing the file descriptor: + * int ret = read(fd_obj.Get(), buf, buf_size); + * + * 4. Closing the file: + * FD will be closed once execution leaves fd_obj scope (on any return, + * exception, destruction of class/struct where object is member, etc.). + * User can also force closing the fd_obj by calling: + * fd_obj = UniqueFd(); + * // fd is closed and fd_obj is empty now. + * + * 5. File descriptor may be transferred to the code, which will close it after + * using. This can be done in 2 ways: + * a. Duplicate the fd, in this case both fds should be closed separately: + * int out_fd = dup(fd_obj.Get(); + * ... + * close(out_fd); + * b. Transfer ownership, use this method if you do not need the fd anymore. + * int out_fd = fd_obj.Release(); + * // fd_obj is empty now. + * ... + * close(out_fd); + * + * 6. Transferring fd into another UniqueFD object: + * UniqueFd fd_obj_2 = std::move(fd_obj); + * // fd_obj empty now + */ + +constexpr int kEmptyFd = -1; + +class UniqueFd +{ + public: + UniqueFd() = default; + explicit UniqueFd(int fd) : fd_(fd){}; + + auto Release [[nodiscard]] () -> int + { + return std::exchange(fd_, kEmptyFd); + } + + auto Get [[nodiscard]] () const -> int + { + return fd_; + } + + explicit operator bool() const + { + return fd_ != kEmptyFd; + } + + ~UniqueFd() + { + Set(kEmptyFd); + } + + /* Allow move semantics */ + UniqueFd(UniqueFd &&rhs) noexcept + { + Set(rhs.Release()); + } + + auto operator=(UniqueFd &&rhs) noexcept -> UniqueFd & + { + Set(rhs.Release()); + return *this; + } + + /* Disable copy semantics */ + UniqueFd(const UniqueFd &) = delete; + auto operator=(const UniqueFd &) = delete; + + private: + void Set(int new_fd) + { + if (fd_ != kEmptyFd) { + close(fd_); + } + fd_ = new_fd; + } + + int fd_ = kEmptyFd; +}; + +#endif diff --git a/gbm_mesa_driver/gbm_mesa_driver.cpp b/gbm_mesa_driver/gbm_mesa_driver.cpp new file mode 100644 index 0000000..d2c6c7d --- /dev/null +++ b/gbm_mesa_driver/gbm_mesa_driver.cpp @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include "gbm_mesa_internals.h" + +#include "drv_priv.h" +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct backend gbm_mesa_backend = { + .name = "gbm_mesa", + .init = gbm_mesa_driver_init, + .close = gbm_mesa_driver_close, + .bo_create = gbm_mesa_bo_create, + .bo_destroy = gbm_mesa_bo_destroy, + .bo_import = gbm_mesa_bo_import, + .bo_map = gbm_mesa_bo_map, + .bo_unmap = gbm_mesa_bo_unmap, + .bo_get_plane_fd = gbm_mesa_bo_get_plane_fd, + .bo_get_map_stride = gbm_mesa_bo_get_map_stride, + .resolve_format_and_use_flags = gbm_mesa_resolve_format_and_use_flags, +}; + +struct backend *init_external_backend() +{ + return &gbm_mesa_backend; +} + +#ifdef __cplusplus +} +#endif diff --git a/gbm_mesa_driver/gbm_mesa_internals.cpp b/gbm_mesa_driver/gbm_mesa_internals.cpp new file mode 100644 index 0000000..5d2b901 --- /dev/null +++ b/gbm_mesa_driver/gbm_mesa_internals.cpp @@ -0,0 +1,524 @@ +/* + * 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" + +extern "C" { +#include "drv_helpers.h" +} + +#include "gbm_mesa_wrapper.h" + +#include "UniqueFd.h" +#include "drv_priv.h" +#include "util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 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 gbm_mesa_get_or_init_driver(struct driver *drv, + bool mapper_sphal) +{ + std::shared_ptr gbm_mesa_drv; + + if (!drv->priv) { + gbm_mesa_drv = std::make_unique(); + + 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 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; +} diff --git a/gbm_mesa_driver/gbm_mesa_internals.h b/gbm_mesa_driver/gbm_mesa_internals.h new file mode 100644 index 0000000..4a8ac9d --- /dev/null +++ b/gbm_mesa_driver/gbm_mesa_internals.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#pragma once + +#define LOG_TAG "MESAGBM-GRALLOC" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int gbm_mesa_driver_init(struct driver *drv); +void gbm_mesa_driver_close(struct driver *drv); + +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); + +int gbm_mesa_bo_create(struct bo *bo, uint32_t width, uint32_t height, uint32_t format, + uint64_t use_flags); + +int gbm_mesa_bo_import(struct bo *bo, struct drv_import_fd_data *data); + +int gbm_mesa_bo_destroy(struct bo *bo); + +uint32_t gbm_mesa_bo_get_map_stride(struct bo *bo); +void *gbm_mesa_bo_map(struct bo *bo, struct vma *vma, uint32_t map_flags); +int gbm_mesa_bo_unmap(struct bo *bo, struct vma *vma); + +int gbm_mesa_bo_get_plane_fd(struct bo *bo, size_t plane); diff --git a/gbm_mesa_driver/gbm_mesa_wrapper.c b/gbm_mesa_driver/gbm_mesa_wrapper.c new file mode 100644 index 0000000..6070431 --- /dev/null +++ b/gbm_mesa_driver/gbm_mesa_wrapper.c @@ -0,0 +1,204 @@ +/* + * 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-WRAPPER" + +#include + +#include "gbm_mesa_wrapper.h" + +#include +#include +#include +#include + +#define ARRAY_SIZE(A) (sizeof(A) / sizeof(*(A))) +#define DRM_TO_GBM_FORMAT(A) \ + { \ + DRM_##A, GBM_##A \ + } + +/* List of the formats that has mesa3d DRI_ equivalent, therefore can be allocated directly */ +static const struct { + uint32_t drm_format; + uint32_t gbm_format; +} drm_to_gbm_image_formats[] = { + DRM_TO_GBM_FORMAT(FORMAT_R8), + DRM_TO_GBM_FORMAT(FORMAT_GR88), + DRM_TO_GBM_FORMAT(FORMAT_ARGB1555), + DRM_TO_GBM_FORMAT(FORMAT_RGB565), + DRM_TO_GBM_FORMAT(FORMAT_XRGB8888), + DRM_TO_GBM_FORMAT(FORMAT_ARGB8888), + DRM_TO_GBM_FORMAT(FORMAT_XBGR8888), + DRM_TO_GBM_FORMAT(FORMAT_ABGR8888), + DRM_TO_GBM_FORMAT(FORMAT_XRGB2101010), + DRM_TO_GBM_FORMAT(FORMAT_XBGR2101010), + DRM_TO_GBM_FORMAT(FORMAT_ARGB2101010), + DRM_TO_GBM_FORMAT(FORMAT_ABGR2101010), + DRM_TO_GBM_FORMAT(FORMAT_XBGR16161616F), + DRM_TO_GBM_FORMAT(FORMAT_ABGR16161616F), +}; + +static uint32_t get_gbm_mesa_format(uint32_t drm_format) +{ + uint32_t i; + for (i = 0; i < ARRAY_SIZE(drm_to_gbm_image_formats); i++) { + if (drm_to_gbm_image_formats[i].drm_format == drm_format) + return drm_to_gbm_image_formats[i].gbm_format; + } + + return 0; +} + +static struct gbm_device *gbm_mesa_dev_create(int fd) +{ + struct gbm_device *gbm = gbm_create_device(fd); + if (!gbm) + ALOGE("Unable to create gbm device"); + + return gbm; +} + +static void gbm_mesa_dev_destroy(struct gbm_device *gbm) +{ + gbm_device_destroy(gbm); +} + +// ALLOCATOR_ONLY! +static int gbm_mesa_alloc(struct alloc_args *args) +{ + struct gbm_bo *bo = NULL; + + uint32_t gbm_format = get_gbm_mesa_format(args->drm_format); + int usage = 0; + if (args->force_linear) + usage |= GBM_BO_USE_LINEAR; + + if (args->use_scanout) + usage |= GBM_BO_USE_SCANOUT; + + bo = gbm_bo_create(args->gbm, args->width, args->height, gbm_format, usage); + + if (!bo) { + ALOGE("Unable to create BO, size=%dx%d, fmt=%d", args->width, args->height, + args->drm_format); + return -EINVAL; + } + + /* gbm_mesa will create new fd, therefore it's our responsibility to close it once we don't + * need the buffer */ + args->out_fd = gbm_bo_get_fd(bo); + + args->out_stride = gbm_bo_get_stride(bo); + args->out_modifier = gbm_bo_get_modifier(bo); + + /* Buffer is now handled through the system via out_fd, we can now destroy gbm_mesa bo */ + gbm_bo_destroy(bo); + + if (args->needs_map_stride) { + /* At least on Intel and nouveau map_stride after gbm_create is different from + * map_stride after gbm_import, We care only about map_stride after importing. */ + struct gbm_import_fd_modifier_data data = { + .width = args->width, + .height = args->height, + .format = gbm_format, + .num_fds = 1, + .fds[0] = args->out_fd, + .strides[0] = (int)args->out_stride, + .modifier = args->out_modifier, + }; + void *addr = NULL; + void *map_data = NULL; + + bo = gbm_bo_import(args->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0); + if (!bo) { + ALOGE("Failed to import BO during map_stride query"); + return -EINVAL; + } + + int flags = GBM_BO_TRANSFER_READ | GBM_BO_TRANSFER_WRITE; + + addr = gbm_bo_map(bo, 0, 0, args->width, args->height, flags, &args->out_map_stride, + &map_data); + if (addr == MAP_FAILED) { + ALOGE("Failed to map the buffer at %s:%d", __FILE__, __LINE__); + } else { + gbm_bo_unmap(bo, map_data); + } + + gbm_bo_destroy(bo); + } + + return 0; +} + +// MAPPER ONLY! +static struct gbm_bo *gbm_import(struct gbm_device *gbm, int buf_fd, uint32_t width, + uint32_t height, uint32_t stride, uint64_t modifier, + uint32_t drm_format) +{ + struct gbm_bo *bo = NULL; + struct gbm_import_fd_modifier_data data = { + .width = width, + .height = height, + .format = get_gbm_mesa_format(drm_format), + .num_fds = 1, + .fds[0] = buf_fd, + .strides[0] = (int)stride, + .modifier = modifier, + }; + + bo = gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0); + + return bo; +} + +static void gbm_free(struct gbm_bo *bo) +{ + gbm_bo_destroy(bo); +} + +static void gbm_map(struct gbm_bo *bo, int w, int h, void **addr, void **map_data) +{ + int flags = GBM_BO_TRANSFER_READ | GBM_BO_TRANSFER_WRITE; + + uint32_t stride = 0; + *addr = gbm_bo_map(bo, 0, 0, w, h, flags, &stride, map_data); + if (addr == MAP_FAILED) { + ALOGE("Failed to map the buffer at %s:%d", __FILE__, __LINE__); + } +} + +static void gbm_unmap(struct gbm_bo *bo, void *map_data) +{ + gbm_bo_unmap(bo, map_data); +} + +struct gbm_ops gbm_ops = { + .get_gbm_format = get_gbm_mesa_format, + .dev_create = gbm_mesa_dev_create, + .dev_destroy = gbm_mesa_dev_destroy, + .alloc = gbm_mesa_alloc, + .import = gbm_import, + .free = gbm_free, + .map = gbm_map, + .unmap = gbm_unmap, +}; + +__attribute__((visibility("default"))) struct gbm_ops *get_gbm_ops() +{ + return &gbm_ops; +} diff --git a/gbm_mesa_driver/gbm_mesa_wrapper.h b/gbm_mesa_driver/gbm_mesa_wrapper.h new file mode 100644 index 0000000..653cb8e --- /dev/null +++ b/gbm_mesa_driver/gbm_mesa_wrapper.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#pragma once + +#include + +struct gbm_device; +struct gbm_bo; + +struct alloc_args { + struct gbm_device *gbm; + uint32_t width; + uint32_t height; + uint32_t drm_format; + bool use_scanout; + bool force_linear; + bool needs_map_stride; + int out_fd; + uint32_t out_stride; + uint64_t out_modifier; + uint32_t out_map_stride; +}; + +struct gbm_ops { + uint32_t (*get_gbm_format)(uint32_t drm_format); + + struct gbm_device *(*dev_create)(int fd); + + void (*dev_destroy)(struct gbm_device *gbm); + + // ALLOCATOR ONLY + int (*alloc)(struct alloc_args *args); + + // MAPPER ONLY + struct gbm_bo *(*import)(struct gbm_device *gbm, int buf_fd, uint32_t width, + uint32_t height, uint32_t stride, uint64_t modifier, + uint32_t drm_format); + + void (*free)(struct gbm_bo *bo); + + void (*map)(struct gbm_bo *bo, int w, int h, void **addr, void **map_data); + + void (*unmap)(struct gbm_bo *bo, void *map_data); +};