| /* |
| * Copyright (C) 2012 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. |
| */ |
| #include <utils/Errors.h> |
| #include <linux/videodev2.h> |
| #include <sys/mman.h> |
| #include <utils/CallStack.h> |
| #include <sync/sync.h> |
| #include "ExynosHWCHelper.h" |
| #include "ExynosHWCDebug.h" |
| #include "ExynosHWC.h" |
| #include "ExynosLayer.h" |
| #include "exynos_sync.h" |
| #include "videodev2_exynos_media.h" |
| #include "VendorVideoAPI.h" |
| |
| #define AFBC_MAGIC 0xafbc |
| |
| #define FT_LOGD(msg, ...) \ |
| {\ |
| if (exynosHWCControl.fenceTracer >= 2) \ |
| ALOGD("[FenceTracer]::" msg, ##__VA_ARGS__); \ |
| } |
| #define FT_LOGE(msg, ...) \ |
| {\ |
| if (exynosHWCControl.fenceTracer > 0) \ |
| ALOGE("[FenceTracer]::" msg, ##__VA_ARGS__); \ |
| } |
| #define FT_LOGW(msg, ...) \ |
| {\ |
| if (exynosHWCControl.fenceTracer >= 1) \ |
| ALOGD("[FenceTracer]::" msg, ##__VA_ARGS__); \ |
| } |
| |
| extern struct exynos_hwc_control exynosHWCControl; |
| extern char fence_names[FENCE_MAX][32]; |
| |
| uint32_t getHWC1CompType(int32_t type) { |
| |
| uint32_t cType = HWC_FRAMEBUFFER; |
| |
| switch(type) { |
| case HWC2_COMPOSITION_DEVICE: |
| case HWC2_COMPOSITION_EXYNOS: |
| cType = HWC_OVERLAY; |
| break; |
| case HWC2_COMPOSITION_SOLID_COLOR: |
| cType = HWC_BACKGROUND; |
| break; |
| case HWC2_COMPOSITION_CURSOR: |
| cType = HWC_CURSOR_OVERLAY; |
| break; |
| case HWC2_COMPOSITION_SIDEBAND: |
| cType = HWC_SIDEBAND; |
| break; |
| case HWC2_COMPOSITION_CLIENT: |
| case HWC2_COMPOSITION_INVALID: |
| default: |
| cType = HWC_FRAMEBUFFER; |
| break; |
| } |
| |
| return cType; |
| } |
| |
| uint32_t getDrmMode(uint64_t flags) |
| { |
| #ifdef GRALLOC_VERSION1 |
| if (flags & GRALLOC1_PRODUCER_USAGE_PROTECTED) { |
| if (flags & GRALLOC1_PRODUCER_USAGE_PRIVATE_NONSECURE) |
| return NORMAL_DRM; |
| else |
| return SECURE_DRM; |
| } |
| #else |
| if (flags & GRALLOC_USAGE_PROTECTED) { |
| if (flags & GRALLOC_USAGE_PRIVATE_NONSECURE) |
| return NORMAL_DRM; |
| else |
| return SECURE_DRM; |
| } |
| #endif |
| return NO_DRM; |
| } |
| |
| uint32_t getDrmMode(const private_handle_t *handle) |
| { |
| #ifdef GRALLOC_VERSION1 |
| if (handle->producer_usage & GRALLOC1_PRODUCER_USAGE_PROTECTED) { |
| if (handle->producer_usage & GRALLOC1_PRODUCER_USAGE_PRIVATE_NONSECURE) |
| return NORMAL_DRM; |
| else |
| return SECURE_DRM; |
| } |
| #else |
| if (handle->flags & GRALLOC_USAGE_PROTECTED) { |
| if (handle->flags & GRALLOC_USAGE_PRIVATE_NONSECURE) |
| return NORMAL_DRM; |
| else |
| return SECURE_DRM; |
| } |
| #endif |
| return NO_DRM; |
| } |
| |
| unsigned int isNarrowRgb(int format, android_dataspace data_space) |
| { |
| if (format == HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_FULL) |
| return 0; |
| else { |
| if (isFormatRgb(format)) |
| return 0; |
| else { |
| uint32_t data_space_range = (data_space & HAL_DATASPACE_RANGE_MASK); |
| if (data_space_range == HAL_DATASPACE_RANGE_UNSPECIFIED) { |
| return 1; |
| } else if (data_space_range == HAL_DATASPACE_RANGE_FULL) { |
| return 0; |
| } else { |
| return 1; |
| } |
| } |
| } |
| } |
| |
| uint8_t formatToBpp(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) |
| return exynos_format_desc[i].bpp; |
| } |
| |
| ALOGW("unrecognized pixel format %u", format); |
| return 0; |
| } |
| |
| uint8_t DeconFormatToBpp(decon_pixel_format format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].s3cFormat == format) |
| return exynos_format_desc[i].bpp; |
| } |
| ALOGW("unrecognized decon format %u", format); |
| return 0; |
| } |
| |
| bool isFormatRgb(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) { |
| if (exynos_format_desc[i].type & RGB) |
| return true; |
| else |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool isFormatYUV(int format) |
| { |
| if (isFormatRgb(format)) |
| return false; |
| return true; |
| } |
| |
| bool isFormatYUV420(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) { |
| if (exynos_format_desc[i].type & YUV420) |
| return true; |
| else |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool isFormat10BitYUV420(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) { |
| if ((exynos_format_desc[i].type & YUV420) && |
| (exynos_format_desc[i].type & BIT10)) |
| return true; |
| else |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool isFormatYUV422(int __unused format) |
| { |
| // Might add support later |
| return false; |
| } |
| bool isFormatYCrCb(int format) |
| { |
| return format == HAL_PIXEL_FORMAT_EXYNOS_YV12_M; |
| } |
| |
| bool formatHasAlphaChannel(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) { |
| return exynos_format_desc[i].hasAlpha; |
| } |
| } |
| return false; |
| } |
| |
| bool checkCompressionFd(const private_handle_t *handle) |
| { |
| if ((getBufferNumOfFormat(handle->format) == 1) && (handle->fd1 > 0)) { |
| void *_map = NULL; |
| uint32_t *isAFBC = NULL; |
| _map = mmap(0, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd1, 0); |
| if (_map == NULL) { |
| ALOGE("%s, map is NULL", __func__); |
| } else if (_map == MAP_FAILED) { |
| ALOGE("%s, map failed", __func__); |
| } else { |
| isAFBC = static_cast<uint32_t *>(_map); |
| if ((isAFBC != NULL) && (*isAFBC == AFBC_MAGIC)) { |
| munmap(isAFBC, sizeof(uint32_t)); |
| return true; |
| } else { |
| if (isAFBC != NULL) munmap(isAFBC, sizeof(uint32_t)); |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool isCompressed(const private_handle_t *handle) |
| { |
| if (handle != NULL) { |
| if (checkCompressionFd(handle)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| uint32_t halDataSpaceToV4L2ColorSpace(android_dataspace data_space) |
| { |
| uint32_t standard_data_space = (data_space & HAL_DATASPACE_STANDARD_MASK); |
| switch (standard_data_space) { |
| case HAL_DATASPACE_STANDARD_BT2020: |
| case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: |
| return V4L2_COLORSPACE_BT2020; |
| case HAL_DATASPACE_STANDARD_DCI_P3: |
| return V4L2_COLORSPACE_DCI_P3; |
| case HAL_DATASPACE_STANDARD_BT709: |
| return V4L2_COLORSPACE_REC709; |
| default: |
| return V4L2_COLORSPACE_DEFAULT; |
| } |
| return V4L2_COLORSPACE_DEFAULT; |
| } |
| |
| enum decon_pixel_format halFormatToS3CFormat(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) |
| return exynos_format_desc[i].s3cFormat; |
| } |
| return DECON_PIXEL_FORMAT_MAX; |
| } |
| |
| uint32_t S3CFormatToHalFormat(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].s3cFormat == static_cast<decon_pixel_format>(format)) |
| return exynos_format_desc[i].halFormat; |
| } |
| return HAL_PIXEL_FORMAT_EXYNOS_UNDEFINED; |
| } |
| |
| int S3CFormatToDrmFormat(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].s3cFormat == static_cast<decon_pixel_format>(format)) |
| return exynos_format_desc[i].drmFormat; |
| } |
| return DRM_FORMAT_UNDEFINED; |
| } |
| |
| android_dataspace colorModeToDataspace(android_color_mode_t mode) |
| { |
| android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN; |
| switch (mode) { |
| case HAL_COLOR_MODE_STANDARD_BT601_625: |
| dataSpace = HAL_DATASPACE_STANDARD_BT601_625; |
| break; |
| case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED: |
| dataSpace = HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED; |
| break; |
| case HAL_COLOR_MODE_STANDARD_BT601_525: |
| dataSpace = HAL_DATASPACE_STANDARD_BT601_525; |
| break; |
| case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED: |
| dataSpace = HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED; |
| break; |
| case HAL_COLOR_MODE_STANDARD_BT709: |
| dataSpace = HAL_DATASPACE_STANDARD_BT709; |
| break; |
| case HAL_COLOR_MODE_DCI_P3: |
| dataSpace = HAL_DATASPACE_DCI_P3; |
| break; |
| case HAL_COLOR_MODE_ADOBE_RGB: |
| dataSpace = HAL_DATASPACE_ADOBE_RGB; |
| break; |
| case HAL_COLOR_MODE_DISPLAY_P3: |
| dataSpace = HAL_DATASPACE_DISPLAY_P3; |
| break; |
| case HAL_COLOR_MODE_SRGB: |
| dataSpace = HAL_DATASPACE_V0_SRGB; |
| break; |
| case HAL_COLOR_MODE_NATIVE: |
| dataSpace = HAL_DATASPACE_UNKNOWN; |
| break; |
| default: |
| break; |
| } |
| return dataSpace; |
| } |
| |
| enum decon_blending halBlendingToS3CBlending(int32_t blending) |
| { |
| switch (blending) { |
| case HWC2_BLEND_MODE_NONE: |
| return DECON_BLENDING_NONE; |
| case HWC2_BLEND_MODE_PREMULTIPLIED: |
| return DECON_BLENDING_PREMULT; |
| case HWC2_BLEND_MODE_COVERAGE: |
| return DECON_BLENDING_COVERAGE; |
| |
| default: |
| return DECON_BLENDING_MAX; |
| } |
| } |
| |
| enum dpp_rotate halTransformToS3CRot(uint32_t halTransform) |
| { |
| switch (halTransform) { |
| case HAL_TRANSFORM_FLIP_H: |
| return DPP_ROT_YFLIP; |
| case HAL_TRANSFORM_FLIP_V: |
| return DPP_ROT_XFLIP; |
| case HAL_TRANSFORM_ROT_180: |
| return DPP_ROT_180; |
| case HAL_TRANSFORM_ROT_90: |
| return DPP_ROT_90; |
| case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H): |
| /* |
| * HAL: HAL_TRANSFORM_FLIP_H -> HAL_TRANSFORM_ROT_90 |
| * VPP: ROT_90 -> XFLIP |
| */ |
| return DPP_ROT_90_XFLIP; |
| case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V): |
| /* |
| * HAL: HAL_TRANSFORM_FLIP_V -> HAL_TRANSFORM_ROT_90 |
| * VPP: ROT_90 -> YFLIP |
| */ |
| return DPP_ROT_90_YFLIP; |
| case HAL_TRANSFORM_ROT_270: |
| return DPP_ROT_270; |
| default: |
| return DPP_ROT_NORMAL; |
| } |
| } |
| |
| void dumpHandle(uint32_t type, private_handle_t *h) |
| { |
| if (h == NULL) |
| return; |
| HDEBUGLOGD(type, "\t\tformat = %d, width = %u, height = %u, stride = %u, vstride = %u", |
| h->format, h->width, h->height, h->stride, h->vstride); |
| } |
| |
| void dumpExynosImage(uint32_t type, exynos_image &img) |
| { |
| if (!hwcCheckDebugMessages(type)) |
| return; |
| ALOGD("\tbufferHandle: %p, fullWidth: %d, fullHeight: %d, x: %d, y: %d, w: %d, h: %d, format: %s", |
| img.bufferHandle, img.fullWidth, img.fullHeight, img.x, img.y, img.w, img.h, getFormatStr(img.format).string()); |
| ALOGD("\tusageFlags: 0x%" PRIx64 ", layerFlags: 0x%8x, acquireFenceFd: %d, releaseFenceFd: %d", |
| img.usageFlags, img.layerFlags, img.acquireFenceFd, img.releaseFenceFd); |
| ALOGD("\tdataSpace(%d), blending(%d), transform(0x%2x), compressed(%d)", |
| img.dataSpace, img.blending, img.transform, img.compressed); |
| } |
| |
| void dumpExynosImage(String8& result, exynos_image &img) |
| { |
| result.appendFormat("\tbufferHandle: %p, fullWidth: %d, fullHeight: %d, x: %d, y: %d, w: %d, h: %d, format: %s\n", |
| img.bufferHandle, img.fullWidth, img.fullHeight, img.x, img.y, img.w, img.h, getFormatStr(img.format).string()); |
| result.appendFormat("\tusageFlags: 0x%" PRIx64 ", layerFlags: 0x%8x, acquireFenceFd: %d, releaseFenceFd: %d\n", |
| img.usageFlags, img.layerFlags, img.acquireFenceFd, img.releaseFenceFd); |
| result.appendFormat("\tdataSpace(%d), blending(%d), transform(0x%2x), compressed(%d)\n", |
| img.dataSpace, img.blending, img.transform, img.compressed); |
| if (img.bufferHandle != NULL) { |
| result.appendFormat("\tbuffer's stride: %d, %d\n", img.bufferHandle->stride, img.bufferHandle->vstride); |
| } |
| } |
| |
| bool isSrcCropFloat(hwc_frect &frect) |
| { |
| return (frect.left != (int)frect.left) || |
| (frect.top != (int)frect.top) || |
| (frect.right != (int)frect.right) || |
| (frect.bottom != (int)frect.bottom); |
| } |
| |
| bool isScaled(exynos_image &src, exynos_image &dst) |
| { |
| uint32_t srcW = src.w; |
| uint32_t srcH = src.h; |
| uint32_t dstW = dst.w; |
| uint32_t dstH = dst.h; |
| |
| if (!!(src.transform & HAL_TRANSFORM_ROT_90)) { |
| dstW = dst.h; |
| dstH = dst.w; |
| } |
| |
| return ((srcW != dstW) || (srcH != dstH)); |
| } |
| |
| bool isScaledDown(exynos_image &src, exynos_image &dst) |
| { |
| uint32_t srcW = src.w; |
| uint32_t srcH = src.h; |
| uint32_t dstW = dst.w; |
| uint32_t dstH = dst.h; |
| |
| if (!!(src.transform & HAL_TRANSFORM_ROT_90)) { |
| dstW = dst.h; |
| dstH = dst.w; |
| } |
| |
| return ((srcW > dstW) || (srcH > dstH)); |
| } |
| |
| bool hasHdrInfo(exynos_image &img) |
| { |
| uint32_t dataSpace = img.dataSpace; |
| |
| /* By reference Layer's dataspace */ |
| uint32_t standard = (dataSpace & HAL_DATASPACE_STANDARD_MASK); |
| uint32_t transfer = (dataSpace & HAL_DATASPACE_TRANSFER_MASK); |
| |
| if ((standard == HAL_DATASPACE_STANDARD_BT2020) || |
| (standard == HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE) || |
| (standard == HAL_DATASPACE_STANDARD_DCI_P3)) { |
| if ((transfer == HAL_DATASPACE_TRANSFER_ST2084) || |
| (transfer == HAL_DATASPACE_TRANSFER_HLG)) |
| return true; |
| else |
| return false; |
| } |
| |
| return false; |
| } |
| |
| bool hasHdrInfo(android_dataspace dataSpace) { |
| exynos_image img; |
| img.dataSpace = dataSpace; |
| return hasHdrInfo(img); |
| } |
| |
| bool hasHdr10Plus(exynos_image &img) { |
| /* TODO Check layer has hdr10 and dynamic metadata here */ |
| return (img.metaType & VIDEO_INFO_TYPE_HDR_DYNAMIC) ? true : false; |
| } |
| |
| String8 getFormatStr(int format) { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) |
| return exynos_format_desc[i].name; |
| } |
| String8 result; |
| result.appendFormat("? %08x", format); |
| return result; |
| } |
| |
| void adjustRect(hwc_rect_t &rect, int32_t width, int32_t height) |
| { |
| if (rect.left < 0) |
| rect.left = 0; |
| if (rect.left > width) |
| rect.left = width; |
| if (rect.top < 0) |
| rect.top = 0; |
| if (rect.top > height) |
| rect.top = height; |
| if (rect.right < rect.left) |
| rect.right = rect.left; |
| if (rect.right > width) |
| rect.right = width; |
| if (rect.bottom < rect.top) |
| rect.bottom = rect.top; |
| if (rect.bottom > height) |
| rect.bottom = height; |
| } |
| |
| uint32_t getBufferNumOfFormat(int format) |
| { |
| for (unsigned int i = 0; i < FORMAT_MAX_CNT; i++){ |
| if (exynos_format_desc[i].halFormat == format) |
| return exynos_format_desc[i].bufferNum; |
| } |
| return 0; |
| } |
| |
| void setFenceName(int fenceFd, hwc_fence_type fenceType) |
| { |
| if (fenceFd >= 3) |
| ioctl(fenceFd, SYNC_IOC_FENCE_NAME, fence_names[fenceType]); |
| else if (fenceFd == -1) { |
| HDEBUGLOGD(eDebugFence, "%s : fence (type %d) is -1", __func__, (int)fenceType); |
| } |
| else { |
| ALOGW("%s : fence (type %d) is less than 3", __func__, (int)fenceType); |
| hwc_print_stack(); |
| } |
| } |
| |
| int pixel_align_down(int x, int a) { |
| if ((a != 0) && ((x % a) != 0)) { |
| int ret = ((x) - (x % a)); |
| if (ret < 0) |
| ret = 0; |
| return ret; |
| } |
| return x; |
| } |
| |
| uint32_t getExynosBufferYLength(uint32_t width, uint32_t height, int format) |
| { |
| switch (format) { |
| case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_FULL: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_PRIV: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YV12_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P_M: |
| return NV12M_Y_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B: |
| HDEBUGLOGD(eDebugMPP, "8bit size(Y) : %d, extra size : %d", NV12M_Y_SIZE(width, height), NV12M_Y_2B_SIZE(width, height)); |
| return NV12M_Y_SIZE(width, height) + NV12M_Y_2B_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_P010_M: |
| HDEBUGLOGD(eDebugMPP, "size(Y) : %d", P010M_Y_SIZE(width, height)); |
| return P010M_Y_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_YCBCR_P010: |
| HDEBUGLOGD(eDebugMPP, "size(Y) : %d", P010_Y_SIZE(width, height)); |
| return P010_Y_SIZE(width, height); |
| } |
| |
| return NV12M_Y_SIZE(width, height) + ((width % 128) == 0 ? 0 : 256); |
| } |
| |
| uint32_t getExynosBufferCbCrLength(uint32_t width, uint32_t height, int format) |
| { |
| switch (format) { |
| case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_FULL: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_PRIV: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YV12_M: |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P_M: |
| return NV12M_CBCR_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B: |
| HDEBUGLOGD(eDebugMPP, "8bit size(CbCr) : %d, extra size : %d",NV12M_CBCR_SIZE(width, height), NV12M_CBCR_2B_SIZE(width, height)); |
| return NV12M_CBCR_SIZE(width, height) + NV12M_CBCR_2B_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_P010_M: |
| HDEBUGLOGD(eDebugMPP, "size(CbCr) : %d", P010M_CBCR_SIZE(width, height)); |
| return P010M_CBCR_SIZE(width, height); |
| case HAL_PIXEL_FORMAT_YCBCR_P010: |
| HDEBUGLOGD(eDebugMPP, "size(CbCr) : %d", P010_CBCR_SIZE(width, height)); |
| return P010_CBCR_SIZE(width, height); |
| } |
| |
| return NV12M_CBCR_SIZE(width, height); |
| } |
| |
| int getBufLength(private_handle_t *handle, uint32_t planerNum, size_t *length, int format, uint32_t width, uint32_t height) |
| { |
| uint32_t bufferNumber = getBufferNumOfFormat(format); |
| if ((bufferNumber == 0) || (bufferNumber > planerNum)) |
| return -EINVAL; |
| |
| switch (bufferNumber) { |
| case 1: |
| length[0] = handle->size; |
| break; |
| case 2: |
| HDEBUGLOGD(eDebugMPP, "-- %s x : %d y : %d format : %d",__func__, width, height, format); |
| length[0] = handle->size; |
| length[1] = handle->size1; |
| HDEBUGLOGD(eDebugMPP, "Y size : %zu CbCr size : %zu", length[0], length[1]); |
| break; |
| case 3: |
| length[0] = width * height; |
| length[1]= (length[0]/4); |
| length[2]= (length[0]/4); |
| break; |
| } |
| return NO_ERROR; |
| } |
| |
| int fence_close(int fence, ExynosDisplay* display, |
| hwc_fdebug_fence_type type, hwc_fdebug_ip_type ip) { |
| if (display != NULL) |
| setFenceInfo(fence, display, type, ip, FENCE_CLOSE); |
| return hwcFdClose(fence); |
| } |
| |
| bool fence_valid(int fence) { |
| if (fence == -1){ |
| HDEBUGLOGD(eDebugFence, "%s : fence is -1", __func__); |
| return false; |
| } else if (fence < 3) { |
| ALOGW("%s : fence (fd:%d) is less than 3", __func__, fence); |
| hwc_print_stack(); |
| return true; |
| } else if (fence >= MAX_FD_NUM) { |
| ALOGW("%s : fence (fd:%d) over MAX fd number", __func__, fence); |
| /* valid but fence will not be traced */ |
| return true; |
| } |
| return true; |
| } |
| |
| int hwcFdClose(int fd) { |
| if (fd>= 3) |
| close(fd); |
| else if (fd == -1){ |
| HDEBUGLOGD(eDebugFence, "%s : Fd is -1", __func__); |
| } else { |
| ALOGW("%s : Fd:%d is less than 3", __func__, fd); |
| hwc_print_stack(); |
| } |
| return -1; |
| } |
| |
| int hwc_dup(int fd, ExynosDisplay* display, |
| hwc_fdebug_fence_type type, hwc_fdebug_ip_type ip) { |
| |
| int dup_fd = -1; |
| |
| if (fd>= 3) |
| dup_fd = dup(fd); |
| else if (fd == -1){ |
| HDEBUGLOGD(eDebugFence, "%s : Fd is -1", __func__); |
| } else { |
| ALOGW("%s : Fd:%d is less than 3", __func__, fd); |
| hwc_print_stack(); |
| } |
| |
| if ((dup_fd < 3) && (dup_fd != -1)) { |
| ALOGW("%s : Dupulicated Fd:%d is less than 3 : %d", __func__, fd, dup_fd); |
| hwc_print_stack(); |
| } |
| |
| setFenceInfo(dup_fd, display, type, ip, FENCE_FROM); |
| FT_LOGD("duplicated %d from %d", dup_fd, fd); |
| |
| return dup_fd; |
| } |
| |
| int hwc_print_stack() { |
| /* CallStack stack; */ |
| /* stack.update(); */ |
| /* stack.log("HWCException", ANDROID_LOG_ERROR, "HWCException"); */ |
| return 0; |
| } |
| |
| struct tm* getLocalTime(struct timeval tv) { |
| return (struct tm*)localtime((time_t*)&tv.tv_sec); |
| } |
| |
| void setFenceInfo(uint32_t fd, ExynosDisplay* display, |
| hwc_fdebug_fence_type type, hwc_fdebug_ip_type ip, |
| uint32_t direction, bool __unused pendingAllowed) { |
| |
| if (!fence_valid(fd) || display == NULL) return; |
| /* valid but fence will not be traced */ |
| if (fd >= MAX_FD_NUM) return; |
| |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = &device->mFenceInfo[fd]; |
| info->displayId = display->mDisplayId; |
| struct timeval tv; |
| |
| // FIXME: sync_fence_info, sync_pt_info are deprecated |
| // HWC guys should fix this. |
| #if 0 |
| if (exynosHWCControl.sysFenceLogging) { |
| struct sync_pt_info* pt_info = NULL; |
| info->sync_data = NULL; |
| if (info->sync_data != NULL) { |
| pt_info = sync_pt_info(info->sync_data, pt_info); |
| if (pt_info !=NULL) { |
| FT_LOGD("real name : %s status : %s pt_obj : %s pt_drv : %s", |
| info->sync_data->name, info->sync_data->status==1 ? "Active":"Signaled", |
| pt_info->obj_name, pt_info->driver_name); |
| } else { |
| FT_LOGD("real name : %s status : %d pt_info : %p", |
| info->sync_data->name, info->sync_data->status, pt_info); |
| } |
| sync_fence_info_free(info->sync_data); |
| } |
| } |
| #endif |
| |
| fenceTrace_t *trace = NULL; |
| |
| switch(direction) { |
| case FENCE_FROM: |
| trace = &info->from; |
| info->to.type = FENCE_TYPE_UNDEFINED; |
| info->to.ip = FENCE_IP_UNDEFINED; |
| info->usage++; |
| break; |
| case FENCE_TO: |
| trace = &info->to; |
| info->usage--; |
| break; |
| case FENCE_DUP: |
| trace = &info->dup; |
| info->usage++; |
| break; |
| case FENCE_CLOSE: |
| trace = &info->close; |
| info->usage--; |
| if (info->usage < 0) info->usage = 0; |
| break; |
| default: |
| ALOGE("Fence trace : Undefined direction!"); |
| break; |
| } |
| |
| if (trace != NULL) { |
| trace->type = type; |
| trace->ip = ip; |
| gettimeofday(&trace->time, NULL); |
| tv = trace->time; |
| trace->curFlag = 1; |
| FT_LOGW("FD : %d, direction : %d, type : %d, ip : %d", fd, direction, trace->type, trace->ip); |
| // device->fenceTracing.appendFormat("FD : %d, From : %s\n", fd, info->trace.fromName); |
| } |
| |
| #if 0 // To be used ? |
| struct tm* localTime = getLocalTime(tv); |
| device->fenceTracing.appendFormat("usage : %d, time:%02d-%02d %02d:%02d:%02d.%03lu(%lu)\n", info->usage, |
| localTime->tm_mon+1, localTime->tm_mday, |
| localTime->tm_hour, localTime->tm_min, |
| localTime->tm_sec, tv.tv_usec/1000, |
| ((tv.tv_sec * 1000) + (tv.tv_usec / 1000))); |
| #endif |
| |
| // Fence's usage count shuld be zero at end of frame(present done). |
| // This flag means usage count of the fence can be pended over frame. |
| info->pendingAllowed = pendingAllowed; |
| |
| if (info->usage == 0) |
| info->pendingAllowed = false; |
| |
| info->last_dir = direction; |
| } |
| |
| void printLastFenceInfo(uint32_t fd, ExynosDisplay* display) { |
| |
| struct timeval tv; |
| |
| if (!fence_valid(fd)) return; |
| /* valid but fence will not be traced */ |
| if (fd >= MAX_FD_NUM) return; |
| |
| ExynosDevice* device = display->mDevice; |
| |
| hwc_fence_info_t* info = &device->mFenceInfo[fd]; |
| FT_LOGD("---- Fence FD : %d, Display(%d) ----", fd, info->displayId); |
| |
| fenceTrace_t *trace = NULL; |
| |
| switch(info->last_dir) { |
| case FENCE_FROM: |
| trace = &info->from; |
| break; |
| case FENCE_TO: |
| trace = &info->to; |
| break; |
| case FENCE_DUP: |
| trace = &info->dup; |
| break; |
| case FENCE_CLOSE: |
| trace = &info->close; |
| break; |
| default: |
| ALOGE("Fence trace : Undefined direction!"); |
| break; |
| } |
| |
| if (trace != NULL) { |
| FT_LOGD("Last state : %d, type(%d), ip(%d)", |
| info->last_dir, trace->type, trace->ip); |
| tv = info->from.time; |
| } |
| |
| FT_LOGD("from : %d, %d (cur : %d), to : %d, %d (cur : %d), hwc_dup : %d, %d (cur : %d),hwc_close : %d, %d (cur : %d)", |
| info->from.type, info->from.ip, info->from.curFlag, |
| info->to.type, info->to.ip, info->to.curFlag, |
| info->dup.type, info->dup.ip, info->dup.curFlag, |
| info->close.type, info->close.ip, info->close.curFlag); |
| |
| struct tm* localTime = getLocalTime(tv); |
| |
| FT_LOGD("usage : %d, time:%02d-%02d %02d:%02d:%02d.%03lu(%lu)", info->usage, |
| localTime->tm_mon+1, localTime->tm_mday, |
| localTime->tm_hour, localTime->tm_min, |
| localTime->tm_sec, tv.tv_usec/1000, |
| ((tv.tv_sec * 1000) + (tv.tv_usec / 1000))); |
| } |
| |
| void dumpFenceInfo(ExynosDisplay *display, int32_t __unused depth) { |
| |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| |
| FT_LOGD("Dump fence ++"); |
| for (int i=0; i < MAX_FD_NUM; i++){ |
| if ((info[i].usage >= 1 || info[i].usage <= -1) && (!info[i].pendingAllowed)) |
| printLastFenceInfo(i, display); |
| } |
| FT_LOGD("Dump fence --"); |
| } |
| |
| void printLeakFds(ExynosDisplay *display){ |
| |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| |
| int cnt = 1, i = 0; |
| String8 errStringOne; |
| String8 errStringMinus; |
| |
| errStringOne.appendFormat("Leak Fds (1) :\n"); |
| |
| for (i=0; i < MAX_FD_NUM; i++){ |
| if(info[i].usage == 1) { |
| errStringOne.appendFormat("%d,", i); |
| if(cnt++%10 == 0) |
| errStringOne.appendFormat("\n"); |
| } |
| } |
| FT_LOGW("%s", errStringOne.string()); |
| |
| errStringMinus.appendFormat("Leak Fds (-1) :\n"); |
| |
| cnt = 1; |
| for (i=0; i < MAX_FD_NUM; i++){ |
| if(info[i].usage < 0) { |
| errStringMinus.appendFormat("%d,", i); |
| if(cnt++%10 == 0) |
| errStringMinus.appendFormat("\n"); |
| } |
| } |
| FT_LOGW("%s", errStringMinus.string()); |
| } |
| |
| void dumpNCheckLeak(ExynosDisplay *display, int32_t __unused depth) { |
| |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| |
| FT_LOGD("Dump leaking fence ++"); |
| for (int i=0; i < MAX_FD_NUM; i++){ |
| if ((info[i].usage >= 1 || info[i].usage <= -1) && (!info[i].pendingAllowed)) |
| // leak is occured in this frame first |
| if (!info[i].leaking) { |
| info[i].leaking = true; |
| printLastFenceInfo(i, display); |
| } |
| } |
| |
| int priv = exynosHWCControl.fenceTracer; |
| exynosHWCControl.fenceTracer = 3; |
| printLeakFds(display); |
| exynosHWCControl.fenceTracer = priv; |
| |
| FT_LOGD("Dump leaking fence --"); |
| } |
| |
| bool fenceWarn(ExynosDisplay *display, uint32_t threshold) { |
| |
| uint32_t cnt = 0, r_cnt = 0; |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| |
| // FIXME: sync_fence_info() is deprecated |
| // HWC guys should fix this. |
| #if 0 |
| if (exynosHWCControl.sysFenceLogging) { |
| for (int i=3; i < MAX_FD_NUM; i++){ |
| struct sync_fence_info_data* data = nullptr; |
| data = NULL; //sync_fence_info(i); |
| if (data != NULL) { |
| r_cnt++; |
| sync_fence_info_free(data); |
| } |
| } |
| } |
| #endif |
| |
| for (int i=0; i < MAX_FD_NUM; i++){ |
| if(info[i].usage >= 1 || info[i].usage <= -1) |
| cnt++; |
| } |
| |
| if ((cnt>threshold) || (exynosHWCControl.fenceTracer > 0)) |
| dumpFenceInfo(display, 0); |
| |
| if (r_cnt>threshold) |
| ALOGE("Fence leak somewhare!!"); |
| |
| FT_LOGD("fence hwc : %d, real : %d", cnt, r_cnt); |
| |
| return (cnt>threshold) ? true : false; |
| } |
| |
| void resetFenceCurFlag(ExynosDisplay *display) { |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| for (int i=0; i < MAX_FD_NUM; i++){ |
| if (info[i].usage == 0) { |
| info[i].displayId = HWC_DISPLAY_PRIMARY; |
| info[i].leaking = false; |
| info[i].from.curFlag = 0; |
| info[i].to.curFlag = 0; |
| info[i].dup.curFlag = 0; |
| info[i].close.curFlag = 0; |
| info[i].curFlag = 0; |
| } else { |
| info[i].curFlag = 0; |
| } |
| } |
| } |
| |
| bool validateFencePerFrame(ExynosDisplay *display) { |
| |
| ExynosDevice* device = display->mDevice; |
| hwc_fence_info_t* info = device->mFenceInfo; |
| bool ret = true; |
| |
| for (int i=0; i < MAX_FD_NUM; i++){ |
| if (info[i].displayId != display->mDisplayId) |
| continue; |
| if ((info[i].usage >= 1 || info[i].usage <= -1) && |
| (!info[i].pendingAllowed) && (!info[i].leaking)) { |
| ret = false; |
| } |
| } |
| |
| if (!ret) { |
| int priv = exynosHWCControl.fenceTracer; |
| exynosHWCControl.fenceTracer = 3; |
| dumpNCheckLeak(display, 0); |
| exynosHWCControl.fenceTracer = priv; |
| } |
| |
| return ret; |
| } |
| |
| void setFenceName(uint32_t fd, ExynosDisplay *display, |
| hwc_fdebug_fence_type type, hwc_fdebug_ip_type ip, |
| uint32_t direction, bool pendingAllowed) { |
| |
| ExynosDevice* device = display->mDevice; |
| if (!fence_valid(fd) || device == NULL) return; |
| /* valid but fence will not be traced */ |
| if (fd >= MAX_FD_NUM) return; |
| |
| hwc_fence_info_t* info = &device->mFenceInfo[fd]; |
| info->displayId = display->mDisplayId; |
| fenceTrace_t *trace = NULL; |
| |
| switch(direction) { |
| case FENCE_FROM: |
| trace = &info->from; |
| break; |
| case FENCE_TO: |
| trace = &info->to; |
| break; |
| case FENCE_DUP: |
| trace = &info->dup; |
| break; |
| case FENCE_CLOSE: |
| trace = &info->close; |
| break; |
| default: |
| ALOGE("Fence trace : Undefined direction!"); |
| break; |
| } |
| |
| if (trace != NULL) { |
| trace->type = type; |
| trace->ip = ip; |
| FT_LOGD("FD : %d, direction : %d, type(%d), ip(%d) (changed)", fd, direction, type, ip); |
| } |
| |
| info->pendingAllowed = pendingAllowed; |
| |
| if (info->usage == 0) |
| info->pendingAllowed = false; |
| } |
| |
| String8 getMPPStr(int typeId) { |
| if (typeId < MPP_DPP_NUM){ |
| int cnt = sizeof(AVAILABLE_OTF_MPP_UNITS)/sizeof(exynos_mpp_t); |
| for (int i = 0; i < cnt; i++){ |
| if (AVAILABLE_OTF_MPP_UNITS[i].physicalType == typeId) |
| return String8(AVAILABLE_OTF_MPP_UNITS[i].name); |
| } |
| } else { |
| int cnt = sizeof(AVAILABLE_M2M_MPP_UNITS)/sizeof(exynos_mpp_t); |
| for (int i = 0; i < cnt; i++){ |
| if (AVAILABLE_M2M_MPP_UNITS[i].physicalType == typeId) |
| return String8(AVAILABLE_M2M_MPP_UNITS[i].name); |
| } |
| } |
| String8 result; |
| result.appendFormat("? %08x", typeId); |
| return result; |
| } |