Merge "sdm: color-manager: Clear feature list when add new feature"
diff --git a/libgralloc1/gr_buf_mgr.cpp b/libgralloc1/gr_buf_mgr.cpp
index 743c1c2..7e0ba14 100644
--- a/libgralloc1/gr_buf_mgr.cpp
+++ b/libgralloc1/gr_buf_mgr.cpp
@@ -771,6 +771,17 @@
       AllocateBuffer(descriptor, hnd, size);
     } break;
 
+    case GRALLOC1_MODULE_PERFORM_GET_INTERLACE_FLAG: {
+      private_handle_t *hnd = va_arg(args, private_handle_t *);
+      int *flag = va_arg(args, int *);
+      if (private_handle_t::validate(hnd) != 0) {
+        return GRALLOC1_ERROR_BAD_HANDLE;
+      }
+      if (getMetaData(hnd, GET_PP_PARAM_INTERLACED, flag) != 0) {
+        *flag = 0;
+      }
+    } break;
+
     default:
       break;
   }
diff --git a/libgralloc1/gr_utils.cpp b/libgralloc1/gr_utils.cpp
index aee8c0d..b3056e1 100644
--- a/libgralloc1/gr_utils.cpp
+++ b/libgralloc1/gr_utils.cpp
@@ -311,6 +311,28 @@
   ycbcr->cstride = VENUS_UV_STRIDE(color_format, INT(width));
 }
 
+void GetYuvUbwcInterlacedSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height,
+                                     int color_format, struct android_ycbcr *ycbcr) {
+  unsigned int uv_stride, uv_height, uv_size;
+  unsigned int alignment = 4096;
+  uint64_t field_base;
+
+  // UBWC interlaced has top-bottom field layout with each field as
+  // 4-plane NV12_UBWC with width = image_width & height = image_height / 2.
+  // Client passed ycbcr argument is ptr to struct android_ycbcr[2].
+  // Plane info to be filled for each field separately.
+  height = (height + 1) >> 1;
+  uv_stride = VENUS_UV_STRIDE(color_format, INT(width));
+  uv_height = VENUS_UV_SCANLINES(color_format, INT(height));
+  uv_size = ALIGN((uv_stride * uv_height), alignment);
+
+  field_base = base;
+  GetYuvUbwcSPPlaneInfo(field_base, width, height, COLOR_FMT_NV12_UBWC, &ycbcr[0]);
+
+  field_base = reinterpret_cast<uint64_t>(ycbcr[0].cb) + uv_size;
+  GetYuvUbwcSPPlaneInfo(field_base, width, height, COLOR_FMT_NV12_UBWC, &ycbcr[1]);
+}
+
 void GetYuvSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height, uint32_t bpp,
                        struct android_ycbcr *ycbcr) {
   unsigned int ystride, cstride;
@@ -332,6 +354,7 @@
   gralloc1_producer_usage_t prod_usage = hnd->GetProducerUsage();
   gralloc1_consumer_usage_t cons_usage = hnd->GetConsumerUsage();
   unsigned int ystride, cstride;
+  bool interlaced = false;
 
   memset(ycbcr->reserved, 0, sizeof(ycbcr->reserved));
   MetaData_t *metadata = reinterpret_cast<MetaData_t *>(hnd->base_metadata);
@@ -354,6 +377,11 @@
     GetAlignedWidthAndHeight(info, &width, &height);
   }
 
+  // Check metadata for interlaced content.
+  if (metadata && (metadata->operation & PP_PARAM_INTERLACED)) {
+    interlaced = metadata->interlaced ? true : false;
+  }
+
   // Get the chroma offsets from the handle width/height. We take advantage
   // of the fact the width _is_ the stride
   switch (format) {
@@ -371,7 +399,11 @@
       break;
 
     case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC:
-      GetYuvUbwcSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      if (!interlaced) {
+        GetYuvUbwcSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      } else {
+        GetYuvUbwcInterlacedSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      }
       ycbcr->chroma_step = 2;
       break;
 
diff --git a/libgralloc1/gr_utils.h b/libgralloc1/gr_utils.h
index aa66fd0..2a08539 100644
--- a/libgralloc1/gr_utils.h
+++ b/libgralloc1/gr_utils.h
@@ -83,6 +83,8 @@
                        struct android_ycbcr *ycbcr);
 void GetYuvUbwcSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height, int color_format,
                            struct android_ycbcr *ycbcr);
+void GetYuvUbwcInterlacedSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height,
+                                     int color_format, struct android_ycbcr *ycbcr);
 void GetRgbUBwcBlockSize(uint32_t bpp, int *block_width, int *block_height);
 unsigned int GetRgbUBwcMetaBufferSize(int width, int height, uint32_t bpp);
 unsigned int GetUBwcSize(int width, int height, int format, unsigned int alignedw,
diff --git a/libgralloc1/gralloc_priv.h b/libgralloc1/gralloc_priv.h
index ba156b6..1839d2f 100644
--- a/libgralloc1/gralloc_priv.h
+++ b/libgralloc1/gralloc_priv.h
@@ -97,6 +97,7 @@
 #define GRALLOC_MODULE_PERFORM_SET_SINGLE_BUFFER_MODE 13
 #define GRALLOC1_MODULE_PERFORM_GET_BUFFER_SIZE_AND_DIMENSIONS 14
 #define GRALLOC1_MODULE_PERFORM_ALLOCATE_BUFFER 15
+#define GRALLOC1_MODULE_PERFORM_GET_INTERLACE_FLAG 16
 
 // OEM specific HAL formats
 #define HAL_PIXEL_FORMAT_RGBA_5551 6
diff --git a/sdm/libs/core/Android.mk b/sdm/libs/core/Android.mk
index 1d55d96..c5002a9 100644
--- a/sdm/libs/core/Android.mk
+++ b/sdm/libs/core/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_MODULE_TAGS             := optional
 LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes)
 LOCAL_HEADER_LIBRARIES        := display_headers
-LOCAL_CFLAGS                  := -Wno-unused-parameter -DLOG_TAG=\"SDM\" \
+LOCAL_CFLAGS                  := -fno-operator-names -Wno-unused-parameter -DLOG_TAG=\"SDM\" \
                                  $(common_flags)
 LOCAL_HW_INTF_PATH_1          := fb
 LOCAL_SHARED_LIBRARIES        := libdl libsdmutils
@@ -51,6 +51,7 @@
                                  $(LOCAL_HW_INTF_PATH_2)/hw_device_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_events_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_scale_drm.cpp \
+                                 $(LOCAL_HW_INTF_PATH_2)/hw_virtual_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_color_manager_drm.cpp
 endif
 
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 1be973c..1028663 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -216,14 +216,6 @@
 }
 
 void HWDeviceDRM::Registry::RegisterCurrent(HWLayers *hw_layers) {
-  DRMMaster *master = nullptr;
-  DRMMaster::GetInstance(&master);
-
-  if (!master) {
-    DLOGE("Failed to acquire DRM Master instance");
-    return;
-  }
-
   HWLayersInfo &hw_layer_info = hw_layers->info;
   uint32_t hw_layer_count = UINT32(hw_layer_info.hw_layers.size());
 
@@ -237,28 +229,41 @@
       input_buffer = &hw_rotator_session->output_buffer;
     }
 
-    int fd = input_buffer->planes[0].fd;
-    if (fd >= 0 && hashmap_[current_index_].find(fd) == hashmap_[current_index_].end()) {
-      AllocatedBufferInfo buf_info {};
-      DRMBuffer layout {};
-      buf_info.fd = layout.fd = fd;
-      buf_info.aligned_width = layout.width = input_buffer->width;
-      buf_info.aligned_height = layout.height = input_buffer->height;
-      buf_info.format = input_buffer->format;
-      GetDRMFormat(buf_info.format, &layout.drm_format, &layout.drm_format_modifier);
-      buffer_allocator_->GetBufferLayout(buf_info, layout.stride, layout.offset,
-                                         &layout.num_planes);
-      uint32_t fb_id = 0;
-      int ret = master->CreateFbId(layout, &fb_id);
-      if (ret < 0) {
-        DLOGE("CreateFbId failed. width %d, height %d, format: %s, stride %u, error %d",
-              layout.width, layout.height, GetFormatString(buf_info.format), layout.stride[0],
-              errno);
-      } else {
-        hashmap_[current_index_][fd] = fb_id;
-      }
+    MapBufferToFbId(input_buffer);
+  }
+}
+
+void HWDeviceDRM::Registry::MapBufferToFbId(LayerBuffer* buffer) {
+  int fd = buffer->planes[0].fd;
+  DRMMaster *master = nullptr;
+  DRMMaster::GetInstance(&master);
+
+  if (!master) {
+    DLOGE("Failed to acquire DRM Master instance");
+    return;
+  }
+
+  if (fd >= 0 && hashmap_[current_index_].find(fd) == hashmap_[current_index_].end()) {
+    AllocatedBufferInfo buf_info{};
+    DRMBuffer layout{};
+    buf_info.fd = layout.fd = fd;
+    buf_info.aligned_width = layout.width = buffer->width;
+    buf_info.aligned_height = layout.height = buffer->height;
+    buf_info.format = buffer->format;
+    GetDRMFormat(buf_info.format, &layout.drm_format, &layout.drm_format_modifier);
+    buffer_allocator_->GetBufferLayout(buf_info, layout.stride, layout.offset,
+        &layout.num_planes);
+    uint32_t fb_id = 0;
+    int ret = master->CreateFbId(layout, &fb_id);
+    if (ret < 0) {
+      DLOGE("CreateFbId failed. width %d, height %d, format: %s, stride %u, error %d",
+          layout.width, layout.height, GetFormatString(buf_info.format), layout.stride[0],
+          errno);
+    } else {
+      hashmap_[current_index_][fd] = fb_id;
     }
   }
+  return;
 }
 
 void HWDeviceDRM::Registry::UnregisterNext() {
@@ -300,6 +305,7 @@
     : hw_info_intf_(hw_info_intf), buffer_sync_handler_(buffer_sync_handler),
       registry_(buffer_allocator) {
   device_type_ = kDevicePrimary;
+  disp_type_ = DRMDisplayType::PERIPHERAL;
   device_name_ = "Peripheral Display";
   hw_info_intf_ = hw_info_intf;
 }
@@ -309,15 +315,13 @@
 
   if (!default_mode_) {
     DRMMaster *drm_master = {};
-    int dev_fd = -1;
     DRMMaster::GetInstance(&drm_master);
-    drm_master->GetHandle(&dev_fd);
-    DRMLibLoader::GetInstance()->FuncGetDRMManager()(dev_fd, &drm_mgr_intf_);
-    if (drm_mgr_intf_->RegisterDisplay(DRMDisplayType::PERIPHERAL, &token_)) {
-      DLOGE("RegisterDisplay failed");
+    drm_master->GetHandle(&dev_fd_);
+    DRMLibLoader::GetInstance()->FuncGetDRMManager()(dev_fd_, &drm_mgr_intf_);
+    if (drm_mgr_intf_->RegisterDisplay(disp_type_, &token_)) {
+      DLOGE("RegisterDisplay failed for display %d", disp_type_);
       return kErrorResources;
     }
-
     drm_mgr_intf_->CreateAtomicReq(token_, &drm_atomic_intf_);
     drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
     InitializeConfigs();
@@ -325,17 +329,18 @@
 
     drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
     // Commit to setup pipeline with mode, which then tells us the topology etc
-    if (drm_atomic_intf_->Commit(true /* synchronous */)) {
-      DLOGE("Setting up CRTC %d, Connector %d for %s failed", token_.crtc_id, token_.conn_id,
-            device_name_);
-      return kErrorResources;
+
+    if (!deferred_initialize_) {
+      if (drm_atomic_intf_->Commit(true /* synchronous */)) {
+        DRM_LOGI("Setting up CRTC %d, Connector %d for %s failed", token_.crtc_id,
+          token_.conn_id, device_name_);
+        return kErrorResources;
+      }
+      // Reload connector info for updated info after 1st commit
+
+      drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
     }
-
-    // Reload connector info for updated info after 1st commit
-    drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
-    DLOGI("Setup CRTC %d, Connector %d for %s", token_.crtc_id, token_.conn_id, device_name_);
   }
-
   PopulateDisplayAttributes();
   PopulateHWPanelInfo();
   UpdateMixerAttributes();
@@ -569,18 +574,19 @@
 
 DisplayError HWDeviceDRM::PowerOn() {
   DTRACE_SCOPED();
-/*
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
   drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::ON);
-*/
+  int ret = drm_atomic_intf_->Commit(false /* synchronous */);
+  if (ret) {
+    DLOGE("%s failed with error %d", __FUNCTION__, ret);
+    return kErrorHardware;
+  }
   return kErrorNone;
 }
 
 DisplayError HWDeviceDRM::PowerOff() {
-/*
   drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::OFF);
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 0);
-*/
   int ret = drm_atomic_intf_->Commit(false /* synchronous */);
   if (ret) {
     DLOGE("%s failed with error %d", __FUNCTION__, ret);
@@ -805,7 +811,7 @@
 
   int ret = drm_atomic_intf_->Commit(false /* synchronous */);
   if (ret) {
-    DLOGE("%s failed with error %d", __FUNCTION__, ret);
+    DLOGE("%s failed with error %d crtc %d", __FUNCTION__, ret, token_.crtc_id);
     return kErrorHardware;
   }
 
@@ -881,7 +887,6 @@
 
 DisplayError HWDeviceDRM::GetPPFeaturesVersion(PPFeatureVersion *vers) {
   struct DRMPPFeatureInfo info = {};
-
   for (uint32_t i = 0; i < kMaxNumPPFeatures; i++) {
     memset(&info, 0, sizeof(struct DRMPPFeatureInfo));
     info.id = HWColorManagerDrm::ToDrmFeatureId(i);
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index 16954af..ad7a3e3 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -49,7 +49,7 @@
 
 class HWDeviceDRM : public HWInterface {
  public:
-  explicit HWDeviceDRM(BufferSyncHandler *buffer_sync_handler, BufferAllocator *buffer_allocator,
+  HWDeviceDRM(BufferSyncHandler *buffer_sync_handler, BufferAllocator *buffer_allocator,
                        HWInfoInterface *hw_info_intf);
   virtual ~HWDeviceDRM() {}
   virtual DisplayError Init();
@@ -93,6 +93,7 @@
   virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
   virtual DisplayError SetMixerAttributes(const HWMixerAttributes &mixer_attributes);
   virtual DisplayError GetMixerAttributes(HWMixerAttributes *mixer_attributes);
+  virtual void InitializeConfigs();
 
   enum {
     kHWEventVSync,
@@ -115,7 +116,6 @@
   void ResetDisplayParams();
   bool EnableHotPlugDetection(int enable);
   void UpdateMixerAttributes();
-  void InitializeConfigs();
   void SetBlending(const LayerBlending &source, sde_drm::DRMBlendType *target);
   void SetSrcConfig(const LayerBuffer &input_buffer, uint32_t *config);
   void SetRect(const LayerRect &source, sde_drm::DRMRect *target);
@@ -132,6 +132,8 @@
     void UnregisterNext();
     // Call on display disconnect to release all gem handles and fb_ids
     void Clear();
+    // Maps given fd to FB ID
+    void MapBufferToFbId(LayerBuffer* buffer);
     // Finds an fb_id corresponding to an fd in current map
     uint32_t GetFbId(int fd);
 
@@ -144,24 +146,30 @@
     BufferAllocator *buffer_allocator_ = {};
   };
 
-  HWResourceInfo hw_resource_ = {};
-  HWPanelInfo hw_panel_info_ = {};
+ protected:
+  const char *device_name_ = {};
+  bool deferred_initialize_ = false;
+  sde_drm::DRMDisplayType disp_type_ = {};
   HWInfoInterface *hw_info_intf_ = {};
   BufferSyncHandler *buffer_sync_handler_ = {};
+  int dev_fd_ = -1;
+  Registry registry_;
+  sde_drm::DRMDisplayToken token_ = {};
+  HWResourceInfo hw_resource_ = {};
+  HWPanelInfo hw_panel_info_ = {};
   HWDeviceType device_type_ = {};
-  const char *device_name_ = {};
-  bool synchronous_commit_ = false;
-  HWDisplayAttributes display_attributes_ = {};
-  HWMixerAttributes mixer_attributes_ = {};
   sde_drm::DRMManagerInterface *drm_mgr_intf_ = {};
   sde_drm::DRMAtomicReqInterface *drm_atomic_intf_ = {};
-  sde_drm::DRMDisplayToken token_ = {};
-  drmModeModeInfo current_mode_ = {};
-  bool default_mode_ = false;
   sde_drm::DRMConnectorInfo connector_info_ = {};
+  drmModeModeInfo current_mode_ = {};
+  HWDisplayAttributes display_attributes_ = {};
+
+ private:
+  bool synchronous_commit_ = false;
+  HWMixerAttributes mixer_attributes_ = {};
+  bool default_mode_ = false;
   std::string interface_str_ = "DSI";
   HWScaleDRM *hw_scale_ = {};
-  Registry registry_;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_virtual_drm.cpp b/sdm/libs/core/drm/hw_virtual_drm.cpp
new file mode 100644
index 0000000..5fe1d86
--- /dev/null
+++ b/sdm/libs/core/drm/hw_virtual_drm.cpp
@@ -0,0 +1,188 @@
+/*
+Copyright (c) 2017, 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 <stdio.h>
+#include <ctype.h>
+#include <drm_logger.h>
+#include <utils/debug.h>
+#include "hw_device_drm.h"
+#include "hw_virtual_drm.h"
+#include "hw_info_drm.h"
+
+#define __CLASS__ "HWVirtualDRM"
+
+using sde_drm::DRMDisplayType;
+using sde_drm::DRMConnectorInfo;
+using sde_drm::DRMRect;
+using sde_drm::DRMOps;
+
+namespace sdm {
+
+HWVirtualDRM::HWVirtualDRM(BufferSyncHandler *buffer_sync_handler,
+                           BufferAllocator *buffer_allocator,
+                           HWInfoInterface *hw_info_intf)
+                           : HWDeviceDRM(buffer_sync_handler, buffer_allocator, hw_info_intf) {
+  HWDeviceDRM::deferred_initialize_ = true;
+  HWDeviceDRM::device_name_ = "Virtual Display Device";
+  HWDeviceDRM::hw_info_intf_ = hw_info_intf;
+  HWDeviceDRM::disp_type_ = DRMDisplayType::VIRTUAL;
+}
+
+DisplayError HWVirtualDRM::Init() {
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::DeferredInit() {
+  if (HWDeviceDRM::Init() != kErrorNone)
+    return kErrorResources;
+
+  drm_mgr_intf_->SetScalerLUT(drm_lut_info_);
+  DLOGI_IF(kTagDriverConfig, "Setup CRTC %d, Connector %d for %s",
+            token_.crtc_id, token_.conn_id, device_name_);
+
+  return kErrorNone;
+}
+
+void HWVirtualDRM::ConfigureWbConnectorFbId(uint32_t fb_id) {
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_FB_ID, token_.conn_id, fb_id);
+  return;
+}
+
+void HWVirtualDRM::ConfigureWbConnectorDestRect() {
+  DRMRect dst = {};
+  dst.left = 0;
+  dst.bottom = height_;
+  dst.top = 0;
+  dst.right = width_;
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_RECT, token_.conn_id, dst);
+  return;
+}
+
+void HWVirtualDRM::InitializeConfigs() {
+  current_mode_.hdisplay = current_mode_.hsync_start = current_mode_.hsync_end \
+  = current_mode_.htotal = (uint16_t) width_;
+  current_mode_.vdisplay = current_mode_.vsync_start = current_mode_.vsync_end \
+  = current_mode_.vtotal = (uint16_t) height_;
+  // Not sure SF has a way to configure refresh rate. Hardcoding to 60 fps for now.
+  // TODO(user): Make this configurable.
+  current_mode_.vrefresh = 60;
+  current_mode_.clock = (current_mode_.htotal * current_mode_.vtotal \
+  * current_mode_.vrefresh) / 1000;
+  struct sde_drm_wb_cfg wb_cfg;
+  wb_cfg.connector_id = token_.conn_id;
+  wb_cfg.flags |= SDE_DRM_WB_CFG_FLAGS_CONNECTED;
+  wb_cfg.count_modes = 1;
+  wb_cfg.modes = (uint64_t)&current_mode_;
+  #ifdef DRM_IOCTL_SDE_WB_CONFIG
+  int ret = drmIoctl(dev_fd_, DRM_IOCTL_SDE_WB_CONFIG, &wb_cfg);
+  #endif
+  if (ret) {
+    DLOGE("WB config failed\n");
+  } else {
+    drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
+    current_mode_ = connector_info_.modes[0];
+    DumpConfigs();
+  }
+}
+
+void HWVirtualDRM::DumpConfigs() {
+  for (uint32_t i = 0; i < (uint32_t)connector_info_.num_modes; i++) {
+  DLOGI(
+    "Name: %s\tvref: %d\thdisp: %d\t hsync_s: %d\thsync_e:%d\thtotal: %d\t"
+    "vdisp: %d\tvsync_s: %d\tvsync_e: %d\tvtotal: %d\n",
+    connector_info_.modes[i].name, connector_info_.modes[i].vrefresh,
+    connector_info_.modes[i].hdisplay,
+    connector_info_.modes[i].hsync_start, connector_info_.modes[i].hsync_end,
+    connector_info_.modes[i].htotal, connector_info_.modes[i].vdisplay,
+    connector_info_.modes[i].vsync_start, connector_info_.modes[i].vsync_end,
+    connector_info_.modes[i].vtotal);
+  }
+}
+
+DisplayError HWVirtualDRM::Commit(HWLayers *hw_layers) {
+  LayerBuffer *output_buffer = hw_layers->info.stack->output_buffer;
+  DisplayError err = kErrorNone;
+
+  registry_.RegisterCurrent(hw_layers);
+  registry_.MapBufferToFbId(output_buffer);
+  uint32_t fb_id = registry_.GetFbId(output_buffer->planes[0].fd);
+
+  ConfigureWbConnectorFbId(fb_id);
+  ConfigureWbConnectorDestRect();
+
+  err = HWDeviceDRM::AtomicCommit(hw_layers);
+  registry_.UnregisterNext();
+  return(err);
+}
+
+DisplayError HWVirtualDRM::Validate(HWLayers *hw_layers) {
+  // TODO(user) : Add validate support
+  return kErrorNone;
+}
+
+
+DisplayError HWVirtualDRM::SetDisplayAttributes(const HWDisplayAttributes &display_attributes) {
+  if (display_attributes.x_pixels == 0 || display_attributes.y_pixels == 0) {
+    return kErrorParameters;
+  }
+
+  display_attributes_ = display_attributes;
+
+  if (display_attributes_.x_pixels > hw_resource_.max_mixer_width) {
+    display_attributes_.is_device_split = true;
+  }
+
+  width_ = display_attributes_.x_pixels;
+  height_ = display_attributes_.y_pixels;
+  DeferredInit();
+
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::GetPPFeaturesVersion(PPFeatureVersion *vers) {
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::SetScaleLutConfig(HWScaleLutInfo *lut_info) {
+  drm_lut_info_.cir_lut = lut_info->cir_lut;
+  drm_lut_info_.dir_lut = lut_info->dir_lut;
+  drm_lut_info_.sep_lut = lut_info->sep_lut;
+  drm_lut_info_.cir_lut_size = lut_info->cir_lut_size;
+  drm_lut_info_.dir_lut_size = lut_info->dir_lut_size;
+  drm_lut_info_.sep_lut_size = lut_info->sep_lut_size;
+
+  // Due to differed Init in WB case, we cannot set scaler config immediately as we
+  // won't have SDE DRM initialized at this point. Hence have to cache LUT info here
+  // and set it in ::DeferredInit
+
+  return kErrorNone;
+}
+
+}  // namespace sdm
+
diff --git a/sdm/libs/core/drm/hw_virtual_drm.h b/sdm/libs/core/drm/hw_virtual_drm.h
new file mode 100644
index 0000000..b63519a
--- /dev/null
+++ b/sdm/libs/core/drm/hw_virtual_drm.h
@@ -0,0 +1,71 @@
+/*
+Copyright (c) 2017, 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 __HW_VIRTUAL_DRM_H__
+#define __HW_VIRTUAL_DRM_H__
+
+#include "hw_device_drm.h"
+#include <drm/msm_drm.h>
+#include <drm/sde_drm.h>
+
+namespace sdm {
+
+class HWVirtualDRM : public HWDeviceDRM {
+ public:
+  HWVirtualDRM(BufferSyncHandler *buffer_sync_handler,
+               BufferAllocator *buffer_allocator, HWInfoInterface *hw_info_intf);
+  virtual ~HWVirtualDRM() {}
+  virtual DisplayError SetVSyncState(bool enable) { return kErrorNotSupported; }
+  virtual DisplayError SetMixerAttributes(const HWMixerAttributes &mixer_attributes) {
+    return kErrorNotSupported;
+  }
+  virtual DisplayError SetDisplayAttributes(const HWDisplayAttributes &display_attributes);
+
+ protected:
+  virtual DisplayError Init();
+  virtual DisplayError Validate(HWLayers *hw_layers);
+  virtual DisplayError DeferredInit();
+  virtual void InitializeConfigs();
+  virtual DisplayError Commit(HWLayers *hw_layers);
+  virtual DisplayError GetPPFeaturesVersion(PPFeatureVersion *vers);
+  virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
+  void ConfigureWbConnectorFbId(uint32_t fb_id);
+  void ConfigureWbConnectorDestRect();
+  void DumpConfigs();
+
+ private:
+  uint32_t width_ = 0;
+  uint32_t height_ = 0;
+  sde_drm::DRMScalerLUTInfo drm_lut_info_ = {};
+};
+
+}  // namespace sdm
+
+#endif  // __HW_VIRTUAL_DRM_H__
+
diff --git a/sdm/libs/core/hw_interface.cpp b/sdm/libs/core/hw_interface.cpp
index b5c9fe9..a7bcabd 100644
--- a/sdm/libs/core/hw_interface.cpp
+++ b/sdm/libs/core/hw_interface.cpp
@@ -37,6 +37,7 @@
 #include "fb/hw_virtual.h"
 #ifdef COMPILE_DRM
 #include "drm/hw_device_drm.h"
+#include "drm/hw_virtual_drm.h"
 #endif
 
 #define __CLASS__ "HWInterface"
@@ -71,7 +72,9 @@
       if (driver_type == DriverType::FB) {
         hw = new HWVirtual(buffer_sync_handler, hw_info_intf);
       } else {
-        return kErrorNotSupported;
+#ifdef COMPILE_DRM
+        hw = new HWVirtualDRM(buffer_sync_handler, buffer_allocator, hw_info_intf);
+#endif
       }
       break;
     default: