| /* |
| * Copyright (c) 2013-14, The Linux Foundation. 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 The Linux Foundation 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 CLIENTS; 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. |
| */ |
| |
| #include <hwc_qclient.h> |
| #include <IQService.h> |
| #include <hwc_utils.h> |
| #include <mdp_version.h> |
| #include <hwc_mdpcomp.h> |
| #include <hwc_virtual.h> |
| #include <overlay.h> |
| #include <display_config.h> |
| #include <hdmi.h> |
| #include <video/msm_hdmi_modes.h> |
| |
| #define QCLIENT_DEBUG 0 |
| |
| using namespace android; |
| using namespace qService; |
| using namespace qhwc; |
| using namespace overlay; |
| using namespace qdutils; |
| |
| namespace qClient { |
| |
| // ---------------------------------------------------------------------------- |
| QClient::QClient(hwc_context_t *ctx) : mHwcContext(ctx), |
| mMPDeathNotifier(new MPDeathNotifier(ctx)) |
| { |
| ALOGD_IF(QCLIENT_DEBUG, "QClient Constructor invoked"); |
| } |
| |
| QClient::~QClient() |
| { |
| ALOGD_IF(QCLIENT_DEBUG,"QClient Destructor invoked"); |
| } |
| |
| static void securing(hwc_context_t *ctx, uint32_t startEnd) { |
| //The only way to make this class in this process subscribe to media |
| //player's death. |
| IMediaDeathNotifier::getMediaPlayerService(); |
| |
| ctx->mDrawLock.lock(); |
| ctx->mSecuring = startEnd; |
| //We're done securing |
| if(startEnd == IQService::END) |
| ctx->mSecureMode = true; |
| ctx->mDrawLock.unlock(); |
| |
| if(ctx->proc) |
| ctx->proc->invalidate(ctx->proc); |
| } |
| |
| static void unsecuring(hwc_context_t *ctx, uint32_t startEnd) { |
| ctx->mDrawLock.lock(); |
| ctx->mSecuring = startEnd; |
| //We're done unsecuring |
| if(startEnd == IQService::END) |
| ctx->mSecureMode = false; |
| ctx->mDrawLock.unlock(); |
| |
| if(ctx->proc) |
| ctx->proc->invalidate(ctx->proc); |
| } |
| |
| void QClient::MPDeathNotifier::died() { |
| mHwcContext->mDrawLock.lock(); |
| ALOGD_IF(QCLIENT_DEBUG, "Media Player died"); |
| mHwcContext->mSecuring = false; |
| mHwcContext->mSecureMode = false; |
| mHwcContext->mDrawLock.unlock(); |
| if(mHwcContext->proc) |
| mHwcContext->proc->invalidate(mHwcContext->proc); |
| } |
| |
| static android::status_t screenRefresh(hwc_context_t *ctx) { |
| status_t result = NO_INIT; |
| if(ctx->proc) { |
| ctx->proc->invalidate(ctx->proc); |
| result = NO_ERROR; |
| } |
| return result; |
| } |
| |
| static void setExtOrientation(hwc_context_t *ctx, uint32_t orientation) { |
| ctx->mExtOrientation = orientation; |
| } |
| |
| static void isExternalConnected(hwc_context_t* ctx, Parcel* outParcel) { |
| int connected; |
| connected = ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected ? 1 : 0; |
| outParcel->writeInt32(connected); |
| } |
| |
| static void getDisplayAttributes(hwc_context_t* ctx, const Parcel* inParcel, |
| Parcel* outParcel) { |
| int dpy = inParcel->readInt32(); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].vsync_period); |
| if (ctx->dpyAttr[dpy].customFBSize) { |
| outParcel->writeInt32(ctx->dpyAttr[dpy].xres_new); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].yres_new); |
| } else { |
| outParcel->writeInt32(ctx->dpyAttr[dpy].xres); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].yres); |
| } |
| outParcel->writeFloat(ctx->dpyAttr[dpy].xdpi); |
| outParcel->writeFloat(ctx->dpyAttr[dpy].ydpi); |
| //XXX: Need to check what to return for HDMI |
| outParcel->writeInt32(ctx->mMDP.panel); |
| } |
| static void setHSIC(const Parcel* inParcel) { |
| int dpy = inParcel->readInt32(); |
| ALOGD_IF(0, "In %s: dpy = %d", __FUNCTION__, dpy); |
| HSICData_t hsic_data; |
| hsic_data.hue = inParcel->readInt32(); |
| hsic_data.saturation = inParcel->readFloat(); |
| hsic_data.intensity = inParcel->readInt32(); |
| hsic_data.contrast = inParcel->readFloat(); |
| //XXX: Actually set the HSIC data through ABL lib |
| } |
| |
| |
| static void setBufferMirrorMode(hwc_context_t *ctx, uint32_t enable) { |
| ctx->mBufferMirrorMode = enable; |
| } |
| |
| static status_t getDisplayVisibleRegion(hwc_context_t* ctx, int dpy, |
| Parcel* outParcel) { |
| // Get the info only if the dpy is valid |
| if(dpy >= HWC_DISPLAY_PRIMARY && dpy <= HWC_DISPLAY_VIRTUAL) { |
| Locker::Autolock _sl(ctx->mDrawLock); |
| if(dpy && (ctx->mExtOrientation || ctx->mBufferMirrorMode)) { |
| // Return the destRect on external, if external orienation |
| // is enabled |
| outParcel->writeInt32(ctx->dpyAttr[dpy].mDstRect.left); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].mDstRect.top); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].mDstRect.right); |
| outParcel->writeInt32(ctx->dpyAttr[dpy].mDstRect.bottom); |
| } else { |
| outParcel->writeInt32(ctx->mViewFrame[dpy].left); |
| outParcel->writeInt32(ctx->mViewFrame[dpy].top); |
| outParcel->writeInt32(ctx->mViewFrame[dpy].right); |
| outParcel->writeInt32(ctx->mViewFrame[dpy].bottom); |
| } |
| return NO_ERROR; |
| } else { |
| ALOGE("In %s: invalid dpy index %d", __FUNCTION__, dpy); |
| return BAD_VALUE; |
| } |
| } |
| |
| // USed for setting the secondary(hdmi/wfd) status |
| static void setSecondaryDisplayStatus(hwc_context_t *ctx, |
| const Parcel* inParcel) { |
| uint32_t dpy = inParcel->readInt32(); |
| uint32_t status = inParcel->readInt32(); |
| ALOGD_IF(QCLIENT_DEBUG, "%s: dpy = %d status = %s", __FUNCTION__, |
| dpy, getExternalDisplayState(status)); |
| |
| if(dpy > HWC_DISPLAY_PRIMARY && dpy <= HWC_DISPLAY_VIRTUAL) { |
| if(dpy == HWC_DISPLAY_VIRTUAL && status == qdutils::EXTERNAL_OFFLINE) { |
| ctx->mWfdSyncLock.lock(); |
| ctx->mWfdSyncLock.signal(); |
| ctx->mWfdSyncLock.unlock(); |
| } else if(status == qdutils::EXTERNAL_PAUSE) { |
| handle_pause(ctx, dpy); |
| } else if(status == qdutils::EXTERNAL_RESUME) { |
| handle_resume(ctx, dpy); |
| } |
| } else { |
| ALOGE("%s: Invalid dpy %d", __FUNCTION__, dpy); |
| return; |
| } |
| } |
| |
| |
| static status_t setViewFrame(hwc_context_t* ctx, const Parcel* inParcel) { |
| int dpy = inParcel->readInt32(); |
| if(dpy >= HWC_DISPLAY_PRIMARY && dpy <= HWC_DISPLAY_VIRTUAL) { |
| Locker::Autolock _sl(ctx->mDrawLock); |
| ctx->mViewFrame[dpy].left = inParcel->readInt32(); |
| ctx->mViewFrame[dpy].top = inParcel->readInt32(); |
| ctx->mViewFrame[dpy].right = inParcel->readInt32(); |
| ctx->mViewFrame[dpy].bottom = inParcel->readInt32(); |
| ALOGD_IF(QCLIENT_DEBUG, "%s: mViewFrame[%d] = [%d %d %d %d]", |
| __FUNCTION__, dpy, |
| ctx->mViewFrame[dpy].left, ctx->mViewFrame[dpy].top, |
| ctx->mViewFrame[dpy].right, ctx->mViewFrame[dpy].bottom); |
| return NO_ERROR; |
| } else { |
| ALOGE("In %s: invalid dpy index %d", __FUNCTION__, dpy); |
| return BAD_VALUE; |
| } |
| } |
| |
| static void toggleDynamicDebug(hwc_context_t* ctx, const Parcel* inParcel) { |
| int debug_type = inParcel->readInt32(); |
| bool enable = !!inParcel->readInt32(); |
| ALOGD("%s: debug_type: %d enable:%d", |
| __FUNCTION__, debug_type, enable); |
| Locker::Autolock _sl(ctx->mDrawLock); |
| switch (debug_type) { |
| //break is ignored for DEBUG_ALL to toggle all of them at once |
| case IQService::DEBUG_ALL: |
| case IQService::DEBUG_MDPCOMP: |
| qhwc::MDPComp::dynamicDebug(enable); |
| if (debug_type != IQService::DEBUG_ALL) |
| break; |
| case IQService::DEBUG_VSYNC: |
| ctx->vstate.debug = enable; |
| if (debug_type != IQService::DEBUG_ALL) |
| break; |
| case IQService::DEBUG_VD: |
| HWCVirtualVDS::dynamicDebug(enable); |
| if (debug_type != IQService::DEBUG_ALL) |
| break; |
| case IQService::DEBUG_PIPE_LIFECYCLE: |
| Overlay::debugPipeLifecycle(enable); |
| if (debug_type != IQService::DEBUG_ALL) |
| break; |
| } |
| } |
| |
| static void setIdleTimeout(hwc_context_t* ctx, const Parcel* inParcel) { |
| uint32_t timeout = (uint32_t)inParcel->readInt32(); |
| ALOGD("%s :%u ms", __FUNCTION__, timeout); |
| Locker::Autolock _sl(ctx->mDrawLock); |
| MDPComp::setIdleTimeout(timeout); |
| } |
| |
| static void setMaxPipesPerMixer(hwc_context_t* ctx, const Parcel* inParcel) { |
| uint32_t value = (uint32_t)inParcel->readInt32(); |
| ALOGD("%s : setting MaxPipesPerMixer: %d ", __FUNCTION__, value); |
| Locker::Autolock _sl(ctx->mDrawLock); |
| MDPComp::setMaxPipesPerMixer(value); |
| } |
| |
| static void toggleBWC(hwc_context_t* ctx, const Parcel* inParcel) { |
| uint32_t enable = (uint32_t)inParcel->readInt32(); |
| if(MDPVersion::getInstance().supportsBWC()) { |
| Locker::Autolock _sl(ctx->mDrawLock); |
| ctx->mBWCEnabled = (bool) enable; |
| ALOGI("%s: Set BWC to %d", __FUNCTION__, enable); |
| } else { |
| ALOGI("%s: Target doesn't support BWC", __FUNCTION__); |
| } |
| } |
| |
| static void configureDynRefreshRate(hwc_context_t* ctx, |
| const Parcel* inParcel) { |
| uint32_t op = (uint32_t)inParcel->readInt32(); |
| uint32_t refresh_rate = (uint32_t)inParcel->readInt32(); |
| MDPVersion& mdpHw = MDPVersion::getInstance(); |
| uint32_t dpy = HWC_DISPLAY_PRIMARY; |
| |
| if(mdpHw.isDynFpsSupported()) { |
| Locker::Autolock _sl(ctx->mDrawLock); |
| |
| switch (op) { |
| case DISABLE_METADATA_DYN_REFRESH_RATE: |
| ctx->mUseMetaDataRefreshRate = false; |
| setRefreshRate(ctx, dpy, ctx->dpyAttr[dpy].refreshRate); |
| break; |
| case ENABLE_METADATA_DYN_REFRESH_RATE: |
| ctx->mUseMetaDataRefreshRate = true; |
| setRefreshRate(ctx, dpy, ctx->dpyAttr[dpy].refreshRate); |
| break; |
| case SET_BINDER_DYN_REFRESH_RATE: |
| if(ctx->mUseMetaDataRefreshRate) |
| ALOGW("%s: Ignoring binder request to change refresh-rate", |
| __FUNCTION__); |
| else { |
| uint32_t rate = roundOff(refresh_rate); |
| if((rate >= mdpHw.getMinFpsSupported() && |
| rate <= mdpHw.getMaxFpsSupported())) { |
| setRefreshRate(ctx, dpy, rate); |
| } else { |
| ALOGE("%s: Requested refresh-rate should be between \ |
| (%d) and (%d). Given (%d)", __FUNCTION__, |
| mdpHw.getMinFpsSupported(), |
| mdpHw.getMaxFpsSupported(), rate); |
| } |
| } |
| break; |
| default: |
| ALOGE("%s: Invalid op %d",__FUNCTION__,op); |
| } |
| } |
| } |
| |
| static status_t setPartialUpdatePref(hwc_context_t *ctx, uint32_t enable) { |
| ALOGD("%s: enable: %d", __FUNCTION__, enable); |
| if(qhwc::MDPComp::setPartialUpdatePref(ctx, (bool)enable) < 0) |
| return NO_INIT; |
| return NO_ERROR; |
| } |
| |
| static void toggleScreenUpdate(hwc_context_t* ctx, uint32_t on) { |
| ALOGD("%s: toggle update: %d", __FUNCTION__, on); |
| if (on == 0) { |
| ctx->mDrawLock.lock(); |
| ctx->dpyAttr[HWC_DISPLAY_PRIMARY].isPause = true; |
| ctx->mOverlay->configBegin(); |
| ctx->mOverlay->configDone(); |
| ctx->mRotMgr->clear(); |
| if(!Overlay::displayCommit(ctx->dpyAttr[0].fd)) { |
| ALOGE("%s: Display commit failed", __FUNCTION__); |
| } |
| ctx->mDrawLock.unlock(); |
| } else { |
| ctx->mDrawLock.lock(); |
| ctx->dpyAttr[HWC_DISPLAY_PRIMARY].isPause = false; |
| ctx->mDrawLock.unlock(); |
| ctx->proc->invalidate(ctx->proc); |
| } |
| } |
| |
| static void setS3DMode(hwc_context_t* ctx, int mode) { |
| if (ctx->mHDMIDisplay) { |
| if(ctx->mHDMIDisplay->isS3DModeSupported(mode)) { |
| ALOGD("%s: Force S3D mode to %d", __FUNCTION__, mode); |
| Locker::Autolock _sl(ctx->mDrawLock); |
| ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].s3dModeForced = true; |
| setup3DMode(ctx, HWC_DISPLAY_EXTERNAL, mode); |
| } else { |
| ALOGD("%s: mode %d is not supported", __FUNCTION__, mode); |
| } |
| } else { |
| ALOGE("%s: No HDMI Display detected", __FUNCTION__); |
| } |
| } |
| |
| status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel, |
| Parcel* outParcel) { |
| status_t ret = NO_ERROR; |
| |
| switch(command) { |
| case IQService::SECURING: |
| securing(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::UNSECURING: |
| unsecuring(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::SCREEN_REFRESH: |
| return screenRefresh(mHwcContext); |
| break; |
| case IQService::EXTERNAL_ORIENTATION: |
| setExtOrientation(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::BUFFER_MIRRORMODE: |
| setBufferMirrorMode(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::GET_DISPLAY_VISIBLE_REGION: |
| ret = getDisplayVisibleRegion(mHwcContext, inParcel->readInt32(), |
| outParcel); |
| break; |
| case IQService::CHECK_EXTERNAL_STATUS: |
| isExternalConnected(mHwcContext, outParcel); |
| break; |
| case IQService::GET_DISPLAY_ATTRIBUTES: |
| getDisplayAttributes(mHwcContext, inParcel, outParcel); |
| break; |
| case IQService::SET_HSIC_DATA: |
| setHSIC(inParcel); |
| break; |
| case IQService::SET_SECONDARY_DISPLAY_STATUS: |
| setSecondaryDisplayStatus(mHwcContext, inParcel); |
| break; |
| case IQService::SET_VIEW_FRAME: |
| setViewFrame(mHwcContext, inParcel); |
| break; |
| case IQService::DYNAMIC_DEBUG: |
| toggleDynamicDebug(mHwcContext, inParcel); |
| break; |
| case IQService::SET_IDLE_TIMEOUT: |
| setIdleTimeout(mHwcContext, inParcel); |
| break; |
| case IQService::SET_MAX_PIPES_PER_MIXER: |
| setMaxPipesPerMixer(mHwcContext, inParcel); |
| break; |
| case IQService::SET_PARTIAL_UPDATE: |
| ret = setPartialUpdatePref(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::TOGGLE_BWC: |
| toggleBWC(mHwcContext, inParcel); |
| break; |
| case IQService::CONFIGURE_DYN_REFRESH_RATE: |
| configureDynRefreshRate(mHwcContext, inParcel); |
| break; |
| case IQService::TOGGLE_SCREEN_UPDATE: |
| toggleScreenUpdate(mHwcContext, inParcel->readInt32()); |
| break; |
| case IQService::SET_S3D_MODE: |
| setS3DMode(mHwcContext, inParcel->readInt32()); |
| break; |
| default: |
| ret = NO_ERROR; |
| } |
| return ret; |
| } |
| |
| } |