Merge remote-tracking branch 'quic/display.lnx.7.0.1' into 8.0

Change-Id: I9312a2c7733584a86ad97d1f6bb81b03b193e190
diff --git a/Android.bp b/Android.bp
index f4e137d..2a55fb3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -140,4 +140,5 @@
     "sdm/libs/core",
     "qmaa",
     "libmemutils",
+    "oem_services",
 ]
diff --git a/composer/Android.bp b/composer/Android.bp
index 924ac69..03b488b 100644
--- a/composer/Android.bp
+++ b/composer/Android.bp
@@ -60,6 +60,8 @@
         "libgralloctypes",
         "libdisplayconfig.qti",
         "libdrm",
+        "libthermalclient",
+        "vendor.qti.hardware.display.demura@1.0",
     ],
     srcs: composer_srcs,
 
diff --git a/composer/display_null.h b/composer/display_null.h
index 7cdd016..37fc4b4 100644
--- a/composer/display_null.h
+++ b/composer/display_null.h
@@ -66,6 +66,7 @@
   virtual bool IsSupportSsppTonemap() { return false; }
   virtual bool CanSkipValidate() { return true; }
   virtual bool GameEnhanceSupported() { return false; }
+  virtual bool HasDemura() { return false; }
 
   MAKE_NO_OP(Commit(LayerStack *))
   MAKE_NO_OP(GetDisplayState(DisplayState *))
diff --git a/composer/hwc_display.cpp b/composer/hwc_display.cpp
index 5fb53e2..c9e7643 100644
--- a/composer/hwc_display.cpp
+++ b/composer/hwc_display.cpp
@@ -2516,6 +2516,10 @@
     return false;
   }
 
+  if (display_intf_->HasDemura()) {
+    return false;
+  }
+
   bool skip_prepare = true;
   for (auto hwc_layer : layer_set_) {
     if (!hwc_layer->GetSDMLayer()->flags.skip ||
diff --git a/composer/ipc_impl.cpp b/composer/ipc_impl.cpp
index e4a6a7b..0d1e992 100644
--- a/composer/ipc_impl.cpp
+++ b/composer/ipc_impl.cpp
@@ -43,6 +43,11 @@
 QRTRClientInterface *IPCImpl::qrtr_client_intf_ = nullptr;
 
 int IPCImpl::Init() {
+  if (init_done_) {
+    DLOGW("IPC intf already initialized");
+    return 0;
+  }
+
   // Try to load extension library & get handle to its interface.
   if (qrtr_client_lib_.Open(QRTR_CLIENT_LIB_NAME)) {
     if (!qrtr_client_lib_.Sym(CREATE_QRTR_CLIENT_INTERFACE_NAME,
@@ -68,11 +73,13 @@
   } else {
     DLOGW("Unable to load = %s, error = %s", QRTR_CLIENT_LIB_NAME, qrtr_client_lib_.Error());
   }
+  init_done_ = true;
   return 0;
 }
 
 int IPCImpl::Deinit() {
   if (destroy_qrtr_client_intf_) {
+    init_done_ = false;
     return destroy_qrtr_client_intf_(qrtr_client_intf_);
   }
   return 0;
@@ -138,11 +145,56 @@
 }
 
 int IPCImpl::ProcessOps(IPCOps op, const GenericPayload &in, GenericPayload *out) {
-  (void)op;
-  (void)in;
-  (void)out;
-  DLOGE("ProcessOps on op %d is not supported", op);
-  return -ENOTSUP;
+  if (!out) {
+    return -EINVAL;
+  }
+
+  int ret = 0;
+
+  switch (op) {
+    case kIpcOpsFilePath: {
+      uint32_t sz = 0;
+      uint64_t* panel_id = nullptr;
+      std::string *demura_file = nullptr;
+      std::string file_path = "";
+      sp<IDemuraFileFinder> mClient = IDemuraFileFinder::getService();
+      if (mClient != NULL) {
+        if ((ret = in.GetPayload(panel_id, &sz))) {
+          DLOGE("Failed to get input payload error = %d", ret);
+          return ret;
+        }
+        DLOGI("panel_id %" PRIu64, *panel_id);
+        if ((ret = out->GetPayload(demura_file, &sz))) {
+          DLOGE("Failed to get output payload error = %d", ret);
+          return ret;
+        }
+        mClient->getCorrectionFile((*panel_id),
+                                  [&](const auto& tmpReturn, const auto& tmpHandle){
+                                      ret = tmpReturn;
+                                      if (ret != 0) {
+                                        file_path = "";
+                                        return;
+                                      }
+                                      file_path=(std::string)tmpHandle;
+                                    });
+        if (ret != 0) {
+          DLOGE("getCorrectionFile failed %d", ret);
+          return ret;
+        }
+        *demura_file = file_path;
+        DLOGI("File Path %s", file_path.c_str());
+      } else {
+        DLOGE("Could not get IDemuraFileFinder");
+        return -ENODEV;
+      }
+      break;
+    }
+    default:
+        DLOGE("Unsupported IPCOps");
+        return -EINVAL;
+  }
+
+  return ret;
 }
 
 int IPCImpl::OnResponse(Response *rsp) {
@@ -168,5 +220,4 @@
   DLOGI("LE server is exited");
 }
 
-
 }  // namespace sdm
diff --git a/composer/ipc_impl.h b/composer/ipc_impl.h
index e93d0a7..3011d57 100644
--- a/composer/ipc_impl.h
+++ b/composer/ipc_impl.h
@@ -35,8 +35,13 @@
 #include "vm_interface.h"
 #include "utils/sys.h"
 
+#include <vendor/qti/hardware/display/demura/1.0/IDemuraFileFinder.h>
+
 namespace sdm {
 
+using ::android::sp;
+using ::vendor::qti::hardware::display::demura::V1_0::IDemuraFileFinder;
+
 class IPCImpl: public IPCIntf, QRTRCallbackInterface {
  public:
   virtual ~IPCImpl() {};
@@ -54,6 +59,7 @@
   static CreateQrtrClientIntf create_qrtr_client_intf_;
   static DestroyQrtrClientIntf destroy_qrtr_client_intf_;
   static QRTRClientInterface *qrtr_client_intf_;
+  bool init_done_ = false;
 };
 }  // namespace sdm
 
diff --git a/config/display-product.mk b/config/display-product.mk
index df074af..fae91cd 100644
--- a/config/display-product.mk
+++ b/config/display-product.mk
@@ -33,6 +33,11 @@
     modetest \
     libmemutils
 
+#oem_services library
+PRODUCT_PACKAGES += \
+    libfilefinder \
+    vendor.qti.hardware.display.demura@1.0-service
+
 ifneq ($(TARGET_HAS_LOW_RAM),true)
 #QDCM calibration xml file for 2k panel
 PRODUCT_COPY_FILES += hardware/qcom/display/config/qdcm_calib_data_nt35597_cmd_mode_dsi_truly_panel_with_DSC.xml:$(TARGET_COPY_OUT_VENDOR)/etc/qdcm_calib_data_nt35597_cmd_mode_dsi_truly_panel_with_DSC.xml
@@ -173,7 +178,11 @@
 SOONG_CONFIG_qtidisplay_default := true
 
 ifeq ($(TARGET_IS_HEADLESS), true)
+    # TODO: QMAA prebuilts
     PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/qmaa
+    PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/gralloc
+    PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/init
+    PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/libdebug
     SOONG_CONFIG_qtidisplay_headless := true
     SOONG_CONFIG_qtidisplay_default := false
 else
@@ -184,15 +193,16 @@
     #Properties that should not be set in QMAA are enabled here.
     PRODUCT_PROPERTY_OVERRIDES += \
         vendor.display.enable_early_wakeup=1
-    PRODUCT_SOONG_NAMESPACES += hardware/qcom/display
+    ifeq ($(BUILD_DISPLAY_TECHPACK_SOURCE), true)
+        PRODUCT_SOONG_NAMESPACES += hardware/qcom/display
+        PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/gralloc
+        PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/init
+        PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/libdebug
+    else
+        PRODUCT_SOONG_NAMESPACES += vendor/qcom/opensource/techpack/artifacts/display
+    endif
 endif
 
-#Modules that will be added in QMAA/Non-QMAA paths
-PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/gralloc
-PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/init
-PRODUCT_SOONG_NAMESPACES += hardware/qcom/display/libdebug
-
-
 QMAA_ENABLED_HAL_MODULES += display
 
 # Properties using default value:
diff --git a/include/display_properties.h b/include/display_properties.h
index d6d6501..375222d 100644
--- a/include/display_properties.h
+++ b/include/display_properties.h
@@ -125,6 +125,12 @@
 #define ENABLE_SPR                           DISPLAY_PROP("enable_spr")
 #define ENABLE_MEMORY_MAPPING                DISPLAY_PROP("enable_memory_mapping")
 
+// Panel Feature Demura Properties
+#define ENABLE_DEMURA                        DISPLAY_PROP("enable_demura")
+#define DISABLE_DEMURA_PRIMARY               DISPLAY_PROP("disable_demura_primary")
+#define DISABLE_DEMURA_SECONDARY             DISPLAY_PROP("disable_demura_secondary")
+#define DISABLE_DEMURA_PANEL_REPLACEMENT     DISPLAY_PROP("disable_demura_panel_replacement")
+
 // PERF hint properties
 #define ENABLE_PERF_HINT_LARGE_COMP_CYCLE    DISPLAY_PROP("enable_perf_hint_large_comp_cycle")
 #define DISABLE_DYNAMIC_FPS                  DISPLAY_PROP("disable_dynamic_fps")
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index 238e878..c22c5e2 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -406,6 +406,11 @@
    */
   COMMIT_PANEL_FEATURES,
   /*
+   * Op: Null Commit panel features.
+   * Arg: drmModeAtomicReq - Atomic request
+   */
+  NULL_COMMIT_PANEL_FEATURES,
+  /*
    * Op: Sets qsync mode on connector
    * Arg: uint32_t - Connector ID
    *     uint32_t - qsync mode
@@ -455,6 +460,7 @@
   OPAQUE = 1,
   PREMULTIPLIED = 2,
   COVERAGE = 3,
+  SKIP_BLENDING = 4,
 };
 
 enum struct DRMSrcConfig {
@@ -521,6 +527,10 @@
   kInlineRotationV2,
 };
 
+/* Type for panel feature resource reservation info */
+typedef std::tuple<std::string, int32_t, int8_t> FetchResource;
+typedef std::vector<FetchResource> FetchResourceList;
+
 /* Per CRTC Resource Info*/
 struct DRMCrtcInfo {
   bool has_src_split;
@@ -565,6 +575,8 @@
   uint32_t ubwc_version = 1;
   bool has_spr = false;
   uint64_t rc_total_mem_size = 0;
+  uint32_t demura_count = 0;
+  uint32_t dspp_count = 0;
 };
 
 enum struct DRMPlaneType {
@@ -612,6 +624,8 @@
   uint32_t dgm_csc_version = 0;  // csc used with DMA
   std::map<DRMTonemapLutType, uint32_t> tonemap_lut_version_map = {};
   bool block_sec_ui = false;
+  int32_t pipe_idx = -1;
+  int32_t demura_block_capability = -1;
 };
 
 // All DRM Planes as map<Plane_id , plane_type_info> listed from highest to lowest priority
@@ -685,6 +699,7 @@
   bool dyn_bitclk_support;
   std::vector<uint8_t> edid;
   uint32_t supported_colorspaces;
+  uint64_t panel_id = 0;
 };
 
 // All DRM Connectors as map<Connector_id , connector_info>
@@ -734,6 +749,7 @@
   kPropEnum,
   kPropRange,
   kPropBlob,
+  kPropBitmask,
   kPropTypeMax,
 };
 
@@ -810,6 +826,7 @@
 };
 
 enum DRMPanelFeatureID {
+  kDRMPanelFeaturePanelId,
   kDRMPanelFeatureDsppIndex,
   kDRMPanelFeatureDsppSPRInfo,
   kDRMPanelFeatureDsppDemuraInfo,
@@ -818,6 +835,7 @@
   kDRMPanelFeatureSPRPackType,
   kDRMPanelFeatureDemuraInit,
   kDRMPanelFeatureRCInit,
+  kDRMPanelFeatureDemuraResources,
   kDRMPanelFeatureMax,
 };
 
@@ -988,6 +1006,7 @@
 /* Destroy DRMManager instance */
 typedef int (*DestroyDRMManager)();
 
+
 /*
  * DRM Manager Interface - Any class which plans to implement helper function for vendor
  * specific DRM driver implementation must implement the below interface routines to work
@@ -1129,6 +1148,33 @@
    */
   virtual void SetPanelFeature(const DRMPanelFeatureInfo &info) = 0;
 
+  /*
+  * Mark particular panel feature property to be applied in the next null commit
+  * [input]: Display token to identify which display the property belongs to
+  * [input]: Feature ID
+  */
+  virtual void MarkPanelFeatureForNullCommit(const DRMDisplayToken &token,
+                                             const DRMPanelFeatureID &id) = 0;
+
+  /*
+  * Get the initial planes (cont. splash) info
+  * [input]: None
+  * [output]: Map from plane id to connector id
+  */
+  virtual void MapPlaneToConnector(std::map<uint32_t, uint32_t> *plane_to_connector) = 0;
+
+  /*
+   * Get the required Demura resources count for each Demura capable display type
+   * [output]: Key: display identifier Value: required demura resource count
+   */
+  virtual void GetRequiredDemuraFetchResourceCount(std::map<uint32_t, uint8_t>*
+                                                   required_demura_fetch_cnt) = 0;
+
+  /*
+  * Get the planes used for Demura in initial boot (cont. splash)
+  * [output]: List of plane ids that were used for Demura
+  */
+  virtual void GetInitialDemuraInfo(std::vector<uint32_t> *initial_demura_planes) = 0;
 };
 
 }  // namespace sde_drm
diff --git a/oem_services/Android.bp b/oem_services/Android.bp
new file mode 100644
index 0000000..69684bc
--- /dev/null
+++ b/oem_services/Android.bp
@@ -0,0 +1,82 @@
+//
+// file finder library
+//
+cc_library_shared {
+
+    name: "libfilefinder",
+    sanitize: {
+        integer_overflow: true,
+    },
+    cflags: [
+        "-Wno-undefined-bool-conversion",
+        "-Wno-format",
+        "-Wall",
+        "-fcolor-diagnostics",
+        "-Wno-unused-parameter",
+        "-DLOG_TAG=\"SDM\"",
+    ],
+    clang: true,
+    header_libs: ["display_headers"],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libutils",
+        "libdisplaydebug",
+        "vendor.qti.hardware.display.demura@1.0",
+    ],
+
+    srcs: ["file_finder_oem_extension.cpp"],
+    owner: "qti",
+    vendor: true,
+}
+
+//
+// demura hidl
+//
+
+cc_binary {
+
+    name: "vendor.qti.hardware.display.demura@1.0-service",
+    sanitize: {
+        integer_overflow: true,
+    },
+    cflags: [
+        "-Wno-undefined-bool-conversion",
+        "-Wno-format",
+        "-Wall",
+        "-fcolor-diagnostics",
+        "-Wno-unused-parameter",
+        "-DLOG_TAG=\"SDM\"",
+    ],
+    clang: true,
+
+    header_libs: ["display_headers"],
+
+    relative_install_path: "hw",
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libutils",
+        "libbinder",
+        "libhidlbase",
+        "libfilefinder",
+        "libdisplaydebug",
+        "libsdmutils",
+        "vendor.qti.hardware.display.demura@1.0",
+    ],
+
+    srcs: [
+        "demura_file_finder.cpp",
+        "service.cpp",
+    ],
+
+    owner: "qti",
+    vendor: true,
+
+    init_rc: ["vendor.qti.hardware.display.demura@1.0-service.rc"],
+    vintf_fragments: ["vendor.qti.hardware.display.demura-service.xml"],
+}
diff --git a/oem_services/demura_file_finder.cpp b/oem_services/demura_file_finder.cpp
new file mode 100644
index 0000000..344cb0b
--- /dev/null
+++ b/oem_services/demura_file_finder.cpp
@@ -0,0 +1,154 @@
+/*
+ *Copyright (c) 2020, 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 <string>
+#include "demura_file_finder.h"
+
+namespace vendor {
+namespace qti {
+namespace hardware {
+namespace display {
+namespace demura {
+namespace V1_0 {
+namespace implementation {
+
+using sdm::FileFinderInterface;
+using sdm::GenericPayload;
+using sdm::kFileFinderFileData;
+
+IDemuraFileFinder *DemuraFileFinder::file_finder_ = NULL;
+FileFinderInterface *DemuraFileFinder::file_intf_ = NULL;
+DestroyFileFinderIntf DemuraFileFinder::destroy_ff_intf_ = NULL;
+
+IDemuraFileFinder *DemuraFileFinder::GetInstance() {
+  if (!file_finder_) {
+    sdm::DynLib demura_file_intf_lib;
+    if (!demura_file_intf_lib.Open(OEM_FILE_FINDER_LIB_NAME)) {
+      ALOGE("Failed to load lib %s", OEM_FILE_FINDER_LIB_NAME);
+      return nullptr;
+    }
+
+    GetFileFinderIntf get_ff_intf = NULL;
+
+    if (!demura_file_intf_lib.Sym(GET_FILE_FINDER_INTF_NAME,
+                                  reinterpret_cast<void **>(&(get_ff_intf)))) {
+      ALOGE("Unable to load symbols, err %s", demura_file_intf_lib.Error());
+      return nullptr;
+    }
+    if (!demura_file_intf_lib.Sym(DESTROY_FILE_FINDER_INTF_NAME,
+                                  reinterpret_cast<void **>(&(destroy_ff_intf_)))) {
+      ALOGE("Unable to load symbols, err %s", demura_file_intf_lib.Error());
+      return nullptr;
+    }
+
+    file_intf_ = get_ff_intf();
+    if (!file_intf_) {
+      ALOGE("Failed to get FileFinder Interface!");
+      return nullptr;
+    }
+
+    int error = file_intf_->Init();
+    if (error) {
+      ALOGE("Failed to initalize FileFinderInterface error = %d", error);
+      return nullptr;
+    }
+    file_finder_ = new DemuraFileFinder();
+  }
+
+  return file_finder_;
+}
+
+DemuraFileFinder::~DemuraFileFinder() {
+  if (file_intf_) {
+    file_intf_->Deinit();
+    if (destroy_ff_intf_) {
+      destroy_ff_intf_();
+    }
+  }
+}
+
+Return<void> DemuraFileFinder::getCorrectionFile(uint64_t panel_id, getCorrectionFile_cb _hidl_cb) {
+  int ret = 0;
+  std::string client_file = "";
+
+  if (!file_intf_) {
+    ALOGE("file_intf_ was not initialized, or not found. Command not supported");
+    ret = -EINVAL;
+    _hidl_cb(ret, client_file);
+    return Void();
+  }
+
+  if (panel_id == UINT64_MAX) {
+    ret = -EINVAL;
+    _hidl_cb(ret, client_file);
+    return Void();
+  }
+
+  uint64_t *input = nullptr;
+  GenericPayload in;
+  GenericPayload out;
+  std::string *path = nullptr;
+
+  int error = 0;
+  if ((error = in.CreatePayload(input))) {
+    ret = -ENOMEM;
+    ALOGE("Failed to create input payload error = %d", error);
+    _hidl_cb(ret, client_file);
+    return Void();
+  }
+
+  if ((error = out.CreatePayload(path))) {
+    ret = -ENOMEM;
+    ALOGE("Failed to create output payload error = %d", error);
+    _hidl_cb(ret, client_file);
+    return Void();
+  }
+
+  *input = panel_id;
+  if ((error = file_intf_->ProcessOps(kFileFinderFileData, in, &out))) {
+    ret = error;
+    ALOGE("Failed to process ops error = %d", error);
+    _hidl_cb(ret, client_file);
+    return Void();
+  }
+
+  ALOGI("Demura correction file %s", path->c_str());
+  client_file = *path;
+  _hidl_cb(ret, client_file);
+
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace demura
+}  // namespace display
+}  // namespace hardware
+}  // namespace qti
+}  // namespace vendor
diff --git a/oem_services/demura_file_finder.h b/oem_services/demura_file_finder.h
new file mode 100644
index 0000000..3aac305
--- /dev/null
+++ b/oem_services/demura_file_finder.h
@@ -0,0 +1,80 @@
+/*
+*Copyright (c) 2020, 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 __DEMURA_FILE_FINDER_H__
+#define __DEMURA_FILE_FINDER_H__
+
+#include <vendor/qti/hardware/display/demura/1.0/IDemuraFileFinder.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <log/log.h>
+#include <utils/sys.h>
+#include <file_finder_interface.h>
+
+namespace vendor {
+namespace qti {
+namespace hardware {
+namespace display {
+namespace demura {
+namespace V1_0 {
+namespace implementation {
+using sdm::FileFinderInterface;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::vendor::qti::hardware::display::demura::V1_0::IDemuraFileFinder;
+
+#define OEM_FILE_FINDER_LIB_NAME "libfilefinder.so"
+#define GET_FILE_FINDER_INTF_NAME "GetFileFinderIntf"
+#define DESTROY_FILE_FINDER_INTF_NAME "DestroyFileFinderIntf"
+typedef FileFinderInterface *(*GetFileFinderIntf)();
+typedef void *(*DestroyFileFinderIntf)();
+
+class DemuraFileFinder : public IDemuraFileFinder {
+ public:
+  virtual ~DemuraFileFinder();
+  static IDemuraFileFinder *GetInstance();
+  static FileFinderInterface *file_intf_;
+  static IDemuraFileFinder *file_finder_;
+  static DestroyFileFinderIntf destroy_ff_intf_;
+
+  // IDemuraFileFinder
+  Return<void> getCorrectionFile(uint64_t panel_id, getCorrectionFile_cb _hidl_cb) override;
+
+ private:
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace demura
+}  // namespace display
+}  // namespace hardware
+}  // namespace qti
+}  // namespace vendor
+
+#endif  // __DEMURA_FILE_FINDER_H__
diff --git a/oem_services/file_finder_interface.h b/oem_services/file_finder_interface.h
new file mode 100644
index 0000000..4303362
--- /dev/null
+++ b/oem_services/file_finder_interface.h
@@ -0,0 +1,53 @@
+/*
+*Copyright (c) 2020, 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 __FILE_FINDER_INTERFACE_H__
+#define __FILE_FINDER_INTERFACE_H__
+
+#include <private/generic_intf.h>
+#include <private/generic_payload.h>
+
+namespace sdm {
+
+enum FileFinderParams {
+  kFileFinderParamMax
+};
+
+enum FileFinderOps {
+  kFileFinderFileData,
+  kFileFinderOpMax
+};
+
+using FileFinderInterface = sdm::GenericIntf<FileFinderParams, FileFinderOps, GenericPayload>;
+extern "C" FileFinderInterface* GetFileFinderIntf();
+extern "C" void DestroyFileFinderIntf();
+
+}  // namespace sdm
+
+#endif  // __FILE_FINDER_INTERFACE_H__
diff --git a/oem_services/file_finder_oem_extension.cpp b/oem_services/file_finder_oem_extension.cpp
new file mode 100644
index 0000000..034b55a
--- /dev/null
+++ b/oem_services/file_finder_oem_extension.cpp
@@ -0,0 +1,210 @@
+/*
+*Copyright (c) 2020, 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 <sstream>
+#include <iomanip>
+#include <string>
+#include "file_finder_oem_extension.h"
+
+#define __CLASS__ "FileFinderOemExtn"
+
+#define DESTINATION_PATH "/data/vendor/display/demura/"
+#define LOCAL_SOURCE_PATH "/vendor/etc/"
+#define FILE_CHUNK_SIZE 8192
+
+namespace sdm {
+
+FileFinderOemExtn *FileFinderOemExtn::file_finder_ = NULL;
+uint32_t FileFinderOemExtn::ref_count_ = 0;
+std::mutex FileFinderOemExtn::lock_ = {};
+
+FileFinderInterface *GetFileFinderIntf() {
+  std::lock_guard<std::mutex> g(FileFinderOemExtn::lock_);
+  if (FileFinderOemExtn::file_finder_ == NULL) {
+    FileFinderOemExtn::file_finder_ = new FileFinderOemExtn();
+  }
+  FileFinderOemExtn::ref_count_++;
+  return FileFinderOemExtn::file_finder_;
+}
+
+void DestroyFileFinderIntf() {
+  std::lock_guard<std::mutex> g(FileFinderOemExtn::lock_);
+  FileFinderOemExtn::ref_count_--;
+  if (FileFinderOemExtn::ref_count_ == 0) {
+    delete FileFinderOemExtn::file_finder_;
+    FileFinderOemExtn::file_finder_ = NULL;
+  }
+}
+
+FileFinderOemExtn::FileFinderOemExtn() {
+  std::function<int(FileFinderOemExtn *, const GenericPayload &, GenericPayload *)> ffd =
+      &FileFinderOemExtn::FindFileData;
+  ops_fcns_.emplace(kFileFinderFileData, ffd);
+}
+
+int FileFinderOemExtn::Init() {
+  return 0;
+}
+
+int FileFinderOemExtn::Deinit() {
+  return 0;
+}
+
+int FileFinderOemExtn::SetParameter(FileFinderParams param, const GenericPayload &in) {
+  (void)param;
+  (void)in;
+  DLOGE("SetParameter on param %d not supported", param);
+  return -ENOTSUP;
+}
+
+int FileFinderOemExtn::GetParameter(FileFinderParams param, GenericPayload *out) {
+  (void)param;
+  (void)out;
+  DLOGE("GetParameter on param %d not supported", param);
+  return -ENOTSUP;
+}
+
+int FileFinderOemExtn::ProcessOps(FileFinderOps op, const GenericPayload &in, GenericPayload *out) {
+  if (out == NULL) {
+    return -EINVAL;
+  }
+
+  if (op < kFileFinderOpMax) {
+    auto fcn = ops_fcns_.at(op);
+    return fcn(this, in, out);
+  } else {
+    DLOGE("Invalid op %d", op);
+    return -EINVAL;
+  }
+}
+
+int FileFinderOemExtn::FindFileData(const GenericPayload &in, GenericPayload *out) {
+  int status = 0;
+  uint32_t sz = 0;
+  uint64_t *panel_id = nullptr;
+  std::string panel_id_hex_str = "";
+  FILE *file_in = nullptr;
+  FILE *file_out = nullptr;
+  std::string *file_path = nullptr;
+  std::string file_path_out = "";
+
+  status = in.GetPayload(panel_id, &sz);
+  if ((status != 0) || sz != 1) {
+    return -EINVAL;
+  }
+  std::stringstream temp;
+  uint64_t id = *panel_id;
+  temp << std::setfill('0') << std::setw(16) << std::hex << id << std::dec;
+  panel_id_hex_str = temp.str();
+
+  status = out->GetPayload(file_path, &sz);
+  if ((status != 0) || sz != 1) {
+    return -EINVAL;
+  }
+
+  file_in = getSrcFile(panel_id_hex_str);
+  if (!file_in) {
+    DLOGE("Did not get a correction file");
+    *file_path = "";
+    return -EINVAL;
+  }
+
+  errno = 0;
+  status = mkdir(DESTINATION_PATH, 755);
+  if ((status != 0) && errno != EEXIST) {
+    DLOGE("Failed to create %s directory errno = %d, desc = %s", DESTINATION_PATH, errno,
+          strerror(errno));
+    fclose(file_in);
+    return -EPERM;
+  }
+
+  // Even if directory exists already, need to explicitly change the permission.
+  if (chmod(DESTINATION_PATH, 0755) != 0) {
+    DLOGE("Failed to change permissions on %s directory", DESTINATION_PATH);
+    fclose(file_in);
+    return -EACCES;
+  }
+
+  std::string ds = DESTINATION_PATH;
+  file_path_out = ds + "demura_config_" + panel_id_hex_str;
+  file_out = fopen(file_path_out.c_str(), "wb+");
+  if (file_out == NULL) {
+    DLOGI("cannot create file for writing in /data\n");
+    fclose(file_in);
+    return -ENOENT;
+  }
+
+  // Read and write contents from file
+  size_t bytes = 0;
+  uint8_t buffer[FILE_CHUNK_SIZE] = {};
+  while ((bytes = fread(buffer, 1, sizeof(buffer), file_in)) > 0) {
+    fwrite(buffer, 1, bytes, file_out);
+  }
+
+  fclose(file_in);
+  fclose(file_out);
+
+  *file_path = file_path_out;
+  return 0;
+}
+
+FILE *FileFinderOemExtn::getSrcFile(const std::string &panel_id_hex_str) {
+  FILE *file = NULL;
+  std::string sp = LOCAL_SOURCE_PATH;
+  std::string src_path = sp + "demura_config_" + panel_id_hex_str;
+  errno = 0;
+  file = fopen(src_path.c_str(), "rb");
+  if (file == NULL) {
+    DLOGW("Failed to open file locally at %s. Error = %s", src_path.c_str(), strerror(errno));
+    src_path = getFileOTA(panel_id_hex_str);
+    errno = 0;
+    file = fopen(src_path.c_str(), "rb");
+    if (file == NULL) {
+      DLOGE("Failed to open file after OTA at %s. Error = %s", src_path.c_str(), strerror(errno));
+    }
+  }
+
+  return file;
+}
+
+std::string FileFinderOemExtn::getFileOTA(const std::string &panel_id_hex_str) {
+  /*
+   * Communication to a server shall begin here.
+   * API shall securely contact the server and download the data to a file on the device
+   * and the location must accessible for both read and write by this process.
+   * This API shall return the path to the file to the caller.
+   *
+   * The stub impl of this API simply returns the local path the caller would have already checked
+   * before requesting server download of the file.
+   */
+  std::string sp = LOCAL_SOURCE_PATH;
+  return sp + "demura_config_" + panel_id_hex_str;
+}
+
+}  // namespace sdm
diff --git a/oem_services/file_finder_oem_extension.h b/oem_services/file_finder_oem_extension.h
new file mode 100644
index 0000000..563151e
--- /dev/null
+++ b/oem_services/file_finder_oem_extension.h
@@ -0,0 +1,70 @@
+/*
+*Copyright (c) 2020, 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 __FILE_FINDER_OEM_EXTENSION_H__
+#define __FILE_FINDER_OEM_EXTENSION_H__
+
+#include <vendor/qti/hardware/display/demura/1.0/IDemuraFileFinder.h>
+#include <log/log.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <mutex>
+#include <map>
+#include <string>
+#include "file_finder_interface.h"
+#include "debug_handler.h"
+
+using ::vendor::qti::hardware::display::demura::V1_0::IDemuraFileFinder;
+
+namespace sdm {
+
+class FileFinderOemExtn : public FileFinderInterface {
+ public:
+  virtual ~FileFinderOemExtn() {}
+  FileFinderOemExtn();
+  int Init();
+  int Deinit();
+  int SetParameter(FileFinderParams param, const GenericPayload &in);
+  int GetParameter(FileFinderParams param, GenericPayload *out);
+  int ProcessOps(FileFinderOps op, const GenericPayload &in, GenericPayload *out);
+  static FileFinderOemExtn *file_finder_;
+  static uint32_t ref_count_;
+  static std::mutex lock_;
+
+ private:
+  int FindFileData(const GenericPayload &in, GenericPayload *out);
+  FILE *getSrcFile(const std::string &panel_id_hex_str);
+  std::string getFileOTA(const std::string &panel_id_hex_str);
+  std::map<FileFinderOps,
+           std::function<int(FileFinderOemExtn *, const GenericPayload &, GenericPayload *)>>
+      ops_fcns_;
+};
+}  // namespace sdm
+
+#endif  // __FILE_FINDER_OEM_EXTENSION_H__
diff --git a/oem_services/service.cpp b/oem_services/service.cpp
new file mode 100644
index 0000000..a97d349
--- /dev/null
+++ b/oem_services/service.cpp
@@ -0,0 +1,63 @@
+/*
+*Copyright (c) 2020, 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 <vendor/qti/hardware/display/demura/1.0/IDemuraFileFinder.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+#include <binder/ProcessState.h>
+#include <hidl/LegacySupport.h>
+#include <log/log.h>
+#include "demura_file_finder.h"
+
+
+using vendor::qti::hardware::display::demura::V1_0::IDemuraFileFinder;
+using vendor::qti::hardware::display::demura::V1_0::implementation::DemuraFileFinder;
+
+using android::sp;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+using android::status_t;
+using android::OK;
+
+int main() {
+    android::ProcessState::initWithDriver("/dev/vndbinder");
+    android::sp<IDemuraFileFinder> demura_file_finder = DemuraFileFinder::GetInstance();
+    if (!demura_file_finder) {
+      ALOGE("Could not create the IDemuraFileFinder, not registering process!!");
+      return -1;
+    }
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+    if (demura_file_finder->registerAsService() != OK) {
+        ALOGE("Cannot register DemuraFileFinder service");
+        return -EINVAL;
+    }
+    joinRpcThreadpool();
+    return 0;
+}
diff --git a/oem_services/vendor.qti.hardware.display.demura-service.xml b/oem_services/vendor.qti.hardware.display.demura-service.xml
new file mode 100644
index 0000000..e8d1cdc
--- /dev/null
+++ b/oem_services/vendor.qti.hardware.display.demura-service.xml
@@ -0,0 +1,39 @@
+<!--
+*Copyright (c) 2020, 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.
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>vendor.qti.hardware.display.demura</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IDemuraFileFinder</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/oem_services/vendor.qti.hardware.display.demura@1.0-service.rc b/oem_services/vendor.qti.hardware.display.demura@1.0-service.rc
new file mode 100644
index 0000000..5316b6c
--- /dev/null
+++ b/oem_services/vendor.qti.hardware.display.demura@1.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.qti.hardware.display.demura /vendor/bin/hw/vendor.qti.hardware.display.demura@1.0-service
+    class hal
+    user system
+    group graphics
diff --git a/sde-drm/drm_atomic_req.cpp b/sde-drm/drm_atomic_req.cpp
index 87f863e..17f1a5a 100644
--- a/sde-drm/drm_atomic_req.cpp
+++ b/sde-drm/drm_atomic_req.cpp
@@ -141,6 +141,9 @@
     case DRMOps::COMMIT_PANEL_FEATURES: {
       drm_mgr_->GetPanelFeatureMgrIntf()->CommitPanelFeatures(drm_atomic_req_, token_);
     } break;
+    case DRMOps::NULL_COMMIT_PANEL_FEATURES: {
+      drm_mgr_->GetPanelFeatureMgrIntf()->NullCommitPanelFeatures(drm_atomic_req_, token_);
+    } break;
     default:
       DRM_LOGE("Invalid opcode %d", opcode);
   }
diff --git a/sde-drm/drm_connector.cpp b/sde-drm/drm_connector.cpp
index 0223f18..0837cba 100644
--- a/sde-drm/drm_connector.cpp
+++ b/sde-drm/drm_connector.cpp
@@ -54,6 +54,7 @@
 using std::string;
 using std::stringstream;
 using std::pair;
+using std::make_pair;
 using std::vector;
 using std::unique_ptr;
 using std::map;
@@ -432,6 +433,77 @@
   token->conn_id = 0;
 }
 
+// DSI only
+int DRMConnectorManager::GetPreferredModeLMCounts(std::map<uint32_t, uint8_t> *lm_counts) {
+  lock_guard<mutex> lock(lock_);
+  int ret = 0;
+  for (auto &conn : connector_pool_) {
+    uint32_t conn_type;
+    const uint32_t &id = conn.first;
+    conn.second->GetType(&conn_type);
+    if (conn_type == DRM_MODE_CONNECTOR_DSI) {
+      DRMConnectorInfo info = {};
+      connector_pool_[id]->GetInfo(&info);
+      uint8_t lm_cnt = 0;
+      uint32_t mode_index = 0;
+      for (uint32_t index = 0; index < info.modes.size(); index++) {
+        if (info.modes[index].mode.type & DRM_MODE_TYPE_PREFERRED) {
+          mode_index = index;
+          break;
+        }
+      }
+
+      switch (info.modes[mode_index].topology) {
+        case DRMTopology::SINGLE_LM:
+        case DRMTopology::SINGLE_LM_DSC:
+        case DRMTopology::PPSPLIT:
+          lm_cnt = 1;
+          break;
+        case DRMTopology::DUAL_LM:
+        case DRMTopology::DUAL_LM_DSC:
+        case DRMTopology::DUAL_LM_MERGE:
+        case DRMTopology::DUAL_LM_MERGE_DSC:
+        case DRMTopology::DUAL_LM_DSCMERGE:
+          lm_cnt = 2;
+          break;
+        case DRMTopology::QUAD_LM_MERGE:
+        case DRMTopology::QUAD_LM_DSCMERGE:
+        case DRMTopology::QUAD_LM_MERGE_DSC:
+        case DRMTopology::QUAD_LM_DSC4HSMERGE:
+          lm_cnt = 4;
+          break;
+        default:
+          lm_cnt = UINT8_MAX;
+          DLOGE("Invalid lm_cnt for connector %u", id);
+          break;
+      }
+
+      if (lm_cnt != UINT8_MAX) {
+        (*lm_counts)[id] = lm_cnt;
+      }
+    }
+  }
+
+  return ret;
+}
+
+void DRMConnectorManager::MapEncoderToConnector(std::map<uint32_t, uint32_t> *encoder_to_connector) {
+  lock_guard<mutex> lock(lock_);
+
+  if (!encoder_to_connector) {
+    DLOGE("map is NULL! Not expected.");
+    return;
+  }
+
+  encoder_to_connector->clear();
+
+  for (auto &conn : connector_pool_) {
+    uint32_t encoder_id = 0;
+    conn.second->GetEncoder(&encoder_id);
+    if (encoder_id)
+      encoder_to_connector->insert(make_pair(encoder_id, conn.first));
+  }
+}
 // ==============================================================================================//
 
 #undef __CLASS__
@@ -618,7 +690,7 @@
       // Move to the next mode_item
       mode_item = &info->modes.at(index++);
     } else if (line.find(topology) != string::npos) {
-      mode_item->topology = GetTopologyEnum(string(line, topology.length()));;
+      mode_item->topology = GetTopologyEnum(string(line, topology.length()));
     } else if (line.find(pu_num_roi) != string::npos) {
       mode_item->num_roi = std::stoi(string(line, pu_num_roi.length()));
     } else if (line.find(pu_xstart) != string::npos) {
@@ -689,6 +761,27 @@
   drmModeFreePropertyBlob(blob);
 }
 
+void DRMConnector::ParseCapabilities(uint64_t blob_id, uint64_t *panel_id) {
+  drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd_, blob_id);
+  if (!blob) {
+    return;
+  }
+
+  if (blob->length != sizeof(uint64_t)) {
+    DRM_LOGE("Expecting %zu bytes but got %u", sizeof(uint64_t), blob->length);
+    return;
+  }
+
+  // Read as-is / big endian. Driver has supplied the value in this manner.
+  uint8_t *data = (uint8_t*)(blob->data);
+  for (size_t i = 0; i < blob->length; i++) {
+    *panel_id = (*panel_id << 8) | *data;
+    data++;
+  }
+
+  drmModeFreePropertyBlob(blob);
+}
+
 int DRMConnector::GetInfo(DRMConnectorInfo *info) {
   uint32_t conn_id = drm_connector_->connector_id;
   if (!skip_connector_reload_ && (IsTVConnector(drm_connector_->connector_type)
@@ -792,6 +885,13 @@
     info->supported_colorspaces = props->prop_values[index];
   }
 
+  if (prop_mgr_.IsPropertyAvailable(DRMProperty::DEMURA_PANEL_ID)) {
+    index = std::distance(props->props,
+                          std::find(props->props, props->props + props->count_props,
+                                    prop_mgr_.GetPropertyId(DRMProperty::DEMURA_PANEL_ID)));
+    ParseCapabilities(props->prop_values[index], &info->panel_id);
+  }
+
   drmModeFreeObjectProperties(props);
 
   return 0;
diff --git a/sde-drm/drm_connector.h b/sde-drm/drm_connector.h
index 24f6e99..bd43cf2 100644
--- a/sde-drm/drm_connector.h
+++ b/sde-drm/drm_connector.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+* Copyright (c) 2019-2020, 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
@@ -57,6 +57,7 @@
   DRMStatus GetStatus() { return status_; }
   int GetInfo(DRMConnectorInfo *info);
   void GetType(uint32_t *conn_type) { *conn_type = drm_connector_->connector_type; }
+  void GetEncoder(uint32_t *encoder_id) { *encoder_id = drm_connector_->encoder_id; }
   void Perform(DRMOps code, drmModeAtomicReq *req, va_list args);
   int IsConnected() { return (DRM_MODE_CONNECTED == drm_connector_->connection); }
   int GetPossibleEncoders(std::set<uint32_t> *possible_encoders);
@@ -70,6 +71,7 @@
   void ParseModeProperties(uint64_t blob_id, DRMConnectorInfo *info);
   void ParseCapabilities(uint64_t blob_id, drm_msm_ext_hdr_properties *hdr_info);
   void ParseCapabilities(uint64_t blob_id, std::vector<uint8_t> *edid);
+  void ParseCapabilities(uint64_t blob_id, uint64_t *panel_id);
   void SetROI(drmModeAtomicReq *req, uint32_t obj_id, uint32_t num_roi,
               DRMRect *conn_rois);
 
@@ -99,6 +101,8 @@
   int GetConnectorInfo(uint32_t conn_id, DRMConnectorInfo *info);
   void GetConnectorList(std::vector<uint32_t> *conn_ids);
   int GetPossibleEncoders(uint32_t connector_id, std::set<uint32_t> *possible_encoders);
+  int GetPreferredModeLMCounts(std::map<uint32_t, uint8_t> *lm_counts);
+  void MapEncoderToConnector(std::map<uint32_t, uint32_t> *encoder_to_connector);
   ~DRMConnectorManager() {}
 
  private:
diff --git a/sde-drm/drm_crtc.cpp b/sde-drm/drm_crtc.cpp
index b17e009..c468b47 100644
--- a/sde-drm/drm_crtc.cpp
+++ b/sde-drm/drm_crtc.cpp
@@ -298,6 +298,13 @@
   crtc_pool_.at(crtc_id)->PostCommit(success);
 }
 
+void DRMCrtcManager::GetCrtcList(std::vector<uint32_t> *crtc_ids) {
+  lock_guard<mutex> lock(lock_);
+  for (auto &crtc : crtc_pool_) {
+    crtc_ids->emplace_back(crtc.first);
+  }
+}
+
 // ==============================================================================================//
 
 #undef __CLASS__
@@ -424,6 +431,8 @@
   string ubwc_version = "UBWC version=";
   string spr = "spr=";
   string rc_total_mem_size = "rc_mem_size=";
+  string demura_count = "demura_count=";
+  string dspp_count = "dspp_count=";
 
   while (std::getline(stream, line)) {
     if (line.find(max_blendstages) != string::npos) {
@@ -544,6 +553,10 @@
       crtc_info_.has_spr = std::stoi(string(line, spr.length())) == -1 ? false: true;
     } else if (line.find(rc_total_mem_size) != string::npos) {
       crtc_info_.rc_total_mem_size = std::stoi(string(line, rc_total_mem_size.length()));
+    } else if (line.find(demura_count) != string::npos) {
+      crtc_info_.demura_count = std::stoi(string(line, demura_count.length()));
+    } else if (line.find(dspp_count) != string::npos) {
+      crtc_info_.dspp_count = std::stoi(string(line, dspp_count.length()));
     }
   }
   drmModeFreePropertyBlob(blob);
diff --git a/sde-drm/drm_crtc.h b/sde-drm/drm_crtc.h
index a17b222..7ba3dc5 100644
--- a/sde-drm/drm_crtc.h
+++ b/sde-drm/drm_crtc.h
@@ -119,6 +119,7 @@
   void GetPPInfo(uint32_t crtc_id, DRMPPFeatureInfo *info);
   void PostValidate(uint32_t crtc_id, bool success);
   void PostCommit(uint32_t crtc_id, bool success);
+  void GetCrtcList(std::vector<uint32_t> *crtc_ids);
 
  private:
   int fd_ = -1;
diff --git a/sde-drm/drm_encoder.cpp b/sde-drm/drm_encoder.cpp
index ed2d8bf..cb38984 100644
--- a/sde-drm/drm_encoder.cpp
+++ b/sde-drm/drm_encoder.cpp
@@ -205,6 +205,22 @@
   return encoder_pool_[encoder_id]->GetPossibleCrtcIndices(possible_crtc_indices);
 }
 
+void DRMEncoderManager::MapCrtcToEncoder(std::map<uint32_t, uint32_t> *crtc_to_encoder) {
+  if (!crtc_to_encoder) {
+    DLOGE("Map is NULL! Not expected.");
+    return;
+  }
+
+  crtc_to_encoder->clear();
+
+  for (auto &encoder : encoder_pool_) {
+    uint32_t crtc_id = 0;
+    encoder.second->GetCrtc(&crtc_id);
+    if (crtc_id)
+      crtc_to_encoder->insert(make_pair(crtc_id, encoder.first));
+  }
+}
+
 // ==============================================================================================//
 
 #undef __CLASS__
diff --git a/sde-drm/drm_encoder.h b/sde-drm/drm_encoder.h
index c4ea586..a9f7217 100644
--- a/sde-drm/drm_encoder.h
+++ b/sde-drm/drm_encoder.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2019, The Linux Foundation. All rights reserved.
+* Copyright (c) 2019-2020, 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
@@ -51,12 +51,13 @@
   DRMStatus GetStatus() { return status_; }
   void GetInfo(DRMEncoderInfo *info);
   void GetType(uint32_t *encoder_type) {
-    drm_encoder_ ? *encoder_type = drm_encoder_->encoder_type
-      : *encoder_type = fake_type_;
+    *encoder_type = drm_encoder_ ? drm_encoder_->encoder_type : fake_type_;
   }
   void GetId(uint32_t *encoder_id) {
-   drm_encoder_ ? *encoder_id = drm_encoder_->encoder_id
-      : *encoder_id = fake_id_;
+    *encoder_id = drm_encoder_ ? drm_encoder_->encoder_id : fake_id_;
+  }
+  void GetCrtc(uint32_t *crtc_id) {
+    *crtc_id = drm_encoder_ ? drm_encoder_->crtc_id : 0;
   }
   int GetPossibleCrtcIndices(std::set<uint32_t> *possible_crtc_indices);
   void Dump();
@@ -89,6 +90,7 @@
   int GetEncoderInfo(uint32_t encoder_id, DRMEncoderInfo *info);
   int GetEncoderList(std::vector<uint32_t> *encoder_ids);
   int GetPossibleCrtcIndices(uint32_t encoder_id, std::set<uint32_t> *possible_crtc_indices);
+  void MapCrtcToEncoder(std::map<uint32_t, uint32_t> *crtc_to_encoder);
 
  private:
   int fd_ = -1;
diff --git a/sde-drm/drm_manager.cpp b/sde-drm/drm_manager.cpp
index 6a45710..04a4b15 100644
--- a/sde-drm/drm_manager.cpp
+++ b/sde-drm/drm_manager.cpp
@@ -39,6 +39,8 @@
 
 using std::lock_guard;
 using std::mutex;
+using std::pair;
+using std::make_pair;
 
 extern "C" {
 
@@ -386,7 +388,7 @@
     dpps_mgr_intf_->GetDppsFeatureInfo(info);
 }
 
-DRMPanelFeatureMgrIntf *DRMManager::GetPanelFeatureMgrIntf() {
+DRMPanelFeatureMgrIntf* DRMManager::GetPanelFeatureMgrIntf() {
   return panel_feature_mgr_intf_;
 }
 
@@ -406,4 +408,95 @@
   }
 }
 
+void DRMManager::MarkPanelFeatureForNullCommit(const DRMDisplayToken &token,
+                                               const DRMPanelFeatureID &id) {
+  if (panel_feature_mgr_intf_) {
+    panel_feature_mgr_intf_->MarkForNullCommit(token, id);
+  } else {
+    DRM_LOGE("Failed, panel feature mgr not available");
+  }
+}
+
+void DRMManager::MapPlaneToConnector(std::map<uint32_t, uint32_t> *plane_to_connector) {
+  if (!plane_to_connector) {
+    DRM_LOGE("Map is NULL! Not expected.");
+    return;
+  }
+
+  plane_to_connector->clear();
+
+  std::map<uint32_t, uint32_t> plane_to_crtc;
+  std::map<uint32_t, uint32_t> crtc_to_encoder;
+  std::map<uint32_t, uint32_t> encoder_to_connector;
+
+  // Cont. Splash planes are detected by CRTC existence on the PLANE
+  // These are the planes that ultimately need to know their CONNECTOR
+  plane_mgr_->MapPlaneToCrtc(&plane_to_crtc);
+  if (!plane_to_crtc.size()) {
+    DRM_LOGI("No cont. splash planes found");
+    return;
+  }
+
+  // CRTC is connected to ENCODER. Find the ENCODERs who have CRTCs and establish
+  // reverse lookup CRTC --> ENCODER
+  encoder_mgr_->MapCrtcToEncoder(&crtc_to_encoder);
+  if (!crtc_to_encoder.size()) {
+    DRM_LOGW("Planes are associated with CRTCs but no CRTC to ENCODERs found");
+    return;
+  }
+
+  // ENCODER is connected to CONNECTOR. Find the CONNECTORs who have ENCODERs and establish
+  // reverse lookup ENCODER --> CONNECTOR
+  conn_mgr_->MapEncoderToConnector(&encoder_to_connector);
+  if (!encoder_to_connector.size()) {
+    DRM_LOGW("CRTCs are associated with ENCODERs but no ENCODERs to CONNECTORs found");
+    return;
+  }
+
+  // Link the Cont. Splash PLANEs with their CONNECTOR
+  for (auto &plane_entry : plane_to_crtc) {
+    uint32_t plane_id = plane_entry.first;
+    auto &crtc_id = plane_entry.second;
+    auto enc_entry = crtc_to_encoder.find(crtc_id);
+    if (enc_entry == crtc_to_encoder.end()) {
+      DRM_LOGW("Plane %u mapped to crtc %u didn't have an encoder. Not expected.", plane_id,
+               crtc_id);
+      continue;
+    }
+    auto &encoder_id = enc_entry->second;
+    auto conn_entry = encoder_to_connector.find(encoder_id);
+    if (conn_entry == encoder_to_connector.end()) {
+      DRM_LOGW("Plane %u, crtc %u, encoder %u, didn't have a connector. Not expected.",
+             plane_id, crtc_id, encoder_id);
+      continue;
+    }
+    uint32_t connector_id = conn_entry->second;
+
+    plane_to_connector->insert(make_pair(plane_id, connector_id));
+  }
+}
+
+void DRMManager::GetRequiredDemuraFetchResourceCount(
+                 std::map<uint32_t, uint8_t> *required_demura_fetch_cnt) {
+  conn_mgr_->GetPreferredModeLMCounts(required_demura_fetch_cnt);
+}
+
+void DRMManager::GetInitialDemuraInfo(std::vector<uint32_t> *initial_demura_planes) {
+  if (panel_feature_mgr_intf_) {
+    initial_demura_planes->clear();
+    std::vector<uint32_t> crtc_ids;
+    crtc_mgr_->GetCrtcList(&crtc_ids);
+    FetchResourceList frl;
+    for (auto &id : crtc_ids) {
+      DRMPanelFeatureInfo info;
+      info.prop_id = kDRMPanelFeatureDemuraResources;
+      info.obj_type = DRM_MODE_OBJECT_CRTC;
+      info.obj_id = id;
+      info.prop_ptr = reinterpret_cast<uint64_t>(&frl);
+      panel_feature_mgr_intf_->GetPanelFeatureInfo(&info);
+    }
+    // Safe to assume pipe to crtc to Demura associations are functionally correct
+    plane_mgr_->GetPlaneIdsFromDescriptions(frl, initial_demura_planes);
+  }
+}
 }  // namespace sde_drm
diff --git a/sde-drm/drm_manager.h b/sde-drm/drm_manager.h
index 6f47594..850c594 100644
--- a/sde-drm/drm_manager.h
+++ b/sde-drm/drm_manager.h
@@ -66,6 +66,12 @@
   virtual void GetDppsFeatureInfo(DRMDppsFeatureInfo *info);
   virtual void GetPanelFeature(DRMPanelFeatureInfo *info);
   virtual void SetPanelFeature(const DRMPanelFeatureInfo &info);
+  virtual void MapPlaneToConnector(std::map<uint32_t, uint32_t> *plane_to_connector);
+  virtual void GetRequiredDemuraFetchResourceCount(std::map<uint32_t, uint8_t>
+                                                   *required_demura_fetch_cnt);
+  virtual void GetInitialDemuraInfo(std::vector<uint32_t> *initial_demura_planes);
+  virtual void MarkPanelFeatureForNullCommit(const DRMDisplayToken &token,
+                                             const DRMPanelFeatureID &id);
 
   DRMPlaneManager *GetPlaneMgr();
   DRMConnectorManager *GetConnectorMgr();
diff --git a/sde-drm/drm_panel_feature_mgr.cpp b/sde-drm/drm_panel_feature_mgr.cpp
index 4aba91b..fac11a2 100644
--- a/sde-drm/drm_panel_feature_mgr.cpp
+++ b/sde-drm/drm_panel_feature_mgr.cpp
@@ -28,6 +28,8 @@
 */
 
 #include <sstream>
+#include <string>
+#include <tuple>
 #include <errno.h>
 #include <string>
 #include <drm_logger.h>
@@ -48,6 +50,12 @@
 
 static DRMPanelFeatureMgr panel_feature_mgr;
 
+// Demura Planes' Default Bit Indices
+static uint8_t DEMURA_DMA1RECT0 = 0x1;
+static uint8_t DEMURA_DMA1RECT1 = 0x2;
+static uint8_t DEMURA_DMA3RECT0 = 0x3;
+static uint8_t DEMURA_DMA3RECT1 = 0x4;
+
 DRMPanelFeatureMgrIntf *GetPanelFeatureManagerIntf() {
   return &panel_feature_mgr;
 }
@@ -85,6 +93,9 @@
     }
   }
 
+  drm_property_map_[kDRMPanelFeatureDemuraResources] = DRMProperty::DEMURA_BOOT_PLANE_V1;
+  drm_property_map_[kDRMPanelFeatureDemuraInit] = DRMProperty::DEMURA_INIT_CFG_V1;
+  drm_property_map_[kDRMPanelFeaturePanelId] = DRMProperty::DEMURA_PANEL_ID;
   drm_property_map_[kDRMPanelFeatureSPRInit] = DRMProperty::SPR_INIT_CFG_V1;
   drm_property_map_[kDRMPanelFeatureSPRPackType] = DRMProperty::CAPABILITIES;
   drm_property_map_[kDRMPanelFeatureDsppIndex] = DRMProperty::DSPP_CAPABILITIES;
@@ -93,6 +104,9 @@
   drm_property_map_[kDRMPanelFeatureDsppRCInfo] = DRMProperty::DSPP_CAPABILITIES;
   drm_property_map_[kDRMPanelFeatureRCInit] = DRMProperty::DSPP_RC_MASK_V1;
 
+  drm_prop_type_map_[kDRMPanelFeatureDemuraResources] = DRMPropType::kPropBitmask;
+  drm_prop_type_map_[kDRMPanelFeatureDemuraInit] = DRMPropType::kPropBlob;
+  drm_prop_type_map_[kDRMPanelFeaturePanelId] = DRMPropType::kPropBlob;
   drm_prop_type_map_[kDRMPanelFeatureSPRInit] = DRMPropType::kPropBlob;
   drm_prop_type_map_[kDRMPanelFeatureRCInit] = DRMPropType::kPropBlob;
   drm_prop_type_map_[kDRMPanelFeatureSPRPackType] = DRMPropType::kPropBlob;
@@ -101,6 +115,12 @@
   drm_prop_type_map_[kDRMPanelFeatureDsppDemuraInfo] = DRMPropType::kPropRange;
   drm_prop_type_map_[kDRMPanelFeatureDsppRCInfo] = DRMPropType::kPropRange;
 
+  feature_info_tbl_[kDRMPanelFeatureDemuraResources] = DRMPanelFeatureInfo {
+    kDRMPanelFeatureDemuraResources, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 0, 0};
+  feature_info_tbl_[kDRMPanelFeatureDemuraInit] = DRMPanelFeatureInfo {kDRMPanelFeatureDemuraInit,
+      DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, sizeof(drm_msm_dem_cfg), 0};
+  feature_info_tbl_[kDRMPanelFeaturePanelId] = DRMPanelFeatureInfo {kDRMPanelFeaturePanelId,
+      DRM_MODE_OBJECT_CONNECTOR, UINT32_MAX, 1, sizeof(uint64_t), 0};
   feature_info_tbl_[kDRMPanelFeatureSPRInit] = DRMPanelFeatureInfo {kDRMPanelFeatureSPRInit,
       DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, sizeof(drm_msm_spr_init_cfg), 0};
   feature_info_tbl_[kDRMPanelFeatureRCInit] = DRMPanelFeatureInfo {
@@ -169,8 +189,64 @@
   return 0;
 }
 
+void DRMPanelFeatureMgr::ParsePanelId(uint32_t blob_id, DRMPanelFeatureInfo *info) {
+  drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, blob_id);
+  if (!blob) {
+    return;
+  }
+
+  if (blob->length != sizeof(uint64_t)) {
+    DRM_LOGE("Expecting %zu bytes but got %u", sizeof(uint64_t), blob->length);
+    return;
+  }
+
+  uint64_t* panel_id = reinterpret_cast<uint64_t *>(info->prop_ptr);
+  // Read as-is / big endian. Driver has supplied the value in this manner.
+  uint8_t *data = (uint8_t*)(blob->data);
+  for (size_t i = 0; i < blob->length; i++) {
+    *panel_id = (*panel_id << 8) | *data;
+    data++;
+  }
+  info->prop_size = sizeof(uint64_t);
+
+  drmModeFreePropertyBlob(blob);
+}
+
+void DRMPanelFeatureMgr::ParseDemuraResources(drmModePropertyRes *prop, uint64_t value,
+                                              DRMPanelFeatureInfo *info) {
+  // Values come as bit indices, not fully-realized values, for DRM_MODE_PROP_BITMASK
+  for (auto i = 0; i < prop->count_enums; i++) {
+    std::string enum_name(prop->enums[i].name);
+    if (enum_name == "demura_dma1_rect0") {
+      DEMURA_DMA1RECT0 = (1 << prop->enums[i].value);
+    } else if (enum_name == "demura_dma1_rect1") {
+      DEMURA_DMA1RECT1 = (1 << prop->enums[i].value);
+    } else if (enum_name == "demura_dma3_rect0") {
+      DEMURA_DMA3RECT0 = (1 << prop->enums[i].value);
+    } else if (enum_name == "demura_dma3_rect1") {
+      DEMURA_DMA3RECT1 = (1 << prop->enums[i].value);
+    }
+  }
+
+  FetchResourceList *frl = reinterpret_cast<FetchResourceList*>(info->prop_ptr);
+  if (value & DEMURA_DMA1RECT0) {
+    frl->push_back(std::make_tuple("DMA", 1, 0));
+  }
+  if (value & DEMURA_DMA1RECT1) {
+    frl->push_back(std::make_tuple("DMA", 1, 1));
+  }
+  if (value & DEMURA_DMA3RECT0) {
+    frl->push_back(std::make_tuple("DMA", 3, 0));
+  }
+  if (value & DEMURA_DMA3RECT1) {
+    frl->push_back(std::make_tuple("DMA", 3, 1));
+  }
+
+  info->prop_size += frl->size();
+}
+
 void DRMPanelFeatureMgr::ParseDsppCapabilities(uint32_t blob_id, std::vector<int> *values,
-                                            uint32_t *size, const std::string str) {
+                                               uint32_t *size, const std::string str) {
   drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, blob_id);
   if (!blob) {
     DRM_LOGW("Unable to find blob for id %d", blob_id);
@@ -289,17 +365,21 @@
       ParseCapabilities(props->prop_values[j],
               reinterpret_cast<char *> (info->prop_ptr), info->prop_size, "spr_pack_type");
     } else if (info->prop_id == kDRMPanelFeatureDsppIndex) {
-      ParseCapabilities(props->prop_values[j],
-              reinterpret_cast<char *> (info->prop_ptr), info->prop_size, "dspp_index");
+      ParseDsppCapabilities(props->prop_values[j],
+              reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "dspp");
+    } else if (info->prop_id == kDRMPanelFeatureDemuraResources) {
+      ParseDemuraResources(property, props->prop_values[j], info);
+    } else if (info->prop_id == kDRMPanelFeaturePanelId) {
+      ParsePanelId(props->prop_values[j], info);
     } else if (info->prop_id == kDRMPanelFeatureDsppSPRInfo) {
-      ParseCapabilities(props->prop_values[j],
-              reinterpret_cast<char *> (info->prop_ptr), info->prop_size, "spr");
+      ParseDsppCapabilities(props->prop_values[j],
+              reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "spr");
     } else if (info->prop_id == kDRMPanelFeatureDsppDemuraInfo) {
-      ParseCapabilities(props->prop_values[j],
-              reinterpret_cast<char *> (info->prop_ptr), info->prop_size, "demura");
+      ParseDsppCapabilities(props->prop_values[j],
+              reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "demura");
     } else if (info->prop_id == kDRMPanelFeatureDsppRCInfo) {
       ParseDsppCapabilities(props->prop_values[j],
-              reinterpret_cast<std::vector<int> *> (info->prop_ptr), &(info->prop_size), "rc");
+              reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "rc");
     } else if (drm_prop_type_map_[info->prop_id] == DRMPropType::kPropBlob) {
       drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, props->prop_values[j]);
       if (!blob || !blob->data || !blob->length) {
@@ -334,74 +414,123 @@
   dirty_features_[info.prop_id].second = info;
 }
 
-void DRMPanelFeatureMgr::CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &tok) {
-  int ret = 0;
-
+void DRMPanelFeatureMgr::CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token) {
   lock_guard<mutex> lock(lock_);
   for (auto it = dirty_features_.begin(); it != dirty_features_.end(); it++) {
     DRMPanelFeatureInfo &info = it->second;
 
     if (!it->first)
-        continue;
-
-    if (info.prop_id >= kDRMPanelFeatureMax) {
-      DRM_LOGE("invalid property info to set id %d value ptr %" PRIu64 , info.prop_id, info.prop_ptr);
       continue;
-    }
 
-    // Commit only features meant for the given DisplayToken
-    if (tok.crtc_id != info.obj_id && tok.conn_id != info.obj_id) {
-      continue;
-    }
-
-    uint32_t prop_id = prop_mgr_.GetPropertyId(drm_property_map_[info.prop_id]);
-    uint64_t value = 0;
-
-    if (DRMPropType::kPropBlob == drm_prop_type_map_[info.prop_id]) {
-      uint32_t blob_id = 0;
-      if (!info.prop_ptr) {
-        // Reset the feature.
-        ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, 0);
-        if (ret < 0) {
-          DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%u value:%" PRIu64,
-                    ret, info.obj_id, prop_id, value);
-          return;
-        }
-        continue;
-      }
-
-      ret = drmModeCreatePropertyBlob(dev_fd_, reinterpret_cast<void *> (info.prop_ptr),
-              info.prop_size, &blob_id);
-      if (ret || blob_id == 0) {
-        DRM_LOGE("failed to create blob ret %d, id = %d prop_ptr:%" PRIu64 " prop_sz:%d",
-                ret, blob_id, info.prop_ptr, info.prop_size);
-        return;
-      }
-
-      if (drm_prop_blob_ids_map_[info.prop_id]) {
-        ret = drmModeDestroyPropertyBlob(dev_fd_, drm_prop_blob_ids_map_[info.prop_id]);
-        if (ret) {
-          DRM_LOGE("failed to destroy blob for feature %d, ret = %d", info.prop_id, ret);
-          return;
-        }
-      }
-      drm_prop_blob_ids_map_[info.prop_id] = blob_id;
-
-      value = blob_id;
-    } else if (info.prop_size == sizeof(uint64_t)) {
-      value = (reinterpret_cast<uint64_t *> (info.prop_ptr))[0];
-    } else {
-      DRM_LOGE("Unsupported property type id = %d size:%d", info.prop_id, info.prop_size);
-    }
-
-    ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, value);
-    if (ret < 0) {
-      DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%x value:%" PRIu64,
-                ret, info.obj_id, prop_id, value);
-    }
+    ApplyDirtyFeature(req, token, info);
     *it = {};
   }
 }
 
-}  // namespace sde_drm
+void DRMPanelFeatureMgr::NullCommitPanelFeatures(drmModeAtomicReq *req,
+                                                 const DRMDisplayToken &token) {
+  lock_guard<mutex> lock(lock_);
+  for (auto it = dirty_features_.begin(); it != dirty_features_.end(); it++) {
+    DRMPanelFeatureInfo &info = it->second;
 
+    auto entry_iter = apply_in_null_commit_.find(info.obj_id);
+    if (entry_iter == apply_in_null_commit_.end())
+      continue;
+    if (entry_iter->second != info.prop_id)
+      continue;
+
+    if (!it->first) {
+      DLOGW("Prop %u is already committed via draw cycle", info.prop_id);
+      apply_in_null_commit_.erase(info.obj_id);
+      continue;
+    }
+
+    ApplyDirtyFeature(req, token, info);
+    apply_in_null_commit_.erase(info.obj_id);
+    *it = {};
+  }
+}
+
+void DRMPanelFeatureMgr::MarkForNullCommit(const DRMDisplayToken &token, const DRMPanelFeatureID &id) {
+  DRMPanelFeatureInfo &info = feature_info_tbl_[id];
+  uint32_t obj_id = 0;
+  switch (info.obj_type) {
+    case DRM_MODE_OBJECT_CRTC:
+      obj_id = token.crtc_id;
+      break;
+    case DRM_MODE_OBJECT_CONNECTOR:
+      obj_id = token.conn_id;
+      break;
+    default:
+      return;
+  }
+  apply_in_null_commit_[obj_id] = id;
+  DLOGI("Marked %u for null commit", id);
+}
+
+void DRMPanelFeatureMgr::ApplyDirtyFeature(drmModeAtomicReq *req, const DRMDisplayToken &token,
+                                           DRMPanelFeatureInfo &info) {
+  int ret = 0;
+  if (info.prop_id >= kDRMPanelFeatureMax) {
+    DRM_LOGE("invalid property info to set id %d value ptr %" PRIu64, info.prop_id, info.prop_ptr);
+    return;
+  }
+
+  // Commit only features meant for the given DisplayToken
+  if (token.crtc_id != info.obj_id && token.conn_id != info.obj_id) {
+    return;
+  }
+
+  uint32_t prop_id = prop_mgr_.GetPropertyId(drm_property_map_[info.prop_id]);
+  if (!prop_id) {
+    DRM_LOGE("prop_id is 0 for panel feature-id %u", info.prop_id);
+    return;
+  }
+  uint64_t value = 0;
+
+  if (DRMPropType::kPropBlob == drm_prop_type_map_[info.prop_id]) {
+    uint32_t blob_id = 0;
+    if (!info.prop_ptr) {
+      // Reset the feature.
+      ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, 0);
+      if (ret < 0) {
+        DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%u value:%" PRIu64,
+                  ret, info.obj_id, prop_id, value);
+      }
+      DLOGI("Commited panel feature [disabled]: %u-%u", info.prop_id, prop_id);
+      return;
+    }
+
+    ret = drmModeCreatePropertyBlob(dev_fd_, reinterpret_cast<void *> (info.prop_ptr),
+            info.prop_size, &blob_id);
+    if (ret || blob_id == 0) {
+      DRM_LOGE("failed to create blob ret %d, id = %d prop_ptr:%" PRIu64 " prop_sz:%d",
+              ret, blob_id, info.prop_ptr, info.prop_size);
+      return;
+    }
+
+    if (drm_prop_blob_ids_map_[info.prop_id]) {
+      ret = drmModeDestroyPropertyBlob(dev_fd_, drm_prop_blob_ids_map_[info.prop_id]);
+      if (ret) {
+        DRM_LOGE("failed to destroy blob for feature %d, ret = %d", info.prop_id, ret);
+        return;
+      }
+    }
+    drm_prop_blob_ids_map_[info.prop_id] = blob_id;
+
+    value = blob_id;
+  } else if (info.prop_size == sizeof(uint64_t)) {
+    value = (reinterpret_cast<uint64_t *> (info.prop_ptr))[0];
+  } else {
+    DRM_LOGE("Unsupported property type id = %d size:%d", info.prop_id, info.prop_size);
+  }
+
+  ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, value);
+  if (ret < 0) {
+    DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%x value:%" PRIu64,
+              ret, info.obj_id, prop_id, value);
+  }
+  DLOGI("Commited panel feature [enabled]: %u-%u", info.prop_id, prop_id);
+}
+
+}  // namespace sde_drm
diff --git a/sde-drm/drm_panel_feature_mgr.h b/sde-drm/drm_panel_feature_mgr.h
index afc314b..14bfdcc 100644
--- a/sde-drm/drm_panel_feature_mgr.h
+++ b/sde-drm/drm_panel_feature_mgr.h
@@ -46,13 +46,19 @@
   void Deinit();
   void GetPanelFeatureInfo(DRMPanelFeatureInfo *info);
   void CachePanelFeature(const DRMPanelFeatureInfo &info);
-  void CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &tok);
+  void CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token);
+  void NullCommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token);
+  void MarkForNullCommit(const DRMDisplayToken &token, const DRMPanelFeatureID &id);
 
  private:
   int InitObjectProps(int obj_id, int obj_type);
   void ParseCapabilities(uint32_t blob_id, char* value, uint32_t max_len, const std::string str);
   void ParseDsppCapabilities(uint32_t blob_id, std::vector<int> *values, uint32_t *size,
                              const std::string str);
+  void ParsePanelId(uint32_t blob_id, DRMPanelFeatureInfo *info);
+  void ParseDemuraResources(drmModePropertyRes *prop, uint64_t value, DRMPanelFeatureInfo *info);
+  void ApplyDirtyFeature(drmModeAtomicReq *req, const DRMDisplayToken &token,
+                         DRMPanelFeatureInfo &info);
 
   std::mutex lock_;
   int dev_fd_ = -1;
@@ -64,6 +70,7 @@
   std::map<DRMPanelFeatureID, DRMPropType> drm_prop_type_map_ {};
   std::map<DRMPanelFeatureID, uint32_t> drm_prop_blob_ids_map_ {};
   std::array<DRMPanelFeatureInfo, kDRMPanelFeatureMax> feature_info_tbl_ {};
+  std::map<uint32_t /* obj_id */, DRMPanelFeatureID> apply_in_null_commit_ {};
 };
 
 }  // namespace sde_drm
diff --git a/sde-drm/drm_panel_feature_mgr_intf.h b/sde-drm/drm_panel_feature_mgr_intf.h
index bb5106f..8731602 100644
--- a/sde-drm/drm_panel_feature_mgr_intf.h
+++ b/sde-drm/drm_panel_feature_mgr_intf.h
@@ -41,8 +41,9 @@
   virtual void Deinit() = 0;
   virtual void GetPanelFeatureInfo(DRMPanelFeatureInfo *info) = 0;
   virtual void CachePanelFeature(const DRMPanelFeatureInfo &info) = 0;
-  virtual void CommitPanelFeatures(drmModeAtomicReq *req,
-                                   const DRMDisplayToken &tok) = 0;
+  virtual void CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token) = 0;
+  virtual void NullCommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token) = 0;
+  virtual void MarkForNullCommit(const DRMDisplayToken &token, const DRMPanelFeatureID &id) = 0;
 };
 
 extern "C" DRMPanelFeatureMgrIntf *GetPanelFeatureManagerIntf();
diff --git a/sde-drm/drm_plane.cpp b/sde-drm/drm_plane.cpp
index b46a1df..43a21af 100644
--- a/sde-drm/drm_plane.cpp
+++ b/sde-drm/drm_plane.cpp
@@ -56,6 +56,7 @@
 using std::string;
 using std::map;
 using std::pair;
+using std::make_pair;
 using std::vector;
 using std::unique_ptr;
 using std::tuple;
@@ -139,6 +140,13 @@
 static uint8_t MULTIRECT_PARALLEL = 1;
 static uint8_t MULTIRECT_SERIAL = 2;
 
+// Blend Type
+static uint8_t UNDEFINED = 0;
+static uint8_t OPAQUE = 1;
+static uint8_t PREMULTIPLIED = 2;
+static uint8_t COVERAGE = 3;
+static uint8_t SKIP_BLENDING = 4;
+
 static void SetRect(DRMRect &source, drm_clip_rect *target) {
   target->x1 = uint16_t(source.left);
   target->y1 = uint16_t(source.top);
@@ -226,6 +234,27 @@
   }
 }
 
+static void PopulateBlendType(drmModePropertyRes *prop) {
+  static bool blend_type_populated = false;
+  if (!blend_type_populated) {
+    for (auto i = 0; i < prop->count_enums; i++) {
+      string enum_name(prop->enums[i].name);
+      if (enum_name == "not_defined") {
+        UNDEFINED = prop->enums[i].value;
+      } else if (enum_name == "opaque") {
+        OPAQUE = prop->enums[i].value;
+      } else if (enum_name == "premultiplied") {
+        PREMULTIPLIED = prop->enums[i].value;
+      } else if (enum_name == "coverage") {
+        COVERAGE = prop->enums[i].value;
+      } else if (enum_name == "skip_blending") {
+        SKIP_BLENDING = prop->enums[i].value;
+      }
+    }
+    blend_type_populated = true;
+  }
+}
+
 static const char *GetColorLutString(DRMTonemapLutType lut_type) {
   switch (lut_type) {
     case DRMTonemapLutType::DMA_1D_IGC:
@@ -416,6 +445,52 @@
   }
 }
 
+void DRMPlaneManager::MapPlaneToCrtc(std::map<uint32_t, uint32_t> *plane_to_crtc) {
+  lock_guard<mutex> lock(lock_);
+
+  if (!plane_to_crtc) {
+    DLOGE("Map is NULL! Not expected.");
+    return;
+  }
+
+  plane_to_crtc->clear();
+
+  for (auto &plane : plane_pool_) {
+    uint32_t crtc_id = 0;
+    plane.second->GetCrtc(&crtc_id);
+    if (crtc_id)
+      plane_to_crtc->insert(make_pair(plane.first, crtc_id));
+  }
+}
+
+void DRMPlaneManager::GetPlaneIdsFromDescriptions(FetchResourceList &descriptions,
+                                                  std::vector<uint32_t> *plane_ids) {
+  lock_guard<mutex> lock(lock_);
+  for (auto &desc : descriptions) {
+    const string &type_str = std::get<0>(desc);
+    DRMPlaneType type = DRMPlaneType::MAX;
+    if (type_str == "DMA") {
+      type = DRMPlaneType::DMA;
+    } else {
+      continue;
+    }
+    const int32_t &idx = std::get<1>(desc);
+    const int8_t &rect = std::get<2>(desc);
+    for (auto &p : plane_pool_) {
+      DRMPlaneType plane_type;
+      p.second->GetType(&plane_type);
+      uint8_t plane_idx;
+      p.second->GetIndex(&plane_idx);
+      uint8_t plane_rect;
+      p.second->GetRect(&plane_rect);
+      if (plane_idx == idx && plane_rect == rect && plane_type == type) {
+        plane_ids->emplace_back(p.first);
+        break;
+      }
+    }
+  }
+}
+
 // ==============================================================================================//
 
 #undef __CLASS__
@@ -483,6 +558,8 @@
   string true_inline_dwnscale_rt_numerator = "true_inline_dwnscale_rt_numerator=";
   string true_inline_dwnscale_rt_denominator = "true_inline_dwnscale_rt_denominator=";
   string true_inline_max_height = "true_inline_max_height=";
+  string pipe_idx = "pipe_idx=";
+  string demura_block = "demura_block=";
 
   while (std::getline(stream, line)) {
     if (line.find(inline_rot_pixel_formats) != string::npos) {
@@ -526,7 +603,12 @@
         true_inline_dwnscale_rt_denominator.length()));
     } else if (line.find(true_inline_max_height) != string::npos) {
       info->max_rotation_linewidth = std::stoi(line.erase(0, true_inline_max_height.length()));
+    }  else if (line.find(pipe_idx) != string::npos) {
+      info->pipe_idx = std::stoi(line.erase(0, pipe_idx.length()));
+    }  else if (line.find(demura_block) != string::npos) {
+      info->demura_block_capability = std::stoi(line.erase(0, demura_block.length()));
     }
+
   }
 
 // TODO(user): Get max_scaler_linewidth and non_scaler_linewidth from driver
@@ -575,6 +657,8 @@
     } else if (prop_enum == DRMProperty::MULTIRECT_MODE) {
       PopulateMultiRectModes(info);
       plane_type_info_.multirect_prop_present = true;
+    } else if (prop_enum == DRMProperty::BLEND_OP) {
+      PopulateBlendType(info);
     }
 
     prop_mgr_.SetPropertyId(prop_enum, info->prop_id);
@@ -866,10 +950,32 @@
     } break;
 
     case DRMOps::PLANE_SET_BLEND_TYPE: {
-      uint32_t blending = va_arg(args, uint32_t);
+      DRMBlendType blending = va_arg(args, DRMBlendType);
+      uint32_t blend_type = UNDEFINED;
+      switch (blending) {
+        case DRMBlendType::OPAQUE:
+          blend_type = OPAQUE;
+          break;
+        case DRMBlendType::PREMULTIPLIED:
+          blend_type = PREMULTIPLIED;
+          break;
+        case DRMBlendType::COVERAGE:
+          blend_type = COVERAGE;
+          break;
+        case DRMBlendType::SKIP_BLENDING:
+          blend_type = SKIP_BLENDING;
+          break;
+        case DRMBlendType::UNDEFINED:
+          blend_type = UNDEFINED;
+          break;
+        default:
+          DRM_LOGE("Invalid blend type %d to set on plane %d", blending, obj_id);
+          break;
+      }
+
       prop_id = prop_mgr_.GetPropertyId(DRMProperty::BLEND_OP);
-      AddProperty(req, obj_id, prop_id, blending, true /* cache */, tmp_prop_val_map_);
-      DRM_LOGV("Plane %d: Setting blending %d", obj_id, blending);
+      AddProperty(req, obj_id, prop_id, blend_type, true /* cache */, tmp_prop_val_map_);
+      DRM_LOGV("Plane %d: Setting blending %d", obj_id, blend_type);
     } break;
 
     case DRMOps::PLANE_SET_H_DECIMATION: {
diff --git a/sde-drm/drm_plane.h b/sde-drm/drm_plane.h
index c827489..18d1e41 100644
--- a/sde-drm/drm_plane.h
+++ b/sde-drm/drm_plane.h
@@ -64,6 +64,7 @@
   void GetPriority(uint32_t *priority) { *priority = priority_; }
   void GetAssignedCrtc(uint32_t *crtc_id) { *crtc_id = assigned_crtc_id_; }
   void GetRequestedCrtc(uint32_t *crtc_id) { *crtc_id = requested_crtc_id_; }
+  void GetCrtc(uint32_t *crtc_id) { *crtc_id = drm_plane_->crtc_id; }
   void SetAssignedCrtc(uint32_t crtc_id) { assigned_crtc_id_ = crtc_id; }
   void SetRequestedCrtc(uint32_t crtc_id) { requested_crtc_id_ = crtc_id; }
   bool SetScalerConfig(drmModeAtomicReq *req, uint64_t handle);
@@ -85,6 +86,8 @@
   void ResetColorLUTState(DRMTonemapLutType lut_type, bool is_commit, drmModeAtomicReq *req);
   void ResetColorLUT(DRMPPFeatureID id, drmModeAtomicReq *req);
   void ResetCache(drmModeAtomicReq *req);
+  void GetIndex(uint8_t *index) { *index = plane_type_info_.pipe_idx; }
+  void GetRect(uint8_t *rect) { *rect = plane_type_info_.master_plane_id ? 1 : 0; }
 
  private:
   typedef std::map<DRMProperty, std::tuple<uint64_t, drmModePropertyRes *>> PropertyMap;
@@ -135,6 +138,9 @@
   void PostValidate(uint32_t crtc_id, bool success);
   void PostCommit(uint32_t crtc_id, bool success);
   void ResetCache(drmModeAtomicReq *req, uint32_t crtc_id);
+  void MapPlaneToCrtc(std::map<uint32_t, uint32_t> *plane_to_crtc);
+  void GetPlaneIdsFromDescriptions(FetchResourceList &descriptions,
+                                   std::vector<uint32_t> *plane_ids);
 
  private:
   void Perform(DRMOps code, drmModeAtomicReq *req, uint32_t obj_id, ...);
diff --git a/sde-drm/drm_property.cpp b/sde-drm/drm_property.cpp
index 8edb0d0..a0af621 100644
--- a/sde-drm/drm_property.cpp
+++ b/sde-drm/drm_property.cpp
@@ -169,6 +169,9 @@
   if (name == "SDE_SPR_INIT_CFG_V1") { return DRMProperty::SPR_INIT_CFG_V1; }
   if (name == "SDE_DSPP_RC_MASK_V1") { return DRMProperty::DSPP_RC_MASK_V1; }
   if (name == "panel_mode") { return DRMProperty::PANEL_MODE; }
+  if (name == "SDE_DEMURA_INIT_CFG_V1") { return DRMProperty::DEMURA_INIT_CFG_V1; }
+  if (name == "DEMURA_PANEL_ID") { return DRMProperty::DEMURA_PANEL_ID; }
+  if (name == "SDE_DEMURA_BOOT_PLANE_V1") { return DRMProperty::DEMURA_BOOT_PLANE_V1; }
 
   return DRMProperty::INVALID;
 }
diff --git a/sde-drm/drm_property.h b/sde-drm/drm_property.h
index 67398eb..2e3fd32 100644
--- a/sde-drm/drm_property.h
+++ b/sde-drm/drm_property.h
@@ -174,6 +174,9 @@
   SPR_INIT_CFG_V1,
   DSPP_RC_MASK_V1,
   PANEL_MODE,
+  DEMURA_INIT_CFG_V1,
+  DEMURA_PANEL_ID,
+  DEMURA_BOOT_PLANE_V1,
 
   // Insert above
   MAX
diff --git a/sdm/include/core/core_interface.h b/sdm/include/core/core_interface.h
index 2ec7b32..bd03592 100644
--- a/sdm/include/core/core_interface.h
+++ b/sdm/include/core/core_interface.h
@@ -37,6 +37,7 @@
 #ifndef __CORE_INTERFACE_H__
 #define __CORE_INTERFACE_H__
 
+#include <core/ipc_interface.h>
 #include <stdint.h>
 #include <map>
 #include <vector>
diff --git a/sdm/include/core/display_interface.h b/sdm/include/core/display_interface.h
index afee291..4798183 100644
--- a/sdm/include/core/display_interface.h
+++ b/sdm/include/core/display_interface.h
@@ -1008,6 +1008,12 @@
   */
   virtual DisplayError NotifyDisplayCalibrationMode(bool in_calibration) = 0;
 
+  /*! @brief Method to check if display has Demura requirement
+
+    @return \link bool \endlink
+  */
+  virtual bool HasDemura() = 0;
+
  protected:
   virtual ~DisplayInterface() { }
 };
@@ -1015,4 +1021,3 @@
 }  // namespace sdm
 
 #endif  // __DISPLAY_INTERFACE_H__
-
diff --git a/sdm/include/core/ipc_interface.h b/sdm/include/core/ipc_interface.h
index d1b979f..4374fd6 100644
--- a/sdm/include/core/ipc_interface.h
+++ b/sdm/include/core/ipc_interface.h
@@ -37,6 +37,7 @@
 };
 
 enum IPCOps {
+  kIpcOpsFilePath,
   kIPCOpMax
 };
 
diff --git a/sdm/include/core/layer_buffer.h b/sdm/include/core/layer_buffer.h
index 6fb12d5..d688c48 100644
--- a/sdm/include/core/layer_buffer.h
+++ b/sdm/include/core/layer_buffer.h
@@ -217,6 +217,9 @@
 
       uint32_t game : 1;            //!< This flag shall be set by the client to indicate that the
                                     //!< the content is game.
+
+      uint32_t demura : 1;          //!< This flag shall be set to indicate that the
+                                    //!< content is demura correction data
     };
 
     uint32_t flags = 0;             //!< For initialization purpose only.
diff --git a/sdm/include/core/layer_stack.h b/sdm/include/core/layer_stack.h
index 29e924b..c55eac1 100644
--- a/sdm/include/core/layer_stack.h
+++ b/sdm/include/core/layer_stack.h
@@ -40,6 +40,8 @@
 #include <unordered_map>
 #include <memory>
 #include <bitset>
+#include <string>
+#include <tuple>
 
 #include "layer_buffer.h"
 #include "sdm_types.h"
@@ -62,6 +64,9 @@
   kBlendingCoverage,        //!< Pixel color is expressed using straight alpha in color tuples. If
                             //!< plane alpha is less than 0xff, apply modulation as well.
                             //!<   pixel.rgb = src.rgb x src.a + dest.rgb x (1 - src.a)
+
+  kBlendingSkip,            //!< Used only to denote layer should not be staged for blending, but
+                            //!< still requires fetch resources for a different HW block
 };
 
 /*! @brief This enum represents display layer composition types.
@@ -84,6 +89,8 @@
   kCompositionStitch,       //!< This layer will be drawn onto the target buffer by GPU. No blend
                             //!< required.
 
+  kCompositionDemura,       //!< This layer will be applied by Demura HW. No blend required.
+
   kCompositionSDE,          //!< This layer will be composed by SDE. It must not be composed by
                             //!< GPU or Blit.
 
@@ -195,6 +202,9 @@
       uint32_t sde_preferred : 1;  //! This flag shall be set by client to indicate that this layer
                                    //! will be composed by display device, layer with this flag
                                    //! will have highest priority. To be used by OEMs only.
+
+      uint32_t is_demura : 1;  //!< This flag shall be set to indicate that this layer
+                               //!< is a demura correction layer
     };
 
     uint32_t flags = 0;       //!< For initialization purpose only.
@@ -290,6 +300,10 @@
       uint32_t layer_id_support : 1;  //! This flag shall be set by Client to indicate that it has
                                       //! set the unique Layer Id on each SDM Layer, which will
                                       //! persist across draw cycles until the layer gets removed.
+
+      uint32_t stitch_present : 1;  //!< This flag shall be set to true to indicate stack has stitch
+
+      uint32_t demura_present : 1;  //!< This flag shall be set to true to indicate stack has demura
     };
 
     uint32_t flags = 0;               //!< For initialization purpose only.
@@ -487,4 +501,3 @@
 }  // namespace sdm
 
 #endif  // __LAYER_STACK_H__
-
diff --git a/sdm/include/private/demura_intf.h b/sdm/include/private/demura_intf.h
new file mode 100644
index 0000000..ace7484
--- /dev/null
+++ b/sdm/include/private/demura_intf.h
@@ -0,0 +1,56 @@
+/*
+* Copyright (c) 2020, 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 __DEMURA_INTF_H__
+#define __DEMURA_INTF_H__
+
+#include <core/buffer_allocator.h>
+#include <core/buffer_sync_handler.h>
+#include <core/ipc_interface.h>
+
+#include <private/generic_intf.h>
+#include <private/generic_payload.h>
+
+#include <string>
+#include <vector>
+#include <bitset>
+
+#define RESOURCE_BITSET 8
+
+namespace sdm {
+
+struct DemuraInputConfig {
+  bool secure_session = false;
+  std::string brightness_path;
+  std::bitset<RESOURCE_BITSET> resources;
+};
+
+// Demura specific param as strings
+const std::string kDemuraFeatureParamActive = "Active";
+const std::string kDemuraFeatureParamCorrectionBuffer = "CorrectionBuffer";
+
+using DemuraIntf = GenericIntf<const std::string&, const std::string&, GenericPayload>;
+}  // namespace sdm
+
+#endif  // __DEMURA_INTF_H__
diff --git a/sdm/include/private/demura_parser_manager_intf.h b/sdm/include/private/demura_parser_manager_intf.h
new file mode 100644
index 0000000..f6c0bdd
--- /dev/null
+++ b/sdm/include/private/demura_parser_manager_intf.h
@@ -0,0 +1,48 @@
+/*
+* Copyright (c) 2020, 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 __DEMURA_PARSER_MANAGER_INTF_H___
+#define __DEMURA_PARSER_MANAGER_INTF_H___
+
+#include <private/generic_payload.h>
+#include <private/generic_intf.h>
+
+namespace sdm {
+
+enum DemuraParserManagerParams {
+  kDemuraParserManagerParamPanelIds,
+  kDemuraParserManagerParamMax,
+};
+
+enum DemuraParserManagerOps {
+  kDemuraParserManagerOpsParser,
+  kDemuraParserManagerOpsMax,
+};
+
+using DemuraParserManagerIntf =
+      GenericIntf<DemuraParserManagerParams, DemuraParserManagerOps, GenericPayload>;
+
+}  // namespace sdm
+
+#endif  // __DEMURA_PARSER_MANAGER_INTF_H__
diff --git a/sdm/include/private/generic_payload.h b/sdm/include/private/generic_payload.h
index 716f13b..59f14e6 100644
--- a/sdm/include/private/generic_payload.h
+++ b/sdm/include/private/generic_payload.h
@@ -43,7 +43,7 @@
 struct GenericPayload {
  public:
   GenericPayload():
-    type_size(0), payload(nullptr), array_size(0) {}
+    type_size(0), payload(nullptr), array_size(0), release(nullptr) {}
 
   GenericPayload(const GenericPayload &in) {
     type_size = 0;
@@ -66,7 +66,7 @@
     type_size = sizeof(A);
     array_size = in.array_size;
 
-    if (payload != nullptr) {
+    if (payload != nullptr && release != nullptr) {
       release();
     }
     A* p2 = nullptr;
@@ -136,6 +136,10 @@
       return -EINVAL;
     }
 
+    if (!sz) {
+      return -EINVAL;
+    }
+
     p = reinterpret_cast<A *>(payload);
     *sz = 0;
     if (p == nullptr && copy_constructed) {
@@ -153,7 +157,7 @@
   }
 
   void DeletePayload() {
-    if (payload != nullptr) {
+    if (payload != nullptr && release != nullptr) {
       release();
     }
 
diff --git a/sdm/include/private/hw_info_types.h b/sdm/include/private/hw_info_types.h
index 2f2cb5c..c793d0d 100644
--- a/sdm/include/private/hw_info_types.h
+++ b/sdm/include/private/hw_info_types.h
@@ -38,6 +38,7 @@
 #include <string>
 #include <vector>
 #include <utility>
+#include <tuple>
 
 namespace sdm {
 using std::string;
@@ -235,6 +236,12 @@
   uint64_t pipe_bw_limit[kBwModeMax] = { 0 };
 };
 
+enum SplashType {
+  kSplashNone,
+  kSplashLayer,
+  kSplashDemura,
+};
+
 struct HWPipeCaps {
   PipeType type = kPipeTypeUnused;
   uint32_t id = 0;
@@ -244,6 +251,10 @@
   uint32_t dgm_csc_version = 0;
   std::map<HWToneMapLut, uint32_t> tm_lut_version_map = {};
   bool block_sec_ui = false;
+  int32_t cont_splash_disp_id = -1;
+  SplashType splash_type = kSplashNone;
+  int32_t pipe_idx = -1;
+  int32_t demura_block_capability = -1;
 };
 
 struct HWRotatorInfo {
@@ -361,6 +372,10 @@
   bool has_micro_idle = false;
   uint32_t ubwc_version = 1;
   uint32_t rc_total_mem_size = 0;
+  std::map<uint32_t, uint32_t> plane_to_connector = {};
+  std::vector<uint32_t> initial_demura_planes = {};
+  uint32_t demura_count = 0;
+  uint32_t dspp_count = 0;
 };
 
 struct HWSplitInfo {
@@ -701,6 +716,9 @@
   std::vector<LayerRect> excl_rects = {};  // list of exclusion rects
 };
 
+typedef std::tuple<std::string, int32_t, int8_t> FetchResource;
+typedef std::vector<FetchResource> FetchResourceList;
+
 struct HWQosData {
   bool valid = false;
   uint64_t core_ab_bps = 0;
@@ -722,8 +740,9 @@
 
 struct HWLayersInfo {
   uint32_t app_layer_count = 0;      // Total number of app layers. Must not be 0.
-  uint32_t gpu_target_index = 0;     // GPU target layer index. 0 if not present.
-  uint32_t stitch_target_index = 0;  // Blit target layer index. 0 if not present.
+  int32_t gpu_target_index = -1;     // GPU target layer index. -1 if not present.
+  int32_t stitch_target_index = -1;  // Blit target layer index. -1 if not present.
+  int32_t demura_target_index = -1;  // Demura target layer index. -1 if not present.
   std::vector<ColorPrimaries> wide_color_primaries = {};  // list of wide color primaries
 
   std::vector<Layer> hw_layers = {};  // Layers which need to be programmed on the HW
@@ -762,6 +781,8 @@
   LayerBuffer *output_buffer = NULL;   //!< Pointer to the buffer where composed buffer would be
                                        //!< rendered for virtual displays.
                                        //!< NOTE: This field applies to a virtual display only.
+  bool stitch_present = false;  // Indicates there is stitch layer or not
+  bool demura_present = false;  // Indicates there is demura layer or not
 };
 
 struct DispLayerStack {
@@ -847,4 +868,3 @@
 }  // namespace sdm
 
 #endif  // __HW_INFO_TYPES_H__
-
diff --git a/sdm/include/private/panel_feature_factory_intf.h b/sdm/include/private/panel_feature_factory_intf.h
index afcc24a..9edeee8 100644
--- a/sdm/include/private/panel_feature_factory_intf.h
+++ b/sdm/include/private/panel_feature_factory_intf.h
@@ -36,6 +36,8 @@
 #include "spr_intf.h"
 #include "panel_feature_property_intf.h"
 #include "rc_intf.h"
+#include "demura_intf.h"
+#include "demura_parser_manager_intf.h"
 
 namespace sdm {
 
@@ -48,15 +50,19 @@
 class PanelFeatureFactoryIntf {
  public:
   virtual ~PanelFeatureFactoryIntf() {}
-
   virtual std::shared_ptr<SPRIntf>
     CreateSPRIntf(const SPRInputConfig &input_cfg, PanelFeaturePropertyIntf *prop_intf) = 0;
   virtual std::unique_ptr<RCIntf> CreateRCIntf(const RCInputConfig &input_cfg,
                                                PanelFeaturePropertyIntf *prop_intf) = 0;
+  virtual std::shared_ptr<DemuraParserManagerIntf>
+    CreateDemuraParserManager(std::shared_ptr<IPCIntf> ipc_intf) = 0;
+  virtual std::unique_ptr<DemuraIntf>
+    CreateDemuraIntf(const DemuraInputConfig &input_cfg,
+                     PanelFeaturePropertyIntf *prop_intf,
+                     BufferAllocator *buffer_allocator,
+                     std::shared_ptr<SPRIntf> spr) = 0;
 };
 
-extern "C" PanelFeatureFactoryIntf *GetPanelFeatureFactoryIntf();
-
 }  // namespace sdm
 
 #endif  // __PANEL_FEATURE_FACTORY_INTF_H__
diff --git a/sdm/include/private/panel_feature_property_intf.h b/sdm/include/private/panel_feature_property_intf.h
index 6cb2aed..3d7a870 100644
--- a/sdm/include/private/panel_feature_property_intf.h
+++ b/sdm/include/private/panel_feature_property_intf.h
@@ -31,6 +31,7 @@
 #define __PANEL_FEATURE_PROPERTY_INTF_H__
 
 #include <utils/constants.h>
+#include <inttypes.h>
 
 namespace sdm {
 
@@ -44,7 +45,8 @@
   kPanelFeatureSPRPackType,
   kPanelFeatureDemuraInitCfg,
   kPanelFeatureRCInitCfg,
-  kPanelFeaturePropertyIDMax,
+  kPanelFeatureDemuraPanelId,
+  kPanelFeaturePropertyIDMax
 };
 
 struct PanelFeaturePropertyInfo {
diff --git a/sdm/include/private/rc_intf.h b/sdm/include/private/rc_intf.h
index 1ddff2d..8da61b9 100644
--- a/sdm/include/private/rc_intf.h
+++ b/sdm/include/private/rc_intf.h
@@ -31,10 +31,11 @@
 #define __RC_INTF_H__
 
 #include <core/display_interface.h>
-#include <string>
 
-#include "generic_intf.h"
-#include "generic_payload.h"
+#include <private/generic_intf.h>
+#include <private/generic_payload.h>
+
+#include <string>
 
 #define SDE_HW_PU_USECASE 0x1000
 
diff --git a/sdm/include/private/resource_interface.h b/sdm/include/private/resource_interface.h
index df53ff6..8d3f6f0 100644
--- a/sdm/include/private/resource_interface.h
+++ b/sdm/include/private/resource_interface.h
@@ -26,6 +26,7 @@
 #define __RESOURCE_INTERFACE_H__
 
 #include <core/display_interface.h>
+#include <map>
 #include "hw_info_types.h"
 
 namespace sdm {
@@ -62,12 +63,10 @@
   virtual DisplayError PostCommit(Handle display_ctx, DispLayerStack *disp_layer_stack) = 0;
   virtual void Purge(Handle display_ctx) = 0;
   virtual DisplayError SetMaxMixerStages(Handle display_ctx, uint32_t max_mixer_stages) = 0;
-  virtual DisplayError ValidateScaling(const LayerRect &crop, const LayerRect &dst,
-                                       bool rotate90, BufferLayout layout,
-                                       bool use_rotator_downscale) = 0;
+  virtual DisplayError ValidateScaling(const LayerRect &crop, const LayerRect &dst, bool rotate90,
+                                       BufferLayout layout, bool use_rotator_downscale) = 0;
   virtual DisplayError ValidateAndSetCursorPosition(Handle display_ctx,
-                                                    DispLayerStack *disp_layer_stack,
-                                                    int x, int y,
+                                                    DispLayerStack *disp_layer_stack, int x, int y,
                                                     DisplayConfigVariableInfo *fb_config) = 0;
   virtual DisplayError SetMaxBandwidthMode(HWBwModes mode) = 0;
   virtual DisplayError GetScaleLutConfig(HWScaleLutInfo *lut_info) = 0;
@@ -77,10 +76,15 @@
                                         const shared_ptr<Fence> &sync_handle) = 0;
   virtual DisplayError Perform(int cmd, ...) = 0;
   virtual bool IsRotatorSupportedFormat(LayerBufferFormat format) = 0;
-  virtual ~ResourceInterface() { }
+  virtual DisplayError FreeDemuraFetchResources(Handle display_ctx) = 0;
+  virtual DisplayError GetDemuraFetchResourceCount(
+                       std::map<uint32_t, uint8_t> *fetch_resource_cnt) = 0;
+  virtual DisplayError ReserveDemuraFetchResources(const int32_t &display_id,
+                                                   const int8_t &preferred_rect) = 0;
+  virtual DisplayError GetDemuraFetchResources(Handle display_ctx, FetchResourceList *frl) = 0;
+  virtual ~ResourceInterface() {}
 };
 
 }  // namespace sdm
 
 #endif  // __RESOURCE_INTERFACE_H__
-
diff --git a/sdm/libs/core/comp_manager.cpp b/sdm/libs/core/comp_manager.cpp
index a695604..380823b 100644
--- a/sdm/libs/core/comp_manager.cpp
+++ b/sdm/libs/core/comp_manager.cpp
@@ -28,6 +28,7 @@
 #include <set>
 #include <string>
 #include <vector>
+#include <map>
 
 #include "comp_manager.h"
 #include "strategy.h"
@@ -156,6 +157,8 @@
     max_sde_ext_layers_ = UINT32(Debug::GetExtMaxlayers());
   }
 
+  display_demura_status_[display_id] = false;
+
   DLOGV_IF(kTagCompManager, "Registered displays [%s], display %d-%d",
            StringDisplayList(registered_displays_).c_str(), display_comp_ctx->display_id,
            display_comp_ctx->display_type);
@@ -274,7 +277,12 @@
     constraints->safe_mode = true;
   }
 
-  uint32_t app_layer_count = UINT32(disp_layer_stack->stack->layers.size()) - 1;
+  uint32_t size_ff = 1;  // gpu target layer always present
+  if (disp_layer_stack->info.stitch_present)
+    size_ff++;
+  if (disp_layer_stack->info.demura_present)
+    size_ff++;
+  uint32_t app_layer_count = UINT32(disp_layer_stack->stack->layers.size()) - size_ff;
   if (display_comp_ctx->idle_fallback || display_comp_ctx->thermal_fallback_) {
     // Handle the idle timeout by falling back
     constraints->safe_mode = true;
@@ -706,4 +714,30 @@
   return display_comp_ctx->strategy->SwapBuffers();
 }
 
+DisplayError CompManager::FreeDemuraFetchResources(Handle display_ctx) {
+  SCOPE_LOCK(locker_);
+  DisplayCompositionContext *display_comp_ctx =
+      reinterpret_cast<DisplayCompositionContext *>(display_ctx);
+  return resource_intf_->FreeDemuraFetchResources(display_comp_ctx->display_resource_ctx);
+}
+
+DisplayError CompManager::GetDemuraFetchResourceCount(
+                          std::map<uint32_t, uint8_t> *fetch_resource_cnt) {
+  SCOPE_LOCK(locker_);
+  return resource_intf_->GetDemuraFetchResourceCount(fetch_resource_cnt);
+}
+
+DisplayError CompManager::ReserveDemuraFetchResources(const uint32_t &display_id,
+                                                      const int8_t &preferred_rect) {
+  SCOPE_LOCK(locker_);
+  return resource_intf_->ReserveDemuraFetchResources(display_id, preferred_rect);
+}
+
+DisplayError CompManager::GetDemuraFetchResources(Handle display_ctx, FetchResourceList *frl) {
+  SCOPE_LOCK(locker_);
+  DisplayCompositionContext *display_comp_ctx =
+      reinterpret_cast<DisplayCompositionContext *>(display_ctx);
+  return resource_intf_->GetDemuraFetchResources(display_comp_ctx->display_resource_ctx, frl);
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/comp_manager.h b/sdm/libs/core/comp_manager.h
index e28849b..db20667 100644
--- a/sdm/libs/core/comp_manager.h
+++ b/sdm/libs/core/comp_manager.h
@@ -32,6 +32,7 @@
 #include <set>
 #include <vector>
 #include <string>
+#include <map>
 
 #include "strategy.h"
 #include "resource_default.h"
@@ -93,6 +94,19 @@
   bool CheckResourceState(Handle display_ctx);
   bool IsRotatorSupportedFormat(LayerBufferFormat format);
   DisplayError SwapBuffers(Handle display_ctx);
+  DisplayError FreeDemuraFetchResources(Handle display_ctx);
+  DisplayError GetDemuraFetchResourceCount(std::map<uint32_t, uint8_t> *fetch_resource_cnt);
+  DisplayError ReserveDemuraFetchResources(const uint32_t &display_id,
+                                           const int8_t &preferred_rect);
+  DisplayError GetDemuraFetchResources(Handle display_ctx, FetchResourceList *frl);
+  void SetDemuraStatus(bool status) { demura_enabled_ = status; }
+  bool GetDemuraStatus() { return demura_enabled_; }
+  void SetDemuraStatusForDisplay(const int32_t &display_id, bool status) {
+    display_demura_status_[display_id] = status;
+  }
+  bool GetDemuraStatusForDisplay(const int32_t &display_id) {
+    return display_demura_status_[display_id];
+  }
 
  private:
   static const int kMaxThermalLevel = 3;
@@ -135,6 +149,8 @@
   uint32_t max_sde_ext_layers_ = 0;
   uint32_t max_sde_builtin_layers_ = 2;
   DppsControlInterface *dpps_ctrl_intf_ = NULL;
+  bool demura_enabled_ = false;
+  std::map<int32_t /* display_id */, bool> display_demura_status_;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/core_impl.cpp b/sdm/libs/core/core_impl.cpp
index 4e0c730..e6fb611 100644
--- a/sdm/libs/core/core_impl.cpp
+++ b/sdm/libs/core/core_impl.cpp
@@ -28,6 +28,8 @@
 #include <utils/debug.h>
 #include <utils/locker.h>
 #include <utils/utils.h>
+#include <map>
+#include <vector>
 
 #include "color_manager.h"
 #include "core_impl.h"
@@ -102,6 +104,11 @@
     DLOGW("Failed getting displays status. Error = %d", error);
   }
 
+  // Must only call after GetDisplaysStatus
+  if (ReserveDemuraResources() != kErrorNone) {
+    comp_mgr_.SetDemuraStatus(false);
+  }
+
   signal(SIGPIPE, SIG_IGN);
   return kErrorNone;
 
@@ -262,5 +269,132 @@
   return comp_mgr_.IsRotatorSupportedFormat(format);
 }
 
-}  // namespace sdm
+DisplayError CoreImpl::ReserveDemuraResources() {
+  DisplayError err = kErrorNone;
+  int enable = 0;
+  Debug::Get()->GetProperty(ENABLE_DEMURA, &enable);
+  if (!enable) {
+    comp_mgr_.SetDemuraStatus(false);
+    DLOGI("Demura is disabled");
+    return kErrorNone;
+  } else {
+    DLOGI("Demura is enabled");
+    comp_mgr_.SetDemuraStatus(true);
+  }
 
+  std::map<uint32_t, uint8_t> required_demura_fetch_cnt;  // display_id, count
+  if ((err = hw_info_intf_->GetRequiredDemuraFetchResourceCount(&required_demura_fetch_cnt)) !=
+      kErrorNone) {
+    DLOGE("Unable to get required demura pipes count");
+    return err;
+  }
+
+  if (!required_demura_fetch_cnt.size()) {
+    DLOGW("Demura is enabled but no panels support it. Disabling..");
+    comp_mgr_.SetDemuraStatus(false);
+    return kErrorNone;
+  }
+
+  int primary_off = 0;
+  int secondary_off = 0;
+  Debug::Get()->GetProperty(DISABLE_DEMURA_PRIMARY, &primary_off);
+  Debug::Get()->GetProperty(DISABLE_DEMURA_SECONDARY, &secondary_off);
+
+  int available_blocks = hw_resource_.demura_count;
+  for (auto r = required_demura_fetch_cnt.begin(); r != required_demura_fetch_cnt.end();) {
+    HWDisplayInfo &info = hw_displays_info_[r->first];
+    DLOGI("[%d] is_primary = %d, p_off = %d, s_off = %d", r->first, info.is_primary, primary_off,
+          secondary_off);
+    if (info.is_primary && primary_off) {
+      r = required_demura_fetch_cnt.erase(r);
+      continue;
+    } else if (!info.is_primary && secondary_off) {
+      r = required_demura_fetch_cnt.erase(r);
+      continue;
+    }
+
+    available_blocks -= r->second;
+    if (available_blocks < 0) {
+      DLOGE("Not enough Demura blocks (%u)", hw_resource_.demura_count);
+      return kErrorResources;
+    }
+    ++r;
+  }
+
+  std::map<uint32_t, uint8_t> fetch_resource_cnt;  // display id, count
+  comp_mgr_.GetDemuraFetchResourceCount(&fetch_resource_cnt);
+  for (auto &req : required_demura_fetch_cnt) {
+    uint8_t cnt = 0;
+    auto it = fetch_resource_cnt.find(req.first);
+    if (it != fetch_resource_cnt.end()) {
+      cnt = it->second;
+    }
+    uint8_t req_cnt = req.second;
+    if (req_cnt != cnt && cnt != 0) {
+      DLOGE("Cont Splash only allocated %u pipes for Demura, but %u is needed", cnt, req_cnt);
+      return kErrorDriverData;
+    }
+    if (req_cnt != 0 && cnt == 0) {
+      DLOGI("[%u] Needs Demura resources %u", req.first, req_cnt);
+      // Reserving demura resources requires knowledge of which rect to reserve when the req_cnt
+      // is 1. As the HW pipeline for any display is not known yet, we shall assume primary display
+      // takes 0 and non-primary takes 1. When req_cnt > 1, pass in -1
+      int8_t preferred_rect = -1;
+      if (req_cnt == 1) {
+        HWDisplayInfo &info = hw_displays_info_[req.first];
+        preferred_rect = info.is_primary ? 0 : 1;
+        DLOGI("[%u] is single LM. Requesting Demura rect %d", req.first, preferred_rect);
+      }
+      if ((err = comp_mgr_.ReserveDemuraFetchResources(req.first, preferred_rect)) !=
+          kErrorNone) {
+        DLOGE("Failed to reserve resources error = %d", err);
+        return err;
+      }
+    }
+  }
+
+  GetPanelFeatureFactory get_factory_f_ptr = nullptr;
+  if (!extension_lib_.Sym(GET_PANEL_FEATURE_FACTORY,
+                          reinterpret_cast<void **>(&get_factory_f_ptr))) {
+    DLOGE("Unable to load symbols, error = %s", extension_lib_.Error());
+    return kErrorUndefined;
+  }
+
+  panel_feature_factory_intf_ = get_factory_f_ptr();
+  std::shared_ptr<DemuraParserManagerIntf> pm_intf =
+                                panel_feature_factory_intf_->CreateDemuraParserManager(ipc_intf_);
+  if (!pm_intf) {
+    DLOGE("Failed to get Parser Manager intf");
+    return kErrorResources;
+  }
+  if (pm_intf->Init() != 0) {
+    DLOGE("Failed to init Parser Manager intf");
+    return kErrorResources;
+  }
+
+  std::vector<uint64_t> *panel_ids;
+  GenericPayload in;
+  int ret = in.CreatePayload<std::vector<uint64_t>>(panel_ids);
+  if (ret) {
+    DLOGE("Failed to create payload for panel ids, error = %d", ret);
+    return kErrorResources;
+  }
+
+  if ((err = hw_info_intf_->GetDemuraPanelIds(panel_ids)) != kErrorNone) {
+    DLOGE("Unable to get demura panel ids");
+    return err;
+  }
+
+  for (auto &id : *panel_ids) {
+    DLOGI("Detected panel_id = %" PRIu64 " (0x%" PRIx64 ")", id, id);
+  }
+
+  if ((ret = pm_intf->SetParameter(kDemuraParserManagerParamPanelIds, in))) {
+    DLOGE("Failed to set the panel ids to the parser manager");
+    return kErrorResources;
+  }
+
+  return err;
+}
+
+}  // namespace sdm
diff --git a/sdm/libs/core/core_impl.h b/sdm/libs/core/core_impl.h
index 51eb6bc..794ae58 100644
--- a/sdm/libs/core/core_impl.h
+++ b/sdm/libs/core/core_impl.h
@@ -28,16 +28,24 @@
 #include <core/core_interface.h>
 #include <private/extension_interface.h>
 #include <private/color_interface.h>
+#include <private/panel_feature_factory_intf.h>
 #include <utils/locker.h>
 #include <utils/sys.h>
 
+#include <memory>
+#include <vector>
+#include <utility>
+
 #include "hw_interface.h"
 #include "comp_manager.h"
 
 #define SET_REVISION(major, minor) ((major << 8) | minor)
+#define GET_PANEL_FEATURE_FACTORY "GetPanelFeatureFactoryIntf"
 
 namespace sdm {
 
+typedef PanelFeatureFactoryIntf* (*GetPanelFeatureFactory)();
+
 class CoreImpl : public CoreInterface {
  public:
   // This class implements display core interface revision 1.0.
@@ -65,6 +73,8 @@
   virtual bool IsRotatorSupportedFormat(LayerBufferFormat format);
 
  protected:
+  DisplayError ReserveDemuraResources();
+
   Locker locker_;
   BufferAllocator *buffer_allocator_ = NULL;
   HWResourceInfo hw_resource_;
@@ -74,6 +84,7 @@
   ExtensionInterface *extension_intf_ = NULL;
   CreateExtensionInterface create_extension_intf_ = NULL;
   DestroyExtensionInterface destroy_extension_intf_ = NULL;
+  PanelFeatureFactoryIntf *panel_feature_factory_intf_ = NULL;
   SocketHandler *socket_handler_ = NULL;
   HWDisplaysInfo hw_displays_info_ = {};
   std::shared_ptr<IPCIntf> ipc_intf_ = nullptr;
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index aa66ec3..fa72473 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -172,6 +172,8 @@
     hw_recovery_threshold_ = (UINT32(hw_recovery_threshold));
   }
 
+  SetupPanelFeatureFactory();
+
   return kErrorNone;
 
 CleanupOnError:
@@ -196,13 +198,58 @@
   HWInterface::Destroy(hw_intf_);
   if (rc_panel_feature_init_) {
     rc_core_->Deinit();
-    rc_panel_feature_init_ =  false;
+    rc_panel_feature_init_ = false;
   }
   return kErrorNone;
 }
 
+DisplayError DisplayBase::SetupPanelFeatureFactory() {
+  if (pf_factory_ && prop_intf_) {
+    return kErrorNone;
+  }
+
+  DynLib feature_impl_lib;
+  GetPanelFeatureFactory get_factory_f_ptr = nullptr;
+  if (feature_impl_lib.Open(EXTENSION_LIBRARY_NAME)) {
+    if (!feature_impl_lib.Sym(GET_PANEL_FEATURE_FACTORY,
+                              reinterpret_cast<void **>(&get_factory_f_ptr))) {
+      DLOGE("Unable to load symbols, error = %s", feature_impl_lib.Error());
+      return kErrorUndefined;
+    }
+  } else {
+    DLOGW("Unable to load = %s, error = %s", EXTENSION_LIBRARY_NAME, feature_impl_lib.Error());
+    DLOGW("SDM Extension is not supported");
+    return kErrorNone;
+  }
+
+  pf_factory_ = get_factory_f_ptr();
+  if (!pf_factory_) {
+    DLOGE("Failed to create PanelFeatureFactory");
+    return kErrorResources;
+  }
+
+  prop_intf_ = hw_intf_->GetPanelFeaturePropertyIntf();
+  if (!prop_intf_) {
+    DLOGW("Failed to create PanelFeaturePropertyIntf");
+    pf_factory_ = nullptr;
+    return kErrorResources;
+  }
+
+  DLOGI("Setup pf factory and prop intf for Panel Features");
+  return kErrorNone;
+}
+
 // Query the dspp capabilities and enable the RC feature.
 DisplayError DisplayBase::SetupRC() {
+  // Get status of RC enablement property. Default RC is disabled.
+  int rc_prop_value = 0;
+  Debug::GetProperty(ENABLE_ROUNDED_CORNER, &rc_prop_value);
+  rc_enable_prop_ = rc_prop_value ? true : false;
+  DLOGI("RC feature %s.", rc_enable_prop_ ? "enabled" : "disabled");
+  if (!rc_enable_prop_) {
+    return kErrorNone;
+  }
+
   RCInputConfig input_cfg = {};
   input_cfg.display_id = display_id_;
   input_cfg.display_type = display_type_;
@@ -228,6 +275,7 @@
     return kErrorResources;
   }
 
+  rc_panel_feature_init_ = true;
   return kErrorNone;
 }
 
@@ -235,6 +283,8 @@
   std::vector<Layer *> &layers = layer_stack->layers;
   HWLayersInfo &hw_layers_info = disp_layer_stack_.info;
   hw_layers_info.app_layer_count = 0;
+  hw_layers_info.gpu_target_index = -1;
+  hw_layers_info.stitch_target_index = -1;
 
   disp_layer_stack_.stack = layer_stack;
   hw_layers_info.flags = layer_stack->flags;
@@ -261,7 +311,6 @@
     }
   }
 
-  hw_layers_info.stitch_target_index = hw_layers_info.gpu_target_index + 1;
   DLOGD_IF(kTagDisplay, "LayerStack layer_count: %zu, app_layer_count: %d, "
                         "gpu_target_index: %d, stitch_index: %d game_present: %d, display: %d-%d",
                         layers.size(), hw_layers_info.app_layer_count,
@@ -273,7 +322,7 @@
     return kErrorNoAppLayers;
   }
 
-  if (hw_layers_info.gpu_target_index) {
+  if (hw_layers_info.gpu_target_index > 0) {
     return ValidateGPUTargetParams();
   }
 
@@ -1119,6 +1168,7 @@
   case kCompositionStitch:        return "STITCH";
   case kCompositionGPUTarget:     return "GPU_TARGET";
   case kCompositionStitchTarget:  return "STITCH_TARGET";
+  case kCompositionDemura:        return "DEMURA";
   default:                        return "UNKNOWN";
   }
 }
@@ -1850,7 +1900,8 @@
     // TODO(user): Other FBT layer attributes like surface damage, dataspace, secure camera and
     // secure display flags are also updated during SetClientTarget() called between validate and
     // commit. Need to revist this and update it accordingly for FBT layer.
-    if (disp_layer_stack_.info.gpu_target_index == sdm_layer_index) {
+    if (disp_layer_stack_.info.gpu_target_index > 0 &&
+        (static_cast<uint32_t>(disp_layer_stack_.info.gpu_target_index) == sdm_layer_index)) {
       hw_layer.input_buffer.flags.secure = sdm_layer->input_buffer.flags.secure;
       hw_layer.input_buffer.format = sdm_layer->input_buffer.format;
       hw_layer.input_buffer.width = sdm_layer->input_buffer.width;
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index dd52659..b0dc999 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -44,11 +44,15 @@
 #include "color_manager.h"
 #include "hw_events_interface.h"
 
+#define GET_PANEL_FEATURE_FACTORY "GetPanelFeatureFactoryIntf"
+
 namespace sdm {
 
 using std::recursive_mutex;
 using std::lock_guard;
 
+typedef PanelFeatureFactoryIntf* (*GetPanelFeatureFactory)();
+
 class DisplayBase : public DisplayInterface {
  public:
   DisplayBase(DisplayType display_type, DisplayEventHandler *event_handler,
@@ -178,6 +182,7 @@
   virtual DisplayError NotifyDisplayCalibrationMode(bool in_calibration) {
     return kErrorNotSupported;
   }
+  virtual bool HasDemura() { return false; }
 
  protected:
   struct DisplayMutex {
@@ -216,7 +221,7 @@
   const char *kBt2020Pq = "bt2020_pq";
   const char *kBt2020Hlg = "bt2020_hlg";
   const char *kDisplayBt2020 = "display_bt2020";
-  DisplayError BuildLayerStackStats(LayerStack *layer_stack);
+  virtual DisplayError BuildLayerStackStats(LayerStack *layer_stack);
   virtual DisplayError ValidateGPUTargetParams();
   void CommitLayerParams(LayerStack *layer_stack);
   void PostCommitLayerParams(LayerStack *layer_stack);
@@ -250,6 +255,7 @@
   DisplayError ResetPendingPowerState(const shared_ptr<Fence> &retire_fence);
   DisplayError GetPendingDisplayState(DisplayState *disp_state);
   void SetPendingPowerState(DisplayState state);
+  DisplayError SetupPanelFeatureFactory();
 
   DisplayMutex disp_mutex_;
   int32_t display_id_ = -1;
diff --git a/sdm/libs/core/display_builtin.cpp b/sdm/libs/core/display_builtin.cpp
index 368caaa..d8065de 100644
--- a/sdm/libs/core/display_builtin.cpp
+++ b/sdm/libs/core/display_builtin.cpp
@@ -27,6 +27,7 @@
 #include <utils/rect.h>
 #include <utils/utils.h>
 #include <utils/formats.h>
+#include <core/buffer_allocator.h>
 #include <iomanip>
 #include <algorithm>
 #include <functional>
@@ -131,19 +132,30 @@
   Debug::Get()->GetProperty(DEFER_FPS_FRAME_COUNT, &value);
   deferred_config_.frame_count = (value > 0) ? UINT32(value) : 0;
 
-  spr_prop_value_ = 0;
-  // Enable SPR as default is disabled.
-  Debug::GetProperty(ENABLE_SPR, &spr_prop_value_);
+  if (pf_factory_ && prop_intf_) {
+    if (DisplayBase::SetupRC() != kErrorNone) {
+      // Non-fatal but not expected, log error
+      DLOGE("RC Failed to initialize. Error = %d", error);
+    }
 
-  error = CreatePanelfeatures();
-  if (error != kErrorNone) {
-    DLOGE("Failed to setup panel feature factory, error: %d", error);
+    if ((error = SetupSPR()) != kErrorNone) {
+      DLOGE("SPR Failed to initialize. Error = %d", error);
+      DisplayBase::Deinit();
+      HWInterface::Destroy(hw_intf_);
+      return error;
+    }
+
+    if (SetupDemura() != kErrorNone) {
+      // Non-fatal but not expected, log error
+      DLOGE("Demura failed to initialize, Error = %d", error);
+      comp_manager_->FreeDemuraFetchResources(display_comp_ctx_);
+      comp_manager_->SetDemuraStatusForDisplay(display_id_, false);
+      if (demura_) {
+        SetDemuraIntfStatus(false);
+      }
+    }
   } else {
-    // Get status of RC enablement property. Default RC is disabled.
-    int rc_prop_value = 0;
-    Debug::GetProperty(ENABLE_ROUNDED_CORNER, &rc_prop_value);
-    rc_enable_prop_ = rc_prop_value ? true : false;
-    DLOGI("RC feature %s.", rc_enable_prop_ ? "enabled" : "disabled");
+    DLOGW("Skipping Panel Feature Setups!");
   }
   value = 0;
   DebugHandler::Get()->GetProperty(DISABLE_DYNAMIC_FPS, &value);
@@ -168,45 +180,18 @@
     ClientLock lock(disp_mutex_);
 
     dpps_info_.Deinit();
-  }
-  return DisplayBase::Deinit();
-}
 
-// Create instance for RC, SPR and demura feature.
-DisplayError DisplayBuiltIn::CreatePanelfeatures() {
-  if (pf_factory_ && prop_intf_) {
-    return kErrorNone;
-  }
+    if (demura_) {
+      SetDemuraIntfStatus(false);
 
-  if (!GetPanelFeatureFactoryIntfFunc_) {
-    DynLib feature_impl_lib;
-    if (feature_impl_lib.Open(EXTENSION_LIBRARY_NAME)) {
-      if (!feature_impl_lib.Sym("GetPanelFeatureFactoryIntf",
-                                reinterpret_cast<void **>(&GetPanelFeatureFactoryIntfFunc_))) {
-        DLOGE("Unable to load symbols, error = %s", feature_impl_lib.Error());
-        return kErrorUndefined;
+      if (demura_->Deinit() != 0) {
+        DLOGE("Unable to DeInit Demura on Display %d", display_id_);
       }
-    } else {
-      DLOGW("Unable to load = %s, error = %s", EXTENSION_LIBRARY_NAME, feature_impl_lib.Error());
-      DLOGW("Panel features are not supported");
-      return kErrorNotSupported;
+
+      comp_manager_->FreeDemuraFetchResources(display_comp_ctx_);
     }
   }
-
-  pf_factory_ = GetPanelFeatureFactoryIntfFunc_();
-  if (!pf_factory_) {
-    DLOGE("Failed to create PanelFeatureFactoryIntf");
-    return kErrorResources;
-  }
-
-  prop_intf_ = hw_intf_->GetPanelFeaturePropertyIntf();
-  if (!prop_intf_) {
-    DLOGE("Failed to create PanelFeaturePropertyIntf");
-    pf_factory_ = nullptr;
-    return kErrorResources;
-  }
-
-  return kErrorNone;
+  return DisplayBase::Deinit();
 }
 
 DisplayError DisplayBuiltIn::Prepare(LayerStack *layer_stack) {
@@ -218,6 +203,7 @@
   uint32_t display_height = display_attributes_.y_pixels;
 
   DTRACE_SCOPED();
+
   if (NeedsMixerReconfiguration(layer_stack, &new_mixer_width, &new_mixer_height)) {
     error = ReconfigureMixer(new_mixer_width, new_mixer_height);
     if (error != kErrorNone) {
@@ -348,32 +334,131 @@
 }
 
 DisplayError DisplayBuiltIn::SetupSPR() {
-  SPRInputConfig spr_cfg;
-  spr_cfg.panel_name = std::string(hw_panel_info_.panel_name);
-  spr_ = pf_factory_->CreateSPRIntf(spr_cfg, prop_intf_);
-  if (!spr_ || spr_->Init() != 0) {
-    DLOGE("Failed to initialize SPR");
-    return kErrorResources;
+  int spr_prop_value = 0;
+  // Enable SPR as default is disabled.
+  Debug::GetProperty(ENABLE_SPR, &spr_prop_value);
+
+  if (spr_prop_value) {
+    SPRInputConfig spr_cfg;
+    spr_cfg.panel_name = std::string(hw_panel_info_.panel_name);
+    spr_ = pf_factory_->CreateSPRIntf(spr_cfg, prop_intf_);
+
+    if (spr_ == nullptr) {
+      DLOGE("Failed to create SPR interface");
+      return kErrorResources;
+    }
+
+    if (spr_->Init() != 0) {
+      DLOGE("Failed to initialize SPR");
+      return kErrorResources;
+    }
   }
 
   return kErrorNone;
 }
 
 DisplayError DisplayBuiltIn::SetupDemura() {
-  return kErrorNone;
+  if (!comp_manager_->GetDemuraStatus()) {
+    comp_manager_->FreeDemuraFetchResources(display_comp_ctx_);
+    comp_manager_->SetDemuraStatusForDisplay(display_id_, false);
+    return kErrorNone;
+  }
+
+  int value = 0;
+  if (IsPrimaryDisplay()) {
+    Debug::Get()->GetProperty(DISABLE_DEMURA_PRIMARY, &value);
+  } else {
+    Debug::Get()->GetProperty(DISABLE_DEMURA_SECONDARY, &value);
+  }
+
+  if (value > 0) {
+    comp_manager_->FreeDemuraFetchResources(display_comp_ctx_);
+    comp_manager_->SetDemuraStatusForDisplay(display_id_, false);
+    return kErrorNone;
+  } else if (value == 0) {
+    DemuraInputConfig input_cfg;
+    input_cfg.secure_session = false;  // TODO(user): Integrate with secure solution
+    std::string brightness_base;
+    hw_intf_->GetPanelBrightnessBasePath(&brightness_base);
+    input_cfg.brightness_path = brightness_base+"brightness";
+
+    FetchResourceList frl;
+    comp_manager_->GetDemuraFetchResources(display_comp_ctx_, &frl);
+    for (auto &fr : frl) {
+      int i = std::get<1>(fr);  // fetch resource index
+      input_cfg.resources.set(i);
+    }
+
+    demura_ = pf_factory_->CreateDemuraIntf(input_cfg, prop_intf_, buffer_allocator_, spr_);
+
+    if (!demura_) {
+      DLOGE("Unable to create Demura on Display %d", display_id_);
+      return kErrorMemory;
+    }
+
+    if (demura_->Init() != 0) {
+      DLOGE("Unable to initialize Demura on Display %d", display_id_);
+      return kErrorUndefined;
+    }
+
+    if (SetupDemuraLayer() != kErrorNone) {
+      DLOGE("Unable to setup Demura layer on Display %d", display_id_);
+      return kErrorUndefined;
+    }
+
+    if (SetDemuraIntfStatus(true)) {
+      return kErrorUndefined;
+    }
+
+    comp_manager_->SetDemuraStatusForDisplay(display_id_, true);
+    demura_intended_ = true;
+    DLOGI("Enabled Demura Core!");
+    return kErrorNone;
+  }
+
+  return kErrorUndefined;
 }
 
-DisplayError DisplayBuiltIn::SetupPanelfeatures() {
-  if (!pf_factory_ || !prop_intf_) {
-    DLOGE("Failed to create PanelFeatures");
+DisplayError DisplayBuiltIn::SetupDemuraLayer() {
+  int ret = 0;
+  GenericPayload pl;
+  BufferInfo* buffer = nullptr;
+  if ((ret = pl.CreatePayload<BufferInfo>(buffer))) {
+    DLOGE("Failed to create payload for BufferInfo, error = %d", ret);
+    return kErrorResources;
+  }
+  if ((ret = demura_->GetParameter(kDemuraFeatureParamCorrectionBuffer, &pl))) {
+    DLOGE("Failed to get BufferInfo, error = %d", ret);
     return kErrorResources;
   }
 
-  DisplayError ret = kErrorNone;
-  if ((ret = SetupSPR()) != kErrorNone) return ret;
-  if ((ret = SetupDemura()) != kErrorNone) return ret;
-
-  return ret;
+  demura_layer_.input_buffer.size = buffer->alloc_buffer_info.size;
+  demura_layer_.input_buffer.buffer_id = buffer->alloc_buffer_info.id;
+  demura_layer_.input_buffer.format = buffer->alloc_buffer_info.format;
+  demura_layer_.input_buffer.width = buffer->alloc_buffer_info.aligned_width;
+  demura_layer_.input_buffer.unaligned_width = buffer->alloc_buffer_info.aligned_width;
+  demura_layer_.input_buffer.height = buffer->alloc_buffer_info.aligned_height;
+  demura_layer_.input_buffer.unaligned_height = buffer->alloc_buffer_info.aligned_height;
+  demura_layer_.input_buffer.planes[0].fd = buffer->alloc_buffer_info.fd;
+  demura_layer_.input_buffer.planes[0].stride = buffer->alloc_buffer_info.stride;
+  demura_layer_.input_buffer.planes[0].offset = 0;
+  demura_layer_.input_buffer.flags.demura = 1;
+  demura_layer_.composition = kCompositionDemura;
+  demura_layer_.blending = kBlendingSkip;
+  demura_layer_.flags.is_demura = 1;
+  // ROI must match input dimensions
+  demura_layer_.src_rect.top = 0;
+  demura_layer_.src_rect.left = 0;
+  demura_layer_.src_rect.right = buffer->buffer_config.width;
+  demura_layer_.src_rect.bottom = buffer->buffer_config.height;
+  LogI(kTagNone, "Demura src: ", demura_layer_.src_rect);
+  demura_layer_.dst_rect.top = 0;
+  demura_layer_.dst_rect.left = 0;
+  demura_layer_.dst_rect.right = buffer->buffer_config.width;
+  demura_layer_.dst_rect.bottom = buffer->buffer_config.height;
+  LogI(kTagNone, "Demura dst: ", demura_layer_.dst_rect);
+  demura_layer_.buffer_map = std::make_shared<LayerBufferMap>();
+  return kErrorNone;
 }
 
 DisplayError DisplayBuiltIn::Commit(LayerStack *layer_stack) {
@@ -535,6 +620,13 @@
     SetDeferredFpsConfig();
   }
 
+  // Must go in NullCommit
+  if (demura_intended_ &&
+      comp_manager_->GetDemuraStatusForDisplay(display_id_) && (state == kStateOff)) {
+    comp_manager_->SetDemuraStatusForDisplay(display_id_, false);
+    SetDemuraIntfStatus(false);
+  }
+
   error = DisplayBase::SetDisplayState(state, teardown, release_fence);
   if (error != kErrorNone) {
     return error;
@@ -553,12 +645,11 @@
     event_handler_->Refresh();
   }
 
-  if (spr_prop_value_ && !panel_feature_init_ && state != kStateOff && state != kStateStandby) {
-    error = SetupPanelfeatures();
-    panel_feature_init_ = true;
-    if (error != kErrorNone) {
-      DLOGE("SetupPanelfeatures failed with error :%d, ignoring!", error);
-    }
+  // Must only happen after NullCommit and get applied in next frame
+  if (demura_intended_ &&
+      !comp_manager_->GetDemuraStatusForDisplay(display_id_) && (state == kStateOn)) {
+    comp_manager_->SetDemuraStatusForDisplay(display_id_, true);
+    SetDemuraIntfStatus(true);
   }
 
   return kErrorNone;
@@ -1547,9 +1638,16 @@
   }
 
   // Check Panel and Layer Stack attributes.
+  int8_t stack_fudge_factor = 1;  // GPU Target Layer always present in input
+  if (layer_stack->flags.stitch_present)
+    stack_fudge_factor++;
+  if (layer_stack->flags.demura_present)
+    stack_fudge_factor++;
+
   if (!hw_panel_info_.partial_update || (hw_panel_info_.left_roi_count != 1) ||
       layer_stack->flags.geometry_changed || layer_stack->flags.config_changed ||
-      (layer_stack->layers.size() != (disp_layer_stack_.info.app_layer_count + 1))) {
+      (layer_stack->layers.size() !=
+       (disp_layer_stack_.info.app_layer_count + stack_fudge_factor))) {
     return false;
   }
 
@@ -1619,7 +1717,13 @@
     }
 
     // Set the composition type for SDM layers.
-    for (uint32_t i = 0; i < (layer_stack->layers.size() - 1); i++) {
+    size_t size_ff = 1;  // GPU Target Layer always present in input
+    if (layer_stack->flags.stitch_present)
+      size_ff++;
+    if (layer_stack->flags.demura_present)
+      size_ff++;
+
+    for (uint32_t i = 0; i < (layer_stack->layers.size() - size_ff); i++) {
       layer_stack->layers.at(i)->composition = kCompositionSDE;
     }
   }
@@ -1627,6 +1731,91 @@
   return same_roi;
 }
 
+DisplayError DisplayBuiltIn::BuildLayerStackStats(LayerStack *layer_stack) {
+  std::vector<Layer *> &layers = layer_stack->layers;
+  HWLayersInfo &hw_layers_info = disp_layer_stack_.info;
+  hw_layers_info.app_layer_count = 0;
+  hw_layers_info.gpu_target_index = -1;
+  hw_layers_info.stitch_target_index = -1;
+  hw_layers_info.demura_target_index = -1;
+
+  disp_layer_stack_.stack = layer_stack;
+  hw_layers_info.flags = layer_stack->flags;
+  hw_layers_info.blend_cs = layer_stack->blend_cs;
+
+  int index = 0;
+  for (auto &layer : layers) {
+    if (layer->buffer_map == nullptr) {
+      layer->buffer_map = std::make_shared<LayerBufferMap>();
+    }
+    if (layer->composition == kCompositionGPUTarget) {
+      hw_layers_info.gpu_target_index = index;
+    } else if (layer->composition == kCompositionStitchTarget) {
+      hw_layers_info.stitch_target_index = index;
+      disp_layer_stack_.stack->flags.stitch_present = true;
+      hw_layers_info.stitch_present = true;
+    } else if (layer->composition == kCompositionDemura) {
+      hw_layers_info.demura_target_index = index;
+      disp_layer_stack_.stack->flags.demura_present = true;
+      hw_layers_info.demura_present = true;
+      DLOGD_IF(kTagDisplay, "Display %d shall request Demura in this frame", display_id_);
+    } else {
+      hw_layers_info.app_layer_count++;
+    }
+    if (IsWideColor(layer->input_buffer.color_metadata.colorPrimaries)) {
+      hw_layers_info.wide_color_primaries.push_back(
+          layer->input_buffer.color_metadata.colorPrimaries);
+    }
+    if (layer->flags.is_game) {
+      hw_layers_info.game_present = true;
+    }
+    index++;
+  }
+  if (comp_manager_->GetDemuraStatus() &&
+      comp_manager_->GetDemuraStatusForDisplay(display_id_) &&
+      demura_layer_.input_buffer.planes[0].fd > 0 &&
+      hw_layers_info.demura_target_index == -1) {
+    layers.push_back(&demura_layer_);
+    hw_layers_info.demura_target_index = index;
+    hw_layers_info.demura_present = true;
+    disp_layer_stack_.stack->flags.demura_present = true;
+    DLOGD_IF(kTagDisplay, "Display %d shall request Demura in this frame", display_id_);
+  } else if ((!comp_manager_->GetDemuraStatus() ||
+              !comp_manager_->GetDemuraStatusForDisplay(display_id_)) &&
+             hw_layers_info.demura_target_index != -1 ) {
+    layers.erase(layers.begin() + hw_layers_info.demura_target_index);
+    hw_layers_info.demura_present = false;
+    disp_layer_stack_.stack->flags.demura_present = false;
+    if (hw_layers_info.gpu_target_index > hw_layers_info.demura_target_index) {
+      hw_layers_info.gpu_target_index--;
+    }
+    if (hw_layers_info.stitch_target_index > hw_layers_info.demura_target_index) {
+      hw_layers_info.stitch_target_index--;
+    }
+    hw_layers_info.demura_target_index = -1;
+    DLOGD_IF(kTagDisplay, "Display %d shall remove Demura in this frame", display_id_);
+  }
+
+  DLOGI_IF(kTagDisplay, "LayerStack layer_count: %zu, app_layer_count: %d, "
+                        "gpu_target_index: %d, stitch_index: %d, demura_index: %d, "
+                        "game_present: %d, display: %d-%d",
+                        layers.size(), hw_layers_info.app_layer_count,
+                        hw_layers_info.gpu_target_index, hw_layers_info.stitch_target_index,
+                        hw_layers_info.demura_target_index, hw_layers_info.game_present,
+                        display_id_, display_type_);
+
+  if (!hw_layers_info.app_layer_count) {
+    DLOGW("Layer count is zero");
+    return kErrorNoAppLayers;
+  }
+
+  if (hw_layers_info.gpu_target_index > 0) {
+    return ValidateGPUTargetParams();
+  }
+
+  return kErrorNone;
+}
+
 DisplayError DisplayBuiltIn::SetActiveConfig(uint32_t index) {
   deferred_config_.MarkDirty();
   return DisplayBase::SetActiveConfig(index);
@@ -1819,4 +2008,28 @@
   }
 }
 
+int DisplayBuiltIn::SetDemuraIntfStatus(bool enable) {
+  if (!demura_) {
+    DLOGE("demura_ is nullptr");
+    return -EINVAL;
+  }
+
+  int ret = 0;
+  GenericPayload pl;
+  bool* enable_ptr = nullptr;
+  if ((ret = pl.CreatePayload<bool>(enable_ptr))) {
+    DLOGE("Failed to create payload for enable, error = %d", ret);
+    return ret;
+  } else {
+    *enable_ptr = enable;
+    if ((ret = demura_->SetParameter(kDemuraFeatureParamActive, pl))) {
+      DLOGE("Failed to set Active, error = %d", ret);
+      return ret;
+    }
+  }
+
+  DLOGI("Demura is now %s", enable ? "Enabled" : "Disabled");
+  return ret;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/display_builtin.h b/sdm/libs/core/display_builtin.h
index 86887fc..a9e3b5c 100644
--- a/sdm/libs/core/display_builtin.h
+++ b/sdm/libs/core/display_builtin.h
@@ -82,8 +82,6 @@
   }
 };
 
-typedef PanelFeatureFactoryIntf* (*GetPanelFeatureFactoryIntfType)();
-
 class DppsInfo {
  public:
   void Init(DppsPropIntf *intf, const std::string &panel_name);
@@ -142,6 +140,7 @@
   DisplayError GetStcColorModes(snapdragoncolor::ColorModeList *mode_list) override;
   DisplayError SetStcColorMode(const snapdragoncolor::ColorMode &color_mode) override;
   DisplayError NotifyDisplayCalibrationMode(bool in_calibration) override;
+  bool HasDemura() override { return demura_intended_; }
   std::string Dump() override;
   DisplayError GetConfig(DisplayConfigFixedInfo *fixed_info) override;
 
@@ -174,9 +173,10 @@
   void SetDeferredFpsConfig();
   void GetFpsConfig(HWDisplayAttributes *display_attributes, HWPanelInfo *panel_info);
   PrimariesTransfer GetBlendSpaceFromStcColorMode(const snapdragoncolor::ColorMode &color_mode);
-  DisplayError SetupPanelfeatures();
   DisplayError SetupSPR();
   DisplayError SetupDemura();
+  DisplayError SetupDemuraLayer();
+  DisplayError BuildLayerStackStats(LayerStack *layer_stack) override;
   void UpdateDisplayModeParams();
   void HandleQsyncPostCommit(LayerStack *layer_stack);
   void UpdateQsyncMode();
@@ -184,6 +184,7 @@
   void SendBacklight();
   void SendDisplayConfigs();
   bool CanLowerFps(bool idle_screen);
+  int SetDemuraIntfStatus(bool enable);
 
   const uint32_t kPuTimeOutMs = 1000;
   std::vector<HWEvent> event_list_;
@@ -213,13 +214,10 @@
   sde_drm::DppsFeaturePayload histogramIRQ;
   void initColorSamplingState();
   DeferFpsConfig deferred_config_ = {};
-
   snapdragoncolor::ColorMode current_color_mode_ = {};
   snapdragoncolor::ColorModeList stc_color_modes_ = {};
 
-  std::shared_ptr<SPRIntf> spr_;
-  GetPanelFeatureFactoryIntfType GetPanelFeatureFactoryIntfFunc_ = nullptr;
-  int spr_prop_value_ = 0;
+  std::shared_ptr<SPRIntf> spr_ = nullptr;
   bool needs_validate_on_pu_enable_ = false;
   bool enable_qsync_idle_ = false;
   bool pending_vsync_enable_ = false;
@@ -228,6 +226,9 @@
   bool enhance_idle_time_ = false;
   int idle_time_ms_ = 0;
   struct timespec idle_timer_start_;
+  std::unique_ptr<DemuraIntf> demura_ = nullptr;
+  Layer demura_layer_ = {};
+  bool demura_intended_ = false;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index ec1829d..9a2b551 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -1286,7 +1286,7 @@
 
           drm_atomic_intf_->Perform(DRMOps::PLANE_SET_ZORDER, pipe_id, pipe_info->z_order);
 
-          DRMBlendType blending = {};
+          DRMBlendType blending = DRMBlendType::UNDEFINED;
           SetBlending(layer.blending, &blending);
           drm_atomic_intf_->Perform(DRMOps::PLANE_SET_BLEND_TYPE, pipe_id, blending);
 
@@ -1372,7 +1372,9 @@
   }
 
   drm_atomic_intf_->Perform(DRMOps::DPPS_COMMIT_FEATURE, 0 /* argument is not used */);
-  drm_atomic_intf_->Perform(DRMOps::COMMIT_PANEL_FEATURES, 0 /* argument is not used */);
+  if (!validate) {
+    drm_atomic_intf_->Perform(DRMOps::COMMIT_PANEL_FEATURES, 0 /* argument is not used */);
+  }
 
   if (reset_output_fence_offset_ && !validate) {
     // Change back the fence_offset
@@ -1733,6 +1735,9 @@
     case kBlendingCoverage:
       *target = DRMBlendType::COVERAGE;
       break;
+    case kBlendingSkip:
+      *target = DRMBlendType::SKIP_BLENDING;
+      break;
     default:
       *target = DRMBlendType::UNDEFINED;
   }
@@ -2406,6 +2411,7 @@
 DisplayError HWDeviceDRM::NullCommit(bool synchronous, bool retain_planes) {
   DTRACE_SCOPED();
   AddDimLayerIfNeeded();
+  drm_atomic_intf_->Perform(DRMOps::NULL_COMMIT_PANEL_FEATURES, 0 /* argument is not used */);
   int ret = drm_atomic_intf_->Commit(synchronous , retain_planes);
   if (ret) {
     DLOGE("failed with error %d", ret);
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index ea7c77e..a858ed1 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -30,6 +30,7 @@
 #ifndef __HW_DEVICE_DRM_H__
 #define __HW_DEVICE_DRM_H__
 
+#include <utils/formats.h>
 #include <drm_interface.h>
 #include <errno.h>
 #include <pthread.h>
diff --git a/sdm/libs/core/drm/hw_info_drm.cpp b/sdm/libs/core/drm/hw_info_drm.cpp
index e995342..53e060f 100644
--- a/sdm/libs/core/drm/hw_info_drm.cpp
+++ b/sdm/libs/core/drm/hw_info_drm.cpp
@@ -74,6 +74,7 @@
 using sde_drm::DRMCrtcInfo;
 using sde_drm::DRMPlaneType;
 using sde_drm::DRMTonemapLutType;
+using sde_drm::DRMPanelFeatureInfo;
 
 using std::vector;
 using std::map;
@@ -250,6 +251,8 @@
   DLOGI("Max Pipe Bw = %" PRIu64 " KBps", hw_resource->dyn_bw_info.pipe_bw_limit[kBwVFEOn]);
   DLOGI("Max Pipe Bw High= %" PRIu64 " KBps", hw_resource->dyn_bw_info.pipe_bw_limit[kBwVFEOff]);
   DLOGI("MaxSDEClock = %d Hz", hw_resource->max_sde_clk);
+  DLOGI("Demura Count = %" PRIu32, hw_resource->demura_count);
+  DLOGI("DSPP Count = %" PRIu32, hw_resource->dspp_count);
   DLOGI("Clock Fudge Factor = %f", hw_resource->clk_fudge_factor);
   DLOGI("Prefill factors:");
   DLOGI("\tTiled_NV12 = %d", hw_resource->macrotile_nv12_factor);
@@ -299,6 +302,8 @@
   hw_resource->extra_fudge_factor = info.extra_prefill_lines;
   hw_resource->amortizable_threshold = info.amortized_threshold;
   hw_resource->has_micro_idle = info.has_micro_idle;
+  hw_resource->demura_count = info.demura_count;
+  hw_resource->dspp_count = info.dspp_count;
 
   for (int index = 0; index < kBwModeMax; index++) {
     if (index == kBwVFEOn) {
@@ -366,6 +371,8 @@
   int disable_src_tonemap = 0;
   Debug::Get()->GetProperty(DISABLE_SRC_TONEMAP_PROP, &disable_src_tonemap);
 
+  MapPlaneToConnector(hw_resource);
+  GetInitialDemuraInfo(hw_resource);
   for (auto &pipe_obj : planes) {
     if (max_vig_pipes && max_dma_pipes) {
       uint32_t master_plane_id = pipe_obj.second.master_plane_id;
@@ -430,6 +437,14 @@
         continue;  // Not adding any other pipe type
     }
     pipe_caps.id = pipe_obj.first;
+    auto it = hw_resource->plane_to_connector.find(pipe_caps.id);
+    if (it != hw_resource->plane_to_connector.end()) {
+      pipe_caps.cont_splash_disp_id = it->second;
+      auto it2 = std::find(hw_resource->initial_demura_planes.begin(),
+                           hw_resource->initial_demura_planes.end(), pipe_caps.id);
+      pipe_caps.splash_type = (it2 != hw_resource->initial_demura_planes.end()) ? kSplashDemura
+                                                                                : kSplashLayer;
+    }
     pipe_caps.master_pipe_id = pipe_obj.second.master_plane_id;
     pipe_caps.block_sec_ui = pipe_obj.second.block_sec_ui;
     DLOGI("Adding %s Pipe : Id %d, master_pipe_id : Id %d block_sec_ui: %d",
@@ -437,6 +452,8 @@
           pipe_obj.second.block_sec_ui);
     pipe_caps.inverse_pma = pipe_obj.second.inverse_pma;
     pipe_caps.dgm_csc_version = pipe_obj.second.dgm_csc_version;
+    pipe_caps.pipe_idx = pipe_obj.second.pipe_idx;
+    pipe_caps.demura_block_capability = pipe_obj.second.demura_block_capability;
     // disable src tonemap feature if its disabled using property.
     if (!disable_src_tonemap) {
       for (auto &it : pipe_obj.second.tonemap_lut_version_map) {
@@ -473,8 +490,16 @@
   hw_resource->has_excl_rect = planes[0].second.has_excl_rect;
 }
 
+void HWInfoDRM::MapPlaneToConnector(HWResourceInfo *hw_resource) {
+  drm_mgr_intf_->MapPlaneToConnector(&hw_resource->plane_to_connector);
+}
+
+void HWInfoDRM::GetInitialDemuraInfo(HWResourceInfo *hw_resource) {
+  drm_mgr_intf_->GetInitialDemuraInfo(&hw_resource->initial_demura_planes);
+}
+
 void HWInfoDRM::PopulatePipeCaps(const sde_drm::DRMPlaneTypeInfo &info,
-                                    HWResourceInfo *hw_resource) {
+                                 HWResourceInfo *hw_resource) {
   hw_resource->max_pipe_width = info.max_linewidth;
   hw_resource->max_scaler_pipe_width = info.max_scaler_linewidth;
   hw_resource->max_rotation_pipe_width = info.max_rotation_linewidth;
@@ -488,14 +513,14 @@
   hw_resource->pipe_qseed3_version = GetQseedStepVersion(info.qseed3_version);
   hw_resource->inline_rot_info.inrot_version = GetInRotVersion(info.inrot_version);
   if (info.true_inline_dwnscale_rt_denom > 0 && info.true_inline_dwnscale_rt_num > 0 &&
-       info.true_inline_dwnscale_rt_num >= info.true_inline_dwnscale_rt_denom) {
+      info.true_inline_dwnscale_rt_num >= info.true_inline_dwnscale_rt_denom) {
     hw_resource->inline_rot_info.max_downscale_rt =
-      info.true_inline_dwnscale_rt_num / info.true_inline_dwnscale_rt_denom;
+        info.true_inline_dwnscale_rt_num / info.true_inline_dwnscale_rt_denom;
   }
 }
 
 void HWInfoDRM::PopulatePipeBWCaps(const sde_drm::DRMPlaneTypeInfo &info,
-                                    HWResourceInfo *hw_resource) {
+                                   HWResourceInfo *hw_resource) {
   for (int index = 0; index < kBwModeMax; index++) {
     if (index == kBwVFEOn) {
       hw_resource->dyn_bw_info.pipe_bw_limit[index] = info.max_pipe_bandwidth / kKiloUnit;
@@ -506,7 +531,7 @@
 }
 
 void HWInfoDRM::PopulateSupportedFmts(HWSubBlockType sub_blk_type,
-                                      const sde_drm::DRMPlaneTypeInfo  &info,
+                                      const sde_drm::DRMPlaneTypeInfo &info,
                                       HWResourceInfo *hw_resource) {
   vector<LayerBufferFormat> sdm_formats;
   FormatsMap &fmts_map = hw_resource->supported_formats_map;
@@ -521,7 +546,7 @@
 }
 
 void HWInfoDRM::PopulateSupportedInlineFmts(const sde_drm::DRMPlaneTypeInfo &info,
-                                 HWResourceInfo *hw_resource) {
+                                            HWResourceInfo *hw_resource) {
   vector<LayerBufferFormat> *inrot_fmts = &hw_resource->inline_rot_info.inrot_fmts_supported;
 
   for (auto &fmts : info.inrot_fmts_supported) {
@@ -940,4 +965,45 @@
   return kErrorNone;
 }
 
+DisplayError HWInfoDRM::GetRequiredDemuraFetchResourceCount(
+                        std::map<uint32_t, uint8_t> *required_demura_fetch_cnt) {
+  if (!required_demura_fetch_cnt)
+    return kErrorParameters;
+
+  if (drm_mgr_intf_) {
+    required_demura_fetch_cnt->clear();
+    drm_mgr_intf_->GetRequiredDemuraFetchResourceCount(required_demura_fetch_cnt);
+
+    // Filter out displays that don't have panel_id
+    for (auto it = required_demura_fetch_cnt->begin(); it != required_demura_fetch_cnt->end();) {
+      sde_drm::DRMConnectorInfo info;
+      drm_mgr_intf_->GetConnectorInfo(it->first, &info);
+      if (!info.panel_id) {
+        it = required_demura_fetch_cnt->erase(it);
+      } else {
+        ++it;
+      }
+    }
+  }
+
+  return kErrorNone;
+}
+
+DisplayError HWInfoDRM::GetDemuraPanelIds(std::vector<uint64_t> *panel_ids) {
+  if (!panel_ids) {
+    return kErrorResources;
+  }
+
+  sde_drm::DRMConnectorsInfo conn_infos;
+  drm_mgr_intf_->GetConnectorsInfo(&conn_infos);
+  for (auto &conn : conn_infos) {
+    sde_drm::DRMConnectorInfo &info = conn.second;
+    if (info.panel_id) {
+      panel_ids->push_back(info.panel_id);
+    }
+  }
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_info_drm.h b/sdm/libs/core/drm/hw_info_drm.h
index 020920b..1cfbfdb 100644
--- a/sdm/libs/core/drm/hw_info_drm.h
+++ b/sdm/libs/core/drm/hw_info_drm.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+* Copyright (c) 2017-2020, 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
@@ -36,12 +36,13 @@
 #include <private/hw_info_types.h>
 #include <bitset>
 #include <vector>
+#include <map>
 
 #include "hw_info_interface.h"
 
 namespace sdm {
 
-class HWInfoDRM: public HWInfoInterface {
+class HWInfoDRM : public HWInfoInterface {
  public:
   virtual DisplayError Init();
   virtual ~HWInfoDRM();
@@ -49,6 +50,9 @@
   virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
   virtual DisplayError GetDisplaysStatus(HWDisplaysInfo *hw_displays_info);
   virtual DisplayError GetMaxDisplaysSupported(DisplayType type, int32_t *max_displays);
+  virtual DisplayError GetRequiredDemuraFetchResourceCount(
+                       std::map<uint32_t, uint8_t> *required_demura_fetch_cnt);
+  virtual DisplayError GetDemuraPanelIds(std::vector<uint64_t> *panel_ids);
 
  private:
   void Deinit();
@@ -63,12 +67,14 @@
   void GetRotatorFormatsForType(int fd, uint32_t type,
                                 std::vector<LayerBufferFormat> *supported_formats);
   DisplayError GetRotatorSupportedFormats(uint32_t v4l2_index, HWResourceInfo *hw_resource);
-  void PopulateSupportedFmts(HWSubBlockType sub_blk_type, const sde_drm::DRMPlaneTypeInfo  &info,
+  void PopulateSupportedFmts(HWSubBlockType sub_blk_type, const sde_drm::DRMPlaneTypeInfo &info,
                              HWResourceInfo *hw_resource);
   void PopulateSupportedInlineFmts(const sde_drm::DRMPlaneTypeInfo &info,
                                    HWResourceInfo *hw_resource);
   void PopulatePipeCaps(const sde_drm::DRMPlaneTypeInfo &info, HWResourceInfo *hw_resource);
   void PopulatePipeBWCaps(const sde_drm::DRMPlaneTypeInfo &info, HWResourceInfo *hw_resource);
+  void MapPlaneToConnector(HWResourceInfo *hw_resource);
+  void GetInitialDemuraInfo(HWResourceInfo *hw_resource);
 
   sde_drm::DRMManagerInterface *drm_mgr_intf_ = {};
   bool default_mode_ = false;
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.cpp b/sdm/libs/core/drm/hw_peripheral_drm.cpp
index 0b0fb2d..080e225 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.cpp
+++ b/sdm/libs/core/drm/hw_peripheral_drm.cpp
@@ -641,6 +641,10 @@
 
 DisplayError HWPeripheralDRM::PowerOff(bool teardown) {
   DTRACE_SCOPED();
+  if (!first_cycle_) {
+    drm_mgr_intf_->MarkPanelFeatureForNullCommit(token_,
+                                           panel_feature_property_map_[kPanelFeatureDemuraInitCfg]);
+  }
   SetVMReqState();
   DisplayError err = kErrorNone;
   if (secure_display_active_) {
@@ -916,6 +920,7 @@
   panel_feature_property_map_[kPanelFeatureDsppDemuraInfo] =
     sde_drm::kDRMPanelFeatureDsppDemuraInfo;
   panel_feature_property_map_[kPanelFeatureRCInitCfg] = sde_drm::kDRMPanelFeatureRCInit;
+  panel_feature_property_map_[kPanelFeatureDemuraPanelId] = sde_drm::kDRMPanelFeaturePanelId;
 }
 
 int HWPeripheralDRM::GetPanelFeature(PanelFeaturePropertyInfo *feature_info) {
@@ -939,6 +944,7 @@
 
   switch (feature_info->prop_id) {
     case kPanelFeatureSPRInitCfg:
+    case kPanelFeatureDemuraInitCfg:
     case kPanelFeatureDsppIndex:
     case kPanelFeatureDsppSPRInfo:
     case kPanelFeatureDsppDemuraInfo:
@@ -948,6 +954,7 @@
       drm_feature.obj_id =  token_.crtc_id;
      break;
     case kPanelFeatureSPRPackType:
+    case kPanelFeatureDemuraPanelId:
       drm_feature.obj_type = DRM_MODE_OBJECT_CONNECTOR;
       drm_feature.obj_id =  token_.conn_id;
      break;
@@ -975,6 +982,7 @@
   switch (feature_info.prop_id) {
     case kPanelFeatureSPRInitCfg:
     case kPanelFeatureRCInitCfg:
+    case kPanelFeatureDemuraInitCfg:
       drm_feature.obj_type = DRM_MODE_OBJECT_CRTC;
       drm_feature.obj_id =  token_.crtc_id;
      break;
diff --git a/sdm/libs/core/hw_info_interface.h b/sdm/libs/core/hw_info_interface.h
index 5eb2107..2ccf27d 100644
--- a/sdm/libs/core/hw_info_interface.h
+++ b/sdm/libs/core/hw_info_interface.h
@@ -25,9 +25,12 @@
 #ifndef __HW_INFO_INTERFACE_H__
 #define __HW_INFO_INTERFACE_H__
 
-#include <inttypes.h>
 #include <core/core_interface.h>
 #include <private/hw_info_types.h>
+#include <inttypes.h>
+#include <vector>
+#include <utility>
+#include <map>
 
 namespace sdm {
 
@@ -40,7 +43,9 @@
   virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) = 0;
   virtual DisplayError GetDisplaysStatus(HWDisplaysInfo *hw_displays_info) = 0;
   virtual DisplayError GetMaxDisplaysSupported(DisplayType type, int32_t *max_displays) = 0;
-
+  virtual DisplayError GetRequiredDemuraFetchResourceCount(
+                       std::map<uint32_t, uint8_t> *required_demura_fetch_cnt) = 0;
+  virtual DisplayError GetDemuraPanelIds(std::vector<uint64_t> *panel_ids) = 0;
  protected:
   virtual ~HWInfoInterface() { }
 };
diff --git a/sdm/libs/core/resource_default.h b/sdm/libs/core/resource_default.h
index 5726a84..ea392e0 100644
--- a/sdm/libs/core/resource_default.h
+++ b/sdm/libs/core/resource_default.h
@@ -29,6 +29,7 @@
 #include <private/resource_interface.h>
 #include <utils/locker.h>
 #include <vector>
+#include <map>
 
 #include "hw_interface.h"
 
@@ -70,6 +71,18 @@
   virtual DisplayError Perform(int cmd, ...) { return kErrorNone; }
   DisplayError SetDisplayState(int32_t display_id, DisplayState state) { return kErrorNone; }
   virtual bool IsRotatorSupportedFormat(LayerBufferFormat format) { return false; }
+  virtual DisplayError FreeDemuraFetchResources(Handle display_ctx) { return kErrorNone; }
+  virtual DisplayError GetDemuraFetchResourceCount(
+                       std::map<uint32_t, uint8_t> *fetch_resource_cnt) {
+    return kErrorNone;
+  }
+  virtual DisplayError ReserveDemuraFetchResources(const int32_t &display_id,
+                                                   const int8_t &preferred_rect) {
+    return kErrorNone;
+  }
+  virtual DisplayError GetDemuraFetchResources(Handle display_ctx, FetchResourceList *frl) {
+    return kErrorNone;
+  }
 
  private:
   enum PipeOwner {