| /* |
| * Copyright (C) 2010 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 <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <hardware/hardware.h> |
| |
| #include <fcntl.h> |
| #include <errno.h> |
| |
| #include <cutils/log.h> |
| #include <cutils/atomic.h> |
| #include <cutils/properties.h> |
| |
| #include <hardware/hwcomposer.h> |
| #include <overlayLib.h> |
| #include <overlayLibUI.h> |
| #include <copybit.h> |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <ui/android_native_buffer.h> |
| #include <gralloc_priv.h> |
| #include <genlock.h> |
| #include <qcom_ui.h> |
| #include <gr.h> |
| |
| /*****************************************************************************/ |
| #define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) |
| #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) |
| #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) |
| |
| #ifdef COMPOSITION_BYPASS |
| #define MAX_BYPASS_LAYERS 3 |
| #define BYPASS_DEBUG 0 |
| #define BYPASS_INDEX_OFFSET 4 |
| |
| enum BypassState { |
| BYPASS_ON, |
| BYPASS_OFF, |
| BYPASS_OFF_PENDING, |
| }; |
| |
| enum BypassBufferLockState { |
| BYPASS_BUFFER_UNLOCKED, |
| BYPASS_BUFFER_LOCKED, |
| }; |
| #endif |
| |
| enum HWCLayerType{ |
| HWC_SINGLE_VIDEO = 0x1, |
| HWC_ORIG_RESOLUTION = 0x2, |
| HWC_S3D_LAYER = 0x4, |
| HWC_STOP_UI_MIRRORING_MASK = 0xF |
| }; |
| |
| enum eHWCOverlayStatus { |
| HWC_OVERLAY_OPEN, |
| HWC_OVERLAY_PREPARE_TO_CLOSE, |
| HWC_OVERLAY_CLOSED |
| }; |
| |
| struct hwc_context_t { |
| hwc_composer_device_t device; |
| /* our private state goes below here */ |
| overlay::Overlay* mOverlayLibObject; |
| native_handle_t *previousOverlayHandle; |
| #ifdef COMPOSITION_BYPASS |
| overlay::OverlayUI* mOvUI[MAX_BYPASS_LAYERS]; |
| native_handle_t* previousBypassHandle[MAX_BYPASS_LAYERS]; |
| BypassBufferLockState bypassBufferLockState[MAX_BYPASS_LAYERS]; |
| int layerindex[MAX_BYPASS_LAYERS]; |
| int nPipesUsed; |
| BypassState bypassState; |
| #endif |
| #if defined HDMI_DUAL_DISPLAY |
| external_display mHDMIEnabled; // Type of external display |
| bool pendingHDMI; |
| #endif |
| int previousLayerCount; |
| eHWCOverlayStatus hwcOverlayStatus; |
| }; |
| |
| static int hwc_device_open(const struct hw_module_t* module, const char* name, |
| struct hw_device_t** device); |
| |
| static struct hw_module_methods_t hwc_module_methods = { |
| open: hwc_device_open |
| }; |
| |
| |
| struct private_hwc_module_t { |
| hwc_module_t base; |
| copybit_device_t *copybitEngine; |
| framebuffer_device_t *fbDevice; |
| int compositionType; |
| bool isBypassEnabled; //from build.prop ro.sf.compbypass.enable |
| }; |
| |
| struct private_hwc_module_t HAL_MODULE_INFO_SYM = { |
| base: { |
| common: { |
| tag: HARDWARE_MODULE_TAG, |
| version_major: 1, |
| version_minor: 0, |
| id: HWC_HARDWARE_MODULE_ID, |
| name: "Hardware Composer Module", |
| author: "The Android Open Source Project", |
| methods: &hwc_module_methods, |
| } |
| }, |
| copybitEngine: NULL, |
| fbDevice: NULL, |
| compositionType: 0, |
| isBypassEnabled: false, |
| }; |
| |
| //Only at this point would the compiler know all storage class sizes. |
| //The header has hooks which need to know those beforehand. |
| #include "external_display_only.h" |
| |
| /*****************************************************************************/ |
| |
| static void dump_layer(hwc_layer_t const* l) { |
| LOGD("\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, {%d,%d,%d,%d}, {%d,%d,%d,%d}", |
| l->compositionType, l->flags, l->handle, l->transform, l->blending, |
| l->sourceCrop.left, |
| l->sourceCrop.top, |
| l->sourceCrop.right, |
| l->sourceCrop.bottom, |
| l->displayFrame.left, |
| l->displayFrame.top, |
| l->displayFrame.right, |
| l->displayFrame.bottom); |
| } |
| |
| static inline int min(const int& a, const int& b) { |
| return (a < b) ? a : b; |
| } |
| |
| static inline int max(const int& a, const int& b) { |
| return (a > b) ? a : b; |
| } |
| #ifdef COMPOSITION_BYPASS |
| void setLayerbypassIndex(hwc_layer_t* layer, const int bypass_index) |
| { |
| layer->flags &= ~HWC_BYPASS_INDEX_MASK; |
| layer->flags |= bypass_index << BYPASS_INDEX_OFFSET; |
| } |
| |
| int getLayerbypassIndex(hwc_layer_t* layer) |
| { |
| int byp_index = -1; |
| |
| if(layer->flags & HWC_COMP_BYPASS) { |
| byp_index = ((layer->flags & HWC_BYPASS_INDEX_MASK) >> BYPASS_INDEX_OFFSET); |
| byp_index = (byp_index < MAX_BYPASS_LAYERS ? byp_index : -1 ); |
| } |
| return byp_index; |
| } |
| |
| void unlockPreviousBypassBuffers(hwc_context_t* ctx) { |
| // Unlock the previous bypass buffers. We can blindly unlock the buffers here, |
| // because buffers will be in this list only if the lock was successfully acquired. |
| for(int i = 0; i < MAX_BYPASS_LAYERS && ctx->previousBypassHandle[i]; i++) { |
| private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i]; |
| |
| // Validate the handle to make sure it hasn't been deallocated. |
| if (private_handle_t::validate(ctx->previousBypassHandle[i])) { |
| continue; |
| } |
| // Check if the handle was locked previously |
| if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) { |
| if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) { |
| LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); |
| } else { |
| ctx->previousBypassHandle[i] = NULL; |
| // Reset the lock flag |
| hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| } |
| } |
| } |
| } |
| |
| void print_info(hwc_layer_t* layer) |
| { |
| hwc_rect_t sourceCrop = layer->sourceCrop; |
| hwc_rect_t displayFrame = layer->displayFrame; |
| |
| int s_l = sourceCrop.left; |
| int s_t = sourceCrop.top; |
| int s_r = sourceCrop.right; |
| int s_b = sourceCrop.bottom; |
| |
| int d_l = displayFrame.left; |
| int d_t = displayFrame.top; |
| int d_r = displayFrame.right; |
| int d_b = displayFrame.bottom; |
| |
| LOGE_IF(BYPASS_DEBUG, "src:[%d,%d,%d,%d] (%d x %d) dst:[%d,%d,%d,%d] (%d x %d)", |
| s_l, s_t, s_r, s_b, (s_r - s_l), (s_b - s_t), |
| d_l, d_t, d_r, d_b, (d_r - d_l), (d_b - d_t)); |
| } |
| |
| //Crops source buffer against destination and FB boundaries |
| void calculate_crop_rects(hwc_rect_t& crop, hwc_rect_t& dst, int hw_w, int hw_h) { |
| |
| int& crop_x = crop.left; |
| int& crop_y = crop.top; |
| int& crop_r = crop.right; |
| int& crop_b = crop.bottom; |
| int crop_w = crop.right - crop.left; |
| int crop_h = crop.bottom - crop.top; |
| |
| int& dst_x = dst.left; |
| int& dst_y = dst.top; |
| int& dst_r = dst.right; |
| int& dst_b = dst.bottom; |
| int dst_w = dst.right - dst.left; |
| int dst_h = dst.bottom - dst.top; |
| |
| if(dst_x < 0) { |
| float scale_x = crop_w * 1.0f / dst_w; |
| float diff_factor = (scale_x * abs(dst_x)); |
| crop_x = crop_x + (int)diff_factor; |
| crop_w = crop_r - crop_x; |
| |
| dst_x = 0; |
| dst_w = dst_r - dst_x;; |
| } |
| if(dst_r > hw_w) { |
| float scale_x = crop_w * 1.0f / dst_w; |
| float diff_factor = scale_x * (dst_r - hw_w); |
| crop_r = crop_r - diff_factor; |
| crop_w = crop_r - crop_x; |
| |
| dst_r = hw_w; |
| dst_w = dst_r - dst_x; |
| } |
| if(dst_y < 0) { |
| float scale_y = crop_h * 1.0f / dst_h; |
| float diff_factor = scale_y * abs(dst_y); |
| crop_y = crop_y + diff_factor; |
| crop_h = crop_b - crop_y; |
| |
| dst_y = 0; |
| dst_h = dst_b - dst_y; |
| } |
| if(dst_b > hw_h) { |
| float scale_y = crop_h * 1.0f / dst_h; |
| float diff_factor = scale_y * (dst_b - hw_h); |
| crop_b = crop_b - diff_factor; |
| crop_h = crop_b - crop_y; |
| |
| dst_b = hw_h; |
| dst_h = dst_b - dst_y; |
| } |
| |
| LOGE_IF(BYPASS_DEBUG,"crop: [%d,%d,%d,%d] dst:[%d,%d,%d,%d]", |
| crop_x, crop_y, crop_w, crop_h,dst_x, dst_y, dst_w, dst_h); |
| } |
| |
| /* |
| * Configures pipe(s) for composition bypass |
| */ |
| static int prepareBypass(hwc_context_t *ctx, hwc_layer_t *layer, |
| int nPipeIndex, int vsync_wait, int isFG) { |
| |
| if (ctx && ctx->mOvUI[nPipeIndex]) { |
| overlay::OverlayUI *ovUI = ctx->mOvUI[nPipeIndex]; |
| |
| private_hwc_module_t* hwcModule = reinterpret_cast< |
| private_hwc_module_t*>(ctx->device.common.module); |
| if (!hwcModule) { |
| LOGE("%s: NULL Module", __FUNCTION__); |
| return -1; |
| } |
| |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| if(!hnd) { |
| LOGE("%s: layer handle is NULL", __FUNCTION__); |
| return -1; |
| } |
| |
| int hw_w = hwcModule->fbDevice->width; |
| int hw_h = hwcModule->fbDevice->height; |
| |
| hwc_rect_t sourceCrop = layer->sourceCrop; |
| hwc_rect_t displayFrame = layer->displayFrame; |
| |
| const int src_w = sourceCrop.right - sourceCrop.left; |
| const int src_h = sourceCrop.bottom - sourceCrop.top; |
| |
| hwc_rect_t crop = sourceCrop; |
| int crop_w = crop.right - crop.left; |
| int crop_h = crop.bottom - crop.top; |
| |
| hwc_rect_t dst = displayFrame; |
| int dst_w = dst.right - dst.left; |
| int dst_h = dst.bottom - dst.top; |
| |
| if(hnd != NULL && (hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) { |
| LOGE("%s: Unable to setup bypass due to non-pmem memory",__FUNCTION__); |
| return -1; |
| } |
| |
| if(dst.left < 0 || dst.top < 0 || dst.right > hw_w || dst.bottom > hw_h) { |
| LOGE_IF(BYPASS_DEBUG,"%s: Destination has negative coordinates", __FUNCTION__); |
| |
| calculate_crop_rects(crop, dst, hw_w, hw_h); |
| |
| //Update calulated width and height |
| crop_w = crop.right - crop.left; |
| crop_h = crop.bottom - crop.top; |
| |
| dst_w = dst.right - dst.left; |
| dst_h = dst.bottom - dst.top; |
| } |
| |
| if( (dst_w > hw_w)|| (dst_h > hw_h)) { |
| LOGE_IF(BYPASS_DEBUG,"%s: Destination rectangle exceeds FB resolution", __FUNCTION__); |
| print_info(layer); |
| dst_w = hw_w; |
| dst_h = hw_h; |
| } |
| |
| overlay_buffer_info info; |
| info.width = src_w; |
| info.height = src_h; |
| info.format = hnd->format; |
| info.size = hnd->size; |
| |
| int fbnum = 0; |
| int orientation = layer->transform; |
| const bool useVGPipe = (nPipeIndex != (MAX_BYPASS_LAYERS-1)); |
| //only last layer should wait for vsync |
| const bool waitForVsync = vsync_wait; |
| const bool isFg = isFG; |
| //Just to differentiate zorders for different layers |
| const int zorder = nPipeIndex; |
| |
| ovUI->setSource(info, orientation); |
| ovUI->setCrop(crop.left, crop.top, crop_w, crop_h); |
| ovUI->setDisplayParams(fbnum, waitForVsync, isFg, zorder, useVGPipe); |
| ovUI->setPosition(dst.left, dst.top, dst_w, dst_h); |
| |
| LOGE_IF(BYPASS_DEBUG,"%s: Bypass set: crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] waitforVsync: %d \ |
| isFg: %d zorder: %d VG = %d nPipe: %d",__FUNCTION__, |
| crop.left, crop.top, crop_w, crop_h, |
| dst.left, dst.top, dst_w, dst_h, |
| waitForVsync, isFg, zorder, useVGPipe, nPipeIndex ); |
| |
| if(ovUI->commit() != overlay::NO_ERROR) { |
| LOGE("%s: Overlay Commit failed", __FUNCTION__); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Checks if doing comp. bypass is possible. |
| * It is possible if |
| * 1. No MDP pipe is used |
| * 2. Rotation is not needed |
| * 3. We have atmost MAX_BYPASS_LAYERS |
| */ |
| inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount, |
| const hwc_layer_list_t* list) { |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| //Check if enabled in build.prop |
| if(hwcModule->isBypassEnabled == false) { |
| return false; |
| } |
| |
| if(list->numHwLayers < 1) { |
| return false; |
| } |
| |
| #if defined HDMI_DUAL_DISPLAY |
| //Disable bypass when HDMI is enabled |
| if(ctx->mHDMIEnabled || ctx->pendingHDMI) { |
| return false; |
| } |
| #endif |
| |
| if(ExtDispOnly::isModeOn()) { |
| return false; |
| } |
| |
| //Bypass is not efficient if rotation or asynchronous mode is needed. |
| for(int i = 0; i < list->numHwLayers; ++i) { |
| if(list->hwLayers[i].transform) { |
| return false; |
| } |
| if(list->hwLayers[i].flags & HWC_LAYER_ASYNCHRONOUS) { |
| return false; |
| } |
| } |
| |
| return (yuvCount == 0) && (ctx->hwcOverlayStatus == HWC_OVERLAY_CLOSED) |
| && (list->numHwLayers <= MAX_BYPASS_LAYERS); |
| } |
| |
| void setBypassLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list) |
| { |
| for(int index = 0 ; index < MAX_BYPASS_LAYERS; index++ ) |
| { |
| int layer_index = ctx->layerindex[index]; |
| if(layer_index >= 0) { |
| hwc_layer_t* layer = &(list->hwLayers[layer_index]); |
| |
| layer->flags |= HWC_COMP_BYPASS; |
| layer->compositionType = HWC_USE_OVERLAY; |
| layer->hints |= HWC_HINT_CLEAR_FB; |
| } |
| } |
| |
| if( list->numHwLayers > ctx->nPipesUsed ) { |
| list->flags &= ~HWC_SKIP_COMPOSITION; //Compose to FB |
| } else { |
| list->flags |= HWC_SKIP_COMPOSITION; // Dont |
| } |
| } |
| |
| bool setupBypass(hwc_context_t* ctx, hwc_layer_list_t* list) { |
| int nPipeIndex, vsync_wait, isFG; |
| int numHwLayers = list->numHwLayers; |
| int nPipeAvailable = MAX_BYPASS_LAYERS; |
| |
| for (int index = 0 ; (index < numHwLayers) && nPipeAvailable; index++) { |
| |
| hwc_layer_t* layer = &(list->hwLayers[index]); |
| |
| nPipeIndex = MAX_BYPASS_LAYERS - nPipeAvailable; |
| //Set VSYNC wait is needed only for the last pipe queued |
| vsync_wait = (nPipeIndex == (numHwLayers-1)); |
| //Set isFG to true for layer with z-order zero |
| isFG = !index; |
| |
| //Clear Bypass flags for the layer |
| layer->flags &= ~HWC_COMP_BYPASS; |
| layer->flags |= HWC_BYPASS_INDEX_MASK; |
| |
| if( prepareBypass(ctx, &(list->hwLayers[index]), nPipeIndex, vsync_wait, isFG) != 0 ) { |
| LOGE_IF(BYPASS_DEBUG, "%s: layer %d failed to configure bypass for pipe index: %d", |
| __FUNCTION__, index, nPipeIndex); |
| return false; |
| } else { |
| ctx->layerindex[nPipeIndex] = index; |
| setLayerbypassIndex(layer, nPipeIndex); |
| nPipeAvailable--; |
| } |
| } |
| ctx->nPipesUsed = MAX_BYPASS_LAYERS - nPipeAvailable; |
| return true; |
| } |
| |
| void unsetBypassLayerFlags(hwc_layer_list_t* list) { |
| if (!list) |
| return; |
| |
| for (int index = 0 ; index < list->numHwLayers; index++) { |
| if(list->hwLayers[index].flags & HWC_COMP_BYPASS) { |
| list->hwLayers[index].flags &= ~HWC_COMP_BYPASS; |
| } |
| } |
| } |
| |
| void unsetBypassBufferLockState(hwc_context_t* ctx) { |
| for (int i= 0; i< MAX_BYPASS_LAYERS; i++) { |
| ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED; |
| } |
| } |
| |
| void storeLockedBypassHandle(hwc_layer_list_t* list, hwc_context_t* ctx) { |
| if (!list) |
| return; |
| |
| for(int index = 0; index < MAX_BYPASS_LAYERS; index++ ) { |
| hwc_layer_t layer = list->hwLayers[ctx->layerindex[index]]; |
| |
| if (layer.flags & HWC_COMP_BYPASS) { |
| private_handle_t *hnd = (private_handle_t*)layer.handle; |
| |
| if (ctx->bypassBufferLockState[index] == BYPASS_BUFFER_LOCKED) { |
| ctx->previousBypassHandle[index] = (native_handle_t*)layer.handle; |
| hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| } else { |
| ctx->previousBypassHandle[index] = NULL; |
| } |
| } |
| } |
| } |
| |
| void closeExtraPipes(hwc_context_t* ctx) { |
| |
| int pipes_used = ctx->nPipesUsed; |
| |
| //Unused pipes must be of higher z-order |
| for (int i = pipes_used ; i < MAX_BYPASS_LAYERS; i++) { |
| if (ctx->previousBypassHandle[i]) { |
| private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i]; |
| |
| if (!private_handle_t::validate(ctx->previousBypassHandle[i])) { |
| if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) { |
| LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); |
| } else { |
| ctx->previousBypassHandle[i] = NULL; |
| ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED; |
| hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| } |
| } |
| } |
| ctx->mOvUI[i]->closeChannel(); |
| ctx->layerindex[i] = -1; |
| } |
| } |
| #endif //COMPOSITION_BYPASS |
| |
| static int setVideoOverlayStatusInGralloc(hwc_context_t* ctx, const bool enable) { |
| #if defined HDMI_DUAL_DISPLAY |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| ctx->device.common.module); |
| if(!hwcModule) { |
| LOGE("%s: invalid params", __FUNCTION__); |
| return -1; |
| } |
| |
| framebuffer_device_t *fbDev = hwcModule->fbDevice; |
| if (!fbDev) { |
| LOGE("%s: fbDev is NULL", __FUNCTION__); |
| return -1; |
| } |
| |
| // Inform the gralloc to stop or start UI mirroring |
| fbDev->videoOverlayStarted(fbDev, enable); |
| #endif |
| return 0; |
| } |
| |
| static void setHWCOverlayStatus(hwc_context_t *ctx, bool isVideoPresent) { |
| |
| switch (ctx->hwcOverlayStatus) { |
| case HWC_OVERLAY_OPEN: |
| ctx->hwcOverlayStatus = |
| isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_PREPARE_TO_CLOSE; |
| break; |
| case HWC_OVERLAY_PREPARE_TO_CLOSE: |
| ctx->hwcOverlayStatus = |
| isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_CLOSED; |
| break; |
| case HWC_OVERLAY_CLOSED: |
| ctx->hwcOverlayStatus = |
| isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_CLOSED; |
| break; |
| default: |
| LOGE("%s: Invalid hwcOverlayStatus (status =%d)", __FUNCTION__, |
| ctx->hwcOverlayStatus); |
| break; |
| } |
| } |
| |
| static int hwc_closeOverlayChannels(hwc_context_t* ctx) { |
| #ifdef USE_OVERLAY |
| overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; |
| if(!ovLibObject) { |
| LOGE("%s: invalid params", __FUNCTION__); |
| return -1; |
| } |
| |
| if (HWC_OVERLAY_PREPARE_TO_CLOSE == ctx->hwcOverlayStatus) { |
| // Video mirroring is going on, and we do not have any layers to |
| // mirror directly. Close the current video channel and inform the |
| // gralloc to start UI mirroring |
| ovLibObject->closeChannel(); |
| // Inform the gralloc that video overlay has stopped. |
| setVideoOverlayStatusInGralloc(ctx, false); |
| } |
| #endif |
| return 0; |
| } |
| |
| /* |
| * Configures mdp pipes |
| */ |
| static int prepareOverlay(hwc_context_t *ctx, hwc_layer_t *layer, const int flags) { |
| int ret = 0; |
| |
| #ifdef COMPOSITION_BYPASS |
| if(ctx && (ctx->bypassState != BYPASS_OFF)) { |
| ctx->nPipesUsed = 0; |
| closeExtraPipes(ctx); |
| ctx->bypassState = BYPASS_OFF; |
| } |
| #endif |
| |
| if (LIKELY(ctx && ctx->mOverlayLibObject)) { |
| private_hwc_module_t* hwcModule = |
| reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module); |
| if (UNLIKELY(!hwcModule)) { |
| LOGE("prepareOverlay null module "); |
| return -1; |
| } |
| |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; |
| overlay_buffer_info info; |
| info.width = hnd->width; |
| info.height = hnd->height; |
| info.format = hnd->format; |
| info.size = hnd->size; |
| |
| int hdmiConnected = 0; |
| |
| #if defined HDMI_DUAL_DISPLAY |
| if(!ctx->pendingHDMI) //makes sure the UI channel is opened first |
| hdmiConnected = (int)ctx->mHDMIEnabled; |
| #endif |
| ret = ovLibObject->setSource(info, layer->transform, |
| hdmiConnected, flags); |
| if (!ret) { |
| LOGE("prepareOverlay setSource failed"); |
| return -1; |
| } |
| |
| ret = ovLibObject->setTransform(layer->transform); |
| if (!ret) { |
| LOGE("prepareOverlay setTransform failed transform %x", |
| layer->transform); |
| return -1; |
| } |
| |
| hwc_rect_t sourceCrop = layer->sourceCrop; |
| ret = ovLibObject->setCrop(sourceCrop.left, sourceCrop.top, |
| (sourceCrop.right - sourceCrop.left), |
| (sourceCrop.bottom - sourceCrop.top)); |
| if (!ret) { |
| LOGE("prepareOverlay setCrop failed"); |
| return -1; |
| } |
| #if defined HDMI_DUAL_DISPLAY |
| // Send the device orientation to overlayLib |
| if(hwcModule) { |
| framebuffer_device_t *fbDev = reinterpret_cast<framebuffer_device_t*> |
| (hwcModule->fbDevice); |
| if(fbDev) { |
| private_module_t* m = reinterpret_cast<private_module_t*>( |
| fbDev->common.module); |
| if(m) |
| ovLibObject->setDeviceOrientation(m->orientation); |
| } |
| } |
| #endif |
| if (layer->flags & HWC_USE_ORIGINAL_RESOLUTION) { |
| framebuffer_device_t* fbDev = hwcModule->fbDevice; |
| ret = ovLibObject->setPosition(0, 0, |
| fbDev->width, fbDev->height); |
| } else { |
| hwc_rect_t displayFrame = layer->displayFrame; |
| ret = ovLibObject->setPosition(displayFrame.left, displayFrame.top, |
| (displayFrame.right - displayFrame.left), |
| (displayFrame.bottom - displayFrame.top)); |
| } |
| if (!ret) { |
| LOGE("prepareOverlay setPosition failed"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| void unlockPreviousOverlayBuffer(hwc_context_t* ctx) |
| { |
| if (ctx->previousOverlayHandle) { |
| // Validate the handle before attempting to use it. |
| if (!private_handle_t::validate(ctx->previousOverlayHandle)) { |
| private_handle_t *hnd = (private_handle_t*)ctx->previousOverlayHandle; |
| // Unlock any previously locked buffers |
| if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) { |
| if (GENLOCK_NO_ERROR == genlock_unlock_buffer(ctx->previousOverlayHandle)) { |
| ctx->previousOverlayHandle = NULL; |
| hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| } else { |
| LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); |
| } |
| } |
| } |
| } |
| } |
| |
| bool canSkipComposition(hwc_context_t* ctx, int yuvBufferCount, int currentLayerCount, |
| int numLayersNotUpdating) |
| { |
| if (!ctx) { |
| LOGE("%s: invalid context",__FUNCTION__); |
| return false; |
| } |
| |
| hwc_composer_device_t* dev = (hwc_composer_device_t *)(ctx); |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| if (hwcModule->compositionType == COMPOSITION_TYPE_CPU) |
| return false; |
| |
| //Video / Camera case |
| if (yuvBufferCount == 1) { |
| //If the previousLayerCount is anything other than the current count, it |
| //means something changed and we need to compose atleast once to FB. |
| if (currentLayerCount != ctx->previousLayerCount) { |
| ctx->previousLayerCount = currentLayerCount; |
| return false; |
| } |
| // We either have only one overlay layer or we have |
| // all non-updating UI layers. |
| // We can skip the composition of the UI layers. |
| if ((currentLayerCount == 1) || |
| ((currentLayerCount - 1) == numLayersNotUpdating)) { |
| return true; |
| } |
| } else { |
| ctx->previousLayerCount = -1; |
| } |
| return false; |
| } |
| |
| inline void getLayerResolution(const hwc_layer_t* layer, int& width, int& height) |
| { |
| hwc_rect_t displayFrame = layer->displayFrame; |
| |
| width = displayFrame.right - displayFrame.left; |
| height = displayFrame.bottom - displayFrame.top; |
| } |
| |
| static bool canUseCopybit(const framebuffer_device_t* fbDev, const hwc_layer_list_t* list) { |
| |
| if(!fbDev) { |
| LOGE("ERROR: %s : fb device is invalid",__func__); |
| return false; |
| } |
| |
| if (!list) |
| return false; |
| |
| int fb_w = fbDev->width; |
| int fb_h = fbDev->height; |
| |
| /* |
| * Use copybit only when we need to blit |
| * max 2 full screen sized regions |
| */ |
| |
| unsigned int renderArea = 0; |
| |
| for(int i = 0; i < list->numHwLayers; i++ ) { |
| int w, h; |
| getLayerResolution(&list->hwLayers[i], w, h); |
| renderArea += w*h; |
| } |
| |
| return (renderArea <= (2 * fb_w * fb_h)); |
| } |
| |
| static void handleHDMIStateChange(hwc_composer_device_t *dev, int externaltype) { |
| #if defined HDMI_DUAL_DISPLAY |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| //Route the event to fbdev only if we are in default mirror mode |
| if(ExtDispOnly::isModeOn() == false) { |
| framebuffer_device_t *fbDev = hwcModule->fbDevice; |
| if (fbDev) { |
| fbDev->enableHDMIOutput(fbDev, externaltype); |
| } |
| |
| if(ctx && ctx->mOverlayLibObject) { |
| overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; |
| if (!externaltype) { |
| // Close the external overlay channels if HDMI is disconnected |
| ovLibObject->closeExternalChannel(); |
| } |
| } |
| } |
| #endif |
| } |
| |
| /* |
| * function to set the status of external display in hwc |
| * Just mark flags and do stuff after eglSwapBuffers |
| * externaltype - can be HDMI, WIFI or OFF |
| */ |
| static void hwc_enableHDMIOutput(hwc_composer_device_t *dev, int externaltype) { |
| #if defined HDMI_DUAL_DISPLAY |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| framebuffer_device_t *fbDev = hwcModule->fbDevice; |
| overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; |
| if(externaltype && ctx->mHDMIEnabled && |
| (externaltype != ctx->mHDMIEnabled)) { |
| // Close the current external display - as the SF will |
| // prioritize and send the correct external display HDMI/WFD |
| handleHDMIStateChange(dev, 0); |
| } |
| // Store the external display |
| ctx->mHDMIEnabled = (external_display)externaltype; |
| if(ctx->mHDMIEnabled) { //On connect, allow bypass to draw once to FB |
| ctx->pendingHDMI = true; |
| } else { //On disconnect, close immediately (there will be no bypass) |
| handleHDMIStateChange(dev, ctx->mHDMIEnabled); |
| } |
| #endif |
| } |
| |
| static bool isValidDestination(const framebuffer_device_t* fbDev, const hwc_rect_t& rect) |
| { |
| if (!fbDev) { |
| LOGE("%s: fbDev is null", __FUNCTION__); |
| return false; |
| } |
| |
| int dest_width = (rect.right - rect.left); |
| int dest_height = (rect.bottom - rect.top); |
| |
| if (rect.left < 0 || rect.right < 0 || rect.top < 0 || rect.bottom < 0 |
| || dest_width <= 0 || dest_height <= 0) { |
| LOGE("%s: destination: left=%d right=%d top=%d bottom=%d width=%d" |
| "height=%d", __FUNCTION__, rect.left, rect.right, rect.top, |
| rect.bottom, dest_width, dest_height); |
| return false; |
| } |
| |
| if ((rect.left+dest_width) > fbDev->width || (rect.top+dest_height) > fbDev->height) { |
| LOGE("%s: destination out of bound params", __FUNCTION__); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int getYUVBufferCount (const hwc_layer_list_t* list) { |
| int yuvBufferCount = 0; |
| if (list) { |
| for (size_t i=0 ; i<list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && |
| !(list->hwLayers[i].flags & HWC_DO_NOT_USE_OVERLAY)) { |
| yuvBufferCount++; |
| if (yuvBufferCount > 1) { |
| break; |
| } |
| } |
| } |
| } |
| return yuvBufferCount; |
| } |
| |
| static int getS3DVideoFormat (const hwc_layer_list_t* list) { |
| int s3dFormat = 0; |
| if (list) { |
| for (size_t i=0; i<list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO)) |
| s3dFormat = FORMAT_3D_INPUT(hnd->format); |
| if (s3dFormat) |
| break; |
| } |
| } |
| return s3dFormat; |
| } |
| |
| static int getS3DFormat (const hwc_layer_list_t* list) { |
| int s3dFormat = 0; |
| if (list) { |
| for (size_t i=0; i<list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if (hnd) |
| s3dFormat = FORMAT_3D_INPUT(hnd->format); |
| if (s3dFormat) |
| break; |
| } |
| } |
| return s3dFormat; |
| } |
| |
| |
| static int getLayerS3DFormat (hwc_layer_t &layer) { |
| int s3dFormat = 0; |
| private_handle_t *hnd = (private_handle_t *)layer.handle; |
| if (hnd) |
| s3dFormat = FORMAT_3D_INPUT(hnd->format); |
| return s3dFormat; |
| } |
| static bool isS3DCompositionRequired() { |
| #ifdef HDMI_AS_PRIMARY |
| return overlay::is3DTV(); |
| #endif |
| return false; |
| } |
| |
| static void markUILayerForS3DComposition (hwc_layer_t &layer, int s3dVideoFormat) { |
| #ifdef HDMI_AS_PRIMARY |
| layer.compositionType = HWC_FRAMEBUFFER; |
| switch(s3dVideoFormat) { |
| case HAL_3D_IN_SIDE_BY_SIDE_L_R: |
| case HAL_3D_IN_SIDE_BY_SIDE_R_L: |
| layer.hints |= HWC_HINT_DRAW_S3D_SIDE_BY_SIDE; |
| break; |
| case HAL_3D_IN_TOP_BOTTOM: |
| layer.hints |= HWC_HINT_DRAW_S3D_TOP_BOTTOM; |
| break; |
| default: |
| LOGE("%s: Unknown S3D input format 0x%x", __FUNCTION__, s3dVideoFormat); |
| break; |
| } |
| #endif |
| return; |
| } |
| |
| static int getLayersNotUpdatingCount(const hwc_layer_list_t* list) { |
| int numLayersNotUpdating = 0; |
| if (list) { |
| for (size_t i=0 ; i<list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if (hnd && (hnd->bufferType != BUFFER_TYPE_VIDEO) && |
| list->hwLayers[i].flags & HWC_LAYER_NOT_UPDATING) |
| numLayersNotUpdating++; |
| } |
| } |
| return numLayersNotUpdating; |
| } |
| |
| static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { |
| |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| |
| if(!ctx) { |
| LOGE("hwc_prepare invalid context"); |
| return -1; |
| } |
| |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| if (!hwcModule) { |
| LOGE("hwc_prepare invalid module"); |
| #ifdef COMPOSITION_BYPASS |
| unlockPreviousBypassBuffers(ctx); |
| unsetBypassBufferLockState(ctx); |
| #endif |
| unlockPreviousOverlayBuffer(ctx); |
| ExtDispOnly::close(); |
| return -1; |
| } |
| |
| int yuvBufferCount = 0; |
| int layerType = 0; |
| bool isS3DCompositionNeeded = false; |
| int s3dVideoFormat = 0; |
| int numLayersNotUpdating = 0; |
| bool useCopybit = false; |
| bool isSkipLayerPresent = false; |
| bool skipComposition = false; |
| |
| if (list) { |
| useCopybit = canUseCopybit(hwcModule->fbDevice, list); |
| yuvBufferCount = getYUVBufferCount(list); |
| numLayersNotUpdating = getLayersNotUpdatingCount(list); |
| skipComposition = canSkipComposition(ctx, yuvBufferCount, |
| list->numHwLayers, numLayersNotUpdating); |
| |
| if (yuvBufferCount == 1) { |
| s3dVideoFormat = getS3DVideoFormat(list); |
| if (s3dVideoFormat) |
| isS3DCompositionNeeded = isS3DCompositionRequired(); |
| } else if((s3dVideoFormat = getS3DFormat(list))){ |
| if (s3dVideoFormat) |
| isS3DCompositionNeeded = isS3DCompositionRequired(); |
| } else { |
| unlockPreviousOverlayBuffer(ctx); |
| } |
| |
| if (list->flags & HWC_GEOMETRY_CHANGED) { |
| if (yuvBufferCount == 1) { |
| // Inform the gralloc of the current video overlay status |
| setVideoOverlayStatusInGralloc(ctx, true); |
| } |
| } |
| |
| for (size_t i=0 ; i<list->numHwLayers ; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| |
| // If there is a single Fullscreen layer, we can bypass it - TBD |
| // If there is only one video/camera buffer, we can bypass itn |
| if (list->hwLayers[i].flags & HWC_SKIP_LAYER) { |
| // During the animaton UI layers are marked as SKIP |
| // need to still mark the layer for S3D composition |
| isSkipLayerPresent = true; |
| skipComposition = false; |
| //Reset count, so that we end up composing once after animation |
| //is over, in case of overlay. |
| ctx->previousLayerCount = -1; |
| |
| if (isS3DCompositionNeeded) |
| markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat); |
| |
| // LGE_CHANGE_E, [G1_Player][bokyung.kim@lge.com], 20120201, Apply SR 00744210 to fix screen flicker { |
| ssize_t layer_countdown = ((ssize_t)i) - 1; |
| // Mark every layer below the SKIP layer to be composed by the GPU |
| while (layer_countdown >= 0) |
| { |
| private_handle_t *countdown_handle = |
| (private_handle_t *)list->hwLayers[layer_countdown].handle; |
| if (countdown_handle && (countdown_handle->bufferType == BUFFER_TYPE_VIDEO) |
| && (yuvBufferCount == 1)) { |
| unlockPreviousOverlayBuffer(ctx); |
| } |
| list->hwLayers[layer_countdown].compositionType = HWC_FRAMEBUFFER; |
| list->hwLayers[layer_countdown].hints &= ~HWC_HINT_CLEAR_FB; |
| layer_countdown--; |
| } |
| // LGE_CHANGE_E, [G1_Player][bokyung.kim@lge.com], 20120201, Apply SR 00744210 to fix screen flicker } |
| continue; |
| } |
| if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && (yuvBufferCount == 1)) { |
| int flags = WAIT_FOR_VSYNC; |
| flags |= (hnd->flags & |
| private_handle_t::PRIV_FLAGS_SECURE_BUFFER)? |
| SECURE_OVERLAY_SESSION : 0; |
| flags |= (1 == list->numHwLayers) ? DISABLE_FRAMEBUFFER_FETCH : 0; |
| if (!isValidDestination(hwcModule->fbDevice, list->hwLayers[i].displayFrame)) { |
| list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; |
| //Even though there are no skip layers, animation is still |
| //ON and in its final stages. |
| //Reset count, so that we end up composing once after animation |
| //is done, if overlay is used. |
| ctx->previousLayerCount = -1; |
| skipComposition = false; |
| #ifdef USE_OVERLAY |
| } else if(prepareOverlay(ctx, &(list->hwLayers[i]), flags) == 0) { |
| list->hwLayers[i].compositionType = HWC_USE_OVERLAY; |
| list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; |
| // We've opened the channel. Set the state to open. |
| ctx->hwcOverlayStatus = HWC_OVERLAY_OPEN; |
| #endif |
| } else if (hwcModule->compositionType & (COMPOSITION_TYPE_C2D| |
| COMPOSITION_TYPE_MDP)) { |
| //Fail safe path: If drawing with overlay fails, |
| |
| //Use C2D if available. |
| list->hwLayers[i].compositionType = HWC_USE_COPYBIT; |
| } else { |
| //If C2D is not enabled fall back to GPU. |
| list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; |
| } |
| if (HWC_USE_OVERLAY != list->hwLayers[i].compositionType) { |
| unlockPreviousOverlayBuffer(ctx); |
| skipComposition = false; |
| } |
| } else if (getLayerS3DFormat(list->hwLayers[i])) { |
| int flags = WAIT_FOR_VSYNC; |
| flags |= (1 == list->numHwLayers) ? DISABLE_FRAMEBUFFER_FETCH : 0; |
| flags |= (hnd->flags & |
| private_handle_t::PRIV_FLAGS_SECURE_BUFFER)? |
| SECURE_OVERLAY_SESSION : 0; |
| #ifdef USE_OVERLAY |
| if(prepareOverlay(ctx, &(list->hwLayers[i]), flags) == 0) { |
| list->hwLayers[i].compositionType = HWC_USE_OVERLAY; |
| list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; |
| // We've opened the channel. Set the state to open. |
| ctx->hwcOverlayStatus = HWC_OVERLAY_OPEN; |
| } |
| #endif |
| } else if (isS3DCompositionNeeded) { |
| markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat); |
| } else if (list->hwLayers[i].flags & HWC_USE_ORIGINAL_RESOLUTION) { |
| list->hwLayers[i].compositionType = HWC_USE_OVERLAY; |
| list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; |
| layerType |= HWC_ORIG_RESOLUTION; |
| } else if (hnd && hnd->flags & private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY) { |
| //handle later after other layers are handled |
| } else if (hnd && (hwcModule->compositionType & |
| (COMPOSITION_TYPE_C2D|COMPOSITION_TYPE_MDP))) { |
| list->hwLayers[i].compositionType = HWC_USE_COPYBIT; |
| } else if ((hwcModule->compositionType == COMPOSITION_TYPE_DYN) |
| && useCopybit) { |
| list->hwLayers[i].compositionType = HWC_USE_COPYBIT; |
| } |
| else { |
| list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; |
| } |
| } |
| |
| //Update the stats and pipe config for external-only layers |
| ExtDispOnly::update(ctx, list); |
| |
| if (skipComposition) { |
| list->flags |= HWC_SKIP_COMPOSITION; |
| } else { |
| list->flags &= ~HWC_SKIP_COMPOSITION; |
| } |
| |
| #ifdef COMPOSITION_BYPASS |
| bool isBypassUsed = true; |
| bool isDoable = isBypassDoable(dev, yuvBufferCount, list); |
| //Check if bypass is feasible |
| if(isDoable && !isSkipLayerPresent) { |
| if(setupBypass(ctx, list)) { |
| setBypassLayerFlags(ctx, list); |
| ctx->bypassState = BYPASS_ON; |
| } else { |
| LOGE_IF(BYPASS_DEBUG,"%s: Bypass setup Failed",__FUNCTION__); |
| isBypassUsed = false; |
| } |
| } else { |
| LOGE_IF(BYPASS_DEBUG,"%s: Bypass not possible[%d,%d]",__FUNCTION__, |
| isDoable, !isSkipLayerPresent ); |
| isBypassUsed = false; |
| } |
| |
| //Reset bypass states |
| if(!isBypassUsed) { |
| ctx->nPipesUsed = 0; |
| unsetBypassLayerFlags(list); |
| if(ctx->bypassState == BYPASS_ON) { |
| ctx->bypassState = BYPASS_OFF_PENDING; |
| } |
| } |
| #endif |
| } else { |
| #ifdef COMPOSITION_BYPASS |
| unlockPreviousBypassBuffers(ctx); |
| unsetBypassBufferLockState(ctx); |
| #endif |
| unlockPreviousOverlayBuffer(ctx); |
| } |
| return 0; |
| } |
| // --------------------------------------------------------------------------- |
| struct range { |
| int current; |
| int end; |
| }; |
| struct region_iterator : public copybit_region_t { |
| |
| region_iterator(hwc_region_t region) { |
| mRegion = region; |
| r.end = region.numRects; |
| r.current = 0; |
| this->next = iterate; |
| } |
| |
| private: |
| static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { |
| if (!self || !rect) { |
| LOGE("iterate invalid parameters"); |
| return 0; |
| } |
| |
| region_iterator const* me = static_cast<region_iterator const*>(self); |
| if (me->r.current != me->r.end) { |
| rect->l = me->mRegion.rects[me->r.current].left; |
| rect->t = me->mRegion.rects[me->r.current].top; |
| rect->r = me->mRegion.rects[me->r.current].right; |
| rect->b = me->mRegion.rects[me->r.current].bottom; |
| me->r.current++; |
| return 1; |
| } |
| return 0; |
| } |
| |
| hwc_region_t mRegion; |
| mutable range r; |
| }; |
| |
| static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, EGLDisplay dpy, |
| EGLSurface surface) |
| { |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| if(!ctx) { |
| LOGE("drawLayerUsingCopybit null context "); |
| return -1; |
| } |
| |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(dev->common.module); |
| if(!hwcModule) { |
| LOGE("drawLayerUsingCopybit null module "); |
| return -1; |
| } |
| |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| if(!hnd) { |
| LOGE("drawLayerUsingCopybit invalid handle"); |
| return -1; |
| } |
| |
| // Lock this buffer for read. |
| genlock_lock_type lockType = GENLOCK_READ_LOCK; |
| int err = genlock_lock_buffer(hnd, lockType, GENLOCK_MAX_TIMEOUT); |
| if (GENLOCK_FAILURE == err) { |
| LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); |
| return -1; |
| } |
| //render buffer |
| android_native_buffer_t *renderBuffer = (android_native_buffer_t *)eglGetRenderBufferANDROID(dpy, surface); |
| if (!renderBuffer) { |
| LOGE("eglGetRenderBufferANDROID returned NULL buffer"); |
| genlock_unlock_buffer(hnd); |
| return -1; |
| } |
| private_handle_t *fbHandle = (private_handle_t *)renderBuffer->handle; |
| if(!fbHandle) { |
| LOGE("Framebuffer handle is NULL"); |
| genlock_unlock_buffer(hnd); |
| return -1; |
| } |
| int alignment = 32; |
| if( HAL_PIXEL_FORMAT_RGB_565 == fbHandle->format ) |
| alignment = 16; |
| // Set the copybit source: |
| copybit_image_t src; |
| src.w = ALIGN(hnd->width, alignment); |
| src.h = hnd->height; |
| src.format = hnd->format; |
| src.base = (void *)hnd->base; |
| src.handle = (native_handle_t *)layer->handle; |
| src.horiz_padding = src.w - hnd->width; |
| // Initialize vertical padding to zero for now, |
| // this needs to change to accomodate vertical stride |
| // if needed in the future |
| src.vert_padding = 0; |
| |
| // Copybit source rect |
| hwc_rect_t sourceCrop = layer->sourceCrop; |
| copybit_rect_t srcRect = {sourceCrop.left, sourceCrop.top, |
| sourceCrop.right, |
| sourceCrop.bottom}; |
| |
| // Copybit destination rect |
| hwc_rect_t displayFrame = layer->displayFrame; |
| copybit_rect_t dstRect = {displayFrame.left, displayFrame.top, |
| displayFrame.right, |
| displayFrame.bottom}; |
| |
| // Copybit dst |
| copybit_image_t dst; |
| dst.w = ALIGN(fbHandle->width,alignment); |
| dst.h = fbHandle->height; |
| dst.format = fbHandle->format; |
| dst.base = (void *)fbHandle->base; |
| dst.handle = (native_handle_t *)renderBuffer->handle; |
| |
| copybit_device_t *copybit = hwcModule->copybitEngine; |
| |
| int32_t screen_w = displayFrame.right - displayFrame.left; |
| int32_t screen_h = displayFrame.bottom - displayFrame.top; |
| int32_t src_crop_width = sourceCrop.right - sourceCrop.left; |
| int32_t src_crop_height = sourceCrop.bottom -sourceCrop.top; |
| |
| float copybitsMaxScale = (float)copybit->get(copybit,COPYBIT_MAGNIFICATION_LIMIT); |
| float copybitsMinScale = (float)copybit->get(copybit,COPYBIT_MINIFICATION_LIMIT); |
| |
| if((layer->transform == HWC_TRANSFORM_ROT_90) || |
| (layer->transform == HWC_TRANSFORM_ROT_270)) { |
| //swap screen width and height |
| int tmp = screen_w; |
| screen_w = screen_h; |
| screen_h = tmp; |
| } |
| private_handle_t *tmpHnd = NULL; |
| |
| if(screen_w <=0 || screen_h<=0 ||src_crop_width<=0 || src_crop_height<=0 ) { |
| LOGE("%s: wrong params for display screen_w=%d src_crop_width=%d screen_w=%d \ |
| src_crop_width=%d", __FUNCTION__, screen_w, |
| src_crop_width,screen_w,src_crop_width); |
| genlock_unlock_buffer(hnd); |
| return -1; |
| } |
| |
| float dsdx = (float)screen_w/src_crop_width; |
| float dtdy = (float)screen_h/src_crop_height; |
| |
| float scaleLimitMax = copybitsMaxScale * copybitsMaxScale; |
| float scaleLimitMin = copybitsMinScale * copybitsMinScale; |
| if(dsdx > scaleLimitMax || dtdy > scaleLimitMax || dsdx < 1/scaleLimitMin || dtdy < 1/scaleLimitMin) { |
| LOGE("%s: greater than max supported size dsdx=%f dtdy=%f scaleLimitMax=%f scaleLimitMin=%f", __FUNCTION__,dsdx,dtdy,scaleLimitMax,1/scaleLimitMin); |
| genlock_unlock_buffer(hnd); |
| return -1; |
| } |
| if(dsdx > copybitsMaxScale || dtdy > copybitsMaxScale || dsdx < 1/copybitsMinScale || dtdy < 1/copybitsMinScale){ |
| // The requested scale is out of the range the hardware |
| // can support. |
| LOGD("%s:%d::Need to scale twice dsdx=%f, dtdy=%f,copybitsMaxScale=%f,copybitsMinScale=%f,screen_w=%d,screen_h=%d \ |
| src_crop_width=%d src_crop_height=%d",__FUNCTION__,__LINE__, |
| dsdx,dtdy,copybitsMaxScale,1/copybitsMinScale,screen_w,screen_h,src_crop_width,src_crop_height); |
| |
| //Driver makes width and height as even |
| //that may cause wrong calculation of the ratio |
| //in display and crop.Hence we make |
| //crop width and height as even. |
| src_crop_width = (src_crop_width/2)*2; |
| src_crop_height = (src_crop_height/2)*2; |
| |
| int tmp_w = src_crop_width; |
| int tmp_h = src_crop_height; |
| |
| if (dsdx > copybitsMaxScale || dtdy > copybitsMaxScale ){ |
| tmp_w = src_crop_width*copybitsMaxScale; |
| tmp_h = src_crop_height*copybitsMaxScale; |
| }else if (dsdx < 1/copybitsMinScale ||dtdy < 1/copybitsMinScale ){ |
| tmp_w = src_crop_width/copybitsMinScale; |
| tmp_h = src_crop_height/copybitsMinScale; |
| tmp_w = (tmp_w/2)*2; |
| tmp_h = (tmp_h/2)*2; |
| } |
| LOGD("%s:%d::tmp_w = %d,tmp_h = %d",__FUNCTION__,__LINE__,tmp_w,tmp_h); |
| |
| int usage = GRALLOC_USAGE_PRIVATE_ADSP_HEAP | |
| GRALLOC_USAGE_PRIVATE_MM_HEAP; |
| |
| if (0 == alloc_buffer(&tmpHnd, tmp_w, tmp_h, fbHandle->format, usage)){ |
| copybit_image_t tmp_dst; |
| copybit_rect_t tmp_rect; |
| tmp_dst.w = tmp_w; |
| tmp_dst.h = tmp_h; |
| tmp_dst.format = tmpHnd->format; |
| tmp_dst.handle = tmpHnd; |
| tmp_dst.horiz_padding = src.horiz_padding; |
| tmp_dst.vert_padding = src.vert_padding; |
| tmp_rect.l = 0; |
| tmp_rect.t = 0; |
| tmp_rect.r = tmp_dst.w; |
| tmp_rect.b = tmp_dst.h; |
| //create one clip region |
| hwc_rect tmp_hwc_rect = {0,0,tmp_rect.r,tmp_rect.b}; |
| hwc_region_t tmp_hwc_reg = {1,(hwc_rect_t const*)&tmp_hwc_rect}; |
| region_iterator tmp_it(tmp_hwc_reg); |
| copybit->set_parameter(copybit,COPYBIT_TRANSFORM,0); |
| copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, |
| (layer->blending == HWC_BLENDING_NONE) ? -1 : layer->alpha); |
| err = copybit->stretch(copybit,&tmp_dst, &src, &tmp_rect, &srcRect, &tmp_it); |
| if(err < 0){ |
| LOGE("%s:%d::tmp copybit stretch failed",__FUNCTION__,__LINE__); |
| if(tmpHnd) |
| free_buffer(tmpHnd); |
| genlock_unlock_buffer(hnd); |
| return err; |
| } |
| // copy new src and src rect crop |
| src = tmp_dst; |
| srcRect = tmp_rect; |
| } |
| } |
| // Copybit region |
| hwc_region_t region = layer->visibleRegionScreen; |
| region_iterator copybitRegion(region); |
| |
| copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_WIDTH, renderBuffer->width); |
| copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_HEIGHT, renderBuffer->height); |
| copybit->set_parameter(copybit, COPYBIT_TRANSFORM, layer->transform); |
| copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, |
| (layer->blending == HWC_BLENDING_NONE) ? -1 : layer->alpha); |
| copybit->set_parameter(copybit, COPYBIT_PREMULTIPLIED_ALPHA, |
| (layer->blending == HWC_BLENDING_PREMULT)? COPYBIT_ENABLE : COPYBIT_DISABLE); |
| copybit->set_parameter(copybit, COPYBIT_DITHER, |
| (dst.format == HAL_PIXEL_FORMAT_RGB_565)? COPYBIT_ENABLE : COPYBIT_DISABLE); |
| err = copybit->stretch(copybit, &dst, &src, &dstRect, &srcRect, ©bitRegion); |
| |
| if(tmpHnd) |
| free_buffer(tmpHnd); |
| |
| if(err < 0) |
| LOGE("%s: copybit stretch failed",__FUNCTION__); |
| |
| // Unlock this buffer since copybit is done with it. |
| err = genlock_unlock_buffer(hnd); |
| if (GENLOCK_FAILURE == err) { |
| LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); |
| } |
| |
| return err; |
| } |
| |
| static int drawLayerUsingOverlay(hwc_context_t *ctx, hwc_layer_t *layer) |
| { |
| if (ctx && ctx->mOverlayLibObject) { |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module); |
| if (!hwcModule) { |
| LOGE("drawLayerUsingLayer null module "); |
| return -1; |
| } |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; |
| int ret = 0; |
| |
| // Lock this buffer for read. |
| if (GENLOCK_NO_ERROR != genlock_lock_buffer(hnd, GENLOCK_READ_LOCK, |
| GENLOCK_MAX_TIMEOUT)) { |
| LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); |
| return -1; |
| } |
| |
| ret = ovLibObject->queueBuffer(hnd); |
| |
| // Unlock the previously locked buffer, since the overlay has completed reading the buffer |
| unlockPreviousOverlayBuffer(ctx); |
| |
| if (!ret) { |
| LOGE("drawLayerUsingOverlay queueBuffer failed"); |
| // Unlock the buffer handle |
| genlock_unlock_buffer(hnd); |
| ctx->previousOverlayHandle = NULL; |
| } else { |
| // Store the current buffer handle as the one that is to be unlocked after |
| // the next overlay play call. |
| ctx->previousOverlayHandle = hnd; |
| hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| } |
| |
| return ret; |
| } |
| return -1; |
| } |
| |
| #ifdef COMPOSITION_BYPASS |
| static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer, int layer_index) { |
| |
| int index = getLayerbypassIndex(layer); |
| |
| if(index < 0) { |
| LOGE("%s: Invalid bypass index (%d)", __FUNCTION__, index); |
| return -1; |
| } |
| |
| if (ctx && ctx->mOvUI[index]) { |
| overlay::OverlayUI *ovUI = ctx->mOvUI[index]; |
| int ret = 0; |
| |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| if(!hnd) { |
| LOGE("%s handle null", __FUNCTION__); |
| return -1; |
| } |
| |
| ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED; |
| |
| if (GENLOCK_FAILURE == genlock_lock_buffer(hnd, GENLOCK_READ_LOCK, |
| GENLOCK_MAX_TIMEOUT)) { |
| LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); |
| return -1; |
| } |
| |
| ctx->bypassBufferLockState[index] = BYPASS_BUFFER_LOCKED; |
| |
| LOGE_IF(BYPASS_DEBUG,"%s: Bypassing layer: %p using pipe: %d",__FUNCTION__, layer, index ); |
| |
| ret = ovUI->queueBuffer(hnd); |
| |
| if (ret) { |
| // Unlock the locked buffer |
| if (GENLOCK_FAILURE == genlock_unlock_buffer(hnd)) { |
| LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); |
| } |
| ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED; |
| return -1; |
| } |
| } |
| return 0; |
| } |
| #endif |
| |
| static int hwc_set(hwc_composer_device_t *dev, |
| hwc_display_t dpy, |
| hwc_surface_t sur, |
| hwc_layer_list_t* list) |
| { |
| hwc_context_t* ctx = (hwc_context_t*)(dev); |
| if(!ctx) { |
| LOGE("hwc_set invalid context"); |
| ExtDispOnly::close(); |
| return -1; |
| } |
| |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| dev->common.module); |
| if (!hwcModule) { |
| LOGE("hwc_set invalid module"); |
| #ifdef COMPOSITION_BYPASS |
| unlockPreviousBypassBuffers(ctx); |
| unsetBypassBufferLockState(ctx); |
| #endif |
| ExtDispOnly::close(); |
| unlockPreviousOverlayBuffer(ctx); |
| return -1; |
| } |
| |
| int ret = 0; |
| if (list) { |
| bool bDumpLayers = needToDumpLayers(); // Check need for debugging dumps |
| for (size_t i=0; i<list->numHwLayers; i++) { |
| if (bDumpLayers) |
| dumpLayer(hwcModule->compositionType, list->flags, i, list->hwLayers); |
| if (list->hwLayers[i].flags & HWC_SKIP_LAYER) { |
| continue; |
| } else if(list->hwLayers[i].flags & HWC_USE_EXT_ONLY) { |
| continue; |
| //Draw after layers for primary are drawn |
| #ifdef COMPOSITION_BYPASS |
| } else if (list->hwLayers[i].flags & HWC_COMP_BYPASS) { |
| drawLayerUsingBypass(ctx, &(list->hwLayers[i]), i); |
| #endif |
| } else if (list->hwLayers[i].compositionType == HWC_USE_OVERLAY) { |
| drawLayerUsingOverlay(ctx, &(list->hwLayers[i])); |
| } else if (list->flags & HWC_SKIP_COMPOSITION) { |
| // LGE_CHANGE_S, [G1_Player][mukyung.jung@lge.com], 20120206, Apply SR 00718706 to fix noise of QCIF contents { |
| //break; |
| continue; |
| // LGE_CHANGE_E, [G1_Player][mukyung.jung@lge.com], 20120206, Apply SR 00718706 to fix noise of QCIF contents } |
| } else if (list->hwLayers[i].compositionType == HWC_USE_COPYBIT) { |
| drawLayerUsingCopybit(dev, &(list->hwLayers[i]), (EGLDisplay)dpy, (EGLSurface)sur); |
| } |
| } |
| } else { |
| //Device in suspended state. Close all the MDP pipes |
| #ifdef COMPOSITION_BYPASS |
| ctx->nPipesUsed = 0; |
| #endif |
| ctx->hwcOverlayStatus = HWC_OVERLAY_PREPARE_TO_CLOSE; |
| } |
| |
| bool canSkipComposition = list && list->flags & HWC_SKIP_COMPOSITION; |
| //Draw External-only layers |
| if(ExtDispOnly::draw(ctx, list) != overlay::NO_ERROR) { |
| ExtDispOnly::close(); |
| } |
| |
| #ifdef COMPOSITION_BYPASS |
| unlockPreviousBypassBuffers(ctx); |
| storeLockedBypassHandle(list, ctx); |
| // We have stored the handles, unset the current lock states in the context. |
| unsetBypassBufferLockState(ctx); |
| closeExtraPipes(ctx); |
| #if BYPASS_DEBUG |
| if(canSkipComposition) |
| LOGE("%s: skipping eglSwapBuffer call", __FUNCTION__); |
| #endif |
| #endif |
| // Do not call eglSwapBuffers if we the skip composition flag is set on the list. |
| if (dpy && sur && !canSkipComposition) { |
| EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur); |
| if (!sucess) { |
| ret = HWC_EGL_ERROR; |
| } else { |
| CALC_FPS(); |
| } |
| } |
| #if defined HDMI_DUAL_DISPLAY |
| if(ctx->pendingHDMI) { |
| handleHDMIStateChange(dev, ctx->mHDMIEnabled); |
| ctx->pendingHDMI = false; |
| } |
| #endif |
| |
| hwc_closeOverlayChannels(ctx); |
| int yuvBufferCount = getYUVBufferCount(list); |
| setHWCOverlayStatus(ctx, yuvBufferCount); |
| |
| return ret; |
| } |
| |
| static int hwc_device_close(struct hw_device_t *dev) |
| { |
| if(!dev) { |
| LOGE("hwc_device_close null device pointer"); |
| return -1; |
| } |
| |
| struct hwc_context_t* ctx = (struct hwc_context_t*)dev; |
| |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( |
| ctx->device.common.module); |
| // Close the overlay and copybit modules |
| if(hwcModule->copybitEngine) { |
| copybit_close(hwcModule->copybitEngine); |
| hwcModule->copybitEngine = NULL; |
| } |
| if(hwcModule->fbDevice) { |
| framebuffer_close(hwcModule->fbDevice); |
| hwcModule->fbDevice = NULL; |
| } |
| |
| unlockPreviousOverlayBuffer(ctx); |
| |
| if (ctx) { |
| delete ctx->mOverlayLibObject; |
| ctx->mOverlayLibObject = NULL; |
| #ifdef COMPOSITION_BYPASS |
| for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { |
| delete ctx->mOvUI[i]; |
| } |
| unlockPreviousBypassBuffers(ctx); |
| unsetBypassBufferLockState(ctx); |
| #endif |
| ExtDispOnly::close(); |
| ExtDispOnly::destroy(); |
| |
| free(ctx); |
| } |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| static int hwc_module_initialize(struct private_hwc_module_t* hwcModule) |
| { |
| |
| // Open the overlay and copybit modules |
| hw_module_t const *module; |
| if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { |
| copybit_open(module, &(hwcModule->copybitEngine)); |
| } |
| if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { |
| framebuffer_open(module, &(hwcModule->fbDevice)); |
| } |
| |
| // get the current composition type |
| char property[PROPERTY_VALUE_MAX]; |
| if (property_get("debug.sf.hw", property, NULL) > 0) { |
| if(atoi(property) == 0) { |
| //debug.sf.hw = 0 |
| hwcModule->compositionType = COMPOSITION_TYPE_CPU; |
| } else { //debug.sf.hw = 1 |
| // Get the composition type |
| property_get("debug.composition.type", property, NULL); |
| if (property == NULL) { |
| hwcModule->compositionType = COMPOSITION_TYPE_GPU; |
| } else if ((strncmp(property, "mdp", 3)) == 0) { |
| hwcModule->compositionType = COMPOSITION_TYPE_MDP; |
| } else if ((strncmp(property, "c2d", 3)) == 0) { |
| hwcModule->compositionType = COMPOSITION_TYPE_C2D; |
| } else if ((strncmp(property, "dyn", 3)) == 0) { |
| hwcModule->compositionType = COMPOSITION_TYPE_DYN; |
| } else { |
| hwcModule->compositionType = COMPOSITION_TYPE_GPU; |
| } |
| |
| if(!hwcModule->copybitEngine) |
| hwcModule->compositionType = COMPOSITION_TYPE_GPU; |
| } |
| } else { //debug.sf.hw is not set. Use cpu composition |
| hwcModule->compositionType = COMPOSITION_TYPE_CPU; |
| } |
| |
| //Check if composition bypass is enabled |
| if(property_get("ro.sf.compbypass.enable", property, NULL) > 0) { |
| if(atoi(property) == 1) { |
| hwcModule->isBypassEnabled = true; |
| } |
| } |
| |
| CALC_INIT(); |
| |
| return 0; |
| } |
| |
| |
| static int hwc_device_open(const struct hw_module_t* module, const char* name, |
| struct hw_device_t** device) |
| { |
| int status = -EINVAL; |
| |
| if (!strcmp(name, HWC_HARDWARE_COMPOSER)) { |
| private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*> |
| (const_cast<hw_module_t*>(module)); |
| hwc_module_initialize(hwcModule); |
| struct hwc_context_t *dev; |
| dev = (hwc_context_t*)malloc(sizeof(*dev)); |
| |
| /* initialize our state here */ |
| memset(dev, 0, sizeof(*dev)); |
| #ifdef USE_OVERLAY |
| dev->mOverlayLibObject = new overlay::Overlay(); |
| if(overlay::initOverlay() == -1) |
| LOGE("overlay::initOverlay() ERROR!!"); |
| #else |
| dev->mOverlayLibObject = NULL; |
| #endif |
| #ifdef COMPOSITION_BYPASS |
| for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { |
| dev->mOvUI[i] = new overlay::OverlayUI(); |
| dev->previousBypassHandle[i] = NULL; |
| } |
| unsetBypassBufferLockState(dev); |
| dev->bypassState = BYPASS_OFF; |
| #endif |
| ExtDispOnly::init(); |
| #if defined HDMI_DUAL_DISPLAY |
| dev->mHDMIEnabled = EXT_DISPLAY_OFF; |
| dev->pendingHDMI = false; |
| #endif |
| dev->previousOverlayHandle = NULL; |
| dev->hwcOverlayStatus = HWC_OVERLAY_CLOSED; |
| dev->previousLayerCount = -1; |
| /* initialize the procs */ |
| dev->device.common.tag = HARDWARE_DEVICE_TAG; |
| dev->device.common.version = 0; |
| dev->device.common.module = const_cast<hw_module_t*>(module); |
| dev->device.common.close = hwc_device_close; |
| |
| dev->device.prepare = hwc_prepare; |
| dev->device.set = hwc_set; |
| dev->device.enableHDMIOutput = hwc_enableHDMIOutput; |
| *device = &dev->device.common; |
| |
| status = 0; |
| } |
| return status; |
| } |