Merge "sdm:scalar:intialize scaling params."
diff --git a/Android.mk b/Android.mk
index 60a48f5..6eef2fc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -3,7 +3,7 @@
 
 ifneq ($(TARGET_IS_HEADLESS), true)
     display-hals += libcopybit liblight libmemtrack hdmi_cec \
-                    $(sdm-libs)/hwc $(sdm-libs)/hwc2
+                    $(sdm-libs)/hwc $(sdm-libs)/hwc2 gpu_tonemapper
 endif
 
 ifneq ($(TARGET_USES_GRALLOC1), true)
diff --git a/common.mk b/common.mk
index d44a664..6476dfd 100644
--- a/common.mk
+++ b/common.mk
@@ -17,6 +17,7 @@
 
 common_includes := $(display_top)/libqdutils
 common_includes += $(display_top)/libqservice
+common_includes += $(display_top)/gpu_tonemapper
 ifneq ($(TARGET_IS_HEADLESS), true)
     common_includes += $(display_top)/libcopybit
 endif
diff --git a/gpu_tonemapper/Android.mk b/gpu_tonemapper/Android.mk
new file mode 100644
index 0000000..9ae3840
--- /dev/null
+++ b/gpu_tonemapper/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH := $(call my-dir)
+include $(LOCAL_PATH)/../common.mk
+include $(CLEAR_VARS)
+
+LOCAL_COPY_HEADERS_TO     := $(common_header_export_path)
+LOCAL_COPY_HEADERS        := TonemapFactory.h Tonemapper.h
+LOCAL_SHARED_LIBRARIES    := libEGL libGLESv2 libui libutils liblog
+include $(BUILD_COPY_HEADERS)
+
+LOCAL_MODULE              := libgpu_tonemapper
+LOCAL_MODULE_TAGS         := optional
+LOCAL_C_INCLUDES          := $(TARGET_OUT_HEADERS)/qcom/display/
+
+LOCAL_CFLAGS              := -Wno-missing-field-initializers -Wall \
+                             -Wno-unused-parameter -std=c++11 -DLOG_TAG=\"GPU_TONEMAPPER\"
+
+LOCAL_SRC_FILES           := TonemapFactory.cpp \
+                             glengine.cpp \
+                             EGLImageBuffer.cpp \
+                             EGLImageWrapper.cpp \
+                             Tonemapper.cpp
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/gpu_tonemapper/EGLImageBuffer.cpp b/gpu_tonemapper/EGLImageBuffer.cpp
new file mode 100644
index 0000000..e64e16f
--- /dev/null
+++ b/gpu_tonemapper/EGLImageBuffer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EGLImageBuffer.h"
+#include <cutils/native_handle.h>
+#include <gralloc_priv.h>
+#include <ui/GraphicBuffer.h>
+#include <map>
+#include "EGLImageWrapper.h"
+#include "glengine.h"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+//-----------------------------------------------------------------------------
+EGLImageKHR create_eglImage(android::sp<android::GraphicBuffer> graphicBuffer)
+//-----------------------------------------------------------------------------
+{
+  bool isProtected = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+  EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+                    isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+                    isProtected ? EGL_TRUE : EGL_NONE, EGL_NONE};
+
+  EGLImageKHR eglImage = eglCreateImageKHR(
+      eglGetCurrentDisplay(), (EGLContext)EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      (EGLClientBuffer)(graphicBuffer->getNativeBuffer()), attrs);
+
+  return eglImage;
+}
+
+//-----------------------------------------------------------------------------
+EGLImageBuffer::EGLImageBuffer(android::sp<android::GraphicBuffer> graphicBuffer)
+//-----------------------------------------------------------------------------
+{
+  // this->graphicBuffer = graphicBuffer;
+  this->eglImageID = create_eglImage(graphicBuffer);
+  this->width = graphicBuffer->getWidth();
+  this->height = graphicBuffer->getHeight();
+
+  textureID = 0;
+  renderbufferID = 0;
+  framebufferID = 0;
+}
+
+//-----------------------------------------------------------------------------
+EGLImageBuffer::~EGLImageBuffer()
+//-----------------------------------------------------------------------------
+{
+  if (textureID != 0) {
+    GL(glDeleteTextures(1, &textureID));
+    textureID = 0;
+  }
+
+  if (renderbufferID != 0) {
+    GL(glDeleteRenderbuffers(1, &renderbufferID));
+    renderbufferID = 0;
+  }
+
+  if (framebufferID != 0) {
+    GL(glDeleteFramebuffers(1, &framebufferID));
+    framebufferID = 0;
+  }
+}
+
+//-----------------------------------------------------------------------------
+int EGLImageBuffer::getWidth()
+//-----------------------------------------------------------------------------
+{
+  return width;
+}
+
+//-----------------------------------------------------------------------------
+int EGLImageBuffer::getHeight()
+//-----------------------------------------------------------------------------
+{
+  return height;
+}
+
+//-----------------------------------------------------------------------------
+unsigned int EGLImageBuffer::getTexture()
+//-----------------------------------------------------------------------------
+{
+  if (textureID == 0) {
+    bindAsTexture();
+  }
+
+  return textureID;
+}
+
+//-----------------------------------------------------------------------------
+unsigned int EGLImageBuffer::getFramebuffer()
+//-----------------------------------------------------------------------------
+{
+  if (framebufferID == 0) {
+    bindAsFramebuffer();
+  }
+
+  return framebufferID;
+}
+
+//-----------------------------------------------------------------------------
+void EGLImageBuffer::bindAsTexture()
+//-----------------------------------------------------------------------------
+{
+  if (textureID == 0) {
+    GL(glGenTextures(1, &textureID));
+    int target = 0x8D65;
+    GL(glBindTexture(target, textureID));
+    GL(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+    GL(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+    GL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+    GL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+    GL(glEGLImageTargetTexture2DOES(0x8D65, eglImageID));
+  }
+
+  GL(glBindTexture(0x8D65, textureID));
+}
+
+//-----------------------------------------------------------------------------
+void EGLImageBuffer::bindAsFramebuffer()
+//-----------------------------------------------------------------------------
+{
+  if (renderbufferID == 0) {
+    GL(glGenFramebuffers(1, &framebufferID));
+    GL(glGenRenderbuffers(1, &renderbufferID));
+
+    GL(glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID));
+    GL(glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, eglImageID));
+
+    GL(glBindFramebuffer(GL_FRAMEBUFFER, framebufferID));
+    GL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+                                 renderbufferID));
+    GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (result != GL_FRAMEBUFFER_COMPLETE) {
+      ALOGI("%s Framebuffer Invalid***************", __FUNCTION__);
+    }
+  }
+
+  GL(glBindFramebuffer(GL_FRAMEBUFFER, framebufferID));
+}
diff --git a/gpu_tonemapper/EGLImageBuffer.h b/gpu_tonemapper/EGLImageBuffer.h
new file mode 100644
index 0000000..23af573
--- /dev/null
+++ b/gpu_tonemapper/EGLImageBuffer.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EGLIMAGE_BUFFER_H__
+#define __EGLIMAGE_BUFFER_H__
+
+#include <cutils/native_handle.h>
+#include <gralloc_priv.h>
+#include <ui/GraphicBuffer.h>
+#include "engine.h"
+
+class EGLImageBuffer {
+  // android::sp<android::GraphicBuffer> graphicBuffer;
+  void *eglImageID;
+  int width;
+  int height;
+  uint textureID;
+  uint renderbufferID;
+  uint framebufferID;
+
+ public:
+  int getWidth();
+  int getHeight();
+  EGLImageBuffer(android::sp<android::GraphicBuffer>);
+  unsigned int getTexture();
+  unsigned int getFramebuffer();
+  void bindAsTexture();
+  void bindAsFramebuffer();
+  ~EGLImageBuffer();
+  static EGLImageBuffer *from(const private_handle_t *src);
+  static void clear();
+};
+
+#endif  //__EGLIMAGE_BUFFER_H__
\ No newline at end of file
diff --git a/gpu_tonemapper/EGLImageWrapper.cpp b/gpu_tonemapper/EGLImageWrapper.cpp
new file mode 100644
index 0000000..eb0a2ca
--- /dev/null
+++ b/gpu_tonemapper/EGLImageWrapper.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EGLImageWrapper.h"
+#include <cutils/native_handle.h>
+#include <gralloc_priv.h>
+#include <ui/GraphicBuffer.h>
+
+std::map<int, EGLImageBuffer *> EGLImageWrapper::eglImageBufferMap;
+
+//-----------------------------------------------------------------------------
+EGLImageBuffer *EGLImageWrapper::wrap(const void *pvt_handle)
+//-----------------------------------------------------------------------------
+{
+  const private_handle_t *src = static_cast<const private_handle_t *>(pvt_handle);
+
+  EGLImageBuffer *result = 0;
+  std::map<int, EGLImageBuffer *>::iterator it = eglImageBufferMap.find(src->fd);
+  if (it == eglImageBufferMap.end()) {
+    native_handle_t *native_handle = const_cast<private_handle_t *>(src);
+
+    int flags = android::GraphicBuffer::USAGE_HW_TEXTURE |
+                android::GraphicBuffer::USAGE_SW_READ_NEVER |
+                android::GraphicBuffer::USAGE_SW_WRITE_NEVER;
+    if (src->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
+      flags |= android::GraphicBuffer::USAGE_PROTECTED;
+    }
+
+    android::sp<android::GraphicBuffer> graphicBuffer =
+        new android::GraphicBuffer(src->width, src->height, src->format, flags,
+                                   src->width /*src->stride*/, native_handle, false);
+
+    result = new EGLImageBuffer(graphicBuffer);
+
+    eglImageBufferMap[src->fd] = result;
+  } else {
+    result = it->second;
+  }
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+void EGLImageWrapper::destroy()
+//-----------------------------------------------------------------------------
+{
+  std::map<int, EGLImageBuffer *>::iterator it = eglImageBufferMap.begin();
+  for (; it != eglImageBufferMap.end(); it++) {
+    delete it->second;
+  }
+  eglImageBufferMap.clear();
+}
\ No newline at end of file
diff --git a/gpu_tonemapper/EGLImageWrapper.h b/gpu_tonemapper/EGLImageWrapper.h
new file mode 100644
index 0000000..d7fc84b
--- /dev/null
+++ b/gpu_tonemapper/EGLImageWrapper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TONEMAPPER_EGLIMAGEWRAPPER_H__
+#define __TONEMAPPER_EGLIMAGEWRAPPER_H__
+
+#include <map>
+#include "EGLImageBuffer.h"
+
+class EGLImageWrapper {
+  static std::map<int, EGLImageBuffer *> eglImageBufferMap;
+
+ public:
+  static EGLImageBuffer *wrap(const void *pvt_handle);
+  static void destroy();
+};
+
+#endif  //__TONEMAPPER_EGLIMAGEWRAPPER_H__
\ No newline at end of file
diff --git a/gpu_tonemapper/TonemapFactory.cpp b/gpu_tonemapper/TonemapFactory.cpp
new file mode 100644
index 0000000..81e9f7f
--- /dev/null
+++ b/gpu_tonemapper/TonemapFactory.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TonemapFactory.h"
+#include <utils/Log.h>
+#include "EGLImageWrapper.h"
+#include "Tonemapper.h"
+#include "engine.h"
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+Tonemapper *TonemapperFactory_GetInstance(int type, void *colorMap, int colorMapSize,
+                                          void *lutXform, int lutXformSize)
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+{
+  // initializes the engine - does nothing if already initialized
+  engine_initialize();
+
+  // build the tonemapper
+  Tonemapper *tonemapper = Tonemapper::build(type, colorMap, colorMapSize, lutXform, lutXformSize);
+
+  return tonemapper;
+}
+
+//------------------------------------------
+void TonemapperFactory_Destroy()
+//------------------------------------------
+{
+  // clear EGLImage mappings
+  EGLImageWrapper::destroy();
+  // shutdown the engine
+  engine_shutdown();
+}
diff --git a/gpu_tonemapper/TonemapFactory.h b/gpu_tonemapper/TonemapFactory.h
new file mode 100644
index 0000000..1004170
--- /dev/null
+++ b/gpu_tonemapper/TonemapFactory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TONEMAPPER_TONEMAPPERFACTORY_H__
+#define __TONEMAPPER_TONEMAPPERFACTORY_H__
+
+#include "Tonemapper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// returns an instance of Tonemapper
+Tonemapper *TonemapperFactory_GetInstance(int type, void *colorMap, int colorMapSize,
+                                          void *lutXform, int lutXformSize);
+
+// destroy tonemap session
+void TonemapperFactory_Destroy();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  //__TONEMAPPER_TONEMAPPERFACTORY_H__
diff --git a/gpu_tonemapper/Tonemapper.cpp b/gpu_tonemapper/Tonemapper.cpp
new file mode 100644
index 0000000..957c133
--- /dev/null
+++ b/gpu_tonemapper/Tonemapper.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <utils/Log.h>
+
+#include "EGLImageWrapper.h"
+#include "Tonemapper.h"
+#include "engine.h"
+#include "forward_tonemap.inl"
+#include "fullscreen_vertex_shader.inl"
+#include "rgba_inverse_tonemap.inl"
+
+//-----------------------------------------------------------------------------
+Tonemapper::Tonemapper()
+//-----------------------------------------------------------------------------
+{
+  tonemapTexture = 0;
+  lutXformTexture = 0;
+  programID = 0;
+}
+
+//-----------------------------------------------------------------------------
+Tonemapper::~Tonemapper()
+//-----------------------------------------------------------------------------
+{
+  engine_deleteInputBuffer(tonemapTexture);
+  engine_deleteInputBuffer(lutXformTexture);
+  engine_deleteProgram(programID);
+}
+
+//-----------------------------------------------------------------------------
+Tonemapper *Tonemapper::build(int type, void *colorMap, int colorMapSize, void *lutXform,
+                              int lutXformSize)
+//-----------------------------------------------------------------------------
+{
+  if (colorMapSize <= 0) {
+      ALOGE("Invalid Color Map size = %d", colorMapSize);
+      return NULL;
+  }
+  engine_bind();
+
+  // build new tonemapper
+  Tonemapper *tonemapper = new Tonemapper();
+  // load the 3d lut
+  tonemapper->tonemapTexture = engine_load3DTexture(colorMap, colorMapSize, 0);
+  // load the non-uniform xform
+  tonemapper->lutXformTexture = engine_load1DTexture(lutXform, lutXformSize, 0);
+  bool bUseXform = (tonemapper->lutXformTexture != 0) && (lutXformSize != 0);
+
+  // create the program
+  const char *fragmentShaders[3];
+  int fragmentShaderCount = 0;
+  const char *version = "#version 300 es\n";
+  const char *define = "#define USE_NONUNIFORM_SAMPLING\n";
+
+  fragmentShaders[fragmentShaderCount++] = version;
+
+  // non-uniform sampling
+  if (bUseXform) {
+    fragmentShaders[fragmentShaderCount++] = define;
+  }
+
+  if (type == TONEMAP_INVERSE) {  // inverse tonemapping
+    fragmentShaders[fragmentShaderCount++] = rgba_inverse_tonemap_shader;
+  } else {  // forward tonemapping
+    fragmentShaders[fragmentShaderCount++] = forward_tonemap_shader;
+  }
+
+  tonemapper->programID =
+      engine_loadProgram(1, &fullscreen_vertex_shader, fragmentShaderCount, fragmentShaders);
+
+  return tonemapper;
+}
+
+//-----------------------------------------------------------------------------
+int Tonemapper::blit(const void *dst, const void *src, int srcFenceFd)
+//-----------------------------------------------------------------------------
+{
+  // make current
+  engine_bind();
+
+  // create eglimages if required
+  EGLImageBuffer *dst_buffer = EGLImageWrapper::wrap(dst);
+  EGLImageBuffer *src_buffer = EGLImageWrapper::wrap(src);
+
+  // bind the program
+  engine_setProgram(programID);
+
+  // set destination
+  engine_setDestination(dst_buffer->getFramebuffer(), 0, 0, dst_buffer->getWidth(),
+                        dst_buffer->getHeight());
+  // set source
+  engine_setExternalInputBuffer(0, src_buffer->getTexture());
+  // set 3d lut
+  engine_set3DInputBuffer(1, tonemapTexture);
+  // set non-uniform xform
+  engine_set2DInputBuffer(2, lutXformTexture);
+
+  // perform
+  int fenceFD = engine_blit(srcFenceFd);
+
+  return fenceFD;
+}
diff --git a/gpu_tonemapper/Tonemapper.h b/gpu_tonemapper/Tonemapper.h
new file mode 100644
index 0000000..9c41670
--- /dev/null
+++ b/gpu_tonemapper/Tonemapper.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TONEMAPPER_TONEMAP_H__
+#define __TONEMAPPER_TONEMAP_H__
+
+#define TONEMAP_FORWARD 0
+#define TONEMAP_INVERSE 1
+
+class Tonemapper {
+ private:
+  unsigned int tonemapTexture;
+  unsigned int lutXformTexture;
+  unsigned int programID;
+  Tonemapper();
+
+ public:
+  ~Tonemapper();
+  static Tonemapper *build(int type, void *colorMap, int colorMapSize, void *lutXform,
+                           int lutXformSize);
+  int blit(const void *dst, const void *src, int srcFenceFd);
+};
+
+#endif  //__TONEMAPPER_TONEMAP_H__
diff --git a/gpu_tonemapper/engine.h b/gpu_tonemapper/engine.h
new file mode 100644
index 0000000..5635ee3
--- /dev/null
+++ b/gpu_tonemapper/engine.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TONEMAPPER_ENGINE_H__
+#define __TONEMAPPER_ENGINE_H__
+
+bool engine_initialize();
+void engine_bind();
+void engine_shutdown();
+
+unsigned int engine_loadProgram(int, const char **, int, const char **);
+void engine_setProgram(int);
+void engine_deleteProgram(unsigned int);
+
+unsigned int engine_load3DTexture(void *data, int sz, int format);
+unsigned int engine_load1DTexture(void *xform, int xformSize, int format);
+void engine_deleteInputBuffer(unsigned int);
+
+void engine_set2DInputBuffer(int binding, unsigned int textureID);
+void engine_set3DInputBuffer(int binding, unsigned int textureID);
+void engine_setExternalInputBuffer(int binding, unsigned int textureID);
+void engine_setDestination(int id, int x, int y, int w, int h);
+
+int engine_blit(int);
+
+#endif  //__TONEMAPPER_ENGINE_H__
\ No newline at end of file
diff --git a/gpu_tonemapper/forward_tonemap.inl b/gpu_tonemapper/forward_tonemap.inl
new file mode 100644
index 0000000..a008949
--- /dev/null
+++ b/gpu_tonemapper/forward_tonemap.inl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const char* forward_tonemap_shader = ""
+    "#extension GL_OES_EGL_image_external_essl3 : require                       \n"
+    "precision highp float;                                                     \n"
+    "precision highp sampler2D;                                                 \n"
+    "layout(binding = 0) uniform samplerExternalOES externalTexture;            \n"
+    "layout(binding = 1) uniform sampler3D tonemapper;                          \n"
+    "layout(binding = 2) uniform sampler2D xform;                               \n"
+    "in vec2 uv;                                                                \n"
+    "out vec4 fs_color;                                                         \n"
+    "void main()                                                                \n"
+    "{                                                                          \n"
+    "vec4 rgb = texture(externalTexture, uv);                                   \n"
+    "#if defined(USE_NONUNIFORM_SAMPLING)                                       \n"
+    "float r = texture(xform, vec2(r, 0.0f)).r;                                 \n"
+    "float g = texture(xform, vec2(g, 0.0f)).g;                                 \n"
+    "float b = texture(xform, vec2(b, 0.0f)).b;                                 \n"
+    "#else                                                                      \n"
+    "float r = rgb.r;                                                           \n"
+    "float g = rgb.g;                                                           \n"
+    "float b = rgb.b;                                                           \n"
+    "#endif                                                                     \n"
+    "fs_color.rgb = texture(tonemapper, vec3(r, g, b)).rgb;                     \n"
+    "}                                                                          \n";
diff --git a/gpu_tonemapper/fullscreen_vertex_shader.inl b/gpu_tonemapper/fullscreen_vertex_shader.inl
new file mode 100644
index 0000000..9a70c2b
--- /dev/null
+++ b/gpu_tonemapper/fullscreen_vertex_shader.inl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const char* fullscreen_vertex_shader = "                                      "
+"#version 300 es                                                            \n"
+"precision highp float;                                                     \n"
+"layout(location = 0) in vec2 iUV;                                          \n"
+"out vec2 uv;                                                               \n"
+"void main()                                                                \n"
+"{                                                                          \n"
+"    vec2 positions[3];                                                     \n"
+"    positions[0] = vec2(-1.0f, 3.0f);                                      \n"
+"    positions[1] = vec2(-1.0f, -1.0f);                                     \n"
+"    positions[2] = vec2(3.0f, -1.0f);                                      \n"
+"    vec2 uvs[3];                                                           \n"
+"    uvs[0] = vec2(0.0f, -1.0f);                                            \n"
+"    uvs[1] = vec2(0.0f, 1.0f);                                             \n"
+"    uvs[2] = vec2(2.0f, 1.0f);                                             \n"
+"    gl_Position = vec4(positions[gl_VertexID], -1.0f, 1.0f);               \n"
+"    uv = uvs[gl_VertexID];                                                 \n"
+"}                                                                          \n";
diff --git a/gpu_tonemapper/glengine.cpp b/gpu_tonemapper/glengine.cpp
new file mode 100644
index 0000000..e5c8e68
--- /dev/null
+++ b/gpu_tonemapper/glengine.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "glengine.h"
+#include <utils/Log.h>
+#include "engine.h"
+
+void checkGlError(const char *, int);
+void checkEglError(const char *, int);
+
+static EGLDisplay eglDisplay;
+static EGLContext eglContext;
+static EGLSurface eglSurface;
+
+static bool isEngineInitialized = false;
+
+//-----------------------------------------------------------------------------
+// Make Current
+void engine_bind()
+//-----------------------------------------------------------------------------
+{
+  EGL(eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext));
+}
+
+//-----------------------------------------------------------------------------
+// initialize GL
+//
+bool engine_initialize()
+//-----------------------------------------------------------------------------
+{
+  if (isEngineInitialized)
+    return true;
+
+  EGLBoolean result = false;
+
+  // display
+  eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  EGL(eglBindAPI(EGL_OPENGL_ES_API));
+
+  // initialize
+  EGL(eglInitialize(eglDisplay, 0, 0));
+
+  // config
+  EGLConfig eglConfig;
+  EGLint eglConfigAttribList[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+                                  EGL_RED_SIZE,     8,
+                                  EGL_GREEN_SIZE,   8,
+                                  EGL_BLUE_SIZE,    8,
+                                  EGL_ALPHA_SIZE,   8,
+                                  EGL_NONE};
+  int numConfig = 0;
+  EGL(eglChooseConfig(eglDisplay, eglConfigAttribList, &eglConfig, 1, &numConfig));
+
+  // context
+  EGLint eglContextAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+  eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, eglContextAttribList);
+
+  // surface
+  EGLint eglSurfaceAttribList[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
+  eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, eglSurfaceAttribList);
+
+  result = (EGL_TRUE == eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext));
+
+  isEngineInitialized = result;
+
+  ALOGI("In %s result = %d context = %p", __FUNCTION__, result, (void *)eglContext);
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+// Shutdown.
+void engine_shutdown()
+//-----------------------------------------------------------------------------
+{
+  EGL(eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
+  EGL(eglDestroySurface(eglDisplay, eglSurface));
+  EGL(eglDestroyContext(eglDisplay, eglContext));
+  EGL(eglTerminate(eglDisplay));
+  eglDisplay = EGL_NO_DISPLAY;
+  eglContext = EGL_NO_CONTEXT;
+  eglSurface = EGL_NO_SURFACE;
+  isEngineInitialized = false;
+}
+
+//-----------------------------------------------------------------------------
+void engine_deleteInputBuffer(unsigned int id)
+//-----------------------------------------------------------------------------
+{
+  if (id != 0) {
+    GL(glDeleteTextures(1, &id));
+  }
+}
+
+//-----------------------------------------------------------------------------
+void engine_deleteProgram(unsigned int id)
+//-----------------------------------------------------------------------------
+{
+  if (id != 0) {
+    GL(glDeleteProgram(id));
+  }
+}
+
+//-----------------------------------------------------------------------------
+unsigned int engine_load3DTexture(void *colorMapData, int sz, int format)
+//-----------------------------------------------------------------------------
+{
+  GLuint texture = 0;
+  GL(glGenTextures(1, &texture));
+  GL(glBindTexture(GL_TEXTURE_3D, texture));
+  GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+  GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+  GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
+  GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+  GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+
+  GL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB10_A2, sz, sz, sz, 0, GL_RGBA,
+                  GL_UNSIGNED_INT_2_10_10_10_REV, colorMapData));
+
+  return texture;
+}
+//-----------------------------------------------------------------------------
+unsigned int engine_load1DTexture(void *data, int sz, int format)
+//-----------------------------------------------------------------------------
+{
+  GLuint texture = 0;
+  if ((data != 0) && (sz != 0)) {
+    GL(glGenTextures(1, &texture));
+    GL(glBindTexture(GL_TEXTURE_2D, texture));
+    GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+    GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+    GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+    GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+
+    GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, sz, 1, 0, GL_RGBA,
+                    GL_UNSIGNED_INT_2_10_10_10_REV, data));
+  }
+  return texture;
+}
+
+//-----------------------------------------------------------------------------
+void dumpShaderLog(int shader)
+//-----------------------------------------------------------------------------
+{
+  int success;
+  GLchar infoLog[512];
+  GL(glGetShaderiv(shader, GL_COMPILE_STATUS, &success));
+  if (!success) {
+    glGetShaderInfoLog(shader, 512, NULL, infoLog);
+    ALOGI("Shader Failed to compile: %s\n", infoLog);
+  }
+}
+
+//-----------------------------------------------------------------------------
+GLuint engine_loadProgram(int vertexEntries, const char **vertex, int fragmentEntries,
+                          const char **fragment)
+//-----------------------------------------------------------------------------
+{
+  GLuint progId = glCreateProgram();
+
+  int vertId = glCreateShader(GL_VERTEX_SHADER);
+  int fragId = glCreateShader(GL_FRAGMENT_SHADER);
+
+  GL(glShaderSource(vertId, vertexEntries, vertex, 0));
+  GL(glCompileShader(vertId));
+  dumpShaderLog(vertId);
+
+  GL(glShaderSource(fragId, fragmentEntries, fragment, 0));
+  GL(glCompileShader(fragId));
+  dumpShaderLog(fragId);
+
+  GL(glAttachShader(progId, vertId));
+  GL(glAttachShader(progId, fragId));
+
+  GL(glLinkProgram(progId));
+
+  GL(glDetachShader(progId, vertId));
+  GL(glDetachShader(progId, fragId));
+
+  GL(glDeleteShader(vertId));
+  GL(glDeleteShader(fragId));
+
+  return progId;
+}
+
+//-----------------------------------------------------------------------------
+void WaitOnNativeFence(int fd)
+//-----------------------------------------------------------------------------
+{
+  if (fd != -1) {
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd, EGL_NONE};
+
+    EGLSyncKHR sync = eglCreateSyncKHR(eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+
+    if (sync == EGL_NO_SYNC_KHR) {
+      ALOGE("%s - Failed to Create sync from source fd", __FUNCTION__);
+    } else {
+      // the gpu will wait for this sync - not this cpu thread.
+      EGL(eglWaitSyncKHR(eglDisplay, sync, 0));
+      EGL(eglDestroySyncKHR(eglDisplay, sync));
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+int CreateNativeFence()
+//-----------------------------------------------------------------------------
+{
+  int fd = -1;
+
+  EGLSyncKHR sync = eglCreateSyncKHR(eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+  GL(glFlush());
+  if (sync == EGL_NO_SYNC_KHR) {
+    ALOGE("%s - Failed to Create Native Fence sync", __FUNCTION__);
+  } else {
+    fd = eglDupNativeFenceFDANDROID(eglDisplay, sync);
+    if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+      ALOGE("%s - Failed to dup sync", __FUNCTION__);
+    }
+    EGL(eglDestroySyncKHR(eglDisplay, sync));
+  }
+
+  return fd;
+}
+
+//-----------------------------------------------------------------------------
+void engine_setDestination(int id, int x, int y, int w, int h)
+//-----------------------------------------------------------------------------
+{
+  GL(glBindFramebuffer(GL_FRAMEBUFFER, id));
+  GL(glViewport(x, y, w, h));
+}
+
+//-----------------------------------------------------------------------------
+void engine_setProgram(int id)
+//-----------------------------------------------------------------------------
+{
+  GL(glUseProgram(id));
+}
+
+//-----------------------------------------------------------------------------
+void engine_set2DInputBuffer(int binding, unsigned int id)
+//-----------------------------------------------------------------------------
+{
+  GL(glActiveTexture(GL_TEXTURE0 + binding));
+  GL(glBindTexture(GL_TEXTURE_2D, id));
+}
+
+//-----------------------------------------------------------------------------
+void engine_set3DInputBuffer(int binding, unsigned int id)
+//-----------------------------------------------------------------------------
+{
+  GL(glActiveTexture(GL_TEXTURE0 + binding));
+  GL(glBindTexture(GL_TEXTURE_3D, id));
+}
+
+//-----------------------------------------------------------------------------
+void engine_setExternalInputBuffer(int binding, unsigned int id)
+//-----------------------------------------------------------------------------
+{
+  GL(glActiveTexture(GL_TEXTURE0 + binding));
+  GL(glBindTexture(0x8D65, id));
+}
+
+//-----------------------------------------------------------------------------
+int engine_blit(int srcFenceFd)
+//-----------------------------------------------------------------------------
+{
+  int fd = -1;
+  WaitOnNativeFence(srcFenceFd);
+  float fullscreen_vertices[]{0.0f, 2.0f, 0.0f, 0.0f, 2.0f, 0.0f};
+  GL(glEnableVertexAttribArray(0));
+  GL(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, fullscreen_vertices));
+  GL(glDrawArrays(GL_TRIANGLES, 0, 3));
+  fd = CreateNativeFence();
+  GL(glFlush());
+  return fd;
+}
+
+//-----------------------------------------------------------------------------
+void checkGlError(const char *file, int line)
+//-----------------------------------------------------------------------------
+{
+  for (GLint error = glGetError(); error; error = glGetError()) {
+    char *pError;
+    switch (error) {
+      case GL_NO_ERROR:
+        pError = (char *)"GL_NO_ERROR";
+        break;
+      case GL_INVALID_ENUM:
+        pError = (char *)"GL_INVALID_ENUM";
+        break;
+      case GL_INVALID_VALUE:
+        pError = (char *)"GL_INVALID_VALUE";
+        break;
+      case GL_INVALID_OPERATION:
+        pError = (char *)"GL_INVALID_OPERATION";
+        break;
+      case GL_OUT_OF_MEMORY:
+        pError = (char *)"GL_OUT_OF_MEMORY";
+        break;
+      case GL_INVALID_FRAMEBUFFER_OPERATION:
+        pError = (char *)"GL_INVALID_FRAMEBUFFER_OPERATION";
+        break;
+
+      default:
+        ALOGE("glError (0x%x) %s:%d\n", error, file, line);
+        return;
+    }
+
+    ALOGE("glError (%s) %s:%d\n", pError, file, line);
+    return;
+  }
+  return;
+}
+
+//-----------------------------------------------------------------------------
+void checkEglError(const char *file, int line)
+//-----------------------------------------------------------------------------
+{
+  for (int i = 0; i < 5; i++) {
+    const EGLint error = eglGetError();
+    if (error == EGL_SUCCESS) {
+      break;
+    }
+
+    char *pError;
+    switch (error) {
+      case EGL_SUCCESS:
+        pError = (char *)"EGL_SUCCESS";
+        break;
+      case EGL_NOT_INITIALIZED:
+        pError = (char *)"EGL_NOT_INITIALIZED";
+        break;
+      case EGL_BAD_ACCESS:
+        pError = (char *)"EGL_BAD_ACCESS";
+        break;
+      case EGL_BAD_ALLOC:
+        pError = (char *)"EGL_BAD_ALLOC";
+        break;
+      case EGL_BAD_ATTRIBUTE:
+        pError = (char *)"EGL_BAD_ATTRIBUTE";
+        break;
+      case EGL_BAD_CONTEXT:
+        pError = (char *)"EGL_BAD_CONTEXT";
+        break;
+      case EGL_BAD_CONFIG:
+        pError = (char *)"EGL_BAD_CONFIG";
+        break;
+      case EGL_BAD_CURRENT_SURFACE:
+        pError = (char *)"EGL_BAD_CURRENT_SURFACE";
+        break;
+      case EGL_BAD_DISPLAY:
+        pError = (char *)"EGL_BAD_DISPLAY";
+        break;
+      case EGL_BAD_SURFACE:
+        pError = (char *)"EGL_BAD_SURFACE";
+        break;
+      case EGL_BAD_MATCH:
+        pError = (char *)"EGL_BAD_MATCH";
+        break;
+      case EGL_BAD_PARAMETER:
+        pError = (char *)"EGL_BAD_PARAMETER";
+        break;
+      case EGL_BAD_NATIVE_PIXMAP:
+        pError = (char *)"EGL_BAD_NATIVE_PIXMAP";
+        break;
+      case EGL_BAD_NATIVE_WINDOW:
+        pError = (char *)"EGL_BAD_NATIVE_WINDOW";
+        break;
+      case EGL_CONTEXT_LOST:
+        pError = (char *)"EGL_CONTEXT_LOST";
+        break;
+      default:
+        ALOGE("eglError (0x%x) %s:%d\n", error, file, line);
+        return;
+    }
+    ALOGE("eglError (%s) %s:%d\n", pError, file, line);
+    return;
+  }
+  return;
+}
diff --git a/gpu_tonemapper/glengine.h b/gpu_tonemapper/glengine.h
new file mode 100644
index 0000000..c5b166c
--- /dev/null
+++ b/gpu_tonemapper/glengine.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TONEMAPPER_GLENGINE_H__
+#define __TONEMAPPER_GLENGINE_H__
+#include <EGL/egl.h>
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/eglext.h>
+#include <GLES3/gl31.h>
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3ext.h>
+
+#if defined(CHECK_GL_ERRORS)
+#define GL(func) func;
+#define EGL(func) func;
+#else
+#define GL(func) \
+  func;          \
+  checkGlError(__FILE__, __LINE__);
+#define EGL(func) \
+  func;           \
+  checkEglError(__FILE__, __LINE__);
+#endif
+
+void checkGlError(const char *file, int line);
+void checkEglError(const char *file, int line);
+
+#endif  //__TONEMAPPER_GLENGINE_H__
\ No newline at end of file
diff --git a/gpu_tonemapper/rgba_inverse_tonemap.inl b/gpu_tonemapper/rgba_inverse_tonemap.inl
new file mode 100644
index 0000000..399e2f8
--- /dev/null
+++ b/gpu_tonemapper/rgba_inverse_tonemap.inl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const char* rgba_inverse_tonemap_shader = ""
+    "#extension GL_OES_EGL_image_external_essl3 : require                                               \n"
+    "precision highp float;                                                                             \n"
+    "precision highp sampler2D;                                                                         \n"
+    "layout(binding = 0) uniform samplerExternalOES externalTexture;                                    \n"
+    "layout(binding = 1) uniform sampler3D tonemapper;                                                  \n"
+    "layout(binding = 2) uniform sampler2D xform;                                                       \n"
+    "in vec2 uv;                                                                                        \n"
+    "out vec4 fs_color;                                                                                 \n"
+    "void main()                                                                                        \n"
+    "{                                                                                                  \n"
+    "vec2 flipped = uv;                                                                                 \n"
+    "flipped.y = 1.0 - flipped.y;                                                                       \n"
+    "flipped.x = flipped.x;                                                                             \n"
+    "vec4 rgb_premulalpha = texture(externalTexture, flipped);                                          \n"
+    "fs_color = rgb_premulalpha;                                                                        \n"
+    "if( rgb_premulalpha.a > 0.0 ) {                                                                    \n"
+    "vec3 rgb = rgb_premulalpha.rgb/rgb_premulalpha.a;                                                  \n"
+    "#if defined(USE_NONUNIFORM_SAMPLING)                                                               \n"
+    "float r = texture(xform, vec2(rgb.r, 0.0f)).r;                                                     \n"
+    "float g = texture(xform, vec2(rgb.g, 0.0f)).g;                                                     \n"
+    "float b = texture(xform, vec2(rgb.b, 0.0f)).b;                                                     \n"
+    "#else                                                                                              \n"
+    "float r = rgb.r;                                                                                   \n"
+    "float g = rgb.g;                                                                                   \n"
+    "float b = rgb.b;                                                                                   \n"
+    "#endif                                                                                             \n"
+    "fs_color.rgb = texture(tonemapper, vec3(r, g, b)).rgb * rgb_premulalpha.a;                         \n"
+    "fs_color.a = rgb_premulalpha.a;                                                                    \n"
+    "}                                                                                                  \n"
+    "}                                                                                                  \n";
diff --git a/libmemtrack/kgsl.c b/libmemtrack/kgsl.c
index 9ec69f0..c3aa86e 100644
--- a/libmemtrack/kgsl.c
+++ b/libmemtrack/kgsl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2016 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/libqdutils/display_config.cpp b/libqdutils/display_config.cpp
index c1335b1..4880cdc 100644
--- a/libqdutils/display_config.cpp
+++ b/libqdutils/display_config.cpp
@@ -357,7 +357,7 @@
         inParcel.writeInt32(mode);
         err = binder->dispatch(IQService::CONTROL_PARTIAL_UPDATE, &inParcel, &outParcel);
         if(err != 0) {
-            ALOGE("%s() failed with err %d", __FUNCTION__, err);
+            ALOGE_IF(getBinder(), "%s() failed with err %d", __FUNCTION__, err);
         } else {
             return outParcel.readInt32();
         }
diff --git a/sdm/include/core/layer_buffer.h b/sdm/include/core/layer_buffer.h
index 4f80f86..4b42e7b 100644
--- a/sdm/include/core/layer_buffer.h
+++ b/sdm/include/core/layer_buffer.h
@@ -30,21 +30,12 @@
 #define __LAYER_BUFFER_H__
 
 #include <stdint.h>
+#include <color_metadata.h>
 
 #include "sdm_types.h"
 
 namespace sdm {
 
-/*! @brief This enum represents display layer color space conversion (CSC) matrix types.
-
-  @sa Layer
-*/
-enum LayerCSC {
-  kCSCLimitedRange601,    //!< 601 limited range color space.
-  kCSCFullRange601,       //!< 601 full range color space.
-  kCSCLimitedRange709,    //!< 709 limited range color space.
-};
-
 /*! @brief This enum represents display layer inverse gamma correction (IGC) types.
 
   @sa Layer
@@ -212,6 +203,9 @@
       uint32_t secure_camera : 1;   //!< This flag shall be set by the client to indicate that the
                                     //!< buffer is associated with secure camera session. A secure
                                     //!< camera layer can co-exist with non-secure layer(s).
+
+      uint32_t hdr : 1;             //!< This flag shall be set by the client to indicate that the
+                                    //!< the content is HDR.
     };
 
     uint32_t flags = 0;             //!< For initialization purpose only.
@@ -234,7 +228,7 @@
                                 //!< Unaligned height of the Layer that this buffer is for.
   uint32_t size = 0;            //!< Size of a single buffer (even if multiple clubbed together)
   LayerBufferFormat format = kFormatRGBA8888;     //!< Format of the buffer content.
-  LayerCSC csc = kCSCFullRange601;                //!< Color Space of the layer.
+  ColorMetaData color_metadata = {};              //!< CSC + Range + Transfer + Matrix + HDR Info
   LayerIGC igc = kIGCNotSpecified;                //!< IGC that will be applied on this layer.
   LayerBufferPlane planes[4] = {};
                                 //!< Array of planes that this buffer contains. RGB buffer formats
@@ -273,6 +267,13 @@
                                 //!< Specifies the buffer id.
 };
 
+// This enum represents buffer layout types.
+enum BufferLayout {
+  kLinear,    //!< Linear data
+  kUBWC,      //!< UBWC aligned data
+  kTPTiled    //!< Tightly Packed data
+};
+
 }  // namespace sdm
 
 #endif  // __LAYER_BUFFER_H__
diff --git a/sdm/include/core/layer_stack.h b/sdm/include/core/layer_stack.h
index 2741f71..701e3ca 100644
--- a/sdm/include/core/layer_stack.h
+++ b/sdm/include/core/layer_stack.h
@@ -179,6 +179,36 @@
   };
 };
 
+/*! @brief This structure defines flags associated with the layer requests. The 1-bit flag can be
+    set to ON(1) or OFF(0).
+
+  @sa Layer
+*/
+struct LayerRequestFlags {
+  union {
+    struct {
+      uint32_t tone_map : 1;  //!< This flag will be set by SDM when the layer needs tone map
+      uint32_t secure: 1;  //!< This flag will be set by SDM when the layer must be secure
+    };
+    uint32_t request_flags = 0;  //!< For initialization purpose only.
+                                 //!< Shall not be refered directly.
+  };
+};
+
+/*! @brief This structure defines LayerRequest.
+   Includes width/height/format of the LayerRequest.
+
+   SDM shall set the properties of LayerRequest to be used by the client
+
+  @sa LayerRequest
+*/
+struct LayerRequest {
+  LayerRequestFlags flags;  // Flags associated with this request
+  LayerBufferFormat format = kFormatRGBA8888;  // Requested format
+  uint32_t width = 0;
+  uint32_t height = 0;
+};
+
 /*! @brief This structure defines flags associated with a layer stack. The 1-bit flag can be set to
   ON(1) or OFF(0).
 
@@ -219,6 +249,8 @@
 
       uint32_t post_processed_output : 1;  // If output_buffer should contain post processed output
                                            // This applies only to primary displays currently
+
+      uint32_t hdr_present : 1;  //!< Set if stack has HDR content
     };
 
     uint32_t flags = 0;               //!< For initialization purpose only.
@@ -264,7 +296,7 @@
   @sa LayerArray
 */
 struct Layer {
-  LayerBuffer *input_buffer = NULL;                //!< Pointer to the buffer to be composed.
+  LayerBuffer input_buffer = {};                   //!< Buffer to be composed.
                                                    //!< If this remains unchanged between two
                                                    //!< consecutive Prepare() calls and
                                                    //!< geometry_changed flag is not set for the
@@ -322,6 +354,11 @@
                                                    //!< no content is associated with the layer.
 
   LayerFlags flags;                                //!< Flags associated with this layer.
+
+  LayerRequest request = {};                       //!< o/p - request on this Layer by SDM.
+
+  Lut3d lut_3d = {};                               //!< o/p - Populated by SDM when tone mapping is
+                                                   //!< needed on this layer.
 };
 
 /*! @brief This structure defines a layer stack that contains layers which need to be composed and
diff --git a/sdm/include/private/hw_info_types.h b/sdm/include/private/hw_info_types.h
index b960955..cd57f32 100644
--- a/sdm/include/private/hw_info_types.h
+++ b/sdm/include/private/hw_info_types.h
@@ -128,6 +128,8 @@
   uint32_t num_rotator = 0;
   bool has_downscale = false;
   std::string device_path = "";
+  float min_downscale = 2.0f;
+  bool downscale_compression = false;
 
   void Reset() { *this = HWRotatorInfo(); }
 };
@@ -429,15 +431,12 @@
   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.
 
+  std::vector<Layer> hw_layers = {};  // Layers which need to be programmed on the HW
+
   uint32_t index[kMaxSDELayers];   // Indexes of the layers from the layer stack which need to be
                                    // programmed on hardware.
-  LayerRect updated_src_rect[kMaxSDELayers];  // Updated layer src rects in s3d mode
-  LayerRect updated_dst_rect[kMaxSDELayers];  // Updated layer dst rects in s3d mode
-  bool updating[kMaxSDELayers] = {0};  // Updated by strategy, considering plane_alpha+updating
   uint32_t roi_index[kMaxSDELayers] = {0};  // Stores the ROI index where the layers are visible.
 
-  uint32_t count = 0;              // Total number of layers which need to be set on hardware.
-
   int sync_handle = -1;
 
   std::vector<LayerRect> left_frame_roi;   // Left ROI.
diff --git a/sdm/include/private/resource_interface.h b/sdm/include/private/resource_interface.h
index 3f34e91..48b7a02 100644
--- a/sdm/include/private/resource_interface.h
+++ b/sdm/include/private/resource_interface.h
@@ -51,7 +51,7 @@
   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, bool ubwc_tiled,
+                                       bool rotate90, BufferLayout layout,
                                        bool use_rotator_downscale) = 0;
   virtual DisplayError ValidateCursorConfig(Handle display_ctx, const Layer *layer,
                                             bool is_top) = 0;
diff --git a/sdm/include/private/strategy_interface.h b/sdm/include/private/strategy_interface.h
index 122a3c6..90e1064 100644
--- a/sdm/include/private/strategy_interface.h
+++ b/sdm/include/private/strategy_interface.h
@@ -53,6 +53,7 @@
                                    const HWMixerAttributes &mixer_attributes,
                                    const DisplayConfigVariableInfo &fb_config) = 0;
   virtual DisplayError SetCompositionState(LayerComposition composition_type, bool enable) = 0;
+  virtual DisplayError Purge() = 0;
 
  protected:
   virtual ~StrategyInterface() { }
diff --git a/sdm/include/utils/constants.h b/sdm/include/utils/constants.h
index 31129c1..72b1bed 100644
--- a/sdm/include/utils/constants.h
+++ b/sdm/include/utils/constants.h
@@ -71,6 +71,8 @@
   const int kMaxRotatePerLayer = 2;
   const uint32_t kMaxBlitTargetLayers = 2;
   const int kPageSize = 4096;
+  const uint32_t kGridSize = 129;  // size used for non-linear transformation before Tone-mapping
+  const uint32_t kLutDim = 17;  // Dim of the 3d LUT for tone-mapping.
 
   typedef void * Handle;
 
diff --git a/sdm/include/utils/formats.h b/sdm/include/utils/formats.h
index 67548f3..dd819dc 100644
--- a/sdm/include/utils/formats.h
+++ b/sdm/include/utils/formats.h
@@ -37,6 +37,7 @@
 bool IsUBWCFormat(LayerBufferFormat format);
 bool Is10BitFormat(LayerBufferFormat format);
 const char *GetFormatString(const LayerBufferFormat &format);
+BufferLayout GetBufferLayout(LayerBufferFormat format);
 
 }  // namespace sdm
 
diff --git a/sdm/libs/core/Android.mk b/sdm/libs/core/Android.mk
index f97ff33..8600c55 100644
--- a/sdm/libs/core/Android.mk
+++ b/sdm/libs/core/Android.mk
@@ -4,7 +4,7 @@
 
 LOCAL_MODULE                  := libsdmcore
 LOCAL_MODULE_TAGS             := optional
-LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes)
+LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes) $(common_header_export_path)
 LOCAL_CFLAGS                  := -Wno-unused-parameter -DLOG_TAG=\"SDM\" $(common_flags)
 LOCAL_HW_INTF_PATH            := fb
 LOCAL_SHARED_LIBRARIES        := libdl libsdmutils
diff --git a/sdm/libs/core/comp_manager.cpp b/sdm/libs/core/comp_manager.cpp
index d1530dc..199be86 100644
--- a/sdm/libs/core/comp_manager.cpp
+++ b/sdm/libs/core/comp_manager.cpp
@@ -33,6 +33,12 @@
 
 namespace sdm {
 
+static bool NeedsScaledComposition(const DisplayConfigVariableInfo &fb_config,
+                                   const HWMixerAttributes &mixer_attributes) {
+  return ((fb_config.x_pixels != mixer_attributes.width) ||
+          (fb_config.y_pixels != mixer_attributes.height));
+}
+
 DisplayError CompManager::Init(const HWResourceInfo &hw_res_info,
                                ExtensionInterface *extension_intf,
                                BufferAllocator *buffer_allocator,
@@ -122,6 +128,7 @@
     max_sde_ext_layers_ = UINT32(Debug::GetExtMaxlayers());
   }
 
+  display_comp_ctx->scaled_composition = NeedsScaledComposition(fb_config, mixer_attributes);
   DLOGV_IF(kTagCompManager, "registered display bit mask 0x%x, configured display bit mask 0x%x, " \
            "display type %d", registered_displays_.to_ulong(), configured_displays_.to_ulong(),
            display_comp_ctx->display_type);
@@ -200,6 +207,8 @@
     }
   }
 
+  display_comp_ctx->scaled_composition = NeedsScaledComposition(fb_config, mixer_attributes);
+
   return error;
 }
 
@@ -212,9 +221,13 @@
   constraints->use_cursor = false;
   constraints->max_layers = max_layers_;
 
-  // Limit 2 layer SDE Comp if its not a Primary Display
+  // Limit 2 layer SDE Comp if its not a Primary Display.
+  // Safe mode is the policy for External display on a low end device.
   if (!display_comp_ctx->is_primary_panel) {
+    bool low_end_hw = ((hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe +
+                        hw_res_info_.num_dma_pipe) <= kSafeModeThreshold);
     constraints->max_layers = max_sde_ext_layers_;
+    constraints->safe_mode = (low_end_hw && !hw_res_info_.separate_rotator) ? true : safe_mode_;
   }
 
   // If a strategy fails after successfully allocating resources, then set safe mode
@@ -369,6 +382,8 @@
                              reinterpret_cast<DisplayCompositionContext *>(display_ctx);
 
   resource_intf_->Purge(display_comp_ctx->display_resource_ctx);
+
+  display_comp_ctx->strategy->Purge();
 }
 
 void CompManager::ProcessIdleTimeout(Handle display_ctx) {
@@ -426,8 +441,8 @@
 
 DisplayError CompManager::ValidateScaling(const LayerRect &crop, const LayerRect &dst,
                                           bool rotate90) {
-  return resource_intf_->ValidateScaling(crop, dst, rotate90, Debug::IsUbwcTiledFrameBuffer(),
-                                         true /* use_rotator_downscale */);
+  BufferLayout layout = Debug::IsUbwcTiledFrameBuffer() ? kUBWC : kLinear;
+  return resource_intf_->ValidateScaling(crop, dst, rotate90, layout, true);
 }
 
 DisplayError CompManager::ValidateCursorPosition(Handle display_ctx, HWLayers *hw_layers,
@@ -447,7 +462,8 @@
   bool supported = false;
   int32_t gpu_index = -1;
 
-  if (!layer_stack->flags.cursor_present) {
+  // HW Cursor cannot be used, if Display configuration needs scaled composition.
+  if (display_comp_ctx->scaled_composition || !layer_stack->flags.cursor_present) {
     return supported;
   }
 
diff --git a/sdm/libs/core/comp_manager.h b/sdm/libs/core/comp_manager.h
index ebb257b..49a7ce0 100644
--- a/sdm/libs/core/comp_manager.h
+++ b/sdm/libs/core/comp_manager.h
@@ -77,6 +77,7 @@
 
  private:
   static const int kMaxThermalLevel = 3;
+  static const int kSafeModeThreshold = 4;
 
   void PrepareStrategyConstraints(Handle display_ctx, HWLayers *hw_layers);
 
@@ -94,6 +95,7 @@
     bool is_primary_panel = false;
     bool valid_cursor = false;
     PUConstraints pu_constraints = {};
+    bool scaled_composition = false;
   };
 
   Locker locker_;
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 843ed6e..77b6edc 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -104,6 +104,8 @@
                                display_attributes_, hw_panel_info_);
   if (!color_mgr_) {
     DLOGW("Unable to create ColorManagerProxy for display = %d", display_type_);
+  } else if (InitializeColorModes() != kErrorNone) {
+    DLOGW("InitColorModes failed for display = %d", display_type_);
   }
 
   return kErrorNone;
@@ -119,6 +121,9 @@
 DisplayError DisplayBase::Deinit() {
   lock_guard<recursive_mutex> obj(recursive_mutex_);
 
+  color_modes_.clear();
+  color_mode_map_.clear();
+
   if (color_mgr_) {
     delete color_mgr_;
     color_mgr_ = NULL;
@@ -216,6 +221,12 @@
     return error;
   }
 
+  error = HandleHDR(layer_stack);
+  if (error != kErrorNone) {
+    DLOGW("HandleHDR failed");
+    return error;
+  }
+
   if (color_mgr_ && color_mgr_->NeedsPartialUpdateDisable()) {
     DisablePartialUpdateOneFrame();
   }
@@ -282,6 +293,8 @@
     }
   }
 
+  CommitLayerParams(layer_stack);
+
   if (comp_manager_->Commit(display_comp_ctx_, &hw_layers_)) {
     if (error != kErrorNone) {
       return error;
@@ -301,6 +314,8 @@
     return error;
   }
 
+  PostCommitLayerParams(layer_stack);
+
   if (partial_update_control_) {
     comp_manager_->ControlPartialUpdate(display_comp_ctx_, true /* enable */);
   }
@@ -320,8 +335,7 @@
   if (!active_) {
     return kErrorPermission;
   }
-
-  hw_layers_.info.count = 0;
+  hw_layers_.info.hw_layers.clear();
   error = hw_intf_->Flush();
   if (error == kErrorNone) {
     comp_manager_->Purge(display_comp_ctx_);
@@ -396,7 +410,7 @@
 
   switch (state) {
   case kStateOff:
-    hw_layers_.info.count = 0;
+    hw_layers_.info.hw_layers.clear();
     error = hw_intf_->Flush();
     if (error == kErrorNone) {
       comp_manager_->Purge(display_comp_ctx_);
@@ -497,7 +511,7 @@
 
   uint32_t num_hw_layers = 0;
   if (hw_layers_.info.stack) {
-    num_hw_layers = hw_layers_.info.count;
+    num_hw_layers = UINT32(hw_layers_.info.hw_layers.size());
   }
 
   if (num_hw_layers == 0) {
@@ -531,9 +545,9 @@
     }
   }
 
-  const char *header  = "\n| Idx |  Comp Type  |  Split | WB |  Pipe |    W x H    |          Format          |  Src Rect (L T R B) |  Dst Rect (L T R B) |  Z |    Flags   | Deci(HxV) | CS |";  //NOLINT
-  const char *newline = "\n|-----|-------------|--------|----|-------|-------------|--------------------------|---------------------|---------------------|----|------------|-----------|----|";  //NOLINT
-  const char *format  = "\n| %3s | %11s "     "| %6s " "| %2s | 0x%03x | %4d x %4d | %24s "                  "| %4d %4d %4d %4d "  "| %4d %4d %4d %4d "  "| %2s | %10s "   "| %9s | %2s |";  //NOLINT
+  const char *header  = "\n| Idx |  Comp Type  |  Split | WB |  Pipe |    W x H    |          Format          |  Src Rect (L T R B) |  Dst Rect (L T R B) |  Z |    Flags   | Deci(HxV) | CS | Range|";  //NOLINT
+  const char *newline = "\n|-----|-------------|--------|----|-------|-------------|--------------------------|---------------------|---------------------|----|------------|-----------|----|------|";  //NOLINT
+  const char *format  = "\n| %3s | %11s "     "| %6s " "| %2s | 0x%03x | %4d x %4d | %24s "                  "| %4d %4d %4d %4d "  "| %4d %4d %4d %4d "  "| %2s | %10s "   "| %9s | %2s | %2s |";  //NOLINT
 
   DumpImpl::AppendString(buffer, length, "\n");
   DumpImpl::AppendString(buffer, length, newline);
@@ -542,13 +556,16 @@
 
   for (uint32_t i = 0; i < num_hw_layers; i++) {
     uint32_t layer_index = hw_layers_.info.index[i];
-    Layer *layer = hw_layers_.info.stack->layers.at(layer_index);
-    LayerBuffer *input_buffer = layer->input_buffer;
+    // sdm-layer from client layer stack
+    Layer *sdm_layer = hw_layers_.info.stack->layers.at(layer_index);
+    // hw-layer from hw layers info
+    Layer &hw_layer = hw_layers_.info.hw_layers.at(i);
+    LayerBuffer *input_buffer = &hw_layer.input_buffer;
     HWLayerConfig &layer_config = hw_layers_.config[i];
     HWRotatorSession &hw_rotator_session = layer_config.hw_rotator_session;
 
     char idx[8] = { 0 };
-    const char *comp_type = GetName(layer->composition);
+    const char *comp_type = GetName(sdm_layer->composition);
     const char *buffer_format = GetFormatString(input_buffer->format);
     const char *rotate_split[2] = { "Rot-1", "Rot-2" };
     const char *comp_split[2] = { "Comp-1", "Comp-2" };
@@ -568,7 +585,7 @@
                              input_buffer->height, buffer_format, INT(src_roi.left),
                              INT(src_roi.top), INT(src_roi.right), INT(src_roi.bottom),
                              INT(dst_roi.left), INT(dst_roi.top), INT(dst_roi.right),
-                             INT(dst_roi.bottom), "-", "-    ", "-    ", "-");
+                             INT(dst_roi.bottom), "-", "-    ", "-    ", "-", "-");
 
       // print the below only once per layer block, fill with spaces for rest.
       idx[0] = 0;
@@ -584,7 +601,8 @@
       char decimation[16] = { 0 };
       char flags[16] = { 0 };
       char z_order[8] = { 0 };
-      char csc[8] = { 0 };
+      char color_primary[8] = { 0 };
+      char range[8] = { 0 };
 
       HWPipeInfo &pipe = (count == 0) ? layer_config.left_pipe : layer_config.right_pipe;
 
@@ -596,17 +614,19 @@
       LayerRect &dst_roi = pipe.dst_roi;
 
       snprintf(z_order, sizeof(z_order), "%d", pipe.z_order);
-      snprintf(flags, sizeof(flags), "0x%08x", layer->flags.flags);
+      snprintf(flags, sizeof(flags), "0x%08x", hw_layer.flags.flags);
       snprintf(decimation, sizeof(decimation), "%3d x %3d", pipe.horizontal_decimation,
                pipe.vertical_decimation);
-      snprintf(csc, sizeof(csc), "%d", layer->input_buffer->csc);
+      ColorMetaData &color_metadata = hw_layer.input_buffer.color_metadata;
+      snprintf(color_primary, sizeof(color_primary), "%d", color_metadata.colorPrimaries);
+      snprintf(range, sizeof(range), "%d", color_metadata.range);
 
       DumpImpl::AppendString(buffer, length, format, idx, comp_type, comp_split[count],
                              "-", pipe.pipe_id, input_buffer->width, input_buffer->height,
                              buffer_format, INT(src_roi.left), INT(src_roi.top),
                              INT(src_roi.right), INT(src_roi.bottom), INT(dst_roi.left),
                              INT(dst_roi.top), INT(dst_roi.right), INT(dst_roi.bottom),
-                             z_order, flags, decimation, csc);
+                             z_order, flags, decimation, color_primary, range);
 
       // print the below only once per layer block, fill with spaces for rest.
       idx[0] = 0;
@@ -650,11 +670,6 @@
     return kErrorNotSupported;
   }
 
-  DisplayError error = color_mgr_->ColorMgrGetNumOfModes(&num_color_modes_);
-  if (error != kErrorNone || !num_color_modes_) {
-    return kErrorNotSupported;
-  }
-
   DLOGV_IF(kTagQDCM, "Number of modes from color manager = %d", num_color_modes_);
   *mode_count = num_color_modes_;
 
@@ -672,30 +687,6 @@
     return kErrorNotSupported;
   }
 
-  if (!color_modes_.size()) {
-    color_modes_.resize(num_color_modes_);
-
-    DisplayError error = color_mgr_->ColorMgrGetModes(&num_color_modes_, color_modes_.data());
-    if (error != kErrorNone) {
-      DLOGE("Failed");
-      return error;
-    }
-
-    for (uint32_t i = 0; i < num_color_modes_; i++) {
-      DLOGV_IF(kTagQDCM, "Color Mode[%d]: Name = %s mode_id = %d", i, color_modes_[i].name,
-               color_modes_[i].id);
-      auto it = color_mode_map_.find(color_modes_[i].name);
-      if (it != color_mode_map_.end()) {
-        if (it->second->id < color_modes_[i].id) {
-          color_mode_map_.erase(it);
-          color_mode_map_.insert(std::make_pair(color_modes_[i].name, &color_modes_[i]));
-        }
-      } else {
-        color_mode_map_.insert(std::make_pair(color_modes_[i].name, &color_modes_[i]));
-      }
-    }
-  }
-
   for (uint32_t i = 0; i < num_color_modes_; i++) {
     DLOGV_IF(kTagQDCM, "Color Mode[%d]: Name = %s mode_id = %d", i, color_modes_[i].name,
              color_modes_[i].id);
@@ -711,6 +702,21 @@
     return kErrorNotSupported;
   }
 
+  DisplayError error = kErrorNone;
+  // Set client requests when not in HDR Mode.
+  if (!hdr_playback_mode_) {
+    error = SetColorModeInternal(color_mode);
+    if (error != kErrorNone) {
+      return error;
+    }
+  }
+  // Store the new color mode request by client
+  current_color_mode_ = color_mode;
+
+  return error;
+}
+
+DisplayError DisplayBase::SetColorModeInternal(const std::string &color_mode) {
   DLOGV_IF(kTagQDCM, "Color Mode = %s", color_mode.c_str());
 
   ColorModeMap::iterator it = color_mode_map_.find(color_mode);
@@ -721,7 +727,7 @@
 
   SDEDisplayMode *sde_display_mode = it->second;
 
-  DLOGD("Color Mode Name = %s corresponding mode_id = %d", sde_display_mode->name,
+  DLOGV_IF(kTagQDCM, "Color Mode Name = %s corresponding mode_id = %d", sde_display_mode->name,
            sde_display_mode->id);
   DisplayError error = kErrorNone;
   error = color_mgr_->ColorMgrSetMode(sde_display_mode->id);
@@ -1074,4 +1080,136 @@
   return comp_manager_->SetCompositionState(display_comp_ctx_, composition_type, enable);
 }
 
+void DisplayBase::CommitLayerParams(LayerStack *layer_stack) {
+  // Copy the acquire fence from clients layers  to HWLayers
+  uint32_t hw_layers_count = UINT32(hw_layers_.info.hw_layers.size());
+
+  for (uint32_t i = 0; i < hw_layers_count; i++) {
+    Layer *sdm_layer = layer_stack->layers.at(hw_layers_.info.index[i]);
+    Layer &hw_layer = hw_layers_.info.hw_layers.at(i);
+
+    hw_layer.input_buffer.planes[0].fd = sdm_layer->input_buffer.planes[0].fd;
+    hw_layer.input_buffer.planes[0].offset = sdm_layer->input_buffer.planes[0].offset;
+    hw_layer.input_buffer.planes[0].stride = sdm_layer->input_buffer.planes[0].stride;
+    hw_layer.input_buffer.size = sdm_layer->input_buffer.size;
+    hw_layer.input_buffer.acquire_fence_fd = sdm_layer->input_buffer.acquire_fence_fd;
+  }
+
+  return;
+}
+
+void DisplayBase::PostCommitLayerParams(LayerStack *layer_stack) {
+  // Copy the release fence from HWLayers to clients layers
+    uint32_t hw_layers_count = UINT32(hw_layers_.info.hw_layers.size());
+
+  std::vector<uint32_t> fence_dup_flag = {};
+
+  for (uint32_t i = 0; i < hw_layers_count; i++) {
+    uint32_t sdm_layer_index = hw_layers_.info.index[i];
+    Layer *sdm_layer = layer_stack->layers.at(sdm_layer_index);
+    Layer &hw_layer = hw_layers_.info.hw_layers.at(i);
+
+    // Copy the release fence only once for a SDM Layer.
+    // In S3D use case, two hw layers can share the same input buffer, So make sure to merge the
+    // output fence fd and assign it to layer's input buffer release fence fd.
+    if (std::find(fence_dup_flag.begin(), fence_dup_flag.end(), sdm_layer_index) ==
+        fence_dup_flag.end()) {
+      sdm_layer->input_buffer.release_fence_fd = hw_layer.input_buffer.release_fence_fd;
+      fence_dup_flag.push_back(sdm_layer_index);
+    } else {
+      int temp = -1;
+      buffer_sync_handler_->SyncMerge(hw_layer.input_buffer.release_fence_fd,
+                                      sdm_layer->input_buffer.release_fence_fd, &temp);
+
+      if (hw_layer.input_buffer.release_fence_fd >= 0) {
+        Sys::close_(hw_layer.input_buffer.release_fence_fd);
+        hw_layer.input_buffer.release_fence_fd = -1;
+      }
+
+      if (sdm_layer->input_buffer.release_fence_fd >= 0) {
+        Sys::close_(sdm_layer->input_buffer.release_fence_fd);
+        sdm_layer->input_buffer.release_fence_fd = -1;
+      }
+
+      sdm_layer->input_buffer.release_fence_fd = temp;
+    }
+
+    // Reset the sync fence fds of HWLayer
+    hw_layer.input_buffer.acquire_fence_fd = -1;
+    hw_layer.input_buffer.release_fence_fd = -1;
+  }
+
+  return;
+}
+
+DisplayError DisplayBase::InitializeColorModes() {
+  if (!color_mgr_) {
+    return kErrorNotSupported;
+  }
+
+  DisplayError error = color_mgr_->ColorMgrGetNumOfModes(&num_color_modes_);
+  if (error != kErrorNone || !num_color_modes_) {
+    DLOGV_IF(kTagQDCM, "GetNumModes failed = %d count = %d", error, num_color_modes_);
+    return kErrorNotSupported;
+  }
+  DLOGI("Number of Color Modes = %d", num_color_modes_);
+
+  if (!color_modes_.size()) {
+    color_modes_.resize(num_color_modes_);
+
+    DisplayError error = color_mgr_->ColorMgrGetModes(&num_color_modes_, color_modes_.data());
+    if (error != kErrorNone) {
+      color_modes_.clear();
+      DLOGE("Failed");
+      return error;
+    }
+
+    for (uint32_t i = 0; i < num_color_modes_; i++) {
+      DLOGV_IF(kTagQDCM, "Color Mode[%d]: Name = %s mode_id = %d", i, color_modes_[i].name,
+               color_modes_[i].id);
+      auto it = color_mode_map_.find(color_modes_[i].name);
+      if (it != color_mode_map_.end()) {
+        if (it->second->id < color_modes_[i].id) {
+          color_mode_map_.erase(it);
+          color_mode_map_.insert(std::make_pair(color_modes_[i].name, &color_modes_[i]));
+        }
+      } else {
+        color_mode_map_.insert(std::make_pair(color_modes_[i].name, &color_modes_[i]));
+      }
+    }
+  }
+
+  return kErrorNone;
+}
+
+DisplayError DisplayBase::HandleHDR(LayerStack *layer_stack) {
+  DisplayError error = kErrorNone;
+
+  if (!color_mgr_) {
+    // TODO(user): Handle the case where color_mgr is not present
+    return kErrorNone;
+  }
+
+  if (!layer_stack->flags.hdr_present) {
+    //  HDR playback off - set prev mode
+    if (hdr_playback_mode_) {
+      hdr_playback_mode_ = false;
+      DLOGI("Setting color mode = %s", current_color_mode_.c_str());
+      error = SetColorModeInternal(current_color_mode_);
+    // TODO(user): Enable DPPS
+    }
+  } else {
+    // hdr is present
+    if (!hdr_playback_mode_ && !layer_stack->flags.animating) {
+      // hdr is starting
+      hdr_playback_mode_ = true;
+      DLOGI("Setting HDR color mode = %s", hdr_color_mode_.c_str());
+      error = SetColorModeInternal(hdr_color_mode_);
+    }
+    // TODO(user): Disable DPPS
+  }
+
+  return error;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index ae1171d..85a1bc2 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -112,6 +112,9 @@
  protected:
   DisplayError BuildLayerStackStats(LayerStack *layer_stack);
   virtual DisplayError ValidateGPUTargetParams();
+  void CommitLayerParams(LayerStack *layer_stack);
+  void PostCommitLayerParams(LayerStack *layer_stack);
+  DisplayError HandleHDR(LayerStack *layer_stack);
 
   // DumpImpl method
   void AppendDump(char *buffer, uint32_t length);
@@ -122,6 +125,8 @@
                                  uint32_t *new_mixer_height);
   DisplayError ReconfigureMixer(uint32_t width, uint32_t height);
   bool NeedsDownScale(const LayerRect &src_rect, const LayerRect &dst_rect, bool needs_rotation);
+  DisplayError InitializeColorModes();
+  DisplayError SetColorModeInternal(const std::string &color_mode);
 
   recursive_mutex recursive_mutex_;
   DisplayType display_type_;
@@ -153,6 +158,9 @@
   DisplayConfigVariableInfo fb_config_ = {};
   uint32_t req_mixer_width_ = 0;
   uint32_t req_mixer_height_ = 0;
+  std::string current_color_mode_ = "hal_native";
+  std::string hdr_color_mode_ = "hal_hdr";
+  bool hdr_playback_mode_ = false;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/display_hdmi.cpp b/sdm/libs/core/display_hdmi.cpp
index a577348..46d1a20 100644
--- a/sdm/libs/core/display_hdmi.cpp
+++ b/sdm/libs/core/display_hdmi.cpp
@@ -259,9 +259,9 @@
   // 2. Layer stack containing only one secure layer along with one s3d layer
   for (uint32_t i = 0; i < layer_count; i++) {
     Layer *layer = layer_stack->layers.at(i);
-    LayerBuffer *layer_buffer = layer->input_buffer;
+    LayerBuffer &layer_buffer = layer->input_buffer;
 
-    if (layer_buffer->s3d_format != kS3dFormatNone) {
+    if (layer_buffer.s3d_format != kS3dFormatNone) {
       s3d_layer_count++;
       if (s3d_layer_count > 1 || layer->flags.skip) {
         s3d_mode = kS3DModeNone;
@@ -269,11 +269,11 @@
       }
 
       std::map<LayerBufferS3DFormat, HWS3DMode>::iterator it =
-                s3d_format_to_mode_.find(layer_buffer->s3d_format);
+                s3d_format_to_mode_.find(layer_buffer.s3d_format);
       if (it != s3d_format_to_mode_.end()) {
         s3d_mode = it->second;
       }
-    } else if (layer_buffer->flags.secure && layer_count > 2) {
+    } else if (layer_buffer.flags.secure && layer_count > 2) {
         s3d_mode = kS3DModeNone;
         break;
     }
diff --git a/sdm/libs/core/fb/hw_device.cpp b/sdm/libs/core/fb/hw_device.cpp
index f7aebe2..e2b684f 100644
--- a/sdm/libs/core/fb/hw_device.cpp
+++ b/sdm/libs/core/fb/hw_device.cpp
@@ -227,11 +227,11 @@
   DisplayError error = kErrorNone;
 
   HWLayersInfo &hw_layer_info = hw_layers->info;
-  LayerStack *stack = hw_layer_info.stack;
+  uint32_t hw_layer_count = UINT32(hw_layer_info.hw_layers.size());
 
   DLOGV_IF(kTagDriverConfig, "************************** %s Validate Input ***********************",
            device_name_);
-  DLOGV_IF(kTagDriverConfig, "SDE layer count is %d", hw_layer_info.count);
+  DLOGV_IF(kTagDriverConfig, "SDE layer count is %d", hw_layer_count);
 
   mdp_layer_commit_v1 &mdp_commit = mdp_disp_commit_.commit_v1;
   uint32_t &mdp_layer_count = mdp_commit.input_layer_cnt;
@@ -241,55 +241,54 @@
   DLOGI_IF(kTagDriverConfig, "right_roi: x = %d, y = %d, w = %d, h = %d", mdp_commit.right_roi.x,
     mdp_commit.right_roi.y, mdp_commit.right_roi.w, mdp_commit.right_roi.h);
 
-  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
-    uint32_t layer_index = hw_layer_info.index[i];
-    Layer *layer = stack->layers.at(layer_index);
-    LayerBuffer *input_buffer = layer->input_buffer;
+  for (uint32_t i = 0; i < hw_layer_count; i++) {
+    const Layer &layer = hw_layer_info.hw_layers.at(i);
+    LayerBuffer input_buffer = layer.input_buffer;
     HWPipeInfo *left_pipe = &hw_layers->config[i].left_pipe;
     HWPipeInfo *right_pipe = &hw_layers->config[i].right_pipe;
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
     bool is_rotator_used = (hw_rotator_session->hw_block_count != 0);
-    bool is_cursor_pipe_used = (hw_layer_info.use_hw_cursor & layer->flags.cursor);
+    bool is_cursor_pipe_used = (hw_layer_info.use_hw_cursor & layer.flags.cursor);
 
     for (uint32_t count = 0; count < 2; count++) {
       HWPipeInfo *pipe_info = (count == 0) ? left_pipe : right_pipe;
       HWRotateInfo *hw_rotate_info = &hw_rotator_session->hw_rotate_info[count];
 
       if (hw_rotate_info->valid) {
-        input_buffer = &hw_rotator_session->output_buffer;
+        input_buffer = hw_rotator_session->output_buffer;
       }
 
       if (pipe_info->valid) {
         mdp_input_layer &mdp_layer = mdp_in_layers_[mdp_layer_count];
         mdp_layer_buffer &mdp_buffer = mdp_layer.buffer;
 
-        mdp_buffer.width = input_buffer->width;
-        mdp_buffer.height = input_buffer->height;
+        mdp_buffer.width = input_buffer.width;
+        mdp_buffer.height = input_buffer.height;
         mdp_buffer.comp_ratio.denom = 1000;
         mdp_buffer.comp_ratio.numer = UINT32(hw_layers->config[i].compression * 1000);
 
-        if (layer->flags.solid_fill) {
+        if (layer.flags.solid_fill) {
           mdp_buffer.format = MDP_ARGB_8888;
         } else {
-          error = SetFormat(input_buffer->format, &mdp_buffer.format);
+          error = SetFormat(input_buffer.format, &mdp_buffer.format);
           if (error != kErrorNone) {
             return error;
           }
         }
-        mdp_layer.alpha = layer->plane_alpha;
+        mdp_layer.alpha = layer.plane_alpha;
         mdp_layer.z_order = UINT16(pipe_info->z_order);
         mdp_layer.transp_mask = 0xffffffff;
-        SetBlending(layer->blending, &mdp_layer.blend_op);
+        SetBlending(layer.blending, &mdp_layer.blend_op);
         mdp_layer.pipe_ndx = pipe_info->pipe_id;
         mdp_layer.horz_deci = pipe_info->horizontal_decimation;
         mdp_layer.vert_deci = pipe_info->vertical_decimation;
 
         SetRect(pipe_info->src_roi, &mdp_layer.src_rect);
         SetRect(pipe_info->dst_roi, &mdp_layer.dst_rect);
-        SetMDPFlags(layer, is_rotator_used, is_cursor_pipe_used, &mdp_layer.flags);
-        SetCSC(layer->input_buffer->csc, &mdp_layer.color_space);
+        SetMDPFlags(&layer, is_rotator_used, is_cursor_pipe_used, &mdp_layer.flags);
+        SetCSC(layer.input_buffer.color_metadata, &mdp_layer.color_space);
         if (pipe_info->flags & kIGC) {
-          SetIGC(layer->input_buffer, mdp_layer_count);
+          SetIGC(&layer.input_buffer, mdp_layer_count);
         }
         if (pipe_info->flags & kMultiRect) {
           mdp_layer.flags |= MDP_LAYER_MULTIRECT_ENABLE;
@@ -297,7 +296,7 @@
             mdp_layer.flags |= MDP_LAYER_MULTIRECT_PARALLEL_MODE;
           }
         }
-        mdp_layer.bg_color = layer->solid_fill_color;
+        mdp_layer.bg_color = layer.solid_fill_color;
 
         // HWScaleData to MDP driver
         hw_scale_->SetHWScaleData(pipe_info->scale_data, mdp_layer_count, &mdp_commit,
@@ -336,7 +335,7 @@
     mdp_out_layer_.buffer.comp_ratio.denom = 1000;
     mdp_out_layer_.buffer.comp_ratio.numer = UINT32(hw_layers->output_compression * 1000);
 #ifdef OUT_LAYER_COLOR_SPACE
-    SetCSC(output_buffer->csc, &mdp_out_layer_.color_space);
+    SetCSC(output_buffer->color_metadata, &mdp_out_layer_.color_space);
 #endif
     SetFormat(output_buffer->format, &mdp_out_layer_.buffer.format);
 
@@ -427,18 +426,18 @@
   DTRACE_SCOPED();
 
   HWLayersInfo &hw_layer_info = hw_layers->info;
-  LayerStack *stack = hw_layer_info.stack;
+  uint32_t hw_layer_count = UINT32(hw_layer_info.hw_layers.size());
 
   DLOGV_IF(kTagDriverConfig, "*************************** %s Commit Input ************************",
            device_name_);
-  DLOGV_IF(kTagDriverConfig, "SDE layer count is %d", hw_layer_info.count);
+  DLOGV_IF(kTagDriverConfig, "SDE layer count is %d", hw_layer_count);
 
   mdp_layer_commit_v1 &mdp_commit = mdp_disp_commit_.commit_v1;
   uint32_t mdp_layer_index = 0;
 
-  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
-    uint32_t layer_index = hw_layer_info.index[i];
-    LayerBuffer *input_buffer = stack->layers.at(layer_index)->input_buffer;
+  for (uint32_t i = 0; i < hw_layer_count; i++) {
+    const Layer &layer = hw_layer_info.hw_layers.at(i);
+    LayerBuffer *input_buffer = const_cast<LayerBuffer *>(&layer.input_buffer);
     HWPipeInfo *left_pipe = &hw_layers->config[i].left_pipe;
     HWPipeInfo *right_pipe = &hw_layers->config[i].right_pipe;
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
@@ -521,6 +520,7 @@
     return kErrorHardware;
   }
 
+  LayerStack *stack = hw_layer_info.stack;
   stack->retire_fence_fd = mdp_commit.retire_fence;
 #ifdef VIDEO_MODE_DEFER_RETIRE_FENCE
   if (hw_panel_info_.mode == kModeVideo) {
@@ -531,12 +531,9 @@
   // MDP returns only one release fence for the entire layer stack. Duplicate this fence into all
   // layers being composed by MDP.
 
-  std::vector<uint32_t> fence_dup_flag;
-  fence_dup_flag.clear();
-
-  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
-    uint32_t layer_index = hw_layer_info.index[i];
-    LayerBuffer *input_buffer = stack->layers.at(layer_index)->input_buffer;
+  for (uint32_t i = 0; i < hw_layer_count; i++) {
+    const Layer &layer = hw_layer_info.hw_layers.at(i);
+    LayerBuffer *input_buffer = const_cast<LayerBuffer *>(&layer.input_buffer);
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
 
     if (hw_rotator_session->hw_block_count) {
@@ -545,14 +542,8 @@
       continue;
     }
 
-    // Make sure the release fence is duplicated only once for each buffer.
-    if (std::find(fence_dup_flag.begin(), fence_dup_flag.end(), layer_index) ==
-        fence_dup_flag.end()) {
-      input_buffer->release_fence_fd = Sys::dup_(mdp_commit.release_fence);
-      fence_dup_flag.push_back(layer_index);
-    }
+    input_buffer->release_fence_fd = Sys::dup_(mdp_commit.release_fence);
   }
-  fence_dup_flag.clear();
 
   hw_layer_info.sync_handle = Sys::dup_(mdp_commit.release_fence);
 
@@ -726,7 +717,7 @@
 
 void HWDevice::SetMDPFlags(const Layer *layer, const bool &is_rotator_used,
                            bool is_cursor_pipe_used, uint32_t *mdp_flags) {
-  const LayerBuffer *input_buffer = layer->input_buffer;
+  const LayerBuffer &input_buffer = layer->input_buffer;
 
   // Flips will be taken care by rotator, if layer uses rotator for downscale/rotation. So ignore
   // flip flags for MDP.
@@ -739,18 +730,18 @@
       *mdp_flags |= MDP_LAYER_FLIP_LR;
     }
 
-    if (input_buffer->flags.interlace) {
+    if (input_buffer.flags.interlace) {
       *mdp_flags |= MDP_LAYER_DEINTERLACE;
     }
   }
 
-  if (input_buffer->flags.secure_camera) {
+  if (input_buffer.flags.secure_camera) {
     *mdp_flags |= MDP_LAYER_SECURE_CAMERA_SESSION;
-  } else if (input_buffer->flags.secure) {
+  } else if (input_buffer.flags.secure) {
     *mdp_flags |= MDP_LAYER_SECURE_SESSION;
   }
 
-  if (input_buffer->flags.secure_display) {
+  if (input_buffer.flags.secure_display) {
     *mdp_flags |= MDP_LAYER_SECURE_DISPLAY_SESSION;
   }
 
@@ -1070,11 +1061,24 @@
   mdp_disp_commit_.commit_v1.dest_scaler = mdp_dest_scalar_data_.data();
 }
 
-void HWDevice::SetCSC(LayerCSC source, mdp_color_space *color_space) {
-  switch (source) {
-  case kCSCLimitedRange601:    *color_space = MDP_CSC_ITU_R_601;      break;
-  case kCSCFullRange601:       *color_space = MDP_CSC_ITU_R_601_FR;   break;
-  case kCSCLimitedRange709:    *color_space = MDP_CSC_ITU_R_709;      break;
+void HWDevice::SetCSC(const ColorMetaData &color_metadata, mdp_color_space *color_space) {
+  switch (color_metadata.colorPrimaries) {
+  case ColorPrimaries_BT601_6_525:
+  case ColorPrimaries_BT601_6_625:
+    *color_space = ((color_metadata.range == Range_Full) ? MDP_CSC_ITU_R_601_FR :
+                                                           MDP_CSC_ITU_R_601);
+    break;
+  case ColorPrimaries_BT709_5:
+    *color_space = MDP_CSC_ITU_R_709;
+    break;
+#if defined MDP_CSC_ITU_R_2020 && defined MDP_CSC_ITU_R_2020_FR
+  case ColorPrimaries_BT2020:
+    *color_space = static_cast<mdp_color_space>((color_metadata.range == Range_Full) ?
+                                                 MDP_CSC_ITU_R_2020_FR : MDP_CSC_ITU_R_2020);
+    break;
+#endif
+  default:
+    break;
   }
 }
 
@@ -1106,7 +1110,7 @@
   DTRACE_SCOPED();
 
   HWLayersInfo &hw_layer_info = hw_layers->info;
-  uint32_t count = hw_layer_info.count;
+  uint32_t count = UINT32(hw_layer_info.hw_layers.size());
   uint32_t cursor_index = count - 1;
   HWPipeInfo *left_pipe = &hw_layers->config[cursor_index].left_pipe;
 
diff --git a/sdm/libs/core/fb/hw_device.h b/sdm/libs/core/fb/hw_device.h
index 72c7a13..aa73177 100644
--- a/sdm/libs/core/fb/hw_device.h
+++ b/sdm/libs/core/fb/hw_device.h
@@ -129,7 +129,7 @@
   int ParseLine(const char *input, const char *delim, char *tokens[],
                 const uint32_t max_token, uint32_t *count);
   void ResetDisplayParams();
-  void SetCSC(const LayerCSC source, mdp_color_space *color_space);
+  void SetCSC(const ColorMetaData &color_metadata, mdp_color_space *color_space);
   void SetIGC(const LayerBuffer *layer_buffer, uint32_t index);
 
   bool EnableHotPlugDetection(int enable);
diff --git a/sdm/libs/core/fb/hw_info.cpp b/sdm/libs/core/fb/hw_info.cpp
index ef450c0..08a7188 100644
--- a/sdm/libs/core/fb/hw_info.cpp
+++ b/sdm/libs/core/fb/hw_info.cpp
@@ -386,18 +386,20 @@
     }
   }
 
-  DLOGI("MDSS Rotator: Count = %d, Downscale = %d", hw_resource->hw_rot_info.num_rotator,
-        hw_resource->hw_rot_info.has_downscale);
+  DLOGI("MDSS Rotator: Count = %d, Downscale = %d, Min_downscale = %f",
+        hw_resource->hw_rot_info.num_rotator, hw_resource->hw_rot_info.has_downscale,
+        hw_resource->hw_rot_info.min_downscale);
 
   return kErrorNone;
 }
 
 DisplayError HWInfo::GetV4L2RotatorInfo(HWResourceInfo *hw_resource) {
+  string v4l2_path = "/sys/class/video4linux/video";
   const uint32_t kMaxV4L2Nodes = 64;
   bool found = false;
 
   for (uint32_t i = 0; (i < kMaxV4L2Nodes) && (false == found); i++) {
-    string path = "/sys/class/video4linux/video" + to_string(i) + "/name";
+    string path = v4l2_path + to_string(i) + "/name";
     Sys::fstream fs(path, fstream::in);
     if (!fs.is_open()) {
       continue;
@@ -410,13 +412,34 @@
        hw_resource->hw_rot_info.num_rotator++;
        hw_resource->hw_rot_info.type = HWRotatorInfo::ROT_TYPE_V4L2;
        hw_resource->hw_rot_info.has_downscale = true;
+
+       string caps_path = v4l2_path + to_string(i) + "/device/caps";
+       Sys::fstream caps_fs(caps_path, fstream::in);
+
+       if (caps_fs.is_open()) {
+         uint32_t token_count = 0;
+         const uint32_t max_count = 10;
+         char *tokens[max_count] = { NULL };
+         string caps;
+         while (Sys::getline_(caps_fs, caps)) {
+           if (!ParseString(caps.c_str(), tokens, max_count, ":, =\n", &token_count)) {
+             if (!strncmp(tokens[0], "downscale_compression", strlen("downscale_compression"))) {
+               hw_resource->hw_rot_info.downscale_compression = UINT8(atoi(tokens[1]));
+             } else if (!strncmp(tokens[0], "min_downscale", strlen("min_downscale"))) {
+               hw_resource->hw_rot_info.min_downscale = FLOAT(atof(tokens[1]));
+             }
+           }
+         }
+       }
+
        // We support only 1 rotator
        found = true;
     }
   }
 
-  DLOGI("V4L2 Rotator: Count = %d, Downscale = %d", hw_resource->hw_rot_info.num_rotator,
-        hw_resource->hw_rot_info.has_downscale);
+  DLOGI("V4L2 Rotator: Count = %d, Downscale = %d, Min_downscale = %f, Downscale_compression = %d",
+        hw_resource->hw_rot_info.num_rotator, hw_resource->hw_rot_info.has_downscale,
+        hw_resource->hw_rot_info.min_downscale, hw_resource->hw_rot_info.downscale_compression);
 
   return kErrorNone;
 }
diff --git a/sdm/libs/core/fb/hw_primary.cpp b/sdm/libs/core/fb/hw_primary.cpp
index 6f62143..b484d5e 100644
--- a/sdm/libs/core/fb/hw_primary.cpp
+++ b/sdm/libs/core/fb/hw_primary.cpp
@@ -65,6 +65,10 @@
 #define MDP_COMMIT_AVR_ONE_SHOT_MODE 0x10
 #endif
 
+#ifndef MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI
+#define MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI  0x20
+#endif
+
 namespace sdm {
 
 using std::string;
@@ -101,6 +105,8 @@
   EnableHotPlugDetection(1);
   InitializeConfigs();
 
+  avr_prop_disabled_ = Debug::IsAVRDisabled();
+
   return error;
 }
 
@@ -223,10 +229,11 @@
     return kErrorHardware;
   }
 
-  // If driver doesn't return width/height information, default to 160 dpi
+  // If driver doesn't return width/height information, default to 320 dpi
   if (INT(var_screeninfo.width) <= 0 || INT(var_screeninfo.height) <= 0) {
-    var_screeninfo.width  = UINT32(((FLOAT(var_screeninfo.xres) * 25.4f)/160.0f) + 0.5f);
-    var_screeninfo.height = UINT32(((FLOAT(var_screeninfo.yres) * 25.4f)/160.0f) + 0.5f);
+    var_screeninfo.width  = UINT32(((FLOAT(var_screeninfo.xres) * 25.4f)/320.0f) + 0.5f);
+    var_screeninfo.height = UINT32(((FLOAT(var_screeninfo.yres) * 25.4f)/320.0f) + 0.5f);
+    DLOGW("Driver doesn't report panel physical width and height - defaulting to 320dpi");
   }
 
   display_attributes_.x_pixels = var_screeninfo.xres;
@@ -291,6 +298,10 @@
 DisplayError HWPrimary::SetRefreshRate(uint32_t refresh_rate) {
   char node_path[kMaxStringLength] = {0};
 
+  if (hw_resource_.has_avr && !avr_prop_disabled_) {
+    return kErrorNotSupported;
+  }
+
   if (refresh_rate == display_attributes_.fps) {
     return kErrorNone;
   }
@@ -373,6 +384,7 @@
 
   // Update second roi information in right_roi
   if (hw_layer_info.left_frame_roi.size() == 2) {
+    mdp_commit.flags |= MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI;
     right_roi = hw_layer_info.left_frame_roi.at(1);
   }
 
@@ -398,7 +410,7 @@
     mdp_out_layer_.buffer.comp_ratio.numer = UINT32(hw_layers->output_compression * 1000);
     mdp_out_layer_.buffer.fence = -1;
 #ifdef OUT_LAYER_COLOR_SPACE
-    SetCSC(output_buffer->csc, &mdp_out_layer_.color_space);
+    SetCSC(output_buffer->color_metadata, &mdp_out_layer_.color_space);
 #endif
     SetFormat(output_buffer->format, &mdp_out_layer_.buffer.format);
     mdp_commit.flags |= MDP_COMMIT_CWB_EN;
diff --git a/sdm/libs/core/fb/hw_primary.h b/sdm/libs/core/fb/hw_primary.h
index ae45318..bd6ea2f 100644
--- a/sdm/libs/core/fb/hw_primary.h
+++ b/sdm/libs/core/fb/hw_primary.h
@@ -85,6 +85,7 @@
   const char *kBrightnessNode = "/sys/class/leds/lcd-backlight/brightness";
   const char *kAutoRefreshNode = "/sys/devices/virtual/graphics/fb0/msm_cmd_autorefresh_en";
   bool auto_refresh_ = false;
+  bool avr_prop_disabled_ = false;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/resource_default.cpp b/sdm/libs/core/resource_default.cpp
index 54a6658..31d8704 100644
--- a/sdm/libs/core/resource_default.cpp
+++ b/sdm/libs/core/resource_default.cpp
@@ -228,14 +228,14 @@
 
   DLOGV_IF(kTagResources, "==== Resource reserving start: hw_block = %d ====", hw_block_id);
 
-  if (layer_info.count > 1) {
+  if (layer_info.hw_layers.size() > 1) {
     DLOGV_IF(kTagResources, "More than one FB layers");
     return kErrorResources;
   }
 
-  Layer *layer = layer_info.stack->layers.at(layer_info.index[0]);
+  const Layer &layer = layer_info.hw_layers.at(0);
 
-  if (layer->composition != kCompositionGPUTarget) {
+  if (layer.composition != kCompositionGPUTarget) {
     DLOGV_IF(kTagResources, "Not an FB layer");
     return kErrorParameters;
   }
@@ -539,9 +539,9 @@
                                 HWLayers *hw_layers) {
   HWLayersInfo &layer_info = hw_layers->info;
   DisplayError error = kErrorNone;
-  Layer *layer = layer_info.stack->layers.at(layer_info.index[0]);
+  const Layer &layer = layer_info.hw_layers.at(0);
 
-  error = ValidateLayerParams(layer);
+  error = ValidateLayerParams(&layer);
   if (error != kErrorNone) {
     return error;
   }
@@ -550,16 +550,16 @@
   HWPipeInfo &left_pipe = layer_config->left_pipe;
   HWPipeInfo &right_pipe = layer_config->right_pipe;
 
-  LayerRect src_rect = layer->src_rect;
-  LayerRect dst_rect = layer->dst_rect;
+  LayerRect src_rect = layer.src_rect;
+  LayerRect dst_rect = layer.dst_rect;
 
   error = ValidateDimensions(src_rect, dst_rect);
   if (error != kErrorNone) {
     return error;
   }
 
-  bool ubwc_tiled = IsUBWCFormat(layer->input_buffer->format);
-  error = ValidateScaling(src_rect, dst_rect, false /*rotated90 */, ubwc_tiled,
+  BufferLayout layout = GetBufferLayout(layer.input_buffer.format);
+  error = ValidateScaling(src_rect, dst_rect, false /*rotated90 */, layout,
                           false /* use_rotator_downscale */);
   if (error != kErrorNone) {
     return error;
@@ -575,7 +575,7 @@
     return error;
   }
 
-  error = AlignPipeConfig(layer, &left_pipe, &right_pipe);
+  error = AlignPipeConfig(&layer, &left_pipe, &right_pipe);
   if (error != kErrorNone) {
     return error;
   }
@@ -584,8 +584,8 @@
   left_pipe.z_order = 0;
 
   DLOGV_IF(kTagResources, "==== FB layer Config ====");
-  Log(kTagResources, "input layer src_rect", layer->src_rect);
-  Log(kTagResources, "input layer dst_rect", layer->dst_rect);
+  Log(kTagResources, "input layer src_rect", layer.src_rect);
+  Log(kTagResources, "input layer dst_rect", layer.dst_rect);
   Log(kTagResources, "cropped src_rect", src_rect);
   Log(kTagResources, "cropped dst_rect", dst_rect);
   Log(kTagResources, "left pipe src", layer_config->left_pipe.src_roi);
@@ -665,10 +665,10 @@
 DisplayError ResourceDefault::ValidateLayerParams(const Layer *layer) {
   const LayerRect &src = layer->src_rect;
   const LayerRect &dst = layer->dst_rect;
-  const LayerBuffer *input_buffer = layer->input_buffer;
+  const LayerBuffer &input_buffer = layer->input_buffer;
 
-  if (input_buffer->format == kFormatInvalid) {
-    DLOGV_IF(kTagResources, "Invalid input buffer format %d", input_buffer->format);
+  if (input_buffer.format == kFormatInvalid) {
+    DLOGV_IF(kTagResources, "Invalid input buffer format %d", input_buffer.format);
     return kErrorNotSupported;
   }
 
@@ -679,7 +679,7 @@
   }
 
   // Make sure source in integral only if it is a non secure layer.
-  if (!input_buffer->flags.secure &&
+  if (!input_buffer.flags.secure &&
       ((src.left - roundf(src.left) != 0.0f) ||
        (src.top - roundf(src.top) != 0.0f) ||
        (src.right - roundf(src.right) != 0.0f) ||
@@ -716,7 +716,7 @@
   return kErrorNone;
 }
 
-DisplayError ResourceDefault::ValidatePipeParams(HWPipeInfo *pipe_info, bool ubwc_tiled) {
+DisplayError ResourceDefault::ValidatePipeParams(HWPipeInfo *pipe_info, LayerBufferFormat format) {
   DisplayError error = kErrorNone;
 
   const LayerRect &src_rect = pipe_info->src_roi;
@@ -727,7 +727,8 @@
     return error;
   }
 
-  error = ValidateScaling(src_rect, dst_rect, false /* rotated90 */, ubwc_tiled,
+  BufferLayout layout = GetBufferLayout(format);
+  error = ValidateScaling(src_rect, dst_rect, false /* rotated90 */, layout,
                           false /* use_rotator_downscale */);
   if (error != kErrorNone) {
     return error;
@@ -737,7 +738,7 @@
 }
 
 DisplayError ResourceDefault::ValidateScaling(const LayerRect &crop, const LayerRect &dst,
-                                              bool rotate90, bool ubwc_tiled,
+                                              bool rotate90, BufferLayout layout,
                                               bool use_rotator_downscale) {
   DisplayError error = kErrorNone;
 
@@ -749,7 +750,7 @@
     return error;
   }
 
-  error = ValidateDownScaling(scale_x, scale_y, ubwc_tiled);
+  error = ValidateDownScaling(scale_x, scale_y, (layout != kLinear));
   if (error != kErrorNone) {
     return error;
   }
@@ -884,8 +885,7 @@
     return kErrorNotSupported;
   }
 
-  bool ubwc_tiled = IsUBWCFormat(layer->input_buffer->format);
-  error = ValidatePipeParams(left_pipe, ubwc_tiled);
+  error = ValidatePipeParams(left_pipe, layer->input_buffer.format);
   if (error != kErrorNone) {
     goto PipeConfigExit;
   }
@@ -894,7 +894,7 @@
     // Make sure the  left and right ROI are conjunct
     right_pipe->src_roi.left = left_pipe->src_roi.right;
     right_pipe->dst_roi.left = left_pipe->dst_roi.right;
-    error = ValidatePipeParams(right_pipe, ubwc_tiled);
+    error = ValidatePipeParams(right_pipe, layer->input_buffer.format);
   }
 
 PipeConfigExit:
diff --git a/sdm/libs/core/resource_default.h b/sdm/libs/core/resource_default.h
index 9f08155..9fab0d0 100644
--- a/sdm/libs/core/resource_default.h
+++ b/sdm/libs/core/resource_default.h
@@ -57,8 +57,8 @@
   virtual DisplayError PostCommit(Handle display_ctx, HWLayers *hw_layers);
   virtual void Purge(Handle display_ctx);
   virtual DisplayError SetMaxMixerStages(Handle display_ctx, uint32_t max_mixer_stages);
-  virtual DisplayError ValidateScaling(const LayerRect &crop, const LayerRect &dst,
-                                       bool rotate90, bool ubwc_tiled, bool use_rotator_downscale);
+  virtual DisplayError ValidateScaling(const LayerRect &crop, const LayerRect &dst, bool rotate90,
+                                       BufferLayout layout, bool use_rotator_downscale);
   DisplayError ValidateCursorConfig(Handle display_ctx, const Layer *layer, bool is_top);
   DisplayError ValidateCursorPosition(Handle display_ctx, HWLayers *hw_layers, int x, int y);
   DisplayError SetMaxBandwidthMode(HWBwModes mode);
@@ -121,7 +121,7 @@
   bool CalculateCropRects(const LayerRect &scissor, LayerRect *crop, LayerRect *dst);
   DisplayError ValidateLayerParams(const Layer *layer);
   DisplayError ValidateDimensions(const LayerRect &crop, const LayerRect &dst);
-  DisplayError ValidatePipeParams(HWPipeInfo *pipe_info, bool ubwc_tiled);
+  DisplayError ValidatePipeParams(HWPipeInfo *pipe_info, LayerBufferFormat format);
   DisplayError ValidateDownScaling(float scale_x, float scale_y, bool ubwc_tiled);
   DisplayError ValidateUpScaling(float scale_x, float scale_y);
   DisplayError GetScaleFactor(const LayerRect &crop, const LayerRect &dst, float *scale_x,
diff --git a/sdm/libs/core/strategy.cpp b/sdm/libs/core/strategy.cpp
index 106e9e1..c987fd0 100644
--- a/sdm/libs/core/strategy.cpp
+++ b/sdm/libs/core/strategy.cpp
@@ -137,14 +137,13 @@
   LayerStack *layer_stack = hw_layers_info_->stack;
   for (uint32_t i = 0; i < hw_layers_info_->app_layer_count; i++) {
     layer_stack->layers.at(i)->composition = kCompositionGPU;
+    layer_stack->layers.at(i)->request.flags.request_flags = 0;  // Reset layer request
   }
 
   if (!extn_start_success_) {
     // When mixer resolution and panel resolutions are same (1600x2560) and FB resolution is
     // 1080x1920 FB_Target destination coordinates(mapped to FB resolution 1080x1920) need to
     // be mapped to destination coordinates of mixer resolution(1600x2560).
-    hw_layers_info_->count = 0;
-    uint32_t &hw_layer_count = hw_layers_info_->count;
     Layer *gpu_target_layer = layer_stack->layers.at(hw_layers_info_->gpu_target_index);
     float layer_mixer_width = FLOAT(mixer_attributes_.width);
     float layer_mixer_height = FLOAT(mixer_attributes_.height);
@@ -153,11 +152,11 @@
     LayerRect src_domain = (LayerRect){0.0f, 0.0f, fb_width, fb_height};
     LayerRect dst_domain = (LayerRect){0.0f, 0.0f, layer_mixer_width, layer_mixer_height};
 
-    hw_layers_info_->updated_src_rect[hw_layer_count] = gpu_target_layer->src_rect;
-    MapRect(src_domain, dst_domain, gpu_target_layer->dst_rect,
-            &hw_layers_info_->updated_dst_rect[hw_layer_count]);
-
-    hw_layers_info_->index[hw_layer_count++] = hw_layers_info_->gpu_target_index;
+    Layer layer = *gpu_target_layer;
+    hw_layers_info_->index[0] = hw_layers_info_->gpu_target_index;
+    MapRect(src_domain, dst_domain, layer.dst_rect, &layer.dst_rect);
+    hw_layers_info_->hw_layers.clear();
+    hw_layers_info_->hw_layers.push_back(layer);
   }
 
   tried_default_ = true;
@@ -246,4 +245,13 @@
   return kErrorNone;
 }
 
+DisplayError Strategy::Purge() {
+  if (strategy_intf_) {
+    return strategy_intf_->Purge();
+  }
+
+  return kErrorNone;
+}
+
+
 }  // namespace sdm
diff --git a/sdm/libs/core/strategy.h b/sdm/libs/core/strategy.h
index 3fadd0d..a84d139 100644
--- a/sdm/libs/core/strategy.h
+++ b/sdm/libs/core/strategy.h
@@ -49,6 +49,7 @@
                            const HWMixerAttributes &mixer_attributes,
                            const DisplayConfigVariableInfo &fb_config);
   DisplayError SetCompositionState(LayerComposition composition_type, bool enable);
+  DisplayError Purge();
 
  private:
   void GenerateROI();
diff --git a/sdm/libs/hwc/Android.mk b/sdm/libs/hwc/Android.mk
index 259a727..d2828b4 100644
--- a/sdm/libs/hwc/Android.mk
+++ b/sdm/libs/hwc/Android.mk
@@ -15,7 +15,7 @@
 
 LOCAL_SHARED_LIBRARIES        := libsdmcore libqservice libbinder libhardware libhardware_legacy \
                                  libutils libcutils libsync libmemalloc libqdutils libdl \
-                                 libpowermanager libsdmutils libc++
+                                 libpowermanager libsdmutils libgpu_tonemapper  libc++
 
 LOCAL_SRC_FILES               := hwc_session.cpp \
                                  hwc_display.cpp \
@@ -28,7 +28,8 @@
                                  hwc_buffer_sync_handler.cpp \
                                  hwc_color_manager.cpp \
                                  blit_engine_c2d.cpp \
-                                 cpuhint.cpp
+                                 cpuhint.cpp \
+                                 hwc_tonemapper.cpp
 
 include $(BUILD_SHARED_LIBRARY)
 endif
diff --git a/sdm/libs/hwc/blit_engine_c2d.cpp b/sdm/libs/hwc/blit_engine_c2d.cpp
index e5cf81c..4efe2f1 100644
--- a/sdm/libs/hwc/blit_engine_c2d.cpp
+++ b/sdm/libs/hwc/blit_engine_c2d.cpp
@@ -207,17 +207,17 @@
 
   for (uint32_t i = blit_target_start_index_-2; (i > 0) && (count < num_blit_target_); i--) {
     Layer *layer = layer_stack->layers.at(i);
-    LayerBuffer *layer_buffer = layer->input_buffer;
+    LayerBuffer &layer_buffer = layer->input_buffer;
     if (layer->composition == kCompositionBlit) {
       int index = blit_target_start_index_ + count;
-      layer_buffer->release_fence_fd =
-        layer_stack->layers.at(index)->input_buffer->release_fence_fd;
-      fence_fd = layer_buffer->release_fence_fd;
-      close(layer_buffer->acquire_fence_fd);
-      layer_buffer->acquire_fence_fd = -1;
-      layer_stack->layers.at(index)->input_buffer->release_fence_fd = -1;
-      fd = layer_stack->layers.at(index)->input_buffer->acquire_fence_fd;
-      layer_stack->layers.at(index)->input_buffer->acquire_fence_fd = -1;
+      layer_buffer.release_fence_fd =
+        layer_stack->layers.at(index)->input_buffer.release_fence_fd;
+      fence_fd = layer_buffer.release_fence_fd;
+      close(layer_buffer.acquire_fence_fd);
+      layer_buffer.acquire_fence_fd = -1;
+      layer_stack->layers.at(index)->input_buffer.release_fence_fd = -1;
+      fd = layer_stack->layers.at(index)->input_buffer.acquire_fence_fd;
+      layer_stack->layers.at(index)->input_buffer.acquire_fence_fd = -1;
       count++;
     }
   }
@@ -262,7 +262,7 @@
     Layer *layer = layer_stack->layers.at(i);
 
     // No 10 bit support for C2D
-    if (Is10BitFormat(layer->input_buffer->format)) {
+    if (Is10BitFormat(layer->input_buffer.format)) {
       return -1;
     }
 
@@ -281,9 +281,9 @@
   blit_target_start_index_ = ++i;
   num_blit_target_ = layer_count - blit_target_start_index_;
 
-  LayerBuffer *layer_buffer = layer_stack->layers.at(gpu_target_index)->input_buffer;
-  int fbwidth = INT(layer_buffer->unaligned_width);
-  int fbheight = INT(layer_buffer->unaligned_height);
+  LayerBuffer &layer_buffer = layer_stack->layers.at(gpu_target_index)->input_buffer;
+  int fbwidth = INT(layer_buffer.unaligned_width);
+  int fbheight = INT(layer_buffer.unaligned_height);
   if ((fbwidth < 0) || (fbheight < 0)) {
     return -1;
   }
@@ -293,17 +293,17 @@
 
   for (uint32_t j = 0; j < num_blit_target_; j++, k++) {
     Layer *layer = layer_stack->layers.at(k);
-    LayerBuffer *layer_buffer = layer->input_buffer;
+    LayerBuffer &layer_buffer = layer->input_buffer;
     int aligned_w = 0;
     int aligned_h = 0;
 
     // Set the buffer height and width
     AdrenoMemInfo::getInstance().getAlignedWidthAndHeight(fbwidth, fbheight/3,
                    INT(HAL_PIXEL_FORMAT_RGBA_8888), 0, aligned_w, aligned_h);
-    layer_buffer->width = aligned_w;
-    layer_buffer->height = aligned_h;
-    layer_buffer->unaligned_width = fbwidth;
-    layer_buffer->unaligned_height = fbheight/3;
+    layer_buffer.width = aligned_w;
+    layer_buffer.height = aligned_h;
+    layer_buffer.unaligned_width = fbwidth;
+    layer_buffer.unaligned_height = fbheight/3;
 
     layer->plane_alpha = 0xFF;
     layer->blending = kBlendingOpaque;
@@ -380,16 +380,16 @@
       Layer *layer = layer_stack->layers.at(j + content_list->numHwLayers);
       private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
       // Set the fd information
-        layer->input_buffer->width = target_aligned_width;
-        layer->input_buffer->height = target_aligned_height;
-        layer->input_buffer->unaligned_width = target_width;
-        layer->input_buffer->unaligned_height = target_height;
+        layer->input_buffer.width = target_aligned_width;
+        layer->input_buffer.height = target_aligned_height;
+        layer->input_buffer.unaligned_width = target_width;
+        layer->input_buffer.unaligned_height = target_height;
       if (target_buffer->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) {
-          layer->input_buffer->format = kFormatRGBA8888Ubwc;
+          layer->input_buffer.format = kFormatRGBA8888Ubwc;
       }
-      layer->input_buffer->planes[0].fd = target_buffer->fd;
-      layer->input_buffer->planes[0].offset = 0;
-      layer->input_buffer->planes[0].stride = target_buffer->width;
+      layer->input_buffer.planes[0].fd = target_buffer->fd;
+      layer->input_buffer.planes[0].offset = 0;
+      layer->input_buffer.planes[0].stride = target_buffer->width;
     }
   }
 
@@ -439,7 +439,7 @@
 
     for (uint32_t k = 0; k <= i; k++) {
       Layer *bottom_layer = layer_stack->layers.at(k);
-      LayerBuffer *layer_buffer = bottom_layer->input_buffer;
+      LayerBuffer &layer_buffer = bottom_layer->input_buffer;
       // if layer below the blit layer does not intersect, ignore that layer
       LayerRect inter_sect = Intersection(layer->dst_rect, bottom_layer->dst_rect);
       if (bottom_layer->composition != kCompositionHybrid && !IsValid(inter_sect)) {
@@ -452,12 +452,12 @@
       }
 
       // For each layer marked as Hybrid, wait for acquire fence and then blit using the C2D
-      if (layer_buffer->acquire_fence_fd >= 0) {
+      if (layer_buffer.acquire_fence_fd >= 0) {
         // Wait for acquire fence on the App buffers.
-        if (sync_wait(layer_buffer->acquire_fence_fd, 1000) < 0) {
+        if (sync_wait(layer_buffer.acquire_fence_fd, 1000) < 0) {
           DLOGE("sync_wait error!! error no = %d err str = %s", errno, strerror(errno));
         }
-        layer_buffer->acquire_fence_fd = -1;
+        layer_buffer.acquire_fence_fd = -1;
       }
       hwc_layer_1_t *hwc_layer = &content_list->hwLayers[k];
       LayerRect &src_rect = bottom_layer->blit_regions.at(processed_blit);
@@ -488,8 +488,8 @@
     uint32_t layer_count = UINT32(layer_stack->layers.size());
     for (uint32_t k = blit_target_start_index_; k < layer_count; k++) {
       Layer *layer = layer_stack->layers.at(k);
-      LayerBuffer *layer_buffer = layer->input_buffer;
-      layer_buffer->acquire_fence_fd = fd;
+      LayerBuffer &layer_buffer = layer->input_buffer;
+      layer_buffer.acquire_fence_fd = fd;
     }
   }
 
@@ -500,7 +500,7 @@
                                         LayerRect blit_rect, LayerRect blit_dest_Rect) {
   private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
   const private_handle_t *hnd = static_cast<const private_handle_t *>(hwc_layer->handle);
-  LayerBuffer *layer_buffer = layer->input_buffer;
+  LayerBuffer &layer_buffer = layer->input_buffer;
 
   // Set the Copybit Source
   copybit_image_t src;
@@ -539,7 +539,7 @@
   region.count = 1;
   region.rect  = &region_rect;
   RegionIterator copybitRegion(region);
-  int acquireFd = layer_buffer->acquire_fence_fd;
+  int acquireFd = layer_buffer.acquire_fence_fd;
 
   // FRAMEBUFFER_WIDTH/HEIGHT for c2d is the target buffer w/h
   blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_FRAMEBUFFER_WIDTH,
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index 3327ed9..305ed66 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -41,9 +41,10 @@
 #include <utility>
 #include <vector>
 
-#include "hwc_display.h"
-#include "hwc_debugger.h"
 #include "blit_engine_c2d.h"
+#include "hwc_debugger.h"
+#include "hwc_display.h"
+#include "hwc_tonemapper.h"
 
 #ifdef QTI_BSP
 #include <hardware/display_defs.h>
@@ -55,7 +56,7 @@
 
 static void ApplyDeInterlaceAdjustment(Layer *layer) {
   // De-interlacing adjustment
-  if (layer->input_buffer->flags.interlace) {
+  if (layer->input_buffer.flags.interlace) {
     float height = (layer->src_rect.bottom - layer->src_rect.top) / 2.0f;
     layer->src_rect.top = ROUND_UP_ALIGN_DOWN(layer->src_rect.top / 2.0f, 2);
     layer->src_rect.bottom = layer->src_rect.top + floorf(height);
@@ -98,6 +99,9 @@
     }
   }
 
+  tone_mapper_ = new HWCToneMapper();
+  tone_mapper_->Init();
+
   display_intf_->GetRefreshRateRange(&min_refresh_rate_, &max_refresh_rate_);
   current_refresh_rate_ = max_refresh_rate_;
 
@@ -127,6 +131,9 @@
     blit_engine_ = NULL;
   }
 
+  delete tone_mapper_;
+  tone_mapper_ = NULL;
+
   return 0;
 }
 
@@ -172,6 +179,7 @@
     // Do not flush until a buffer is successfully submitted again.
     flush_on_error = false;
     state = kStateOff;
+    tone_mapper_->Terminate();
     break;
 
   case HWC_POWER_MODE_NORMAL:
@@ -273,6 +281,10 @@
     blit_engine_->SetFrameDumpConfig(count);
   }
 
+  if (tone_mapper_) {
+    tone_mapper_->SetFrameDumpConfig(count);
+  }
+
   DLOGI("num_frame_dump %d, input_layer_dump_enable %d", dump_frame_count_, dump_input_layers_);
 }
 
@@ -323,8 +335,6 @@
 
   for (size_t i = 0; i < num_hw_layers + blit_target_count; i++) {
     Layer *layer = new Layer();
-    LayerBuffer *layer_buffer = new LayerBuffer();
-    layer->input_buffer = layer_buffer;
     layer_stack_.layers.push_back(layer);
   }
 
@@ -333,7 +343,6 @@
 
 void HWCDisplay::FreeLayerStack() {
   for (Layer *layer : layer_stack_.layers) {
-    delete layer->input_buffer;
     delete layer;
   }
   layer_stack_ = {};
@@ -342,10 +351,10 @@
 int HWCDisplay::PrepareLayerParams(hwc_layer_1_t *hwc_layer, Layer* layer) {
   const private_handle_t *pvt_handle = static_cast<const private_handle_t *>(hwc_layer->handle);
 
-  LayerBuffer *layer_buffer = layer->input_buffer;
+  LayerBuffer &layer_buffer = layer->input_buffer;
 
   if (pvt_handle) {
-    layer_buffer->format = GetSDMFormat(pvt_handle->format, pvt_handle->flags);
+    layer_buffer.format = GetSDMFormat(pvt_handle->format, pvt_handle->flags);
     int aligned_width, aligned_height;
     int unaligned_width, unaligned_height;
 
@@ -354,10 +363,10 @@
     AdrenoMemInfo::getInstance().getUnalignedWidthAndHeight(pvt_handle, unaligned_width,
                                                             unaligned_height);
 
-    layer_buffer->width = UINT32(aligned_width);
-    layer_buffer->height = UINT32(aligned_height);
-    layer_buffer->unaligned_width = UINT32(unaligned_width);
-    layer_buffer->unaligned_height = UINT32(unaligned_height);
+    layer_buffer.width = UINT32(aligned_width);
+    layer_buffer.height = UINT32(aligned_height);
+    layer_buffer.unaligned_width = UINT32(unaligned_width);
+    layer_buffer.unaligned_height = UINT32(unaligned_height);
 
     if (SetMetaData(pvt_handle, layer) != kErrorNone) {
       return -EINVAL;
@@ -365,14 +374,14 @@
 
     if (pvt_handle->bufferType == BUFFER_TYPE_VIDEO) {
       layer_stack_.flags.video_present = true;
-      layer_buffer->flags.video = true;
+      layer_buffer.flags.video = true;
     }
     // TZ Protected Buffer - L1
     if (pvt_handle->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
       layer_stack_.flags.secure_present = true;
-      layer_buffer->flags.secure = true;
+      layer_buffer.flags.secure = true;
       if (pvt_handle->flags & private_handle_t::PRIV_FLAGS_CAMERA_WRITE) {
-        layer_buffer->flags.secure_camera = true;
+        layer_buffer.flags.secure_camera = true;
       }
     }
     // Gralloc Usage Protected Buffer - L3 - which needs to be treated as Secure & avoid fallback
@@ -380,7 +389,7 @@
       layer_stack_.flags.secure_present = true;
     }
     if (pvt_handle->flags & private_handle_t::PRIV_FLAGS_SECURE_DISPLAY) {
-      layer_buffer->flags.secure_display = true;
+      layer_buffer.flags.secure_display = true;
     }
 
     // check if this is special solid_fill layer without input_buffer.
@@ -409,11 +418,11 @@
 
       AdrenoMemInfo::getInstance().getAlignedWidthAndHeight(INT(x_pixels), INT(y_pixels), format,
                                                             usage, aligned_width, aligned_height);
-      layer_buffer->width = UINT32(aligned_width);
-      layer_buffer->height = UINT32(aligned_height);
-      layer_buffer->unaligned_width = x_pixels;
-      layer_buffer->unaligned_height = y_pixels;
-      layer_buffer->format = GetSDMFormat(format, flags);
+      layer_buffer.width = UINT32(aligned_width);
+      layer_buffer.height = UINT32(aligned_height);
+      layer_buffer.unaligned_width = x_pixels;
+      layer_buffer.unaligned_height = y_pixels;
+      layer_buffer.format = GetSDMFormat(format, flags);
     }
   }
 
@@ -422,13 +431,13 @@
 
 void HWCDisplay::CommitLayerParams(hwc_layer_1_t *hwc_layer, Layer *layer) {
   const private_handle_t *pvt_handle = static_cast<const private_handle_t *>(hwc_layer->handle);
-  LayerBuffer *layer_buffer = layer->input_buffer;
+  LayerBuffer &layer_buffer = layer->input_buffer;
 
   if (pvt_handle) {
-    layer_buffer->planes[0].fd = pvt_handle->fd;
-    layer_buffer->planes[0].offset = pvt_handle->offset;
-    layer_buffer->planes[0].stride = UINT32(pvt_handle->width);
-    layer_buffer->size = pvt_handle->size;
+    layer_buffer.planes[0].fd = pvt_handle->fd;
+    layer_buffer.planes[0].offset = pvt_handle->offset;
+    layer_buffer.planes[0].stride = UINT32(pvt_handle->width);
+    layer_buffer.size = pvt_handle->size;
   }
 
   // if swapinterval property is set to 0 then close and reset the acquireFd
@@ -436,7 +445,7 @@
     close(hwc_layer->acquireFenceFd);
     hwc_layer->acquireFenceFd = -1;
   }
-  layer_buffer->acquire_fence_fd = hwc_layer->acquireFenceFd;
+  layer_buffer.acquire_fence_fd = hwc_layer->acquireFenceFd;
 }
 
 int HWCDisplay::PrePrepareLayerStack(hwc_display_contents_1_t *content_list) {
@@ -521,7 +530,7 @@
     //    - incoming planeAlpha,
     //    - blending to Coverage.
     if (hwc_layer.flags & kDimLayer) {
-      layer->input_buffer->format = kFormatARGB8888;
+      layer->input_buffer.format = kFormatARGB8888;
       layer->solid_fill_color = 0xff000000;
 #ifdef QTI_BSP
       // Get ARGB color from HWC Dim Layer color
@@ -544,15 +553,15 @@
     // TODO(user): Remove below block.
     // For solid fill, only dest rect need to be specified.
     if (layer->flags.solid_fill) {
-      LayerBuffer *input_buffer = layer->input_buffer;
-      input_buffer->width = UINT32(layer->dst_rect.right - layer->dst_rect.left);
-      input_buffer->height = UINT32(layer->dst_rect.bottom - layer->dst_rect.top);
-      input_buffer->unaligned_width = input_buffer->width;
-      input_buffer->unaligned_height = input_buffer->height;
+      LayerBuffer &input_buffer = layer->input_buffer;
+      input_buffer.width = UINT32(layer->dst_rect.right - layer->dst_rect.left);
+      input_buffer.height = UINT32(layer->dst_rect.bottom - layer->dst_rect.top);
+      input_buffer.unaligned_width = input_buffer.width;
+      input_buffer.unaligned_height = input_buffer.height;
       layer->src_rect.left = 0;
       layer->src_rect.top = 0;
-      layer->src_rect.right = input_buffer->width;
-      layer->src_rect.bottom = input_buffer->height;
+      layer->src_rect.right = input_buffer.width;
+      layer->src_rect.bottom = input_buffer.height;
     }
 
     layer->plane_alpha = hwc_layer.planeAlpha;
@@ -577,7 +586,7 @@
 
     PrepareDynamicRefreshRate(layer);
 
-    layer->input_buffer->buffer_id = reinterpret_cast<uint64_t>(hwc_layer.handle);
+    layer->input_buffer.buffer_id = reinterpret_cast<uint64_t>(hwc_layer.handle);
   }
 
   // Prepare the Blit Target
@@ -665,7 +674,7 @@
         // Align HWC and client's dispaly ID in case of HDMI as primary
         meta_data->s3dComp.displayId =
           display_intf_->IsPrimaryDisplay() ? HWC_DISPLAY_PRIMARY: id_;
-        SetLayerS3DMode(layer->input_buffer->s3d_format,
+        SetLayerS3DMode(layer->input_buffer.s3d_format,
             &meta_data->s3dComp.s3dMode);
       }
     }
@@ -705,6 +714,15 @@
       }
     }
 
+    if (layer_stack_.flags.hdr_present) {
+      status = tone_mapper_->HandleToneMap(content_list, &layer_stack_);
+      if (status != 0) {
+        DLOGE("Error handling HDR in ToneMapper");
+      }
+    } else {
+      tone_mapper_->Terminate();
+    }
+
     DisplayError error = kErrorUndefined;
     if (status == 0) {
       error = display_intf_->Commit(&layer_stack_);
@@ -741,6 +759,11 @@
     display_intf_->Flush();
   }
 
+
+  if (tone_mapper_ && tone_mapper_->IsActive()) {
+     tone_mapper_->PostCommit(&layer_stack_);
+  }
+
   // Set the release fence fd to the blit engine
   if (use_blit_comp_ && blit_engine_->BlitActive()) {
     blit_engine_->PostCommit(&layer_stack_);
@@ -749,17 +772,17 @@
   for (size_t i = 0; i < num_hw_layers; i++) {
     hwc_layer_1_t &hwc_layer = content_list->hwLayers[i];
     Layer *layer = layer_stack_.layers.at(i);
-    LayerBuffer *layer_buffer = layer->input_buffer;
+    LayerBuffer &layer_buffer = layer->input_buffer;
 
     if (!flush_) {
       // If swapinterval property is set to 0 or for single buffer layers, do not update f/w
       // release fences and discard fences from driver
       if (swap_interval_zero_ || layer->flags.single_buffer) {
         hwc_layer.releaseFenceFd = -1;
-        close(layer_buffer->release_fence_fd);
-        layer_buffer->release_fence_fd = -1;
+        close(layer_buffer.release_fence_fd);
+        layer_buffer.release_fence_fd = -1;
       } else if (layer->composition != kCompositionGPU) {
-        hwc_layer.releaseFenceFd = layer_buffer->release_fence_fd;
+        hwc_layer.releaseFenceFd = layer_buffer.release_fence_fd;
       }
 
       // During animation on external/virtual display, SDM will use the cached
@@ -1226,14 +1249,36 @@
 void HWCDisplay::ApplyScanAdjustment(hwc_rect_t *display_frame) {
 }
 
-DisplayError HWCDisplay::SetCSC(ColorSpace_t source, LayerCSC *target) {
-  switch (source) {
-  case ITU_R_601:       *target = kCSCLimitedRange601;   break;
-  case ITU_R_601_FR:    *target = kCSCFullRange601;      break;
-  case ITU_R_709:       *target = kCSCLimitedRange709;   break;
-  default:
-    DLOGE("Unsupported CSC: %d", source);
-    return kErrorNotSupported;
+DisplayError HWCDisplay::SetCSC(const MetaData_t *meta_data, ColorMetaData *color_metadata) {
+  if (meta_data->operation & COLOR_METADATA) {
+#ifdef USE_COLOR_METADATA
+    *color_metadata = meta_data->color;
+#endif
+  } else if (meta_data->operation & UPDATE_COLOR_SPACE) {
+    ColorSpace_t csc = meta_data->colorSpace;
+    color_metadata->range = Range_Limited;
+
+    if (csc == ITU_R_601_FR || csc == ITU_R_2020_FR) {
+      color_metadata->range = Range_Full;
+    }
+
+    switch (csc) {
+    case ITU_R_601:
+    case ITU_R_601_FR:
+      // display driver uses 601 irrespective of 525 or 625
+      color_metadata->colorPrimaries = ColorPrimaries_BT601_6_525;
+      break;
+    case ITU_R_709:
+      color_metadata->colorPrimaries = ColorPrimaries_BT709_5;
+      break;
+    case ITU_R_2020:
+    case ITU_R_2020_FR:
+      color_metadata->colorPrimaries = ColorPrimaries_BT2020;
+      break;
+    default:
+      DLOGE("Unsupported CSC: %d", csc);
+      return kErrorNotSupported;
+    }
   }
 
   return kErrorNone;
@@ -1253,20 +1298,25 @@
 
 DisplayError HWCDisplay::SetMetaData(const private_handle_t *pvt_handle, Layer *layer) {
   const MetaData_t *meta_data = reinterpret_cast<MetaData_t *>(pvt_handle->base_metadata);
-  LayerBuffer *layer_buffer = layer->input_buffer;
+  LayerBuffer &layer_buffer = layer->input_buffer;
 
   if (!meta_data) {
     return kErrorNone;
   }
 
-  if (meta_data->operation & UPDATE_COLOR_SPACE) {
-    if (SetCSC(meta_data->colorSpace, &layer_buffer->csc) != kErrorNone) {
-      return kErrorNotSupported;
-    }
+  if (SetCSC(meta_data, &layer_buffer.color_metadata) != kErrorNone) {
+    return kErrorNotSupported;
+  }
+
+  if (layer_buffer.color_metadata.colorPrimaries == ColorPrimaries_BT2020 &&
+     (layer_buffer.color_metadata.transfer == Transfer_SMPTE_ST2084 ||
+      layer_buffer.color_metadata.transfer == Transfer_HLG)) {
+    layer_buffer.flags.hdr = true;
+    layer_stack_.flags.hdr_present = true;
   }
 
   if (meta_data->operation & SET_IGC) {
-    if (SetIGC(meta_data->igc, &layer_buffer->igc) != kErrorNone) {
+    if (SetIGC(meta_data->igc, &layer_buffer.igc) != kErrorNone) {
       return kErrorNotSupported;
     }
   }
@@ -1276,11 +1326,11 @@
   }
 
   if ((meta_data->operation & PP_PARAM_INTERLACED) && meta_data->interlaced) {
-    layer_buffer->flags.interlace = true;
+    layer_buffer.flags.interlace = true;
   }
 
   if (meta_data->operation & LINEAR_FORMAT) {
-    layer_buffer->format = GetSDMFormat(INT32(meta_data->linearFormat), 0);
+    layer_buffer.format = GetSDMFormat(INT32(meta_data->linearFormat), 0);
   }
 
   if (meta_data->operation & SET_SINGLE_BUFFER_MODE) {
@@ -1294,7 +1344,7 @@
     std::map<int, LayerBufferS3DFormat>::iterator it =
         s3d_format_hwc_to_sdm_.find(INT32(meta_data->s3dFormat));
     if (it != s3d_format_hwc_to_sdm_.end()) {
-      layer->input_buffer->s3d_format = it->second;
+      layer->input_buffer.s3d_format = it->second;
     } else {
       DLOGW("Invalid S3D format %d", meta_data->s3dFormat);
     }
@@ -1398,8 +1448,8 @@
     // need play in dedicate resolution and fps, if DRC switch the
     // mode to an non S3D supported mode, it would break S3D playback.
     // Need figure out a way to make S3D and DRC co-exist.
-    if (layer->flags.updating && (layer->input_buffer->flags.video == true) &&
-       (layer->input_buffer->s3d_format == kS3dFormatNone)) {
+    if (layer->flags.updating && (layer->input_buffer.flags.video == true) &&
+       (layer->input_buffer.s3d_format == kS3dFormatNone)) {
       updating_count++;
     }
   }
diff --git a/sdm/libs/hwc/hwc_display.h b/sdm/libs/hwc/hwc_display.h
index 7dab6d5..dad78b6 100644
--- a/sdm/libs/hwc/hwc_display.h
+++ b/sdm/libs/hwc/hwc_display.h
@@ -36,6 +36,7 @@
 namespace sdm {
 
 class BlitEngine;
+class HWCToneMapper;
 
 // Subclasses set this to their type. This has to be different from DisplayType.
 // This is to avoid RTTI and dynamic_cast
@@ -170,7 +171,7 @@
   const char *GetDisplayString();
   void MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list);
   virtual void ApplyScanAdjustment(hwc_rect_t *display_frame);
-  DisplayError SetCSC(ColorSpace_t source, LayerCSC *target);
+  DisplayError SetCSC(const MetaData_t *meta_data, ColorMetaData *color_metadata);
   DisplayError SetIGC(IGC_t source, LayerIGC *target);
   DisplayError SetMetaData(const private_handle_t *pvt_handle, Layer *layer);
   bool NeedsFrameBufferRefresh(hwc_display_contents_1_t *content_list);
@@ -217,6 +218,7 @@
   LayerRect display_rect_;
   std::map<int, LayerBufferS3DFormat> s3d_format_hwc_to_sdm_;
   bool animating_ = false;
+  HWCToneMapper *tone_mapper_ = NULL;
 
  private:
   void DumpInputBuffers(hwc_display_contents_1_t *content_list);
diff --git a/sdm/libs/hwc/hwc_display_external.cpp b/sdm/libs/hwc/hwc_display_external.cpp
index 8a3a591..5b277c7 100644
--- a/sdm/libs/hwc/hwc_display_external.cpp
+++ b/sdm/libs/hwc/hwc_display_external.cpp
@@ -277,7 +277,7 @@
 }
 
 void HWCDisplayExternal::PrepareDynamicRefreshRate(Layer *layer) {
-  if (layer->input_buffer->flags.video) {
+  if (layer->input_buffer.flags.video) {
     if (layer->frame_rate != 0) {
       metadata_refresh_rate_ = SanitizeRefreshRate(layer->frame_rate);
     } else {
diff --git a/sdm/libs/hwc/hwc_display_virtual.cpp b/sdm/libs/hwc/hwc_display_virtual.cpp
index a4d343b..0279fc2 100644
--- a/sdm/libs/hwc/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc/hwc_display_virtual.cpp
@@ -292,6 +292,11 @@
     output_buffer_->flags.secure = 0;
     output_buffer_->flags.video = 0;
 
+    const MetaData_t *meta_data = reinterpret_cast<MetaData_t *>(output_handle->base_metadata);
+    if (meta_data && SetCSC(meta_data, &output_buffer_->color_metadata) != kErrorNone) {
+      return kErrorNotSupported;
+    }
+
     // TZ Protected Buffer - L1
     if (output_handle->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
       output_buffer_->flags.secure = 1;
diff --git a/sdm/libs/hwc/hwc_tonemapper.cpp b/sdm/libs/hwc/hwc_tonemapper.cpp
new file mode 100644
index 0000000..694acc0
--- /dev/null
+++ b/sdm/libs/hwc/hwc_tonemapper.cpp
@@ -0,0 +1,290 @@
+/*
+* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*  * Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+*  * Redistributions in binary form must reproduce the above
+*    copyright notice, this list of conditions and the following
+*    disclaimer in the documentation and/or other materials provided
+*    with the distribution.
+*  * Neither the name of The Linux Foundation nor the names of its
+*    contributors may be used to endorse or promote products derived
+*    from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <alloc_controller.h>
+#include <gr.h>
+#include <gralloc_priv.h>
+#include <memalloc.h>
+#include <sync/sync.h>
+
+#include <TonemapFactory.h>
+
+#include <core/buffer_allocator.h>
+
+#include <utils/constants.h>
+#include <utils/debug.h>
+#include <utils/formats.h>
+#include <utils/rect.h>
+
+#include <vector>
+
+#include "hwc_debugger.h"
+#include "hwc_tonemapper.h"
+
+#define __CLASS__ "HWCToneMapper"
+
+namespace sdm {
+
+int HWCToneMapper::Init() {
+  for (uint32_t i = 0; i < kNumIntermediateBuffers; i++) {
+    intermediate_buffer_[i] = NULL;
+    release_fence_fd_[i] = -1;
+  }
+  return 0;
+}
+
+void HWCToneMapper::DeInit() {
+  return;
+}
+
+int HWCToneMapper::HandleToneMap(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) {
+  uint32_t layer_count = UINT32(layer_stack->layers.size());
+  std::vector<uint32_t> tonemap_layer_index = {};
+  Layer *layer = NULL;
+  uint32_t i = 0;
+  uint32_t gpu_count = 0;
+  int fence_fd = -1;
+  int acquire_fd = -1;
+  int merged_fd = -1;
+  hwc_layer_1_t *hwc_layer = NULL;
+  const private_handle_t *dst_hnd = NULL;
+  const private_handle_t *src_hnd = NULL;
+
+  for (; i < layer_count; i++) {
+    layer = layer_stack->layers.at(i);
+    if (layer->request.flags.tone_map) {
+      tonemap_layer_index.push_back(i);
+      break;
+    }
+    if (layer->composition == kCompositionGPU) {
+      gpu_count++;
+    }
+  }
+
+  if (tonemap_layer_index.empty()) {
+    return 0;
+  }
+  // gpu count can be 0 when a layer is on FB and in next cycle it doesn't update and SDM marks
+  // it as SDE comp
+  if (gpu_count == 0) {
+    // TODO(akumarkr): Remove goto when added multiple instance support
+    // intermediate buffer can be null
+    goto update_fd;
+  }
+
+  if (intermediate_buffer_[0] == NULL) {
+    DLOGI("format = %d width = %d height = %d", layer->request.format, layer->request.width,
+         layer->request.height);
+     // TODO(akumarkr): use flags from LayerRequestFlags for format change etc.,
+     uint32_t usage = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP | GRALLOC_USAGE_HW_TEXTURE |
+                      GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
+     AllocateIntermediateBuffers(layer->input_buffer.width, layer->input_buffer.height,
+                                 HAL_PIXEL_FORMAT_RGBA_8888, usage);
+  }
+  current_intermediate_buffer_index_ =
+    (current_intermediate_buffer_index_ + 1) % kNumIntermediateBuffers;
+
+  if (!gpu_tone_mapper_) {
+    Color10Bit *grid_entries = NULL;
+    int grid_size = 0;
+    if (layer->lut_3d.validGridEntries) {
+      grid_entries = layer->lut_3d.gridEntries;
+      grid_size = INT(layer->lut_3d.gridSize);
+    }
+    gpu_tone_mapper_ = TonemapperFactory_GetInstance(TONEMAP_INVERSE, layer->lut_3d.lutEntries,
+                                                     layer->lut_3d.dim, grid_entries, grid_size);
+    if (gpu_tone_mapper_ == NULL) {
+      DLOGE("Get Tonemapper failed");
+      return -1;
+    }
+  }
+
+  hwc_layer = &content_list->hwLayers[i];
+  dst_hnd = intermediate_buffer_[current_intermediate_buffer_index_];
+  src_hnd = static_cast<const private_handle_t *>(hwc_layer->handle);
+  acquire_fd = dup(layer->input_buffer.acquire_fence_fd);
+  buffer_sync_handler_.SyncMerge(acquire_fd, release_fence_fd_[current_intermediate_buffer_index_],
+                                 &merged_fd);
+  if (acquire_fd >= 0) {
+    CloseFd(&acquire_fd);
+  }
+
+  if (release_fence_fd_[current_intermediate_buffer_index_] >= 0) {
+    CloseFd(&release_fence_fd_[current_intermediate_buffer_index_]);
+  }
+  DTRACE_BEGIN("GPU_TM_BLIT");
+  fence_fd = gpu_tone_mapper_->blit(reinterpret_cast<const void *>(dst_hnd),
+                                    reinterpret_cast<const void *>(src_hnd), merged_fd);
+  DTRACE_END();
+
+
+  DumpToneMapOutput(&fence_fd);
+
+update_fd:
+  // Acquire fence will be closed by HWC Display
+  // Fence returned by GPU will be closed in PostCommit
+  layer->input_buffer.acquire_fence_fd = fence_fd;
+  layer->input_buffer.planes[0].fd = intermediate_buffer_[current_intermediate_buffer_index_]->fd;
+
+  active_ = true;
+
+  tonemap_layer_index.clear();
+
+  return 0;
+}
+
+int HWCToneMapper::AllocateIntermediateBuffers(uint32_t width, uint32_t height, uint32_t format,
+                                               uint32_t usage) {
+  int status = 0;
+  if (width <= 0 || height <= 0) {
+    return false;
+  }
+
+  for (uint32_t i = 0; i < kNumIntermediateBuffers; i++) {
+    status = alloc_buffer(&intermediate_buffer_[i], INT(width), INT(height), INT(format),
+                          INT(usage));
+    if (status < 0) {
+      DLOGE("Allocation of Intermediate Buffer failed");
+      FreeIntermediateBuffers();
+      break;
+    }
+  }
+
+  return status;
+}
+void HWCToneMapper::FreeIntermediateBuffers() {
+  if (!intermediate_buffer_[0]) {
+    return;
+  }
+  for (uint32_t i = 0; i < kNumIntermediateBuffers; i++) {
+    private_handle_t *buffer = intermediate_buffer_[i];
+    if (buffer) {
+      // Free the valid fence
+      if (release_fence_fd_[i] >= 0) {
+        CloseFd(&release_fence_fd_[i]);
+      }
+      free_buffer(buffer);
+      intermediate_buffer_[i] = NULL;
+    }
+  }
+}
+
+void HWCToneMapper::PostCommit(LayerStack *layer_stack) {
+  uint32_t layer_count = UINT32(layer_stack->layers.size());
+  std::vector<uint32_t> tonemap_layer_index = {};
+  Layer *layer = NULL;
+  int rel_fence_fd = -1;
+  bool has_tonemap = false;
+  uint32_t i;
+
+  for (i = 0; i < layer_count; i++) {
+    layer = layer_stack->layers.at(i);
+    if (layer->request.flags.tone_map) {
+      tonemap_layer_index.push_back(i);
+      has_tonemap = true;
+      break;
+    }
+  }
+
+  if (has_tonemap) {
+    LayerBuffer &layer_buffer = layer->input_buffer;
+
+    rel_fence_fd = layer_buffer.release_fence_fd;
+    // close the fd returned by GPU Tonemapper
+    CloseFd(&layer_buffer.acquire_fence_fd);
+
+    SetReleaseFence(rel_fence_fd);
+  }
+
+  active_ = false;
+}
+
+void HWCToneMapper::SetReleaseFence(int fd) {
+  CloseFd(&release_fence_fd_[current_intermediate_buffer_index_]);
+  // used to give to GPU tonemapper along with input layer fd
+  release_fence_fd_[current_intermediate_buffer_index_] = dup(fd);
+}
+
+void HWCToneMapper::CloseFd(int *fd) {
+  if (*fd >= 0) {
+    close(*fd);
+    *fd = -1;
+  }
+}
+
+void HWCToneMapper::Terminate() {
+  if (!gpu_tone_mapper_) {
+    return;
+  }
+  // fix this on multiple instance: only delete obj and call ToneMapperDestroy on deInit.
+  delete gpu_tone_mapper_;
+  gpu_tone_mapper_ = NULL;
+
+  TonemapperFactory_Destroy();
+  FreeIntermediateBuffers();
+  active_ = false;
+}
+
+void HWCToneMapper::SetFrameDumpConfig(uint32_t count) {
+  DLOGI("Dump FrameConfig count = %d", count);
+  dump_frame_count_ = count;
+  dump_frame_index_ = 0;
+}
+
+void HWCToneMapper::DumpToneMapOutput(int *acquire_fd) {
+  if (!dump_frame_count_) {
+    return;
+  }
+
+  private_handle_t *target_buffer = intermediate_buffer_[current_intermediate_buffer_index_];
+
+  if (*acquire_fd >= 0) {
+    int error = sync_wait(*acquire_fd, 1000);
+    if (error < 0) {
+      DLOGW("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
+      return;
+    }
+  }
+
+  char dump_file_name[PATH_MAX];
+  size_t result = 0;
+  snprintf(dump_file_name, sizeof(dump_file_name), "/data/misc/display/frame_dump_primary"
+           "/tonemap_%d.raw", (dump_frame_index_));
+  FILE* fp = fopen(dump_file_name, "w+");
+  if (fp) {
+    DLOGI("base addr = %x", target_buffer->base);
+    result = fwrite(reinterpret_cast<void *>(target_buffer->base), target_buffer->size, 1, fp);
+    fclose(fp);
+  }
+  dump_frame_count_--;
+  dump_frame_index_++;
+  CloseFd(acquire_fd);
+}
+
+}  // namespace sdm
diff --git a/sdm/libs/hwc/hwc_tonemapper.h b/sdm/libs/hwc/hwc_tonemapper.h
new file mode 100644
index 0000000..7ed5373
--- /dev/null
+++ b/sdm/libs/hwc/hwc_tonemapper.h
@@ -0,0 +1,81 @@
+/*
+* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*  * Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+*  * Redistributions in binary form must reproduce the above
+*    copyright notice, this list of conditions and the following
+*    disclaimer in the documentation and/or other materials provided
+*    with the distribution.
+*  * Neither the name of The Linux Foundation nor the names of its
+*    contributors may be used to endorse or promote products derived
+*    from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __HWC_TONEMAPPER_H__
+#define __HWC_TONEMAPPER_H__
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <hardware/hwcomposer.h>
+
+#include <core/layer_stack.h>
+#include <utils/sys.h>
+
+#include "hwc_buffer_sync_handler.h"
+
+class Tonemapper;
+
+namespace sdm {
+
+class HWCToneMapper {
+ public:
+  HWCToneMapper() {}
+  ~HWCToneMapper() {}
+
+  int Init();
+  void DeInit();
+  int HandleToneMap(hwc_display_contents_1_t *content_list, LayerStack *layer_stack);
+  void Terminate();
+  void PostCommit(LayerStack *layer_stack);
+  bool IsActive() { return active_; }
+  void SetFrameDumpConfig(uint32_t count);
+
+ private:
+  int AllocateIntermediateBuffers(uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+  void FreeIntermediateBuffers();
+  void SetReleaseFence(int fence_fd);
+  void CloseFd(int *fd);
+  void DumpToneMapOutput(int *acquire_fence);
+
+  static const uint32_t kNumIntermediateBuffers = 2;
+  bool active_ = false;
+
+  private_handle_t *intermediate_buffer_[kNumIntermediateBuffers] = {NULL, NULL};
+  uint32_t current_intermediate_buffer_index_ = 0;
+  int release_fence_fd_[kNumIntermediateBuffers];
+
+  HWCBufferSyncHandler buffer_sync_handler_ = {};
+  Tonemapper *gpu_tone_mapper_ = NULL;
+  uint32_t dump_frame_count_ = 0;
+  uint32_t dump_frame_index_ = 0;
+};
+
+}  // namespace sdm
+#endif  // __HWC_TONEMAPPER_H__
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 0f2b900..f603f18 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -49,7 +49,7 @@
 
 static void ApplyDeInterlaceAdjustment(Layer *layer) {
   // De-interlacing adjustment
-  if (layer->input_buffer->flags.interlace) {
+  if (layer->input_buffer.flags.interlace) {
     float height = (layer->src_rect.bottom - layer->src_rect.top) / 2.0f;
     layer->src_rect.top = ROUND_UP_ALIGN_DOWN(layer->src_rect.top / 2.0f, 2);
     layer->src_rect.bottom = layer->src_rect.top + floorf(height);
@@ -341,14 +341,14 @@
     layer->composition = kCompositionGPU;
 
     if (swap_interval_zero_) {
-      if (layer->input_buffer->acquire_fence_fd >= 0) {
-        close(layer->input_buffer->acquire_fence_fd);
-        layer->input_buffer->acquire_fence_fd = -1;
+      if (layer->input_buffer.acquire_fence_fd >= 0) {
+        close(layer->input_buffer.acquire_fence_fd);
+        layer->input_buffer.acquire_fence_fd = -1;
       }
     }
 
     const private_handle_t *handle =
-        reinterpret_cast<const private_handle_t *>(layer->input_buffer->buffer_id);
+        reinterpret_cast<const private_handle_t *>(layer->input_buffer.buffer_id);
     if (handle) {
       if (handle->bufferType == BUFFER_TYPE_VIDEO) {
         layer_stack_.flags.video_present = true;
@@ -363,6 +363,13 @@
       }
     }
 
+    if (layer->color_metadata.colorPrimaries == ColorPrimaries_BT2020 &&
+       (layer->color_metadata.transfer == Transfer_SMPTE_ST2084 ||
+        layer->color_metadata.transfer == Transfer_HLG)) {
+      layer->flags.hdr = true;
+      layer_stack_.flags.hdr_present = true;
+    }
+
     if (layer->flags.skip) {
       layer_stack_.flags.skip_present = true;
     }
@@ -383,7 +390,7 @@
     ApplyDeInterlaceAdjustment(layer);
     // SDM requires these details even for solid fill
     if (layer->flags.solid_fill) {
-      LayerBuffer *layer_buffer = layer->input_buffer;
+      LayerBuffer *layer_buffer = &layer->input_buffer;
       layer_buffer->width = UINT32(layer->dst_rect.right - layer->dst_rect.left);
       layer_buffer->height = UINT32(layer->dst_rect.bottom - layer->dst_rect.top);
       layer_buffer->unaligned_width = layer_buffer->width;
@@ -906,7 +913,7 @@
 
   // TODO(user): No way to set the client target release fence on SF
   int32_t &client_target_release_fence =
-      client_target_->GetSDMLayer()->input_buffer->release_fence_fd;
+      client_target_->GetSDMLayer()->input_buffer.release_fence_fd;
   if (client_target_release_fence >= 0) {
     close(client_target_release_fence);
     client_target_release_fence = -1;
@@ -915,7 +922,7 @@
   for (auto hwc_layer : layer_set_) {
     hwc_layer->ResetGeometryChanges();
     Layer *layer = hwc_layer->GetSDMLayer();
-    LayerBuffer *layer_buffer = layer->input_buffer;
+    LayerBuffer *layer_buffer = &layer->input_buffer;
 
     if (!flush_) {
       // If swapinterval property is set to 0 or for single buffer layers, do not update f/w
@@ -1120,8 +1127,8 @@
   for (uint32_t i = 0; i < layer_stack_.layers.size(); i++) {
     auto layer = layer_stack_.layers.at(i);
     const private_handle_t *pvt_handle =
-        reinterpret_cast<const private_handle_t *>(layer->input_buffer->buffer_id);
-    auto acquire_fence_fd = layer->input_buffer->acquire_fence_fd;
+        reinterpret_cast<const private_handle_t *>(layer->input_buffer.buffer_id);
+    auto acquire_fence_fd = layer->input_buffer.acquire_fence_fd;
 
     if (acquire_fence_fd >= 0) {
       int error = sync_wait(acquire_fence_fd, 1000);
@@ -1325,11 +1332,11 @@
 
   // TODO(user): How does the dirty region get set on the client target? File bug on Google
   client_target_layer->composition = kCompositionGPUTarget;
-  client_target_layer->input_buffer->format = GetSDMFormat(format, flags);
-  client_target_layer->input_buffer->width = UINT32(aligned_width);
-  client_target_layer->input_buffer->height = UINT32(aligned_height);
-  client_target_layer->input_buffer->unaligned_width = x_pixels;
-  client_target_layer->input_buffer->unaligned_height = y_pixels;
+  client_target_layer->input_buffer.format = GetSDMFormat(format, flags);
+  client_target_layer->input_buffer.width = UINT32(aligned_width);
+  client_target_layer->input_buffer.height = UINT32(aligned_height);
+  client_target_layer->input_buffer.unaligned_width = x_pixels;
+  client_target_layer->input_buffer.unaligned_height = y_pixels;
   client_target_layer->plane_alpha = 255;
 
   DLOGI("New framebuffer resolution (%dx%d)", fb_config.x_pixels, fb_config.y_pixels);
@@ -1476,12 +1483,11 @@
     if (solid_fill_layer_ == NULL) {
       // Create a dummy layer here
       solid_fill_layer_ = new Layer();
-      solid_fill_layer_->input_buffer = new LayerBuffer();
     }
     uint32_t primary_width = 0, primary_height = 0;
     GetMixerResolution(&primary_width, &primary_height);
 
-    LayerBuffer *layer_buffer = solid_fill_layer_->input_buffer;
+    LayerBuffer *layer_buffer = &solid_fill_layer_->input_buffer;
     layer_buffer->width = primary_width;
     layer_buffer->height = primary_height;
     layer_buffer->unaligned_width = primary_width;
@@ -1506,9 +1512,6 @@
     solid_fill_layer_->flags.solid_fill = true;
   } else {
     // delete the dummy layer
-    if (solid_fill_layer_) {
-      delete solid_fill_layer_->input_buffer;
-    }
     delete solid_fill_layer_;
     solid_fill_layer_ = NULL;
   }
@@ -1523,7 +1526,7 @@
 
 void HWCDisplay::SolidFillCommit() {
   if (solid_fill_enable_ && solid_fill_layer_) {
-    LayerBuffer *layer_buffer = solid_fill_layer_->input_buffer;
+    LayerBuffer *layer_buffer = &solid_fill_layer_->input_buffer;
     if (layer_buffer->release_fence_fd > 0) {
       close(layer_buffer->release_fence_fd);
       layer_buffer->release_fence_fd = -1;
@@ -1627,13 +1630,13 @@
 void HWCDisplay::CloseAcquireFds() {
   for (auto hwc_layer : layer_set_) {
     auto layer = hwc_layer->GetSDMLayer();
-    if (layer->input_buffer->acquire_fence_fd >= 0) {
-      close(layer->input_buffer->acquire_fence_fd);
-      layer->input_buffer->acquire_fence_fd = -1;
+    if (layer->input_buffer.acquire_fence_fd >= 0) {
+      close(layer->input_buffer.acquire_fence_fd);
+      layer->input_buffer.acquire_fence_fd = -1;
     }
   }
   int32_t &client_target_acquire_fence =
-      client_target_->GetSDMLayer()->input_buffer->acquire_fence_fd;
+      client_target_->GetSDMLayer()->input_buffer.acquire_fence_fd;
   if (client_target_acquire_fence >= 0) {
     close(client_target_acquire_fence);
     client_target_acquire_fence = -1;
@@ -1655,10 +1658,10 @@
     os << "\tdevice(SDM) composition: " <<
           to_string(layer->GetDeviceSelectedCompositionType()).c_str() << std::endl;
     os << "\tplane_alpha: " << std::to_string(sdm_layer->plane_alpha).c_str() << std::endl;
-    os << "\tformat: " << GetFormatString(sdm_layer->input_buffer->format) << std::endl;
+    os << "\tformat: " << GetFormatString(sdm_layer->input_buffer.format) << std::endl;
     os << "\ttransform: rot: " << transform.rotation << " flip_h: " << transform.flip_horizontal <<
           " flip_v: "<< transform.flip_vertical << std::endl;
-    os << "\tbuffer_id: " << std::hex << "0x" << sdm_layer->input_buffer->buffer_id << std::dec
+    os << "\tbuffer_id: " << std::hex << "0x" << sdm_layer->input_buffer.buffer_id << std::dec
        << std::endl;
   }
   return os.str();
diff --git a/sdm/libs/hwc2/hwc_display_virtual.cpp b/sdm/libs/hwc2/hwc_display_virtual.cpp
index 5436faa..1197c7b 100644
--- a/sdm/libs/hwc2/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc2/hwc_display_virtual.cpp
@@ -204,6 +204,11 @@
     output_buffer_->flags.secure = 0;
     output_buffer_->flags.video = 0;
 
+    const MetaData_t *meta_data = reinterpret_cast<MetaData_t *>(output_handle->base_metadata);
+    if (meta_data && SetCSC(meta_data, &output_buffer_->color_metadata) != kErrorNone) {
+      return HWC2::Error::BadParameter;
+    }
+
     // TZ Protected Buffer - L1
     if (output_handle->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
       output_buffer_->flags.secure = 1;
diff --git a/sdm/libs/hwc2/hwc_layers.cpp b/sdm/libs/hwc2/hwc_layers.cpp
index f4ae584..2ed21df 100644
--- a/sdm/libs/hwc2/hwc_layers.cpp
+++ b/sdm/libs/hwc2/hwc_layers.cpp
@@ -28,10 +28,44 @@
 
 std::atomic<hwc2_layer_t> HWCLayer::next_id_(1);
 
+DisplayError SetCSC(const MetaData_t *meta_data, ColorMetaData *color_metadata) {
+  if (meta_data->operation & COLOR_METADATA) {
+#ifdef USE_COLOR_METADATA
+    *color_metadata = meta_data->color;
+#endif
+  } else if (meta_data->operation & UPDATE_COLOR_SPACE) {
+    ColorSpace_t csc = meta_data->colorSpace;
+    color_metadata->range = Range_Limited;
+
+    if (csc == ITU_R_601_FR || csc == ITU_R_2020_FR) {
+      color_metadata->range = Range_Full;
+    }
+
+    switch (csc) {
+    case ITU_R_601:
+    case ITU_R_601_FR:
+      // video and display driver uses 601_525
+      color_metadata->colorPrimaries = ColorPrimaries_BT601_6_525;
+      break;
+    case ITU_R_709:
+      color_metadata->colorPrimaries = ColorPrimaries_BT709_5;
+      break;
+    case ITU_R_2020:
+    case ITU_R_2020_FR:
+        color_metadata->colorPrimaries = ColorPrimaries_BT2020;
+        break;
+    default:
+      DLOGE("Unsupported CSC: %d", csc);
+      return kErrorNotSupported;
+    }
+  }
+
+  return kErrorNone;
+}
+
 // Layer operations
 HWCLayer::HWCLayer(hwc2_display_t display_id) : id_(next_id_++), display_id_(display_id) {
   layer_ = new Layer();
-  layer_->input_buffer = new LayerBuffer();
   // Fences are deferred, so the first time this layer is presented, return -1
   // TODO(user): Verify that fences are properly obtained on suspend/resume
   release_fences_.push(-1);
@@ -45,9 +79,6 @@
   }
   close(ion_fd_);
   if (layer_) {
-    if (layer_->input_buffer) {
-      delete (layer_->input_buffer);
-    }
     delete layer_;
   }
 }
@@ -74,7 +105,7 @@
     ion_fd_ = dup(handle->fd);
   }
 
-  LayerBuffer *layer_buffer = layer_->input_buffer;
+  LayerBuffer *layer_buffer = &layer_->input_buffer;
   int aligned_width, aligned_height;
   int unaligned_width, unaligned_height;
 
@@ -151,7 +182,7 @@
 
 HWC2::Error HWCLayer::SetLayerColor(hwc_color_t color) {
   layer_->solid_fill_color = GetUint32Color(color);
-  layer_->input_buffer->format = kFormatARGB8888;
+  layer_->input_buffer.format = kFormatARGB8888;
   DLOGV_IF(kTagCompManager, "[%" PRIu64 "][%" PRIu64 "] Layer color set to %x", display_id_, id_,
            layer_->solid_fill_color);
   return HWC2::Error::None;
@@ -440,16 +471,14 @@
 
 DisplayError HWCLayer::SetMetaData(const private_handle_t *pvt_handle, Layer *layer) {
   const MetaData_t *meta_data = reinterpret_cast<MetaData_t *>(pvt_handle->base_metadata);
-  LayerBuffer *layer_buffer = layer->input_buffer;
+  LayerBuffer *layer_buffer = &layer->input_buffer;
 
   if (!meta_data) {
     return kErrorNone;
   }
 
-  if (meta_data->operation & UPDATE_COLOR_SPACE) {
-    if (SetCSC(meta_data->colorSpace, &layer_buffer->csc) != kErrorNone) {
-      return kErrorNotSupported;
-    }
+  if (sdm::SetCSC(meta_data, &layer_buffer->color_metadata) != kErrorNone) {
+    return kErrorNotSupported;
   }
 
   if (meta_data->operation & SET_IGC) {
@@ -477,25 +506,6 @@
   return kErrorNone;
 }
 
-DisplayError HWCLayer::SetCSC(ColorSpace_t source, LayerCSC *target) {
-  switch (source) {
-    case ITU_R_601:
-      *target = kCSCLimitedRange601;
-      break;
-    case ITU_R_601_FR:
-      *target = kCSCFullRange601;
-      break;
-    case ITU_R_709:
-      *target = kCSCLimitedRange709;
-      break;
-    default:
-      DLOGE("Unsupported CSC: %d", source);
-      return kErrorNotSupported;
-  }
-
-  return kErrorNone;
-}
-
 DisplayError HWCLayer::SetIGC(IGC_t source, LayerIGC *target) {
   switch (source) {
     case IGC_NotSpecified:
diff --git a/sdm/libs/hwc2/hwc_layers.h b/sdm/libs/hwc2/hwc_layers.h
index ed93a5a..6a80b1b 100644
--- a/sdm/libs/hwc2/hwc_layers.h
+++ b/sdm/libs/hwc2/hwc_layers.h
@@ -37,6 +37,8 @@
 
 namespace sdm {
 
+DisplayError SetCSC(const MetaData_t *meta_data, ColorMetaData *color_metadata);
+
 enum GeometryChanges {
   kNone         = 0x000,
   kBlendMode    = 0x001,
@@ -100,7 +102,7 @@
   LayerBufferFormat GetSDMFormat(const int32_t &source, const int flags);
   LayerBufferS3DFormat GetS3DFormat(uint32_t s3d_format);
   DisplayError SetMetaData(const private_handle_t *pvt_handle, Layer *layer);
-  DisplayError SetCSC(ColorSpace_t source, LayerCSC *target);
+  DisplayError SetCSC(const MetaData_t *meta_data, ColorMetaData *color_metadata);
   DisplayError SetIGC(IGC_t source, LayerIGC *target);
   uint32_t RoundToStandardFPS(float fps);
 };
diff --git a/sdm/libs/utils/formats.cpp b/sdm/libs/utils/formats.cpp
index 546c0c7..8e9d0b8 100644
--- a/sdm/libs/utils/formats.cpp
+++ b/sdm/libs/utils/formats.cpp
@@ -114,5 +114,14 @@
   }
 }
 
+BufferLayout GetBufferLayout(LayerBufferFormat format) {
+  switch (format) {
+  case kFormatYCbCr420TP10Ubwc:
+    return kTPTiled;
+  default:
+    return (IsUBWCFormat(format) ? kUBWC : kLinear);
+  }
+}
+
 }  // namespace sdm