diff --git a/libexternal/external.cpp b/libexternal/external.cpp index 0a1335ae..b0dbf2e7 100644 --- a/libexternal/external.cpp +++ b/libexternal/external.cpp @@ -651,6 +651,8 @@ void ExternalDisplay::setAttributes() { (int)mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres; mHwcContext->mViewFrame[HWC_DISPLAY_EXTERNAL].bottom = (int)mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres; + mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].refreshRate = fps; + mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].dynRefreshRate = fps; mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period = (int) 1000000000l / fps; } diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk index c8229a89..8e3fc8e9 100644 --- a/libhwcomposer/Android.mk +++ b/libhwcomposer/Android.mk @@ -18,6 +18,11 @@ LOCAL_SHARED_LIBRARIES += libskia endif #TARGET_USES_QCOM_BSP LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"qdhwcomposer\" +#Enable Dynamic FPS if PHASE_OFFSET is not set +ifeq ($(VSYNC_EVENT_PHASE_OFFSET_NS),) + LOCAL_CFLAGS += -DDYNAMIC_FPS +endif + LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps) LOCAL_SRC_FILES := hwc.cpp \ hwc_utils.cpp \ diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp index 6f4f9c61..099a82e5 100644 --- a/libhwcomposer/hwc.cpp +++ b/libhwcomposer/hwc.cpp @@ -792,6 +792,8 @@ void hwc_dump(struct hwc_composer_device_1* dev, char *buff, int buff_len) dumpsys_log(aBuf, "Qualcomm HWC state:\n"); dumpsys_log(aBuf, " MDPVersion=%d\n", ctx->mMDP.version); dumpsys_log(aBuf, " DisplayPanel=%c\n", ctx->mMDP.panel); + dumpsys_log(aBuf, " DynRefreshRate=%d\n", + ctx->dpyAttr[HWC_DISPLAY_PRIMARY].dynRefreshRate); for(int dpy = 0; dpy < HWC_NUM_DISPLAY_TYPES; dpy++) { if(ctx->mMDPComp[dpy]) ctx->mMDPComp[dpy]->dump(aBuf, ctx); diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp index b222d323..552beae6 100644 --- a/libhwcomposer/hwc_mdpcomp.cpp +++ b/libhwcomposer/hwc_mdpcomp.cpp @@ -1063,7 +1063,7 @@ bool MDPComp::cacheBasedComp(hwc_context_t *ctx, int numAppLayers = ctx->listStats[mDpy].numAppLayers; mCurrentFrame.reset(numAppLayers); - updateLayerCache(ctx, list); + updateLayerCache(ctx, list, mCurrentFrame); //If an MDP marked layer is unsupported cannot do partial MDP Comp for(int i = 0; i < numAppLayers; i++) { @@ -1078,7 +1078,7 @@ bool MDPComp::cacheBasedComp(hwc_context_t *ctx, } } - updateYUV(ctx, list, false /*secure only*/); + updateYUV(ctx, list, false /*secure only*/, mCurrentFrame); /* mark secure RGB layers for MDP comp */ updateSecureRGB(ctx, list); bool ret = markLayersForCaching(ctx, list); //sets up fbZ also @@ -1238,7 +1238,7 @@ bool MDPComp::videoOnlyComp(hwc_context_t *ctx, mCurrentFrame.reset(numAppLayers); mCurrentFrame.fbCount -= mCurrentFrame.dropCount; - updateYUV(ctx, list, secureOnly); + updateYUV(ctx, list, secureOnly, mCurrentFrame); int mdpCount = mCurrentFrame.mdpCount; if(!isYuvPresent(ctx, mDpy) or (mdpCount == 0)) { @@ -1297,7 +1297,7 @@ bool MDPComp::mdpOnlyLayersComp(hwc_context_t *ctx, mCurrentFrame.reset(numAppLayers); mCurrentFrame.fbCount -= mCurrentFrame.dropCount; - updateYUV(ctx, list, secureOnly); + updateYUV(ctx, list, secureOnly, mCurrentFrame); /* mark secure RGB layers for MDP comp */ updateSecureRGB(ctx, list); @@ -1548,56 +1548,53 @@ bool MDPComp::markLayersForCaching(hwc_context_t* ctx, } void MDPComp::updateLayerCache(hwc_context_t* ctx, - hwc_display_contents_1_t* list) { + hwc_display_contents_1_t* list, FrameInfo& frame) { int numAppLayers = ctx->listStats[mDpy].numAppLayers; int fbCount = 0; for(int i = 0; i < numAppLayers; i++) { if (mCachedFrame.hnd[i] == list->hwLayers[i].handle) { - if(!mCurrentFrame.drop[i]) + if(!frame.drop[i]) fbCount++; - mCurrentFrame.isFBComposed[i] = true; + frame.isFBComposed[i] = true; } else { - mCurrentFrame.isFBComposed[i] = false; + frame.isFBComposed[i] = false; } } - mCurrentFrame.fbCount = fbCount; - mCurrentFrame.mdpCount = mCurrentFrame.layerCount - mCurrentFrame.fbCount - - mCurrentFrame.dropCount; + frame.fbCount = fbCount; + frame.mdpCount = frame.layerCount - frame.fbCount + - frame.dropCount; - ALOGD_IF(isDebug(),"%s: MDP count: %d FB count %d drop count: %d" - ,__FUNCTION__, mCurrentFrame.mdpCount, mCurrentFrame.fbCount, - mCurrentFrame.dropCount); + ALOGD_IF(isDebug(),"%s: MDP count: %d FB count %d drop count: %d", + __FUNCTION__, frame.mdpCount, frame.fbCount, frame.dropCount); } void MDPComp::updateYUV(hwc_context_t* ctx, hwc_display_contents_1_t* list, - bool secureOnly) { + bool secureOnly, FrameInfo& frame) { int nYuvCount = ctx->listStats[mDpy].yuvCount; for(int index = 0;index < nYuvCount; index++){ int nYuvIndex = ctx->listStats[mDpy].yuvIndices[index]; hwc_layer_1_t* layer = &list->hwLayers[nYuvIndex]; if(!isYUVDoable(ctx, layer)) { - if(!mCurrentFrame.isFBComposed[nYuvIndex]) { - mCurrentFrame.isFBComposed[nYuvIndex] = true; - mCurrentFrame.fbCount++; + if(!frame.isFBComposed[nYuvIndex]) { + frame.isFBComposed[nYuvIndex] = true; + frame.fbCount++; } } else { - if(mCurrentFrame.isFBComposed[nYuvIndex]) { + if(frame.isFBComposed[nYuvIndex]) { private_handle_t *hnd = (private_handle_t *)layer->handle; if(!secureOnly || isSecureBuffer(hnd)) { - mCurrentFrame.isFBComposed[nYuvIndex] = false; - mCurrentFrame.fbCount--; + frame.isFBComposed[nYuvIndex] = false; + frame.fbCount--; } } } } - mCurrentFrame.mdpCount = mCurrentFrame.layerCount - - mCurrentFrame.fbCount - mCurrentFrame.dropCount; - ALOGD_IF(isDebug(),"%s: fb count: %d",__FUNCTION__, - mCurrentFrame.fbCount); + frame.mdpCount = frame.layerCount - frame.fbCount - frame.dropCount; + ALOGD_IF(isDebug(),"%s: fb count: %d",__FUNCTION__, frame.fbCount); } void MDPComp::updateSecureRGB(hwc_context_t* ctx, @@ -1878,6 +1875,26 @@ int MDPComp::prepare(hwc_context_t *ctx, hwc_display_contents_1_t* list) { ALOGD("%s",sDump.string()); } +#ifdef DYNAMIC_FPS + //For primary display, set the dynamic refreshrate + if(!mDpy && qdutils::MDPVersion::getInstance().isDynFpsSupported()) { + FrameInfo frame; + frame.reset(mCurrentFrame.layerCount); + ALOGD_IF(isDebug(), "%s: Update Cache and YUVInfo for Dyn Refresh Rate", + __FUNCTION__); + updateLayerCache(ctx, list, frame); + updateYUV(ctx, list, false /*secure only*/, frame); + uint32_t refreshRate = ctx->dpyAttr[mDpy].refreshRate; + //Set the new fresh rate, if there is only one updating YUV layer + //or there is one single RGB layer with this request + if((ctx->listStats[mDpy].yuvCount == frame.mdpCount) || + (frame.layerCount == 1)) { + refreshRate = ctx->listStats[mDpy].refreshRateRequest; + } + setRefreshRate(ctx, mDpy, refreshRate); + } +#endif + mCachedFrame.cacheAll(list); mCachedFrame.updateCounts(mCurrentFrame); return ret; diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h index 8c833c2e..e43c4f4f 100644 --- a/libhwcomposer/hwc_mdpcomp.h +++ b/libhwcomposer/hwc_mdpcomp.h @@ -209,7 +209,8 @@ protected: /* checks for mdp comp dimension limitation */ bool isValidDimension(hwc_context_t *ctx, hwc_layer_1_t *layer); /* tracks non updating layers*/ - void updateLayerCache(hwc_context_t* ctx, hwc_display_contents_1_t* list); + void updateLayerCache(hwc_context_t* ctx, hwc_display_contents_1_t* list, + FrameInfo& frame); /* optimize layers for mdp comp*/ bool markLayersForCaching(hwc_context_t* ctx, hwc_display_contents_1_t* list); @@ -223,7 +224,7 @@ protected: /* updates cache map with YUV info */ void updateYUV(hwc_context_t* ctx, hwc_display_contents_1_t* list, - bool secureOnly); + bool secureOnly, FrameInfo& frame); /* updates cache map with secure RGB info */ void updateSecureRGB(hwc_context_t* ctx, hwc_display_contents_1_t* list); diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp index b339cb75..d0b35671 100644 --- a/libhwcomposer/hwc_utils.cpp +++ b/libhwcomposer/hwc_utils.cpp @@ -75,6 +75,9 @@ EGLAPI EGLBoolean eglGpuPerfHintQCOM(EGLDisplay dpy, EGLContext ctx, namespace qhwc { +//Std refresh rates for digital videos- 24p, 30p and 48p +uint32_t stdRefreshRates[] = { 30, 24, 48 }; + bool isValidResolution(hwc_context_t *ctx, uint32_t xres, uint32_t yres) { return !((xres > qdutils::MDPVersion::getInstance().getMaxMixerWidth() && @@ -170,6 +173,8 @@ static int openFramebufferDevice(hwc_context_t *ctx) ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres = info.yres; ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi; ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi; + ctx->dpyAttr[HWC_DISPLAY_PRIMARY].refreshRate = (uint32_t)fps; + ctx->dpyAttr[HWC_DISPLAY_PRIMARY].dynRefreshRate = (uint32_t)fps; ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period = (uint32_t)(1000000000l / fps); @@ -362,6 +367,50 @@ void closeContext(hwc_context_t *ctx) } +//Helper to roundoff the refreshrates +static uint32_t roundOff(uint32_t refreshRate) { + int count = (int) (sizeof(stdRefreshRates)/sizeof(stdRefreshRates[0])); + uint32_t rate = refreshRate; + for(int i=0; i< count; i++) { + if(abs(stdRefreshRates[i] - refreshRate) < 2) { + // Most likely used for video, the fps can fluctuate + // Ex: b/w 29 and 30 for 30 fps clip + rate = stdRefreshRates[i]; + break; + } + } + return rate; +} + +//Helper func to set the dyn fps +void setRefreshRate(hwc_context_t* ctx, int dpy, uint32_t refreshRate) { + //Update only if different + if(!ctx || refreshRate == ctx->dpyAttr[dpy].dynRefreshRate) + return; + const int fbNum = Overlay::getFbForDpy(dpy); + char sysfsPath[qdutils::MAX_SYSFS_FILE_PATH]; + snprintf (sysfsPath, sizeof(sysfsPath), + "/sys/class/graphics/fb%d/dynamic_fps", fbNum); + + int fd = open(sysfsPath, O_WRONLY); + if(fd >= 0) { + char str[64]; + snprintf(str, sizeof(str), "%d", refreshRate); + ssize_t ret = write(fd, str, strlen(str)); + if(ret < 0) { + ALOGE("%s: Failed to write %d with error %s", + __FUNCTION__, refreshRate, strerror(errno)); + } else { + ctx->dpyAttr[dpy].dynRefreshRate = refreshRate; + ALOGD_IF(HWC_UTILS_DEBUG, "%s: Wrote %d to dynamic_fps", + __FUNCTION__, refreshRate); + } + close(fd); + } else { + ALOGE("%s: Failed to open %s with error %s", __FUNCTION__, sysfsPath, + strerror(errno)); + } +} void dumpsys_log(android::String8& buf, const char* fmt, ...) { @@ -829,6 +878,9 @@ void setListStats(hwc_context_t *ctx, ctx->dpyAttr[dpy].mActionSafePresent = isActionSafePresent(ctx, dpy); ctx->listStats[dpy].renderBufIndexforABC = -1; ctx->listStats[dpy].secureRGBCount = 0; + ctx->listStats[dpy].refreshRateRequest = ctx->dpyAttr[dpy].refreshRate; + uint32_t refreshRate = 0; + qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance(); resetROI(ctx, dpy); @@ -882,6 +934,27 @@ void setListStats(hwc_context_t *ctx, if(layer->blending == HWC_BLENDING_PREMULT) ctx->listStats[dpy].preMultipliedAlpha = true; +#ifdef DYNAMIC_FPS + if (dpy == HWC_DISPLAY_PRIMARY && mdpHw.isDynFpsSupported()) { + //dyn fps: get refreshrate from metadata + //Support multiple refresh rates if they are same + //else set to default + MetaData_t *mdata = hnd ? (MetaData_t *)hnd->base_metadata : NULL; + if (mdata && (mdata->operation & UPDATE_REFRESH_RATE)) { + // Valid refreshRate in metadata and within the range + uint32_t rate = roundOff(mdata->refreshrate); + if((rate >= mdpHw.getMinFpsSupported() && + rate <= mdpHw.getMaxFpsSupported())) { + if (!refreshRate) { + refreshRate = rate; + } else if(refreshRate != rate) { + // multiple refreshrate requests, set to default + refreshRate = ctx->dpyAttr[dpy].refreshRate; + } + } + } + } +#endif } if(ctx->listStats[dpy].yuvCount > 0) { if (property_get("hw.cabl.yuv", property, NULL) > 0) { @@ -905,6 +978,9 @@ void setListStats(hwc_context_t *ctx, if(dpy == HWC_DISPLAY_PRIMARY) { ctx->mAD->markDoable(ctx, list); + //Store the requested fresh rate + ctx->listStats[dpy].refreshRateRequest = refreshRate ? + refreshRate : ctx->dpyAttr[dpy].refreshRate; } } diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h index 4858b3a9..9d1e7c01 100644 --- a/libhwcomposer/hwc_utils.h +++ b/libhwcomposer/hwc_utils.h @@ -80,6 +80,8 @@ struct MDPInfo { }; struct DisplayAttributes { + uint32_t refreshRate; + uint32_t dynRefreshRate; uint32_t vsync_period; //nanos uint32_t xres; uint32_t yres; @@ -137,6 +139,8 @@ struct ListStats { // Secure RGB specific int secureRGBCount; int secureRGBIndices[MAX_NUM_APP_LAYERS]; + //dyn refresh rate-Client requested refreshrate + uint32_t refreshRateRequest; }; //PTOR Comp info @@ -305,6 +309,8 @@ void getActionSafePosition(hwc_context_t *ctx, int dpy, hwc_rect_t& dst); void getAspectRatioPosition(hwc_context_t* ctx, int dpy, int extOrientation, hwc_rect_t& inRect, hwc_rect_t& outRect); +void setRefreshRate(hwc_context_t *ctx, int dpy, uint32_t refreshRate); + bool isPrimaryPortrait(hwc_context_t *ctx); bool isOrientationPortrait(hwc_context_t *ctx); diff --git a/libqdutils/mdp_version.cpp b/libqdutils/mdp_version.cpp index 575a97cd..b0a8d7d0 100644 --- a/libqdutils/mdp_version.cpp +++ b/libqdutils/mdp_version.cpp @@ -226,6 +226,19 @@ void MDPVersion::updatePanelInfo() { mPanelInfo.mNeedsROIMerge = atoi(tokens[1]); ALOGI("Needs ROI Merge: %d", mPanelInfo.mNeedsROIMerge); } + if(!strncmp(tokens[0], "dyn_fps_en", strlen("dyn_fps_en"))) { + mPanelInfo.mDynFpsSupported = atoi(tokens[1]); + ALOGI("Dynamic Fps: %s", mPanelInfo.mDynFpsSupported ? + "Enabled" : "Disabled"); + } + if(!strncmp(tokens[0], "min_fps", strlen("min_fps"))) { + mPanelInfo.mMinFps = atoi(tokens[1]); + ALOGI("Min Panel fps: %d", mPanelInfo.mMinFps); + } + if(!strncmp(tokens[0], "max_fps", strlen("max_fps"))) { + mPanelInfo.mMaxFps = atoi(tokens[1]); + ALOGI("Max Panel fps: %d", mPanelInfo.mMaxFps); + } } } if((property_get("persist.hwc.pubypass", property, 0) > 0) && diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h index 1ddad70d..dd1aa3d0 100644 --- a/libqdutils/mdp_version.h +++ b/libqdutils/mdp_version.h @@ -92,9 +92,13 @@ struct PanelInfo { int mMinROIWidth; // Min width needed for ROI int mMinROIHeight; // Min height needed for ROI bool mNeedsROIMerge; // Merge ROI's of both the DSI's + bool mDynFpsSupported; // Panel Supports dyn fps + uint32_t mMinFps; // Min fps supported by panel + uint32_t mMaxFps; // Max fps supported by panel PanelInfo() : mType(NO_PANEL), mPartialUpdateEnable(0), mLeftAlign(0), mWidthAlign(0), mTopAlign(0), mHeightAlign(0), - mMinROIWidth(0), mMinROIHeight(0), mNeedsROIMerge(false){} + mMinROIWidth(0), mMinROIHeight(0), mNeedsROIMerge(false), + mDynFpsSupported(0), mMinFps(0), mMaxFps(0) {} friend class MDPVersion; }; @@ -130,6 +134,9 @@ public: unsigned long getLowBw() { return mLowBw; } unsigned long getHighBw() { return mHighBw; } bool isRotDownscaleEnabled() { return mRotDownscale; } + bool isDynFpsSupported() { return mPanelInfo.mDynFpsSupported; } + uint32_t getMinFpsSupported() { return mPanelInfo.mMinFps; } + uint32_t getMaxFpsSupported() { return mPanelInfo.mMaxFps; } int getMaxMixerWidth() const { return mMaxMixerWidth; } bool isSrcSplit() const; bool isSrcSplitAlways() const; diff --git a/libqdutils/qdMetaData.cpp b/libqdutils/qdMetaData.cpp index dbd6f109..de94591d 100644 --- a/libqdutils/qdMetaData.cpp +++ b/libqdutils/qdMetaData.cpp @@ -83,7 +83,10 @@ int setMetaData(private_handle_t *handle, DispParamType paramType, break; case UPDATE_BUFFER_GEOMETRY: memcpy((void *)&data->bufferDim, param, sizeof(BufferDim_t)); - break; + break; + case UPDATE_REFRESH_RATE: + data->refreshrate = *((uint32_t *)param); + break; case UPDATE_COLOR_SPACE: data->colorSpace = *((ColorSpace_t *)param); break; diff --git a/libqdutils/qdMetaData.h b/libqdutils/qdMetaData.h index 3ebe7827..a71ee8b9 100644 --- a/libqdutils/qdMetaData.h +++ b/libqdutils/qdMetaData.h @@ -77,6 +77,7 @@ struct MetaData_t { struct IGCData_t igcData; struct Sharp2Data_t Sharp2Data; int64_t timestamp; + uint32_t refreshrate; enum ColorSpace_t colorSpace; }; @@ -89,6 +90,7 @@ enum DispParamType { PP_PARAM_SHARP2 = 0x0020, PP_PARAM_TIMESTAMP = 0x0040, UPDATE_BUFFER_GEOMETRY = 0x0080, + UPDATE_REFRESH_RATE = 0x0100, UPDATE_COLOR_SPACE = 0x0200, };