display: Add support for dynamic refresh rate

- The primary panels refresh rate can be change by clients
- Required refresh rate will be set by client using metadata
- If there are multiple clients requesting, it will be used only
  when the refresh rates are equal, else it resets to default
- Set the new refresh rate only when there are only YUV layers
  updating or when the list has only one RGB layer updating
- MdpVersion gets the dyn fps capabilities from panelInfo

Change-Id: If3e7e6b2f028eb301399c4d32c748eed8a97c41f
diff --git a/libexternal/external.cpp b/libexternal/external.cpp
index 0a1335a..b0dbf2e 100644
--- a/libexternal/external.cpp
+++ b/libexternal/external.cpp
@@ -651,6 +651,8 @@
             (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 c8229a8..8e3fc8e 100644
--- a/libhwcomposer/Android.mk
+++ b/libhwcomposer/Android.mk
@@ -18,6 +18,11 @@
 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 6f4f9c6..099a82e 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -792,6 +792,8 @@
     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 b222d32..552beae 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -1063,7 +1063,7 @@
 
     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 @@
         }
     }
 
-    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 @@
 
     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 @@
     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 @@
 }
 
 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 @@
         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 8c833c2..e43c4f4 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -209,7 +209,8 @@
     /* 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 @@
 
         /* 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 b339cb7..d0b3567 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -75,6 +75,9 @@
 
 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 @@
     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 @@
 
 }
 
+//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 @@
     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 @@
         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 @@
 
     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 4858b3a..9d1e7c0 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -80,6 +80,8 @@
 };
 
 struct DisplayAttributes {
+    uint32_t refreshRate;
+    uint32_t dynRefreshRate;
     uint32_t vsync_period; //nanos
     uint32_t xres;
     uint32_t yres;
@@ -137,6 +139,8 @@
     // 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 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 575a97c..b0a8d7d 100644
--- a/libqdutils/mdp_version.cpp
+++ b/libqdutils/mdp_version.cpp
@@ -226,6 +226,19 @@
                     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 1ddad70..dd1aa3d 100644
--- a/libqdutils/mdp_version.h
+++ b/libqdutils/mdp_version.h
@@ -92,9 +92,13 @@
     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 @@
     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 dbd6f10..de94591 100644
--- a/libqdutils/qdMetaData.cpp
+++ b/libqdutils/qdMetaData.cpp
@@ -83,7 +83,10 @@
             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 3ebe782..a71ee8b 100644
--- a/libqdutils/qdMetaData.h
+++ b/libqdutils/qdMetaData.h
@@ -77,6 +77,7 @@
     struct IGCData_t igcData;
     struct Sharp2Data_t Sharp2Data;
     int64_t timestamp;
+    uint32_t refreshrate;
     enum ColorSpace_t colorSpace;
 };
 
@@ -89,6 +90,7 @@
     PP_PARAM_SHARP2     = 0x0020,
     PP_PARAM_TIMESTAMP  = 0x0040,
     UPDATE_BUFFER_GEOMETRY = 0x0080,
+    UPDATE_REFRESH_RATE = 0x0100,
     UPDATE_COLOR_SPACE = 0x0200,
 };