| /* |
| * Copyright (c) 2012, Code Aurora Forum. All rights reserved. |
| |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of Code Aurora Forum, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #define EXTDEBUG 0 |
| class ExtDispOnly { |
| |
| enum ExternalOnlyMode { |
| EXT_ONLY_MODE_OFF = 0, |
| EXT_ONLY_MODE_ON = 1, |
| }; |
| |
| enum { |
| MAX_EXT_ONLY_LAYERS = 2, |
| }; |
| |
| public: |
| /* Initialize, allocate data members */ |
| static void init(); |
| |
| /* Deallocate data members */ |
| static void destroy(); |
| |
| /* Closes all the overlay channels */ |
| static void close(); |
| |
| /* Prepare overlay and configures mdp pipes */ |
| static int prepare(hwc_context_t *ctx, hwc_layer_t *layer, int index, |
| bool waitForVsync); |
| |
| /* Returns status of external-only mode */ |
| static bool isModeOn(); |
| |
| /* Updates stats and pipe config related to external_only and external_block layers |
| * If we are staring or stopping this mode, update default mirroring. |
| */ |
| static int update(hwc_context_t* ctx, hwc_layer_list_t* list); |
| |
| /* Stores the locked handle for the buffer that was successfully queued */ |
| static void storeLockedHandles(hwc_layer_list_t* list); |
| |
| /* Queue buffers to mdp for display */ |
| static int draw(hwc_context_t *ctx, hwc_layer_list_t *list); |
| |
| private: |
| /* Locks a buffer and marks it as locked */ |
| static void lockBuffer(native_handle_t *hnd); |
| |
| /* Unlocks a buffer and clears the locked flag */ |
| static void unlockBuffer(native_handle_t *hnd); |
| |
| /* Unlocks buffers queued in previous round (and displayed by now) |
| * Clears the handle cache. |
| */ |
| static void unlockPreviousBuffers(); |
| |
| /* Closes the a range of overlay channels */ |
| static void closeRange(int start); |
| |
| /* Start default external mirroring */ |
| static void startDefaultMirror(hwc_context_t* ctx); |
| |
| /* Stop default external mirroring */ |
| static void stopDefaultMirror(hwc_context_t* ctx); |
| |
| /* Checks if external-only mode is starting */ |
| static bool isExtModeStarting(hwc_context_t* ctx, const int& |
| numExtLayers); |
| |
| /* Checks if external-only mode is stopping */ |
| static bool isExtModeStopping(hwc_context_t* ctx, const int& |
| numExtLayers); |
| |
| //Data members |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| static overlay::OverlayUI* sOvExtUI[MAX_EXT_ONLY_LAYERS]; |
| static native_handle_t* sPreviousExtHandle[MAX_EXT_ONLY_LAYERS]; |
| static ExternalOnlyMode sExtOnlyMode; |
| static int sNumExtOnlyLayers; |
| static bool sSkipLayerPresent; |
| static bool sBlockLayerPresent; |
| static int sBlockLayerIndex; |
| #endif |
| }; //class ExtDispOnly |
| |
| void ExtDispOnly::lockBuffer(native_handle_t *hnd) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| private_handle_t* phnd = (private_handle_t*)hnd; |
| |
| //Genlock is reference counted and recursive. |
| //Do not accidently lock a locked buffer. |
| if(phnd && (phnd->flags & private_handle_t::PRIV_FLAGS_HWC_LOCK)) { |
| LOGE_IF(EXTDEBUG, "%s: handle %p already locked", __func__, phnd); |
| return; |
| } |
| if (GENLOCK_FAILURE == genlock_lock_buffer(hnd, GENLOCK_READ_LOCK, |
| GENLOCK_MAX_TIMEOUT)) { |
| LOGE("%s: genlock_lock_buffer(READ) failed", __func__); |
| return; |
| } |
| phnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| LOGE_IF(EXTDEBUG, "%s: locked handle = %p", __func__, hnd); |
| #endif |
| } |
| |
| void ExtDispOnly::unlockBuffer(native_handle_t *hnd) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| //Check if buffer is still around |
| if(private_handle_t::validate(hnd) != 0) { |
| LOGE("%s Handle already deallocated", __func__); |
| return; |
| } |
| |
| private_handle_t* phnd = (private_handle_t*)hnd; |
| |
| //Check if buffer was locked in the first place |
| if((phnd->flags & private_handle_t::PRIV_FLAGS_HWC_LOCK) == 0) { |
| LOGE("%s Handle not locked, cannot unlock", __func__); |
| return; |
| } |
| |
| //Actually try to unlock |
| if (GENLOCK_FAILURE == genlock_unlock_buffer(hnd)) { |
| LOGE("%s: genlock_unlock_buffer failed", __func__); |
| return; |
| } |
| |
| //Clear the locked flag |
| phnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; |
| LOGE_IF(EXTDEBUG, "%s: unlocked handle = %p", __func__, hnd); |
| #endif |
| } |
| |
| void ExtDispOnly::unlockPreviousBuffers() { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| for(int i = 0; (i < MAX_EXT_ONLY_LAYERS) && sPreviousExtHandle[i]; i++) { |
| LOGE_IF(EXTDEBUG, "%s", __func__); |
| ExtDispOnly::unlockBuffer(sPreviousExtHandle[i]); |
| sPreviousExtHandle[i] = NULL; |
| } |
| #endif |
| } |
| |
| void ExtDispOnly::init() { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| for(int i = 0; i < MAX_EXT_ONLY_LAYERS; i++) { |
| sOvExtUI[i] = new overlay::OverlayUI(); |
| sPreviousExtHandle[i] = NULL; |
| } |
| sExtOnlyMode = EXT_ONLY_MODE_OFF; |
| sNumExtOnlyLayers = 0; |
| sSkipLayerPresent = false; |
| sBlockLayerPresent = false; |
| sBlockLayerIndex = -1; |
| LOGE_IF(EXTDEBUG, "%s", __func__); |
| #endif |
| } |
| |
| void ExtDispOnly::destroy() { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| for(int i = 0; i < MAX_EXT_ONLY_LAYERS; i++) { |
| delete sOvExtUI[i]; |
| } |
| #endif |
| } |
| |
| void ExtDispOnly::closeRange(int start) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| for (int index = start; index < MAX_EXT_ONLY_LAYERS; index++) { |
| if(sPreviousExtHandle[index]) { |
| LOGE_IF(EXTDEBUG, "%s", __func__); |
| ExtDispOnly::unlockBuffer(sPreviousExtHandle[index]); |
| sPreviousExtHandle[index] = NULL; |
| } |
| sOvExtUI[index]->closeChannel(); |
| } |
| #endif |
| } |
| |
| void inline ExtDispOnly::close() { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| closeRange(0); |
| #endif |
| } |
| |
| int ExtDispOnly::prepare(hwc_context_t *ctx, hwc_layer_t *layer, int index, |
| bool waitForVsync) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| if(ctx->mHDMIEnabled == EXT_DISPLAY_OFF || |
| ctx->pendingHDMI == true) |
| return -1; |
| |
| if (ctx && sOvExtUI[index]) { |
| private_hwc_module_t* hwcModule = reinterpret_cast< |
| private_hwc_module_t*>(ctx->device.common.module); |
| if (!hwcModule) { |
| LOGE("%s null module", __func__); |
| return -1; |
| } |
| private_handle_t *hnd = (private_handle_t *)layer->handle; |
| if(!hnd) { |
| LOGE("%s handle null", __func__); |
| return -1; |
| } |
| overlay::OverlayUI *ovUI = sOvExtUI[index]; |
| int ret = 0; |
| //int orientation = layer->transform; |
| //Assuming layers will always be source landscape |
| const int orientation = 0; |
| overlay_buffer_info info; |
| hwc_rect_t sourceCrop = layer->sourceCrop; |
| info.width = sourceCrop.right - sourceCrop.left; |
| info.height = sourceCrop.bottom - sourceCrop.top; |
| info.format = hnd->format; |
| info.size = hnd->size; |
| |
| |
| const int fbnum = ctx->mHDMIEnabled; //HDMI or WFD |
| const bool isFg = false; |
| //Just to differentiate zorders for different layers |
| const int zorder = index; |
| const bool isVGPipe = true; |
| ovUI->setSource(info, orientation); |
| ovUI->setDisplayParams(fbnum, waitForVsync, isFg, zorder, isVGPipe); |
| const int fbWidth = ovUI->getFBWidth(); |
| const int fbHeight = ovUI->getFBHeight(); |
| ovUI->setPosition(0, 0, fbWidth, fbHeight); |
| if(ovUI->commit() != overlay::NO_ERROR) { |
| LOGE("%s: Overlay Commit failed", __func__); |
| return -1; |
| } |
| } |
| LOGE_IF(EXTDEBUG, "%s", __func__); |
| #endif |
| return overlay::NO_ERROR; |
| } |
| |
| inline void ExtDispOnly::startDefaultMirror(hwc_context_t* ctx) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| hwc_composer_device_t* dev = (hwc_composer_device_t*) ctx; |
| private_hwc_module_t* hwcModule = |
| reinterpret_cast<private_hwc_module_t*>(dev->common.module); |
| framebuffer_device_t *fbDev = hwcModule->fbDevice; |
| if (fbDev) { |
| //mHDMIEnabled could be HDMI/WFD/NO EXTERNAL |
| fbDev->enableHDMIOutput(fbDev, ctx->mHDMIEnabled); |
| } |
| #endif |
| } |
| |
| inline void ExtDispOnly::stopDefaultMirror(hwc_context_t* ctx) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| hwc_composer_device_t* dev = (hwc_composer_device_t*) ctx; |
| private_hwc_module_t* hwcModule = |
| reinterpret_cast<private_hwc_module_t*>(dev->common.module); |
| framebuffer_device_t *fbDev = hwcModule->fbDevice; |
| if (fbDev) { |
| fbDev->enableHDMIOutput(fbDev, EXT_DISPLAY_OFF); |
| } |
| #endif |
| } |
| |
| inline bool ExtDispOnly::isExtModeStarting(hwc_context_t* ctx, const int& |
| numExtLayers) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| return ((sExtOnlyMode == EXT_ONLY_MODE_OFF) && numExtLayers); |
| #endif |
| return false; |
| } |
| |
| inline bool ExtDispOnly::isExtModeStopping(hwc_context_t* ctx, const int& |
| numExtLayers) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| return ((sExtOnlyMode == EXT_ONLY_MODE_ON) && (numExtLayers == 0)); |
| #endif |
| return false; |
| } |
| |
| inline bool ExtDispOnly::isModeOn() { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| return (sExtOnlyMode == EXT_ONLY_MODE_ON); |
| #endif |
| return false; |
| } |
| |
| int ExtDispOnly::update(hwc_context_t* ctx, hwc_layer_list_t* list) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| int aNumExtLayers = 0; |
| bool aSkipLayerPresent = false; |
| bool aBlockLayerPresent = false; |
| int aBlockLayerIndex = -1; |
| |
| //Book-keeping done each cycle |
| for (size_t i = 0; i < list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| // Dont draw in this round |
| if(list->hwLayers[i].flags & HWC_SKIP_LAYER) { |
| aSkipLayerPresent = true; |
| } |
| if(hnd && (hnd->flags & private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY)) { |
| aNumExtLayers++; |
| // No way we can let this be drawn by GPU to fb0 |
| if(list->hwLayers[i].flags & HWC_SKIP_LAYER) { |
| list->hwLayers[i].flags &= ~ HWC_SKIP_LAYER; |
| } |
| list->hwLayers[i].flags |= HWC_USE_EXT_ONLY; |
| list->hwLayers[i].compositionType = HWC_USE_OVERLAY; |
| list->hwLayers[i].hints &= ~HWC_HINT_CLEAR_FB; |
| //EXTERNAL_BLOCK is always an add-on |
| if(hnd && (hnd->flags & |
| private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK)) { |
| aBlockLayerPresent = true; |
| aBlockLayerIndex = i; |
| list->hwLayers[i].flags |= HWC_USE_EXT_BLOCK; |
| } |
| } |
| } |
| |
| //Update Default mirroring state |
| if (isExtModeStarting(ctx, aNumExtLayers)) { |
| stopDefaultMirror(ctx); |
| } else if (isExtModeStopping(ctx, aNumExtLayers)) { |
| startDefaultMirror(ctx); |
| } |
| |
| //Cache our stats |
| sExtOnlyMode = aNumExtLayers ? EXT_ONLY_MODE_ON : EXT_ONLY_MODE_OFF; |
| sNumExtOnlyLayers = aNumExtLayers; |
| sSkipLayerPresent = aSkipLayerPresent; |
| sBlockLayerPresent = aBlockLayerPresent; |
| sBlockLayerIndex = aBlockLayerIndex; |
| |
| LOGE_IF(EXTDEBUG, "%s: numExtLayers = %d skipLayerPresent = %d", __func__, |
| aNumExtLayers, aSkipLayerPresent); |
| //If skip layer present return. Buffers to be unlocked in draw phase. |
| if(aSkipLayerPresent) { |
| return overlay::NO_ERROR; |
| } |
| |
| //If External is not connected, dont setup pipes, just return |
| if(ctx->mHDMIEnabled == EXT_DISPLAY_OFF || |
| ctx->pendingHDMI == true) { |
| ExtDispOnly::close(); |
| return -1; |
| } |
| |
| |
| //Update pipes |
| bool waitForVsync = true; |
| bool index = 0; |
| |
| if (aBlockLayerPresent) { |
| ExtDispOnly::closeRange(1); |
| ExtDispOnly::prepare(ctx, &(list->hwLayers[aBlockLayerIndex]), |
| index, waitForVsync); |
| } else if (aNumExtLayers) { |
| ExtDispOnly::closeRange(aNumExtLayers); |
| for (size_t i = 0; i < list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if(hnd && hnd->flags & private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY) { |
| waitForVsync = (index == (aNumExtLayers - 1)); |
| ExtDispOnly::prepare(ctx, &(list->hwLayers[i]), |
| index, waitForVsync); |
| index++; |
| } |
| } |
| } else { |
| ExtDispOnly::close(); |
| } |
| #endif |
| return overlay::NO_ERROR; |
| } |
| |
| void ExtDispOnly::storeLockedHandles(hwc_layer_list_t* list) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| int index = 0; |
| if(sBlockLayerPresent) { |
| private_handle_t *hnd = (private_handle_t *) |
| list->hwLayers[sBlockLayerIndex].handle; |
| if(list->hwLayers[sBlockLayerIndex].flags & HWC_USE_EXT_ONLY) { |
| if(!(hnd->flags & private_handle_t::PRIV_FLAGS_HWC_LOCK)) { |
| ExtDispOnly::lockBuffer(hnd); |
| } |
| sPreviousExtHandle[index] = hnd; |
| LOGE_IF(EXTDEBUG, "%s BLOCK: handle = %p", __func__, hnd); |
| return; |
| } |
| } |
| for(int i = 0; i < list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if(list->hwLayers[i].flags & HWC_USE_EXT_ONLY) { |
| if(!(hnd->flags & private_handle_t::PRIV_FLAGS_HWC_LOCK)) { |
| ExtDispOnly::lockBuffer(hnd); |
| } |
| sPreviousExtHandle[index] = hnd; |
| index++; |
| LOGE_IF(EXTDEBUG, "%s: handle = %p", __func__, hnd); |
| } |
| } |
| #endif |
| } |
| |
| int ExtDispOnly::draw(hwc_context_t *ctx, hwc_layer_list_t *list) { |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| LOGE_IF(EXTDEBUG, "%s", __func__); |
| if(ctx->mHDMIEnabled == EXT_DISPLAY_OFF || |
| ctx->pendingHDMI == true) { |
| ExtDispOnly::close(); |
| return -1; |
| } |
| |
| int ret = overlay::NO_ERROR; |
| int index = 0; |
| |
| //If skip layer present or list invalid unlock and return. |
| if(sSkipLayerPresent || list == NULL) { |
| ExtDispOnly::unlockPreviousBuffers(); |
| return overlay::NO_ERROR; |
| } |
| |
| if(sBlockLayerPresent) { |
| private_handle_t *hnd = (private_handle_t*) |
| list->hwLayers[sBlockLayerIndex].handle; |
| ExtDispOnly::lockBuffer(hnd); |
| ret = sOvExtUI[index]->queueBuffer(hnd); |
| if (ret) { |
| LOGE("%s queueBuffer failed", __func__); |
| // Unlock the locked buffer |
| ExtDispOnly::unlockBuffer(hnd); |
| ExtDispOnly::close(); |
| return -1; |
| } |
| ExtDispOnly::unlockPreviousBuffers(); |
| ExtDispOnly::storeLockedHandles(list); |
| return overlay::NO_ERROR; |
| } |
| |
| for(int i = 0; i < list->numHwLayers; i++) { |
| private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; |
| if(hnd && list->hwLayers[i].flags & HWC_USE_EXT_ONLY) { |
| overlay::OverlayUI *ovUI = sOvExtUI[index]; |
| ExtDispOnly::lockBuffer(hnd); |
| ret = ovUI->queueBuffer(hnd); |
| if (ret) { |
| LOGE("%s queueBuffer failed", __func__); |
| // Unlock the all the currently locked buffers |
| for (int j = 0; j <= i; j++) { |
| private_handle_t *tmphnd = |
| (private_handle_t *)list->hwLayers[j].handle; |
| if(hnd && list->hwLayers[j].flags & HWC_USE_EXT_ONLY) |
| ExtDispOnly::unlockBuffer(tmphnd); |
| } |
| ExtDispOnly::close(); |
| return -1; |
| } |
| index++; |
| } |
| } |
| ExtDispOnly::unlockPreviousBuffers(); |
| ExtDispOnly::storeLockedHandles(list); |
| #endif |
| return overlay::NO_ERROR; |
| } |
| |
| #if defined (HDMI_DUAL_DISPLAY) && defined (USE_OVERLAY) |
| overlay::OverlayUI* ExtDispOnly::sOvExtUI[MAX_EXT_ONLY_LAYERS]; |
| native_handle_t* ExtDispOnly::sPreviousExtHandle[MAX_EXT_ONLY_LAYERS]; |
| ExtDispOnly::ExternalOnlyMode ExtDispOnly::sExtOnlyMode; |
| int ExtDispOnly::sNumExtOnlyLayers; |
| bool ExtDispOnly::sSkipLayerPresent; |
| bool ExtDispOnly::sBlockLayerPresent; |
| int ExtDispOnly::sBlockLayerIndex; |
| #endif |