sdm: HDMI as primary and headless display support

Add support for a NULL display that closes all fences and allows
SurfaceFlinger to work without a display connected.

Also add support for ability to connect/disconnect and suspend/resume
HDMI primary display by using NULL display.

CRs-Fixed: 814136
Change-Id: I6ec1149de2a6e5e34aeae0a10e9bc226edd4aa04
diff --git a/sdm/include/core/core_interface.h b/sdm/include/core/core_interface.h
index 582222f..8f97cb4 100644
--- a/sdm/include/core/core_interface.h
+++ b/sdm/include/core/core_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, 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
@@ -88,6 +88,18 @@
   kBwModeMax,      //!< Limiter for maximum available bandwidth modes.
 };
 
+
+/*! @brief Information on hardware for the first display
+
+  @details This structure returns the display type of the first display on the device
+  (internal display or HDMI etc) and whether it is currently connected,
+
+*/
+struct HWDisplayInterfaceInfo {
+  DisplayType type;
+  bool is_connected;
+};
+
 /*! @brief Display core interface.
 
   @details This class defines display core interfaces. It contains methods which client shall use
@@ -172,6 +184,18 @@
    */
     virtual DisplayError SetMaxBandwidthMode(HWBwModes mode) = 0;
 
+  /*! @brief Method to get characteristics of the first display.
+
+    @details Client shall use this method to determine if the first display is HDMI, and whether
+    it is currently connected.
+
+    @param[in] hw_disp_info structure that this method will fill up with info.
+
+    @return \link DisplayError \endlink
+
+   */
+    virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) = 0;
+
 
  protected:
   virtual ~CoreInterface() { }
diff --git a/sdm/libs/core/core_impl.cpp b/sdm/libs/core/core_impl.cpp
index 424be43..82f578e 100644
--- a/sdm/libs/core/core_impl.cpp
+++ b/sdm/libs/core/core_impl.cpp
@@ -212,5 +212,9 @@
   return comp_mgr_.SetMaxBandwidthMode(mode);
 }
 
+DisplayError CoreImpl::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) {
+  return hw_info_intf_->GetFirstDisplayInterfaceType(hw_disp_info);
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/core_impl.h b/sdm/libs/core/core_impl.h
index 06ca3d9..97e8655 100644
--- a/sdm/libs/core/core_impl.h
+++ b/sdm/libs/core/core_impl.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, 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:
@@ -58,6 +58,7 @@
                                      DisplayInterface **intf);
   virtual DisplayError DestroyDisplay(DisplayInterface *intf);
   virtual DisplayError SetMaxBandwidthMode(HWBwModes mode);
+  virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
 
  protected:
   Locker locker_;
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 9fce6a6..c230674 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -747,4 +747,15 @@
   return kErrorNotSupported;
 }
 
+DisplayError DisplayBase::SetVSyncState(bool enable) {
+  DisplayError error = kErrorNone;
+  if (vsync_enable_ != enable) {
+    error = hw_intf_->SetVSyncState(enable);
+    if (error == kErrorNone) {
+      vsync_enable_ = enable;
+    }
+  }
+  return error;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 27d10f6..7242156 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -74,6 +74,7 @@
   virtual DisplayError SetCursorPosition(int x, int y);
   virtual DisplayError GetRefreshRateRange(uint32_t *min_refresh_rate, uint32_t *max_refresh_rate);
   virtual DisplayError GetPanelBrightness(int *level);
+  virtual DisplayError SetVSyncState(bool enable);
 
  protected:
   // DumpImpl method
diff --git a/sdm/libs/core/display_hdmi.cpp b/sdm/libs/core/display_hdmi.cpp
index 331ddcc..12ddd51 100644
--- a/sdm/libs/core/display_hdmi.cpp
+++ b/sdm/libs/core/display_hdmi.cpp
@@ -166,7 +166,7 @@
 
 DisplayError DisplayHDMI::SetVSyncState(bool enable) {
   SCOPE_LOCK(locker_);
-  return kErrorNotSupported;
+  return DisplayBase::SetVSyncState(enable);
 }
 
 void DisplayHDMI::SetIdleTimeoutMs(uint32_t timeout_ms) { }
@@ -368,5 +368,15 @@
   event_handler_->CECMessage(message);
 }
 
+DisplayError DisplayHDMI::VSync(int64_t timestamp) {
+  if (vsync_enable_) {
+    DisplayEventVSync vsync;
+    vsync.timestamp = timestamp;
+    event_handler_->VSync(vsync);
+  }
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/display_hdmi.h b/sdm/libs/core/display_hdmi.h
index 15a0019..88e553d 100644
--- a/sdm/libs/core/display_hdmi.h
+++ b/sdm/libs/core/display_hdmi.h
@@ -67,7 +67,7 @@
   virtual DisplayError SetCursorPosition(int x, int y);
 
   // Implement the HWEventHandlers
-  virtual DisplayError VSync(int64_t timestamp) { return kErrorNone; }
+  virtual DisplayError VSync(int64_t timestamp);
   virtual DisplayError Blank(bool blank) { return kErrorNone; }
   virtual void IdleTimeout() { }
   virtual void ThermalEvent(int64_t thermal_level) { }
@@ -81,7 +81,8 @@
   Locker locker_;
   HWScanSupport scan_support_;
   std::map<LayerBufferS3DFormat, HWS3DMode> s3d_format_to_mode_;
-  std::vector<const char *> event_list_ = {"cec/rd_msg", "thread_exit"};
+  std::vector<const char *> event_list_ = {"vsync_event", "idle_notify", "cec/rd_msg",
+                                           "thread_exit"};
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/display_primary.cpp b/sdm/libs/core/display_primary.cpp
index 9e12c8a..e5751df 100644
--- a/sdm/libs/core/display_primary.cpp
+++ b/sdm/libs/core/display_primary.cpp
@@ -193,15 +193,7 @@
 
 DisplayError DisplayPrimary::SetVSyncState(bool enable) {
   SCOPE_LOCK(locker_);
-  DisplayError error = kErrorNone;
-  if (vsync_enable_ != enable) {
-    error = hw_intf_->SetVSyncState(enable);
-    if (error == kErrorNone) {
-      vsync_enable_ = enable;
-    }
-  }
-
-  return error;
+  return DisplayBase::SetVSyncState(enable);
 }
 
 void DisplayPrimary::SetIdleTimeoutMs(uint32_t timeout_ms) {
diff --git a/sdm/libs/core/fb/hw_device.cpp b/sdm/libs/core/fb/hw_device.cpp
index 33974bf..48560cb 100644
--- a/sdm/libs/core/fb/hw_device.cpp
+++ b/sdm/libs/core/fb/hw_device.cpp
@@ -1030,7 +1030,12 @@
 }
 
 DisplayError HWDevice::SetVSyncState(bool enable) {
-  return kErrorNotSupported;
+  int vsync_on = enable ? 1 : 0;
+  if (Sys::ioctl_(device_fd_, MSMFB_OVERLAY_VSYNC_CTRL, &vsync_on) < 0) {
+    IOCTL_LOGE(MSMFB_OVERLAY_VSYNC_CTRL, device_type_);
+    return kErrorHardware;
+  }
+  return kErrorNone;
 }
 
 void HWDevice::SetIdleTimeoutMs(uint32_t timeout_ms) {
diff --git a/sdm/libs/core/fb/hw_info.cpp b/sdm/libs/core/fb/hw_info.cpp
index ac8b8a6..f6f664a 100644
--- a/sdm/libs/core/fb/hw_info.cpp
+++ b/sdm/libs/core/fb/hw_info.cpp
@@ -524,5 +524,57 @@
   hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats));
 }
 
+DisplayError HWInfo::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) {
+  char *stringbuffer = reinterpret_cast<char *>(malloc(kMaxStringLength));
+  if (stringbuffer == NULL) {
+    DLOGE("Failed to allocate Stringbuffer");
+    return kErrorMemory;
+  }
+
+  char *line = stringbuffer;
+  size_t len = kMaxStringLength;
+  ssize_t read;
+
+  FILE *fileptr = Sys::fopen_("/sys/class/graphics/fb0/msm_fb_type", "r");
+  if (!fileptr) {
+    free(stringbuffer);
+    return kErrorHardware;
+  }
+
+  if ((read = Sys::getline_(&line, &len, fileptr)) != -1) {
+    if (!strncmp(line, "dtv panel", strlen("dtv panel"))) {
+      hw_disp_info->type = kHDMI;
+      DLOGI("First display is HDMI");
+    } else {
+      hw_disp_info->type = kPrimary;
+      DLOGI("First display is internal display");
+    }
+  } else {
+    free(stringbuffer);
+    fclose(fileptr);
+    return kErrorHardware;
+  }
+
+  fclose(fileptr);
+
+  fileptr = Sys::fopen_("/sys/class/graphics/fb0/connected", "r");
+  if (!fileptr) {
+    // If fb0 is for a DSI/connected panel, then connected node will not exist
+    hw_disp_info->is_connected = true;
+  } else {
+    if ((read = Sys::getline_(&line, &len, fileptr)) != -1) {
+        hw_disp_info->is_connected =  (!strncmp(line, "1", strlen("1")));
+    } else {
+        fclose(fileptr);
+        free(stringbuffer);
+        return kErrorHardware;
+    }
+    fclose(fileptr);
+  }
+
+  free(stringbuffer);
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/fb/hw_info.h b/sdm/libs/core/fb/hw_info.h
index ba98f87..f22acb0 100644
--- a/sdm/libs/core/fb/hw_info.h
+++ b/sdm/libs/core/fb/hw_info.h
@@ -26,6 +26,7 @@
 #define __HW_INFO_H__
 
 #include <core/sdm_types.h>
+#include <core/core_interface.h>
 #include <private/hw_info_types.h>
 #include <linux/msm_mdp.h>
 #include "hw_info_interface.h"
@@ -39,6 +40,7 @@
 class HWInfo: public HWInfoInterface {
  public:
   virtual DisplayError GetHWResourceInfo(HWResourceInfo *hw_resource);
+  virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
 
  private:
   virtual DisplayError GetHWRotatorInfo(HWResourceInfo *hw_resource);
diff --git a/sdm/libs/core/fb/hw_primary.cpp b/sdm/libs/core/fb/hw_primary.cpp
index da88623..84eea2a 100644
--- a/sdm/libs/core/fb/hw_primary.cpp
+++ b/sdm/libs/core/fb/hw_primary.cpp
@@ -487,14 +487,7 @@
 
 DisplayError HWPrimary::SetVSyncState(bool enable) {
   DTRACE_SCOPED();
-
-  int vsync_on = enable ? 1 : 0;
-  if (Sys::ioctl_(device_fd_, MSMFB_OVERLAY_VSYNC_CTRL, &vsync_on) < 0) {
-    IOCTL_LOGE(MSMFB_OVERLAY_VSYNC_CTRL, device_type_);
-    return kErrorHardware;
-  }
-
-  return kErrorNone;
+  return HWDevice::SetVSyncState(enable);
 }
 
 DisplayError HWPrimary::SetDisplayMode(const HWDisplayMode hw_display_mode) {
diff --git a/sdm/libs/core/fb/hw_virtual.h b/sdm/libs/core/fb/hw_virtual.h
index f587230..330a067 100644
--- a/sdm/libs/core/fb/hw_virtual.h
+++ b/sdm/libs/core/fb/hw_virtual.h
@@ -34,6 +34,7 @@
   static DisplayError Create(HWInterface **intf, HWInfoInterface *hw_info_intf,
                              BufferSyncHandler *buffer_sync_handler);
   static DisplayError Destroy(HWInterface *intf);
+  virtual DisplayError SetVSyncState(bool enable) { return kErrorNotSupported; }
 
  protected:
   HWVirtual(BufferSyncHandler *buffer_sync_handler, HWInfoInterface *hw_info_intf);
diff --git a/sdm/libs/core/hw_info_interface.h b/sdm/libs/core/hw_info_interface.h
index 014ab0d..401c8bf 100644
--- a/sdm/libs/core/hw_info_interface.h
+++ b/sdm/libs/core/hw_info_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, 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:
@@ -26,6 +26,7 @@
 #define __HW_INFO_INTERFACE_H__
 
 #include <inttypes.h>
+#include <core/core_interface.h>
 #include <private/hw_info_types.h>
 
 namespace sdm {
@@ -35,6 +36,7 @@
   static DisplayError Create(HWInfoInterface **intf);
   static DisplayError Destroy(HWInfoInterface *intf);
   virtual DisplayError GetHWResourceInfo(HWResourceInfo *hw_resource) = 0;
+  virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) = 0;
 
  protected:
   virtual ~HWInfoInterface() { }
diff --git a/sdm/libs/hwc/Android.mk b/sdm/libs/hwc/Android.mk
index 8ce3757..795ba39 100644
--- a/sdm/libs/hwc/Android.mk
+++ b/sdm/libs/hwc/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_SRC_FILES               := hwc_session.cpp \
                                  hwc_display.cpp \
+                                 hwc_display_null.cpp \
                                  hwc_display_primary.cpp \
                                  hwc_display_external.cpp \
                                  hwc_display_virtual.cpp \
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index 081ad37..5dd0e6b 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -74,9 +74,10 @@
 }
 
 HWCDisplay::HWCDisplay(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, DisplayType type,
-                       int id, bool needs_blit, qService::QService *qservice)
+                       int id, bool needs_blit, qService::QService *qservice,
+                       DisplayClass display_class)
   : core_intf_(core_intf), hwc_procs_(hwc_procs), type_(type), id_(id), needs_blit_(needs_blit),
-    qservice_(qservice) {
+    qservice_(qservice), display_class_(display_class) {
 }
 
 int HWCDisplay::Init() {
@@ -1581,4 +1582,8 @@
   return refresh_rate;
 }
 
+DisplayClass HWCDisplay::GetDisplayClass() {
+  return display_class_;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display.h b/sdm/libs/hwc/hwc_display.h
index 9466633..8a4d5b8 100644
--- a/sdm/libs/hwc/hwc_display.h
+++ b/sdm/libs/hwc/hwc_display.h
@@ -36,6 +36,16 @@
 
 class BlitEngine;
 
+// Subclasses set this to their type. This has to be different from DisplayType.
+// This is to avoid RTTI and dynamic_cast
+enum DisplayClass {
+  DISPLAY_CLASS_PRIMARY,
+  DISPLAY_CLASS_EXTERNAL,
+  DISPLAY_CLASS_VIRTUAL,
+  DISPLAY_CLASS_NULL
+};
+
+
 class HWCDisplay : public DisplayEventHandler {
  public:
   virtual ~HWCDisplay() { }
@@ -91,6 +101,7 @@
                            PPDisplayAPIPayload *out_payload,
                            PPPendingParams *pending_action);
   int GetVisibleDisplayRect(hwc_rect_t* rect);
+  DisplayClass GetDisplayClass();
 
  protected:
   enum DisplayStatus {
@@ -133,7 +144,7 @@
   };
 
   HWCDisplay(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, DisplayType type, int id,
-             bool needs_blit, qService::QService *qservice);
+             bool needs_blit, qService::QService *qservice, DisplayClass display_class);
 
   // DisplayEventHandler methods
   virtual DisplayError VSync(const DisplayEventVSync &vsync);
@@ -215,6 +226,7 @@
   void ResetLayerCacheStack();
   BlitEngine *blit_engine_ = NULL;
   qService::QService *qservice_ = NULL;
+  DisplayClass display_class_;
 };
 
 inline int HWCDisplay::Perform(uint32_t operation, ...) {
diff --git a/sdm/libs/hwc/hwc_display_external.cpp b/sdm/libs/hwc/hwc_display_external.cpp
index c36582f..813b123 100644
--- a/sdm/libs/hwc/hwc_display_external.cpp
+++ b/sdm/libs/hwc/hwc_display_external.cpp
@@ -39,8 +39,14 @@
 namespace sdm {
 
 int HWCDisplayExternal::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
-                               uint32_t primary_width, uint32_t primary_height,
                                qService::QService *qservice, HWCDisplay **hwc_display) {
+  return Create(core_intf, hwc_procs, 0, 0, qservice, false, hwc_display);
+}
+
+int HWCDisplayExternal::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+                               uint32_t primary_width, uint32_t primary_height,
+                               qService::QService *qservice, bool use_primary_res,
+                               HWCDisplay **hwc_display) {
   uint32_t external_width = 0;
   uint32_t external_height = 0;
 
@@ -53,10 +59,19 @@
 
   hwc_display_external->GetPanelResolution(&external_width, &external_height);
 
-  int downscale_enabled = 0;
-  HWCDebugHandler::Get()->GetProperty("sdm.debug.downscale_external", &downscale_enabled);
-  if (downscale_enabled) {
-    GetDownscaleResolution(primary_width, primary_height, &external_width, &external_height);
+  if (primary_width && primary_height) {
+    // use_primary_res means HWCDisplayExternal should directly set framebuffer resolution to the
+    // provided primary_width and primary_height
+    if (use_primary_res) {
+      external_width = primary_width;
+      external_height = primary_height;
+    } else {
+      int downscale_enabled = 0;
+      HWCDebugHandler::Get()->GetProperty("sdm.debug.downscale_external", &downscale_enabled);
+      if (downscale_enabled) {
+        GetDownscaleResolution(primary_width, primary_height, &external_width, &external_height);
+      }
+    }
   }
 
   status = hwc_display_external->SetFrameBufferResolution(external_width, external_height);
@@ -77,7 +92,8 @@
 
 HWCDisplayExternal::HWCDisplayExternal(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
                                        qService::QService *qservice)
-  : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice) {
+  : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice,
+               DISPLAY_CLASS_EXTERNAL) {
 }
 
 int HWCDisplayExternal::Prepare(hwc_display_contents_1_t *content_list) {
diff --git a/sdm/libs/hwc/hwc_display_external.h b/sdm/libs/hwc/hwc_display_external.h
index d9f894d..436190d 100644
--- a/sdm/libs/hwc/hwc_display_external.h
+++ b/sdm/libs/hwc/hwc_display_external.h
@@ -32,8 +32,10 @@
 class HWCDisplayExternal : public HWCDisplay {
  public:
   static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, uint32_t primary_width,
-                    uint32_t primary_height, qService::QService *qservice,
+                    uint32_t primary_height, qService::QService *qservice, bool use_primary_res,
                     HWCDisplay **hwc_display);
+  static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+                    qService::QService *qservice, HWCDisplay **hwc_display);
   static void Destroy(HWCDisplay *hwc_display);
   virtual int Prepare(hwc_display_contents_1_t *content_list);
   virtual int Commit(hwc_display_contents_1_t *content_list);
diff --git a/sdm/libs/hwc/hwc_display_null.cpp b/sdm/libs/hwc/hwc_display_null.cpp
new file mode 100644
index 0000000..11cf47a
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_null.cpp
@@ -0,0 +1,120 @@
+/*
+* Copyright (c) 2016, 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 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.
+*/
+
+#include <hardware/hwcomposer_defs.h>
+#include <utils/constants.h>
+#include <utils/debug.h>
+#include "hwc_display_null.h"
+
+#define __CLASS__ "HWCDisplayNull"
+
+namespace sdm {
+
+int HWCDisplayNull::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+                           HWCDisplay **hwc_display) {
+  int status;
+
+  DLOGI("Null display is being created");
+  HWCDisplayNull *hwc_display_null = new HWCDisplayNull(core_intf, hwc_procs);
+
+  status = hwc_display_null->Init();
+  if (status) {
+    delete hwc_display_null;
+    return status;
+  }
+
+  *hwc_display = hwc_display_null;
+
+  return 0;
+}
+
+void HWCDisplayNull::Destroy(HWCDisplay *hwc_display) {
+  DLOGI("Null display is being destroyed");
+  hwc_display->Deinit();
+  delete hwc_display;
+}
+
+// We pass the display type as HWC_DISPLAY_PRIMARY to HWCDisplay, but since we override
+// and don't chain to HWCDisplay::Init(), that type does not actually get used.
+HWCDisplayNull::HWCDisplayNull(CoreInterface *core_intf, hwc_procs_t const **hwc_procs)
+  : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, false, NULL,
+               DISPLAY_CLASS_NULL) {
+}
+
+int HWCDisplayNull::Init() {
+  // Don't call HWCDisplay::Init() for null display, we don't want the chain of
+  // DisplayPrimary / HWPrimary etc objects to be created.
+  return 0;
+}
+
+int HWCDisplayNull::Deinit() {
+  return 0;
+}
+
+int HWCDisplayNull::Prepare(hwc_display_contents_1_t *content_list) {
+  for (size_t i = 0; i < content_list->numHwLayers; i++) {
+    if (content_list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET ||
+        content_list->hwLayers[i].compositionType == HWC_BACKGROUND) {
+      continue;
+    }
+
+    content_list->hwLayers[i].compositionType = HWC_OVERLAY;
+  }
+
+  return 0;
+}
+
+int HWCDisplayNull::Commit(hwc_display_contents_1_t *content_list) {
+  // HWCSession::Commit (from where this is called) already closes all the acquire
+  // fences once we return from here. So no need to close acquire fences here.
+  for (size_t i = 0; i < content_list->numHwLayers; i++) {
+    content_list->hwLayers[i].releaseFenceFd = -1;
+  }
+
+  return 0;
+}
+
+#define NULL_DISPLAY_FPS 60
+
+int HWCDisplayNull::GetDisplayAttributes(uint32_t config, const uint32_t *attributes,
+                                         int32_t *values) {
+  for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+    // We fake display resolution as 1080P by default, though it can be overriden through a call to
+    // SetResolution(), and DPI as 160, though what the DPI value does is not clear
+    switch (attributes[i]) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+      values[i] = INT32(1000000000L / NULL_DISPLAY_FPS);
+      break;
+    case HWC_DISPLAY_WIDTH:
+      values[i] = static_cast<int32_t>(x_res_);
+      break;
+    case HWC_DISPLAY_HEIGHT:
+      values[i] = static_cast<int32_t>(y_res_);
+      break;
+    }
+  }
+  return 0;
+}
+
+}  // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display_null.h b/sdm/libs/hwc/hwc_display_null.h
new file mode 100644
index 0000000..2f6438a
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_null.h
@@ -0,0 +1,104 @@
+/*
+* Copyright (c) 2016, 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 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.
+*/
+
+#ifndef __HWC_DISPLAY_NULL_H__
+#define __HWC_DISPLAY_NULL_H__
+
+#include <qdMetaData.h>
+#include <gralloc_priv.h>
+#include "hwc_display.h"
+
+namespace sdm {
+
+class HWCDisplayNull : public HWCDisplay {
+ public:
+  static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+                    HWCDisplay **hwc_display);
+  static void Destroy(HWCDisplay *hwc_display);
+  virtual int Init();
+  virtual int Deinit();
+  virtual int Prepare(hwc_display_contents_1_t *content_list);
+  virtual int Commit(hwc_display_contents_1_t *content_list);
+  virtual int EventControl(int event, int enable) { return 0; }
+  virtual int SetPowerMode(int mode) { return 0; }
+
+  // Framebuffer configurations
+  virtual int GetDisplayConfigs(uint32_t *configs, size_t *num_configs) {
+    return HWCDisplay::GetDisplayConfigs(configs, num_configs);
+  }
+
+  virtual int GetDisplayAttributes(uint32_t config, const uint32_t *attributes, int32_t *values);
+  virtual int GetActiveConfig() { return 0; }
+  virtual int SetActiveConfig(int index) { return -1; }
+
+  virtual void SetIdleTimeoutMs(uint32_t timeout_ms) { return; }
+  virtual void SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type) { return; }
+  virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages) { return kErrorNone; }
+  virtual DisplayError ControlPartialUpdate(bool enable, uint32_t *pending) { return kErrorNone; }
+  virtual uint32_t GetLastPowerMode() { return 0; }
+  virtual int SetFrameBufferResolution(uint32_t x_pixels, uint32_t y_pixels) { return 0; }
+
+  virtual void GetFrameBufferResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+    *x_pixels = x_res_;
+    *y_pixels = y_res_;
+  }
+
+  virtual void GetPanelResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+    *x_pixels = x_res_;
+    *y_pixels = y_res_;
+  }
+
+  virtual int SetDisplayStatus(uint32_t display_status) { return 0; }
+  virtual int OnMinHdcpEncryptionLevelChange(uint32_t min_enc_level) { return 0; }
+  virtual int Perform(uint32_t operation, ...) { return 0; }
+  virtual int SetCursorPosition(int x, int y) { return 0; }
+  virtual void SetSecureDisplay(bool secure_display_active) { return; }
+
+  // Display Configurations
+  virtual int SetActiveDisplayConfig(int config) { return 0; }
+  virtual int GetActiveDisplayConfig(uint32_t *config) { return -1; }
+  virtual int GetDisplayConfigCount(uint32_t *count) { return -1; }
+  virtual int GetDisplayAttributesForConfig(int config, DisplayConfigVariableInfo *attributes) {
+    return -1;
+  }
+  virtual bool IsValidContentList(hwc_display_contents_1_t *content_list) {
+    return true;
+  }
+
+  void SetResolution(uint32_t x_res, uint32_t y_res) {
+    x_res_ = x_res;
+    y_res_ = y_res;
+  }
+
+
+ private:
+  HWCDisplayNull(CoreInterface *core_intf, hwc_procs_t const **hwc_procs);
+  uint32_t x_res_ = 1920;
+  uint32_t y_res_ = 1080;
+};
+
+}  // namespace sdm
+
+#endif  // __HWC_DISPLAY_NULL_H__
+
diff --git a/sdm/libs/hwc/hwc_display_primary.cpp b/sdm/libs/hwc/hwc_display_primary.cpp
index 2a3270e..8915bba 100644
--- a/sdm/libs/hwc/hwc_display_primary.cpp
+++ b/sdm/libs/hwc/hwc_display_primary.cpp
@@ -85,8 +85,8 @@
                                      BufferAllocator *buffer_allocator,
                                      hwc_procs_t const **hwc_procs,
                                      qService::QService *qservice)
-  : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, true, qservice),
-    buffer_allocator_(buffer_allocator), cpu_hint_(NULL) {
+  : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, true, qservice,
+               DISPLAY_CLASS_PRIMARY), buffer_allocator_(buffer_allocator), cpu_hint_(NULL) {
 }
 
 int HWCDisplayPrimary::Init() {
diff --git a/sdm/libs/hwc/hwc_display_virtual.cpp b/sdm/libs/hwc/hwc_display_virtual.cpp
index 5b0caab..0d3cdf2 100644
--- a/sdm/libs/hwc/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc/hwc_display_virtual.cpp
@@ -96,7 +96,8 @@
 }
 
 HWCDisplayVirtual::HWCDisplayVirtual(CoreInterface *core_intf, hwc_procs_t const **hwc_procs)
-  : HWCDisplay(core_intf, hwc_procs, kVirtual, HWC_DISPLAY_VIRTUAL, false, NULL),
+  : HWCDisplay(core_intf, hwc_procs, kVirtual, HWC_DISPLAY_VIRTUAL, false, NULL,
+               DISPLAY_CLASS_VIRTUAL),
     dump_output_layer_(false), output_buffer_(NULL) {
 }
 
diff --git a/sdm/libs/hwc/hwc_session.cpp b/sdm/libs/hwc/hwc_session.cpp
index ffd8cc1..7928765 100644
--- a/sdm/libs/hwc/hwc_session.cpp
+++ b/sdm/libs/hwc/hwc_session.cpp
@@ -49,6 +49,7 @@
 #include "hwc_buffer_sync_handler.h"
 #include "hwc_session.h"
 #include "hwc_debugger.h"
+#include "hwc_display_null.h"
 #include "hwc_display_primary.h"
 #include "hwc_display_virtual.h"
 
@@ -146,9 +147,33 @@
     return -EINVAL;
   }
 
-  // Create and power on primary display
-  status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
-                                     &hwc_display_[HWC_DISPLAY_PRIMARY]);
+  // Read which display is first, and create it and store it in primary slot
+  HWDisplayInterfaceInfo hw_disp_info;
+  error = core_intf_->GetFirstDisplayInterfaceType(&hw_disp_info);
+  if (error == kErrorNone) {
+    if (hw_disp_info.type == kHDMI) {
+      // HDMI is primary display. If already connected, then create it and store in
+      // primary display slot. If not connected, create a NULL display for now.
+      if (hw_disp_info.is_connected) {
+        status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, qservice_,
+                                            &hwc_display_[HWC_DISPLAY_PRIMARY]);
+      } else {
+        // NullDisplay simply closes all its fences, and advertizes a standard
+        // resolution to SurfaceFlinger
+        status = HWCDisplayNull::Create(core_intf_, &hwc_procs_,
+                                        &hwc_display_[HWC_DISPLAY_PRIMARY]);
+      }
+    } else {
+        // Create and power on primary display
+        status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
+                                           &hwc_display_[HWC_DISPLAY_PRIMARY]);
+    }
+  } else {
+        // Create and power on primary display
+        status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
+                                           &hwc_display_[HWC_DISPLAY_PRIMARY]);
+  }
+
   if (status) {
     CoreInterface::DestroyCore();
     return status;
@@ -597,7 +622,7 @@
 
   if (disp == HWC_DISPLAY_EXTERNAL) {
     status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
-                                        qservice_, &hwc_display_[disp]);
+                                        qservice_, false, &hwc_display_[disp]);
   } else if (disp == HWC_DISPLAY_VIRTUAL) {
     status = HWCDisplayVirtual::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
                                        content_list, &hwc_display_[disp]);
@@ -1307,6 +1332,7 @@
 int HWCSession::HotPlugHandler(bool connected) {
   int status = 0;
   bool notify_hotplug = false;
+  bool hdmi_primary = false;
 
   // To prevent sending events to client while a lock is held, acquire scope locks only within
   // below scope so that those get automatically unlocked after the scope ends.
@@ -1318,36 +1344,112 @@
       return -1;
     }
 
-    if (connected) {
-      if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-        DLOGE("HDMI is already connected");
-        return -1;
-      }
 
-      // Connect external display if virtual display is not connected.
-      // Else, defer external display connection and process it when virtual display
-      // tears down; Do not notify SurfaceFlinger since connection is deferred now.
-      if (!hwc_display_[HWC_DISPLAY_VIRTUAL]) {
-        status = ConnectDisplay(HWC_DISPLAY_EXTERNAL, NULL);
+    HWCDisplay *primary_display = hwc_display_[HWC_DISPLAY_PRIMARY];
+    HWCDisplay *external_display = NULL;
+    HWCDisplay *null_display = NULL;
+
+    if (primary_display->GetDisplayClass() == DISPLAY_CLASS_EXTERNAL) {
+        external_display = static_cast<HWCDisplayExternal *>(hwc_display_[HWC_DISPLAY_PRIMARY]);
+    } else if (primary_display->GetDisplayClass() == DISPLAY_CLASS_NULL) {
+        null_display = static_cast<HWCDisplayNull *>(hwc_display_[HWC_DISPLAY_PRIMARY]);
+    }
+
+    if (external_display || null_display) {
+      hdmi_primary = true;
+    }
+
+    // If primary display connected is a NULL display, then replace it with the external display
+    if (connected) {
+      // If we are in HDMI as primary and the primary display just got plugged in
+      if (null_display) {
+        assert(hdmi_primary);
+        uint32_t primary_width, primary_height;
+        null_display->GetFrameBufferResolution(&primary_width, &primary_height);
+        delete null_display;
+        hwc_display_[HWC_DISPLAY_PRIMARY] = NULL;
+
+        // Create external display with a forced framebuffer resolution to that of what the NULL
+        // display had. This is necessary because SurfaceFlinger does not dynamically update
+        // framebuffer resolution once it reads it at bootup. So we always have to have the NULL
+        // display/external display both at the bootup resolution.
+        int status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width,
+                                                primary_height, qservice_, true,
+                                                &hwc_display_[HWC_DISPLAY_PRIMARY]);
         if (status) {
-          return status;
+          DLOGE("Could not create external display");
+          return -1;
         }
-        notify_hotplug = true;
+
+        // Next, go ahead and enable vsync on external display. This is expliclity required
+        // because in HDMI as primary case, SurfaceFlinger may not be aware of underlying
+        // changing display. and thus may not explicitly enable vsync
+
+        status = hwc_display_[HWC_DISPLAY_PRIMARY]->EventControl(HWC_EVENT_VSYNC, true);
+        if (status) {
+          DLOGE("Error enabling vsync for HDMI as primary case");
+        }
+        // Don't do hotplug notification for HDMI as primary case for now
+        notify_hotplug = false;
       } else {
-        DLOGI("Virtual display is connected, pending connection");
-        external_pending_connect_ = true;
+        if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+            DLOGE("HDMI is already connected");
+            return -1;
+        }
+
+        // Connect external display if virtual display is not connected.
+        // Else, defer external display connection and process it when virtual display
+        // tears down; Do not notify SurfaceFlinger since connection is deferred now.
+        if (!hwc_display_[HWC_DISPLAY_VIRTUAL]) {
+            status = ConnectDisplay(HWC_DISPLAY_EXTERNAL, NULL);
+            if (status) {
+            return status;
+            }
+            notify_hotplug = true;
+        } else {
+            DLOGI("Virtual display is connected, pending connection");
+            external_pending_connect_ = true;
+        }
       }
     } else {
       // Do not return error if external display is not in connected status.
       // Due to virtual display concurrency, external display connection might be still pending
       // but hdmi got disconnected before pending connection could be processed.
-      if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-        status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
-        notify_hotplug = true;
+
+      if (hdmi_primary) {
+        assert(external_display != NULL);
+        uint32_t x_res, y_res;
+        external_display->GetFrameBufferResolution(&x_res, &y_res);
+        // Need to manually disable VSYNC as SF is not aware of connect/disconnect cases
+        // for HDMI as primary
+        external_display->EventControl(HWC_EVENT_VSYNC, false);
+        HWCDisplayExternal::Destroy(external_display);
+
+        HWCDisplayNull *null_display;
+
+        int status = HWCDisplayNull::Create(core_intf_, &hwc_procs_,
+                                            reinterpret_cast<HWCDisplay **>(&null_display));
+
+        if (status) {
+          DLOGE("Could not create Null display when primary got disconnected");
+          return -1;
+        }
+
+        null_display->SetResolution(x_res, y_res);
+        hwc_display_[HWC_DISPLAY_PRIMARY] = null_display;
+
+        // Don't do hotplug notification for HDMI as primary case for now
+        notify_hotplug = false;
+      } else {
+        if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+            status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
+            notify_hotplug = true;
+        }
+        external_pending_connect_ = false;
       }
-      external_pending_connect_ = false;
     }
   }
+
   if (connected && notify_hotplug) {
     // trigger screen refresh to ensure sufficient resources are available to process new
     // new display connection.