Merge "hwc: Query MDP capability for blending stages supported."
diff --git a/libhdmi/hdmi.cpp b/libhdmi/hdmi.cpp
index 4fb7cfa..1aee664 100644
--- a/libhdmi/hdmi.cpp
+++ b/libhdmi/hdmi.cpp
@@ -101,15 +101,16 @@
     }
     readCEUnderscanInfo();
     readResolution();
-    // TODO: Move this to activate
     /* Used for changing the resolution
-     * getUserMode will get the preferred
-     * mode set thru adb shell */
-    mCurrentMode = getUserMode();
-    if (mCurrentMode == -1) {
+     * getUserConfig will get the preferred
+     * config index set thru adb shell */
+    mActiveConfig = getUserConfig();
+    if (mActiveConfig == -1) {
         //Get the best mode and set
-        mCurrentMode = getBestMode();
+        mActiveConfig = getBestConfig();
     }
+    // Set the mode corresponding to the active index
+    mCurrentMode = mEDIDModes[mActiveConfig];
     setAttributes();
     // set system property
     property_set("hw.hdmiON", "1");
@@ -121,6 +122,14 @@
             && !strcmp(value, "true")) {
         mMDPDownscaleEnabled = true;
     }
+
+    // XXX: A debug property can be used to enable resolution change for
+    // testing purposes: debug.hwc.enable_resolution_change
+    mEnableResolutionChange = false;
+    if(property_get("debug.hwc.enable_resolution_change", value, "false")
+            && !strcmp(value, "true")) {
+        mEnableResolutionChange = true;
+    }
     return 0;
 }
 
@@ -425,8 +434,8 @@
     return -1;
 }
 
-/// Returns the user mode set(if any) using adb shell
-int HDMIDisplay::getUserMode() {
+/// Returns the index of the user mode set(if any) using adb shell
+int HDMIDisplay::getUserConfig() {
     /* Based on the property set the resolution */
     char property_value[PROPERTY_VALUE_MAX];
     property_get("hw.hdmi.resolution", property_value, "-1");
@@ -434,15 +443,16 @@
     // We dont support interlaced modes
     if(isValidMode(mode) && !isInterlacedMode(mode)) {
         ALOGD_IF(DEBUG, "%s: setting the HDMI mode = %d", __FUNCTION__, mode);
-        return mode;
+        return getModeIndex(mode);
     }
     return -1;
 }
 
-// Get the best mode for the current HD TV
-int HDMIDisplay::getBestMode() {
+// Get the index of the best mode for the current HD TV
+int HDMIDisplay::getBestConfig() {
     int bestOrder = 0;
     int bestMode = HDMI_VFRMT_640x480p60_4_3;
+    int bestModeIndex = -1;
     // for all the edid read, get the best mode
     for(int i = 0; i < mModeCount; i++) {
         int mode = mEDIDModes[i];
@@ -450,9 +460,19 @@
         if (order > bestOrder) {
             bestOrder = order;
             bestMode = mode;
+            bestModeIndex = i;
         }
     }
-    return bestMode;
+    // If we fail to read from EDID when HDMI is connected, then
+    // mModeCount will be 0 and bestModeIndex will be invalid.
+    // In this case, we populate the mEDIDModes structure with
+    // a default mode at index 0.
+    if (bestModeIndex == -1) {
+        bestModeIndex = 0;
+        mModeCount = 1;
+        mEDIDModes[bestModeIndex] = bestMode;
+    }
+    return bestModeIndex;
 }
 
 inline bool HDMIDisplay::isValidMode(int ID)
@@ -696,4 +716,79 @@
     mPrimaryWidth = primaryWidth;
 }
 
+int HDMIDisplay::setActiveConfig(int newConfig) {
+    if(newConfig < 0 || newConfig > mModeCount) {
+        ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
+        return -EINVAL;
+    }
+
+    // XXX: Currently, we only support a change in frame rate.
+    // We need to validate the new config before proceeding.
+    if (!isValidConfigChange(newConfig)) {
+        ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
+        return -EINVAL;
+    }
+
+    mCurrentMode =  mEDIDModes[newConfig];
+    mActiveConfig = newConfig;
+    activateDisplay();
+    ALOGD("%s config(%d) mode(%d)", __FUNCTION__, mActiveConfig, mCurrentMode);
+    return 0;
+}
+
+// returns false if the xres or yres of the new config do
+// not match the current config
+bool HDMIDisplay::isValidConfigChange(int newConfig) {
+    int newMode = mEDIDModes[newConfig];
+    uint32_t width = 0, height = 0, refresh = 0;
+    getAttrForConfig(newConfig, width, height, refresh);
+    return ((mXres == width) && (mYres == height)) || mEnableResolutionChange;
+}
+
+int HDMIDisplay::getModeIndex(int mode) {
+    int modeIndex = -1;
+    for(int i = 0; i < mModeCount; i++) {
+        if(mode == mEDIDModes[i]) {
+            modeIndex = i;
+            break;
+        }
+    }
+    return modeIndex;
+}
+
+int HDMIDisplay::getAttrForConfig(int config, uint32_t& xres,
+        uint32_t& yres, uint32_t& refresh) const {
+    if(config < 0 || config > mModeCount) {
+        ALOGE("%s Invalid configuration %d", __FUNCTION__, config);
+        return -EINVAL;
+    }
+    int mode = mEDIDModes[config];
+    uint32_t fps = 0;
+    // Retrieve the mode attributes from gEDIDData
+    for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
+        if (gEDIDData[dataIndex].mMode == mode) {
+            xres = gEDIDData[dataIndex].mWidth;
+            yres = gEDIDData[dataIndex].mHeight;
+            fps = gEDIDData[dataIndex].mFps;
+        }
+    }
+    refresh = (uint32_t) 1000000000l / fps;
+    ALOGD_IF(DEBUG, "%s xres(%d) yres(%d) fps(%d) refresh(%d)", __FUNCTION__,
+            xres, yres, fps, refresh);
+    return 0;
+}
+
+int HDMIDisplay::getDisplayConfigs(uint32_t* configs,
+        size_t* numConfigs) const {
+    if (*numConfigs <= 0) {
+        ALOGE("%s Invalid number of configs (%d)", __FUNCTION__, *numConfigs);
+        return -EINVAL;
+    }
+    *numConfigs = mModeCount;
+    for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
+        configs[configIndex] = (uint32_t)configIndex;
+    }
+    return 0;
+}
+
 };
diff --git a/libhdmi/hdmi.h b/libhdmi/hdmi.h
index 605d9be..d262a63 100644
--- a/libhdmi/hdmi.h
+++ b/libhdmi/hdmi.h
@@ -68,6 +68,11 @@
     /* when HDMI is an EXTERNAL display, PRIMARY display attributes are needed
        for scaling mode */
     void setPrimaryAttributes(uint32_t primaryWidth, uint32_t primaryHeight);
+    int getActiveConfig() const { return mActiveConfig; };
+    int setActiveConfig(int newConfig);
+    int getAttrForConfig(int config, uint32_t& xres,
+            uint32_t& yres, uint32_t& refresh) const;
+    int getDisplayConfigs(uint32_t* configs, size_t* numConfigs) const;
 
 private:
     int getModeCount() const;
@@ -80,17 +85,26 @@
     bool writeHPDOption(int userOption) const;
     bool isValidMode(int mode);
     int  getModeOrder(int mode);
-    int  getUserMode();
-    int  getBestMode();
+    int  getUserConfig();
+    int  getBestConfig();
     bool isInterlacedMode(int mode);
     void resetInfo();
     void setAttributes();
     void getAttrForMode(uint32_t& width, uint32_t& height, uint32_t& fps);
     int openDeviceNode(const char* node, int fileMode) const;
+    int getModeIndex(int mode);
+    bool isValidConfigChange(int newConfig);
 
     int mFd;
     int mFbNum;
+    // mCurrentMode is the HDMI video format that corresponds to the mEDIDMode
+    // entry referenced by mActiveConfig
     int mCurrentMode;
+    // mActiveConfig is the index correponding to the currently active mode for
+    // the HDMI display. It basically indexes the mEDIDMode array
+    int mActiveConfig;
+    // mEDIDModes contains a list of HDMI video formats (modes) supported by the
+    // HDMI display
     int mEDIDModes[64];
     int mModeCount;
     fb_var_screeninfo mVInfo;
@@ -102,6 +116,7 @@
     // Downscale feature switch, set via system property
     // sys.hwc.mdp_downscale_enabled
     bool mMDPDownscaleEnabled;
+    bool mEnableResolutionChange;
     int mDisplayId;
 };
 
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index b5ba073..a7eb561 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -187,6 +187,12 @@
     }
 }
 
+static bool isHotPluggable(hwc_context_t *ctx, int dpy) {
+    return ((dpy == HWC_DISPLAY_EXTERNAL) ||
+            ((dpy == HWC_DISPLAY_PRIMARY) &&
+             ctx->mHDMIDisplay->isHDMIPrimaryDisplay()));
+}
+
 static void reset(hwc_context_t *ctx, int numDisplays,
                   hwc_display_contents_1_t** displays) {
 
@@ -735,40 +741,54 @@
 
 int hwc_getDisplayConfigs(struct hwc_composer_device_1* dev, int disp,
         uint32_t* configs, size_t* numConfigs) {
-    int ret = 0;
     hwc_context_t* ctx = (hwc_context_t*)(dev);
-    //Currently we allow only 1 config, reported as config id # 0
-    //This config is passed in to getDisplayAttributes. Ignored for now.
+
+    Locker::Autolock _l(ctx->mDrawLock);
+    bool hotPluggable = isHotPluggable(ctx, disp);
+    bool isVirtualDisplay = (disp == HWC_DISPLAY_VIRTUAL);
+    // If hotpluggable or virtual displays are inactive return error
+    if ((hotPluggable || isVirtualDisplay) && !ctx->dpyAttr[disp].connected) {
+        ALOGE("%s display (%d) is inactive", __FUNCTION__, disp);
+        return -EINVAL;
+    }
+
+    if (*numConfigs <= 0) {
+        ALOGE("%s Invalid number of configs (%d)", __FUNCTION__, *numConfigs);
+        return -EINVAL;
+    }
+
     switch(disp) {
         case HWC_DISPLAY_PRIMARY:
-            if(*numConfigs > 0) {
+            if (hotPluggable) {
+                ctx->mHDMIDisplay->getDisplayConfigs(configs, numConfigs);
+            } else {
                 configs[0] = 0;
                 *numConfigs = 1;
             }
-            ret = 0; //NO_ERROR
             break;
         case HWC_DISPLAY_EXTERNAL:
+                ctx->mHDMIDisplay->getDisplayConfigs(configs, numConfigs);
+            break;
         case HWC_DISPLAY_VIRTUAL:
-            ret = -1; //Not connected
-            if(ctx->dpyAttr[disp].connected) {
-                ret = 0; //NO_ERROR
-                if(*numConfigs > 0) {
-                    configs[0] = 0;
-                    *numConfigs = 1;
-                }
-            }
+            configs[0] = 0;
+            *numConfigs = 1;
             break;
     }
-    return ret;
+    return 0;
 }
 
 int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp,
-        uint32_t /*config*/, const uint32_t* attributes, int32_t* values) {
+        uint32_t config, const uint32_t* attributes, int32_t* values) {
 
     hwc_context_t* ctx = (hwc_context_t*)(dev);
-    //If hotpluggable displays(i.e, HDMI, WFD) are inactive return error
-    if( (disp != HWC_DISPLAY_PRIMARY) && !ctx->dpyAttr[disp].connected) {
-        return -1;
+
+    Locker::Autolock _l(ctx->mDrawLock);
+    bool hotPluggable = isHotPluggable(ctx, disp);
+    bool isVirtualDisplay = (disp == HWC_DISPLAY_VIRTUAL);
+    // If hotpluggable or virtual displays are inactive return error
+    if ((hotPluggable || isVirtualDisplay) && !ctx->dpyAttr[disp].connected) {
+        ALOGE("%s display (%d) is inactive", __FUNCTION__, disp);
+        return -EINVAL;
     }
 
     //From HWComposer
@@ -784,16 +804,27 @@
     const size_t NUM_DISPLAY_ATTRIBUTES = (sizeof(DISPLAY_ATTRIBUTES) /
             sizeof(DISPLAY_ATTRIBUTES)[0]);
 
+    uint32_t xres = 0, yres = 0, refresh = 0;
+    int ret = 0;
+    if (hotPluggable) {
+        ret = ctx->mHDMIDisplay->getAttrForConfig(config, xres, yres, refresh);
+        if(ret < 0) {
+            ALOGE("%s Error getting attributes for config %d", config);
+            return ret;
+        }
+    }
+
     for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
         switch (attributes[i]) {
         case HWC_DISPLAY_VSYNC_PERIOD:
-            values[i] = ctx->dpyAttr[disp].vsync_period;
+            values[i] =
+                    hotPluggable ? refresh : ctx->dpyAttr[disp].vsync_period;
             break;
         case HWC_DISPLAY_WIDTH:
             if (ctx->dpyAttr[disp].customFBSize)
                 values[i] = ctx->dpyAttr[disp].xres_new;
             else
-                values[i] = ctx->dpyAttr[disp].xres;
+                values[i] = hotPluggable ? xres : ctx->dpyAttr[disp].xres;
 
             ALOGD("%s disp = %d, width = %d",__FUNCTION__, disp,
                     values[i]);
@@ -802,7 +833,7 @@
             if (ctx->dpyAttr[disp].customFBSize)
                 values[i] = ctx->dpyAttr[disp].yres_new;
             else
-                values[i] = ctx->dpyAttr[disp].yres;
+                values[i] = hotPluggable ? yres : ctx->dpyAttr[disp].yres;
             ALOGD("%s disp = %d, height = %d",__FUNCTION__, disp,
                     values[i]);
             break;
@@ -849,15 +880,49 @@
     strlcpy(buff, aBuf.string(), buff_len);
 }
 
-int hwc_getActiveConfig(struct hwc_composer_device_1* /*dev*/, int /*disp*/) {
-    //Supports only the default config (0th index) for now
-    return 0;
+int hwc_getActiveConfig(struct hwc_composer_device_1* dev, int disp)
+{
+    hwc_context_t* ctx = (hwc_context_t*)(dev);
+
+    Locker::Autolock _l(ctx->mDrawLock);
+    bool hotPluggable = isHotPluggable(ctx, disp);
+    bool isVirtualDisplay = (disp == HWC_DISPLAY_VIRTUAL);
+    // If hotpluggable or virtual displays are inactive return error
+    if ((hotPluggable || isVirtualDisplay) && !ctx->dpyAttr[disp].connected) {
+        ALOGE("%s display (%d) is inactive", __FUNCTION__, disp);
+        return -EINVAL;
+    }
+
+    // For use cases when primary panel is the default interface we only have
+    // the default config (0th index)
+    if (!hotPluggable) {
+        return 0;
+    }
+
+    return ctx->mHDMIDisplay->getActiveConfig();
 }
 
-int hwc_setActiveConfig(struct hwc_composer_device_1* /*dev*/, int /*disp*/,
-        int index) {
-    //Supports only the default config (0th index) for now
-    return (index == 0) ? index : -EINVAL;
+int hwc_setActiveConfig(struct hwc_composer_device_1* dev, int disp, int index)
+{
+    hwc_context_t* ctx = (hwc_context_t*)(dev);
+
+    Locker::Autolock _l(ctx->mDrawLock);
+    bool hotPluggable = isHotPluggable(ctx, disp);
+    bool isVirtualDisplay = (disp == HWC_DISPLAY_VIRTUAL);
+    // If hotpluggable or virtual displays are inactive return error
+    if ((hotPluggable || isVirtualDisplay) && !ctx->dpyAttr[disp].connected) {
+        ALOGE("%s display (%d) is inactive", __FUNCTION__, disp);
+        return -EINVAL;
+    }
+
+    // For use cases when primary panel is the default interface we only have
+    // the default config (0th index)
+    if (!hotPluggable) {
+        // Primary and virtual supports only the default config (0th index)
+        return (index == 0) ? index : -EINVAL;
+    }
+
+    return ctx->mHDMIDisplay->setActiveConfig(index);
 }
 
 static int hwc_device_close(struct hw_device_t *dev)