| /* |
| * Mesa 3-D graphics library |
| * |
| * Copyright (C) 2023 Roman Stratiienko (r.stratiienko@gmail.com) |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #include <assert.h> |
| #include <dlfcn.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <hardware/gralloc.h> |
| #include <hardware/gralloc1.h> |
| |
| #include "drm-uapi/drm_fourcc.h" |
| |
| #include "util/log.h" |
| #include "util/u_memory.h" |
| |
| #include "u_gralloc_internal.h" |
| |
| /* Using this gralloc is not recommended for new distributions. */ |
| |
| struct qcom_gralloc { |
| struct u_gralloc base; |
| hw_module_t *gralloc_module; |
| gralloc1_device_t *gralloc1_device; |
| void *perform_handle; |
| int (* perform)(void *dev, int op, ...); |
| struct u_gralloc *fallback_gralloc; |
| }; |
| |
| #define GRALLOC1_FUNCTION_PERFORM 0x00001000 /* QCOM gralloc-specific */ |
| |
| static const char qcom_gralloc_name[] = "Graphics Memory Allocator Module"; |
| static const char qcom_gralloc_author[] = "The Android Open Source Project"; |
| |
| static const char caf_gralloc_name[] = "Graphics Memory Module"; |
| static const char caf_gralloc_author[] = "Code Aurora Forum"; |
| |
| #define QCOM_GRALLOC_PROBE_WIDTH 1024 |
| #define QCOM_GRALLOC_PROBE_FORMAT 1 /* HAL_PIXEL_FORMAT_RGBA_8888 */ |
| |
| #define GRALLOC_MODULE_PERFORM_GET_STRIDE 2 |
| #define GRALLOC_MODULE_PERFORM_GET_YUV_PLANE_INFO 7 |
| #define GRALLOC_MODULE_PERFORM_GET_UBWC_FLAG 9 |
| |
| static int |
| fallback_gralloc_get_yuv_info(struct u_gralloc *gralloc, |
| struct u_gralloc_buffer_handle *hnd, |
| struct u_gralloc_buffer_basic_info *out) |
| { |
| struct qcom_gralloc *gr = (struct qcom_gralloc *)gralloc; |
| struct android_ycbcr ycbcr; |
| int ret; |
| |
| memset(&ycbcr, 0, sizeof(ycbcr)); |
| ret = gr->perform(gr->perform_handle, |
| GRALLOC_MODULE_PERFORM_GET_YUV_PLANE_INFO, |
| hnd->handle, &ycbcr); |
| if (ret) { |
| /* HACK: See native_window_buffer_get_buffer_info() and |
| * https://issuetracker.google.com/32077885.*/ |
| if (hnd->hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) |
| return -EAGAIN; |
| |
| mesa_logw("gralloc->lock_ycbcr failed: %d", ret); |
| return -EINVAL; |
| } |
| |
| ret = bufferinfo_from_ycbcr(&ycbcr, hnd, out); |
| if (ret) |
| return ret; |
| |
| out->fds[1] = out->fds[0] = hnd->handle->data[0]; |
| if (out->num_planes == 3) |
| out->fds[2] = hnd->handle->data[0]; |
| |
| return 0; |
| } |
| |
| static int |
| get_buffer_info(struct u_gralloc *gralloc, |
| struct u_gralloc_buffer_handle *hnd, |
| struct u_gralloc_buffer_basic_info *out) |
| { |
| struct qcom_gralloc *gr = (struct qcom_gralloc *)gralloc; |
| |
| int drm_fourcc = 0; |
| int stride = 0; |
| int out_flag = 0; |
| int err; |
| |
| err = gr->perform(gr->perform_handle, GRALLOC_MODULE_PERFORM_GET_UBWC_FLAG, |
| hnd->handle, &out_flag); |
| /* This may fail since some earlier MSM grallocs do not support this |
| * perform call |
| */ |
| if (!err && out_flag) |
| out->modifier = DRM_FORMAT_MOD_QCOM_COMPRESSED; |
| else |
| out->modifier = DRM_FORMAT_MOD_LINEAR; |
| |
| if (is_hal_format_yuv(hnd->hal_format)) { |
| int ret = fallback_gralloc_get_yuv_info(gralloc, hnd, out); |
| /* |
| * HACK: https://issuetracker.google.com/32077885 |
| * There is no API available to properly query the |
| * IMPLEMENTATION_DEFINED format. As a workaround we rely here on |
| * gralloc allocating either an arbitrary YCbCr 4:2:0 or RGBX_8888, with |
| * the latter being recognized by lock_ycbcr failing. |
| */ |
| if (ret != -EAGAIN) |
| return ret; |
| } |
| |
| drm_fourcc = get_fourcc_from_hal_format(hnd->hal_format); |
| if (drm_fourcc == -1) { |
| mesa_loge("Failed to get drm_fourcc"); |
| return -EINVAL; |
| } |
| |
| stride = hnd->pixel_stride * get_hal_format_bpp(hnd->hal_format); |
| if (stride == 0) { |
| mesa_loge("Failed to calcuulate stride"); |
| return -EINVAL; |
| } |
| |
| out->drm_fourcc = drm_fourcc; |
| out->num_planes = 1; |
| out->fds[0] = hnd->handle->data[0]; |
| out->strides[0] = stride; |
| |
| return 0; |
| } |
| |
| static int |
| destroy(struct u_gralloc *gralloc) |
| { |
| struct qcom_gralloc *gr = (struct qcom_gralloc *)gralloc; |
| if (gr->gralloc1_device) |
| gralloc1_close(gr->gralloc1_device); |
| |
| if (gr->gralloc_module) |
| dlclose(gr->gralloc_module->dso); |
| |
| if (gr->fallback_gralloc) |
| gr->fallback_gralloc->ops.destroy(gr->fallback_gralloc); |
| |
| FREE(gr); |
| |
| return 0; |
| } |
| |
| struct u_gralloc * |
| u_gralloc_qcom_create() |
| { |
| struct qcom_gralloc *gr = CALLOC_STRUCT(qcom_gralloc); |
| int err = 0; |
| |
| err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, |
| (const hw_module_t **)&gr->gralloc_module); |
| |
| if (err) |
| goto fail; |
| |
| bool match = false; |
| |
| if (strcmp(gr->gralloc_module->name, qcom_gralloc_name) == 0 && |
| strcmp(gr->gralloc_module->author, qcom_gralloc_author) == 0) { |
| match = true; |
| } |
| |
| if (strcmp(gr->gralloc_module->name, caf_gralloc_name) == 0 && |
| strcmp(gr->gralloc_module->author, caf_gralloc_author) == 0) { |
| match = true; |
| } |
| |
| if (!match) |
| goto fail; |
| |
| if (gr->gralloc_module->module_api_version <= GRALLOC_MODULE_API_VERSION_0_3) { |
| gralloc_module_t *gralloc_module = |
| (gralloc_module_t *)gr->gralloc_module; |
| gr->perform = (int (*)(void *, int, ...))gralloc_module->perform; |
| gr->perform_handle = gr->gralloc_module; |
| } else { |
| err = gralloc1_open(gr->gralloc_module, &gr->gralloc1_device); |
| if (err) |
| goto fail; |
| |
| gr->perform = (int (*)(void *, int, ...))gr->gralloc1_device-> |
| getFunction(gr->gralloc1_device, GRALLOC1_FUNCTION_PERFORM); |
| |
| gr->perform_handle = gr->gralloc1_device; |
| } |
| |
| if (!gr->perform) |
| goto fail; |
| |
| /* Check if the gralloc module supports the required perform call */ |
| int out_stride = 0; |
| err = gr->perform(gr->perform_handle, |
| GRALLOC_MODULE_PERFORM_GET_STRIDE, |
| QCOM_GRALLOC_PROBE_WIDTH, |
| QCOM_GRALLOC_PROBE_FORMAT, |
| &out_stride); |
| if (err) |
| goto fail; |
| |
| if (out_stride == 0) |
| goto fail; |
| |
| gr->base.ops.get_buffer_basic_info = get_buffer_info; |
| gr->base.ops.destroy = destroy; |
| |
| mesa_logi("Using QCOM gralloc (aosp/hardware/qcom/display/*/libgralloc). "); |
| mesa_logw("QCOM Gralloc API is old. Consider using Gralloc4 API instead."); |
| |
| gr->fallback_gralloc = u_gralloc_fallback_create(); |
| |
| return &gr->base; |
| |
| fail: |
| destroy(&gr->base); |
| |
| return NULL; |
| } |