Ozone integration from chromium @ https://crrev.com/336230

Adds linux direct rendering capability via mesa gbm.

Refactored gpu_platform_support (and _host) to allow different
implementations of messaging transport between host and gpu.

drm_cursor is stubbed out.

Removed ScopedAllowIO in drm_host_display_manager and added
header documentation indicating where IO is performed.

Removed unused files:
 ui/ozone/common/gpu/ozone_gpu_message_generator*
 ui/ozone/common/gpu/ozone_gpu_messages.h

BUG=
R=jamesr@chromium.org, spang@chromium.org

Review URL: https://codereview.chromium.org/1285183008 .
diff --git a/device/udev_linux/BUILD.gn b/device/udev_linux/BUILD.gn
new file mode 100644
index 0000000..6a3c831
--- /dev/null
+++ b/device/udev_linux/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+if (use_udev) {
+  source_set("udev_linux") {
+    sources = [
+      "scoped_udev.h",
+      "udev.cc",
+      "udev.h",
+      "udev0_loader.cc",
+      "udev0_loader.h",
+      "udev1_loader.cc",
+      "udev1_loader.h",
+      "udev_loader.cc",
+      "udev_loader.h",
+    ]
+
+    deps = [
+      "//base",
+      "//build/config/linux:udev",
+    ]
+  }
+}
diff --git a/device/udev_linux/scoped_udev.h b/device/udev_linux/scoped_udev.h
new file mode 100644
index 0000000..06e3926
--- /dev/null
+++ b/device/udev_linux/scoped_udev.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_SCOPED_UDEV_H_
+#define DEVICE_UDEV_LINUX_SCOPED_UDEV_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "device/udev_linux/udev.h"
+
+#if !defined(USE_UDEV)
+#error "USE_UDEV not defined"
+#endif
+
+namespace device {
+
+struct UdevDeleter {
+  void operator()(udev* dev) const {
+    udev_unref(dev);
+  }
+};
+struct UdevEnumerateDeleter {
+  void operator()(udev_enumerate* enumerate) const {
+    udev_enumerate_unref(enumerate);
+  }
+};
+struct UdevDeviceDeleter {
+  void operator()(udev_device* device) const {
+    udev_device_unref(device);
+  }
+};
+struct UdevMonitorDeleter {
+  void operator()(udev_monitor* monitor) const {
+    udev_monitor_unref(monitor);
+  }
+};
+
+typedef scoped_ptr<udev, UdevDeleter> ScopedUdevPtr;
+typedef scoped_ptr<udev_enumerate, UdevEnumerateDeleter> ScopedUdevEnumeratePtr;
+typedef scoped_ptr<udev_device, UdevDeviceDeleter> ScopedUdevDevicePtr;
+typedef scoped_ptr<udev_monitor, UdevMonitorDeleter> ScopedUdevMonitorPtr;
+
+}  // namespace device
+
+#endif  // DEVICE_UDEV_LINUX_SCOPED_UDEV_H_
diff --git a/device/udev_linux/udev.cc b/device/udev_linux/udev.cc
new file mode 100644
index 0000000..e2b93d2
--- /dev/null
+++ b/device/udev_linux/udev.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/udev_linux/udev.h"
+
+#include "base/strings/string_util.h"
+#include "device/udev_linux/udev_loader.h"
+
+namespace device {
+
+namespace {
+
+std::string StringOrEmptyIfNull(const char* value) {
+  return value ? value : std::string();
+}
+
+}  // namespace
+
+const char* udev_device_get_action(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_action(udev_device);
+}
+
+const char* udev_device_get_devnode(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_devnode(udev_device);
+}
+
+udev_device* udev_device_get_parent(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_parent(udev_device);
+}
+
+udev_device* udev_device_get_parent_with_subsystem_devtype(
+    udev_device* udev_device,
+    const char* subsystem,
+    const char* devtype) {
+  return UdevLoader::Get()->udev_device_get_parent_with_subsystem_devtype(
+      udev_device, subsystem, devtype);
+}
+
+const char* udev_device_get_property_value(udev_device* udev_device,
+                                           const char* key) {
+  return UdevLoader::Get()->udev_device_get_property_value(udev_device, key);
+}
+
+const char* udev_device_get_subsystem(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_subsystem(udev_device);
+}
+
+const char* udev_device_get_sysattr_value(udev_device* udev_device,
+                                          const char* sysattr) {
+  return UdevLoader::Get()->udev_device_get_sysattr_value(udev_device, sysattr);
+}
+
+const char* udev_device_get_sysname(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_sysname(udev_device);
+}
+
+const char* udev_device_get_syspath(udev_device* udev_device) {
+  return UdevLoader::Get()->udev_device_get_syspath(udev_device);
+}
+
+udev_device* udev_device_new_from_devnum(udev* udev, char type, dev_t devnum) {
+  return UdevLoader::Get()->udev_device_new_from_devnum(udev, type, devnum);
+}
+
+udev_device* udev_device_new_from_subsystem_sysname(
+    udev* udev,
+    const char* subsystem,
+    const char* sysname) {
+  return UdevLoader::Get()->udev_device_new_from_subsystem_sysname(
+      udev, subsystem, sysname);
+}
+
+udev_device* udev_device_new_from_syspath(udev* udev, const char* syspath) {
+  return UdevLoader::Get()->udev_device_new_from_syspath(udev, syspath);
+}
+
+void udev_device_unref(udev_device* udev_device) {
+  UdevLoader::Get()->udev_device_unref(udev_device);
+}
+
+int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
+                                       const char* subsystem) {
+  return UdevLoader::Get()->udev_enumerate_add_match_subsystem(udev_enumerate,
+                                                               subsystem);
+}
+
+udev_list_entry* udev_enumerate_get_list_entry(udev_enumerate* udev_enumerate) {
+  return UdevLoader::Get()->udev_enumerate_get_list_entry(udev_enumerate);
+}
+
+udev_enumerate* udev_enumerate_new(udev* udev) {
+  return UdevLoader::Get()->udev_enumerate_new(udev);
+}
+
+int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) {
+  return UdevLoader::Get()->udev_enumerate_scan_devices(udev_enumerate);
+}
+
+void udev_enumerate_unref(udev_enumerate* udev_enumerate) {
+  UdevLoader::Get()->udev_enumerate_unref(udev_enumerate);
+}
+
+udev_list_entry* udev_list_entry_get_next(udev_list_entry* list_entry) {
+  return UdevLoader::Get()->udev_list_entry_get_next(list_entry);
+}
+
+const char* udev_list_entry_get_name(udev_list_entry* list_entry) {
+  return UdevLoader::Get()->udev_list_entry_get_name(list_entry);
+}
+
+int udev_monitor_enable_receiving(udev_monitor* udev_monitor) {
+  return UdevLoader::Get()->udev_monitor_enable_receiving(udev_monitor);
+}
+
+int udev_monitor_filter_add_match_subsystem_devtype(udev_monitor* udev_monitor,
+                                                    const char* subsystem,
+                                                    const char* devtype) {
+  return UdevLoader::Get()->udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor, subsystem, devtype);
+}
+
+int udev_monitor_get_fd(udev_monitor* udev_monitor) {
+  return UdevLoader::Get()->udev_monitor_get_fd(udev_monitor);
+}
+
+udev_monitor* udev_monitor_new_from_netlink(udev* udev, const char* name) {
+  return UdevLoader::Get()->udev_monitor_new_from_netlink(udev, name);
+}
+
+udev_device* udev_monitor_receive_device(udev_monitor* udev_monitor) {
+  return UdevLoader::Get()->udev_monitor_receive_device(udev_monitor);
+}
+
+void udev_monitor_unref(udev_monitor* udev_monitor) {
+  UdevLoader::Get()->udev_monitor_unref(udev_monitor);
+}
+
+udev* udev_new() {
+  return UdevLoader::Get()->udev_new();
+}
+
+void udev_set_log_fn(
+    struct udev* udev,
+    void (*log_fn)(struct udev* udev, int priority, const char* file, int line,
+                   const char* fn, const char* format, va_list args)) {
+  return UdevLoader::Get()->udev_set_log_fn(udev, log_fn);
+}
+
+void udev_set_log_priority(struct udev* udev, int priority) {
+  return UdevLoader::Get()->udev_set_log_priority(udev, priority);
+}
+
+void udev_unref(udev* udev) {
+  UdevLoader::Get()->udev_unref(udev);
+}
+
+std::string UdevDeviceGetPropertyValue(udev_device* udev_device,
+                                       const char* key) {
+  return StringOrEmptyIfNull(udev_device_get_property_value(udev_device, key));
+}
+
+std::string UdevDeviceGetSysattrValue(udev_device* udev_device,
+                                      const char* key) {
+  return StringOrEmptyIfNull(udev_device_get_sysattr_value(udev_device, key));
+}
+
+std::string UdevDecodeString(const std::string& encoded) {
+  std::string decoded;
+  const size_t size = encoded.size();
+  for (size_t i = 0; i < size; ++i) {
+    char c = encoded[i];
+    if ((i + 3 < size) && c == '\\' && encoded[i + 1] == 'x') {
+      c = (HexDigitToInt(encoded[i + 2]) << 4) +
+          HexDigitToInt(encoded[i + 3]);
+      i += 3;
+    }
+    decoded.push_back(c);
+  }
+  return decoded;
+}
+
+}  // namespace device
diff --git a/device/udev_linux/udev.h b/device/udev_linux/udev.h
new file mode 100644
index 0000000..c40603d
--- /dev/null
+++ b/device/udev_linux/udev.h
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_UDEV_H_
+#define DEVICE_UDEV_LINUX_UDEV_H_
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <string>
+
+#if !defined(USE_UDEV)
+#error "USE_UDEV not defined"
+#endif
+
+// Adapted from libudev.h.
+#define udev_list_entry_foreach(list_entry, first_entry) \
+  for (list_entry = first_entry; list_entry != NULL;     \
+       list_entry = ::device::udev_list_entry_get_next(list_entry))
+
+// Forward declarations of opaque structs.
+struct udev;
+struct udev_device;
+struct udev_enumerate;
+struct udev_list_entry;
+struct udev_monitor;
+
+namespace device {
+
+const char* udev_device_get_action(udev_device* udev_device);
+const char* udev_device_get_devnode(udev_device* udev_device);
+udev_device* udev_device_get_parent(udev_device* udev_device);
+udev_device* udev_device_get_parent_with_subsystem_devtype(
+    udev_device* udev_device,
+    const char* subsystem,
+    const char* devtype);
+const char* udev_device_get_property_value(udev_device* udev_device,
+                                           const char* key);
+const char* udev_device_get_subsystem(udev_device* udev_device);
+const char* udev_device_get_sysattr_value(udev_device* udev_device,
+                                          const char* sysattr);
+const char* udev_device_get_sysname(udev_device* udev_device);
+const char* udev_device_get_syspath(udev_device* udev_device);
+udev_device* udev_device_new_from_devnum(udev* udev, char type, dev_t devnum);
+udev_device* udev_device_new_from_subsystem_sysname(
+    udev* udev,
+    const char* subsystem,
+    const char* sysname);
+udev_device* udev_device_new_from_syspath(udev* udev, const char* syspath);
+void udev_device_unref(udev_device* udev_device);
+int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
+                                       const char* subsystem);
+udev_list_entry* udev_enumerate_get_list_entry(udev_enumerate* udev_enumerate);
+udev_enumerate* udev_enumerate_new(udev* udev);
+int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate);
+void udev_enumerate_unref(udev_enumerate* udev_enumerate);
+udev_list_entry* udev_list_entry_get_next(udev_list_entry* list_entry);
+const char* udev_list_entry_get_name(udev_list_entry* list_entry);
+int udev_monitor_enable_receiving(udev_monitor* udev_monitor);
+int udev_monitor_filter_add_match_subsystem_devtype(udev_monitor* udev_monitor,
+                                                    const char* subsystem,
+                                                    const char* devtype);
+int udev_monitor_get_fd(udev_monitor* udev_monitor);
+udev_monitor* udev_monitor_new_from_netlink(udev* udev, const char* name);
+udev_device* udev_monitor_receive_device(udev_monitor* udev_monitor);
+void udev_monitor_unref(udev_monitor* udev_monitor);
+udev* udev_new();
+void udev_set_log_fn(
+    struct udev* udev,
+    void (*log_fn)(struct udev* udev, int priority, const char* file, int line,
+                   const char* fn, const char* format, va_list args));
+void udev_set_log_priority(struct udev* udev, int priority);
+void udev_unref(udev* udev);
+
+// Calls udev_device_get_property_value() and replaces missing values with
+// the empty string.
+std::string UdevDeviceGetPropertyValue(udev_device* udev_device,
+                                       const char* key);
+
+// Calls udev_device_get_sysattr_value() and replaces missing values with
+// the empty string.
+std::string UdevDeviceGetSysattrValue(udev_device* udev_device,
+                                      const char* key);
+
+// Decodes udev-encoded string. Useful for decoding "*_ENC" udev properties.
+std::string UdevDecodeString(const std::string& encoded);
+
+}  // namespace device
+
+#endif  // DEVICE_UDEV_LINUX_UDEV_H_
diff --git a/device/udev_linux/udev0_loader.cc b/device/udev_linux/udev0_loader.cc
new file mode 100644
index 0000000..9b0276c
--- /dev/null
+++ b/device/udev_linux/udev0_loader.cc
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/udev_linux/udev0_loader.h"
+
+#include "library_loaders/libudev0.h"
+
+namespace device {
+
+Udev0Loader::Udev0Loader() {
+}
+
+Udev0Loader::~Udev0Loader() {
+}
+
+bool Udev0Loader::Init() {
+  if (lib_loader_)
+    return lib_loader_->loaded();
+  lib_loader_.reset(new LibUdev0Loader);
+  return lib_loader_->Load("libudev.so.0");
+}
+
+const char* Udev0Loader::udev_device_get_action(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_action(udev_device);
+}
+
+const char* Udev0Loader::udev_device_get_devnode(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_devnode(udev_device);
+}
+
+udev_device* Udev0Loader::udev_device_get_parent(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_parent(udev_device);
+}
+
+udev_device* Udev0Loader::udev_device_get_parent_with_subsystem_devtype(
+    udev_device* udev_device,
+    const char* subsystem,
+    const char* devtype) {
+  return lib_loader_->udev_device_get_parent_with_subsystem_devtype(
+      udev_device, subsystem, devtype);
+}
+
+const char* Udev0Loader::udev_device_get_property_value(
+    udev_device* udev_device,
+    const char* key) {
+  return lib_loader_->udev_device_get_property_value(udev_device, key);
+}
+
+const char* Udev0Loader::udev_device_get_subsystem(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_subsystem(udev_device);
+}
+
+const char* Udev0Loader::udev_device_get_sysattr_value(udev_device* udev_device,
+                                                       const char* sysattr) {
+  return lib_loader_->udev_device_get_sysattr_value(udev_device, sysattr);
+}
+
+const char* Udev0Loader::udev_device_get_sysname(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_sysname(udev_device);
+}
+
+const char* Udev0Loader::udev_device_get_syspath(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_syspath(udev_device);
+}
+
+udev_device* Udev0Loader::udev_device_new_from_devnum(udev* udev,
+                                                      char type,
+                                                      dev_t devnum) {
+  return lib_loader_->udev_device_new_from_devnum(udev, type, devnum);
+}
+
+udev_device* Udev0Loader::udev_device_new_from_subsystem_sysname(
+    udev* udev,
+    const char* subsystem,
+    const char* sysname) {
+  return lib_loader_->udev_device_new_from_subsystem_sysname(
+      udev, subsystem, sysname);
+}
+
+udev_device* Udev0Loader::udev_device_new_from_syspath(udev* udev,
+                                                       const char* syspath) {
+  return lib_loader_->udev_device_new_from_syspath(udev, syspath);
+}
+
+void Udev0Loader::udev_device_unref(udev_device* udev_device) {
+  lib_loader_->udev_device_unref(udev_device);
+}
+
+int Udev0Loader::udev_enumerate_add_match_subsystem(
+    udev_enumerate* udev_enumerate,
+    const char* subsystem) {
+  return lib_loader_->udev_enumerate_add_match_subsystem(udev_enumerate,
+                                                         subsystem);
+}
+
+udev_list_entry* Udev0Loader::udev_enumerate_get_list_entry(
+    udev_enumerate* udev_enumerate) {
+  return lib_loader_->udev_enumerate_get_list_entry(udev_enumerate);
+}
+
+udev_enumerate* Udev0Loader::udev_enumerate_new(udev* udev) {
+  return lib_loader_->udev_enumerate_new(udev);
+}
+
+int Udev0Loader::udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) {
+  return lib_loader_->udev_enumerate_scan_devices(udev_enumerate);
+}
+
+void Udev0Loader::udev_enumerate_unref(udev_enumerate* udev_enumerate) {
+  lib_loader_->udev_enumerate_unref(udev_enumerate);
+}
+
+udev_list_entry* Udev0Loader::udev_list_entry_get_next(
+    udev_list_entry* list_entry) {
+  return lib_loader_->udev_list_entry_get_next(list_entry);
+}
+
+const char* Udev0Loader::udev_list_entry_get_name(udev_list_entry* list_entry) {
+  return lib_loader_->udev_list_entry_get_name(list_entry);
+}
+
+int Udev0Loader::udev_monitor_enable_receiving(udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_enable_receiving(udev_monitor);
+}
+
+int Udev0Loader::udev_monitor_filter_add_match_subsystem_devtype(
+    udev_monitor* udev_monitor,
+    const char* subsystem,
+    const char* devtype) {
+  return lib_loader_->udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor, subsystem, devtype);
+}
+
+int Udev0Loader::udev_monitor_get_fd(udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_get_fd(udev_monitor);
+}
+
+udev_monitor* Udev0Loader::udev_monitor_new_from_netlink(udev* udev,
+                                                         const char* name) {
+  return lib_loader_->udev_monitor_new_from_netlink(udev, name);
+}
+
+udev_device* Udev0Loader::udev_monitor_receive_device(
+    udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_receive_device(udev_monitor);
+}
+
+void Udev0Loader::udev_monitor_unref(udev_monitor* udev_monitor) {
+  lib_loader_->udev_monitor_unref(udev_monitor);
+}
+
+udev* Udev0Loader::udev_new() {
+  return lib_loader_->udev_new();
+}
+
+void Udev0Loader::udev_set_log_fn(
+      struct udev* udev,
+      void (*log_fn)(struct udev* udev, int priority,
+                     const char* file, int line,
+                     const char* fn, const char* format, va_list args)) {
+  return lib_loader_->udev_set_log_fn(udev, log_fn);
+}
+
+void Udev0Loader::udev_set_log_priority(struct udev* udev, int priority) {
+  return lib_loader_->udev_set_log_priority(udev, priority);
+}
+
+void Udev0Loader::udev_unref(udev* udev) {
+  lib_loader_->udev_unref(udev);
+}
+
+}  // namespace device
diff --git a/device/udev_linux/udev0_loader.h b/device/udev_linux/udev0_loader.h
new file mode 100644
index 0000000..8108ff9
--- /dev/null
+++ b/device/udev_linux/udev0_loader.h
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_UDEV0_LOADER_H_
+#define DEVICE_UDEV_LINUX_UDEV0_LOADER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "device/udev_linux/udev_loader.h"
+
+class LibUdev0Loader;
+
+namespace device {
+
+class Udev0Loader : public UdevLoader {
+ public:
+  Udev0Loader();
+  ~Udev0Loader() override;
+
+ private:
+  bool Init() override;
+  const char* udev_device_get_action(udev_device* udev_device) override;
+  const char* udev_device_get_devnode(udev_device* udev_device) override;
+  udev_device* udev_device_get_parent(udev_device* udev_device) override;
+  udev_device* udev_device_get_parent_with_subsystem_devtype(
+      udev_device* udev_device,
+      const char* subsystem,
+      const char* devtype) override;
+  const char* udev_device_get_property_value(udev_device* udev_device,
+                                             const char* key) override;
+  const char* udev_device_get_subsystem(udev_device* udev_device) override;
+  const char* udev_device_get_sysattr_value(udev_device* udev_device,
+                                            const char* sysattr) override;
+  const char* udev_device_get_sysname(udev_device* udev_device) override;
+  const char* udev_device_get_syspath(udev_device* udev_device) override;
+  udev_device* udev_device_new_from_devnum(udev* udev,
+                                           char type,
+                                           dev_t devnum) override;
+  udev_device* udev_device_new_from_subsystem_sysname(
+      udev* udev,
+      const char* subsystem,
+      const char* sysname) override;
+  udev_device* udev_device_new_from_syspath(udev* udev,
+                                            const char* syspath) override;
+  void udev_device_unref(udev_device* udev_device) override;
+  int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
+                                         const char* subsystem) override;
+  udev_list_entry* udev_enumerate_get_list_entry(
+      udev_enumerate* udev_enumerate) override;
+  udev_enumerate* udev_enumerate_new(udev* udev) override;
+  int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) override;
+  void udev_enumerate_unref(udev_enumerate* udev_enumerate) override;
+  udev_list_entry* udev_list_entry_get_next(
+      udev_list_entry* list_entry) override;
+  const char* udev_list_entry_get_name(udev_list_entry* list_entry) override;
+  int udev_monitor_enable_receiving(udev_monitor* udev_monitor) override;
+  int udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor* udev_monitor,
+      const char* subsystem,
+      const char* devtype) override;
+  int udev_monitor_get_fd(udev_monitor* udev_monitor) override;
+  udev_monitor* udev_monitor_new_from_netlink(udev* udev,
+                                              const char* name) override;
+  udev_device* udev_monitor_receive_device(udev_monitor* udev_monitor) override;
+  void udev_monitor_unref(udev_monitor* udev_monitor) override;
+  udev* udev_new() override;
+  void udev_set_log_fn(
+      struct udev* udev,
+      void (*log_fn)(struct udev* udev, int priority,
+                     const char* file, int line,
+                     const char* fn, const char* format,
+                     va_list args)) override;
+  void udev_set_log_priority(struct udev* udev, int priority) override;
+  void udev_unref(udev* udev) override;
+
+  scoped_ptr<LibUdev0Loader> lib_loader_;
+
+  DISALLOW_COPY_AND_ASSIGN(Udev0Loader);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_UDEV_LINUX_UDEV0_LOADER_H_
diff --git a/device/udev_linux/udev1_loader.cc b/device/udev_linux/udev1_loader.cc
new file mode 100644
index 0000000..34c2848
--- /dev/null
+++ b/device/udev_linux/udev1_loader.cc
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/udev_linux/udev1_loader.h"
+
+#include "library_loaders/libudev1.h"
+
+namespace device {
+
+Udev1Loader::Udev1Loader() {
+}
+
+Udev1Loader::~Udev1Loader() {
+}
+
+bool Udev1Loader::Init() {
+  if (lib_loader_)
+    return lib_loader_->loaded();
+  lib_loader_.reset(new LibUdev1Loader);
+  return lib_loader_->Load("libudev.so.1");
+}
+
+const char* Udev1Loader::udev_device_get_action(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_action(udev_device);
+}
+
+const char* Udev1Loader::udev_device_get_devnode(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_devnode(udev_device);
+}
+
+udev_device* Udev1Loader::udev_device_get_parent(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_parent(udev_device);
+}
+
+udev_device* Udev1Loader::udev_device_get_parent_with_subsystem_devtype(
+    udev_device* udev_device,
+    const char* subsystem,
+    const char* devtype) {
+  return lib_loader_->udev_device_get_parent_with_subsystem_devtype(
+      udev_device, subsystem, devtype);
+}
+
+const char* Udev1Loader::udev_device_get_property_value(
+    udev_device* udev_device,
+    const char* key) {
+  return lib_loader_->udev_device_get_property_value(udev_device, key);
+}
+
+const char* Udev1Loader::udev_device_get_subsystem(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_subsystem(udev_device);
+}
+
+const char* Udev1Loader::udev_device_get_sysattr_value(udev_device* udev_device,
+                                                       const char* sysattr) {
+  return lib_loader_->udev_device_get_sysattr_value(udev_device, sysattr);
+}
+
+const char* Udev1Loader::udev_device_get_sysname(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_sysname(udev_device);
+}
+
+const char* Udev1Loader::udev_device_get_syspath(udev_device* udev_device) {
+  return lib_loader_->udev_device_get_syspath(udev_device);
+}
+
+udev_device* Udev1Loader::udev_device_new_from_devnum(udev* udev,
+                                                      char type,
+                                                      dev_t devnum) {
+  return lib_loader_->udev_device_new_from_devnum(udev, type, devnum);
+}
+
+udev_device* Udev1Loader::udev_device_new_from_subsystem_sysname(
+    udev* udev,
+    const char* subsystem,
+    const char* sysname) {
+  return lib_loader_->udev_device_new_from_subsystem_sysname(
+      udev, subsystem, sysname);
+}
+
+udev_device* Udev1Loader::udev_device_new_from_syspath(udev* udev,
+                                                       const char* syspath) {
+  return lib_loader_->udev_device_new_from_syspath(udev, syspath);
+}
+
+void Udev1Loader::udev_device_unref(udev_device* udev_device) {
+  lib_loader_->udev_device_unref(udev_device);
+}
+
+int Udev1Loader::udev_enumerate_add_match_subsystem(
+    udev_enumerate* udev_enumerate,
+    const char* subsystem) {
+  return lib_loader_->udev_enumerate_add_match_subsystem(udev_enumerate,
+                                                         subsystem);
+}
+
+udev_list_entry* Udev1Loader::udev_enumerate_get_list_entry(
+    udev_enumerate* udev_enumerate) {
+  return lib_loader_->udev_enumerate_get_list_entry(udev_enumerate);
+}
+
+udev_enumerate* Udev1Loader::udev_enumerate_new(udev* udev) {
+  return lib_loader_->udev_enumerate_new(udev);
+}
+
+int Udev1Loader::udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) {
+  return lib_loader_->udev_enumerate_scan_devices(udev_enumerate);
+}
+
+void Udev1Loader::udev_enumerate_unref(udev_enumerate* udev_enumerate) {
+  lib_loader_->udev_enumerate_unref(udev_enumerate);
+}
+
+udev_list_entry* Udev1Loader::udev_list_entry_get_next(
+    udev_list_entry* list_entry) {
+  return lib_loader_->udev_list_entry_get_next(list_entry);
+}
+
+const char* Udev1Loader::udev_list_entry_get_name(udev_list_entry* list_entry) {
+  return lib_loader_->udev_list_entry_get_name(list_entry);
+}
+
+int Udev1Loader::udev_monitor_enable_receiving(udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_enable_receiving(udev_monitor);
+}
+
+int Udev1Loader::udev_monitor_filter_add_match_subsystem_devtype(
+    udev_monitor* udev_monitor,
+    const char* subsystem,
+    const char* devtype) {
+  return lib_loader_->udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor, subsystem, devtype);
+}
+
+int Udev1Loader::udev_monitor_get_fd(udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_get_fd(udev_monitor);
+}
+
+udev_monitor* Udev1Loader::udev_monitor_new_from_netlink(udev* udev,
+                                                         const char* name) {
+  return lib_loader_->udev_monitor_new_from_netlink(udev, name);
+}
+
+udev_device* Udev1Loader::udev_monitor_receive_device(
+    udev_monitor* udev_monitor) {
+  return lib_loader_->udev_monitor_receive_device(udev_monitor);
+}
+
+void Udev1Loader::udev_monitor_unref(udev_monitor* udev_monitor) {
+  lib_loader_->udev_monitor_unref(udev_monitor);
+}
+
+udev* Udev1Loader::udev_new() {
+  return lib_loader_->udev_new();
+}
+
+void Udev1Loader::udev_set_log_fn(
+      struct udev* udev,
+      void (*log_fn)(struct udev* udev, int priority,
+                     const char* file, int line,
+                     const char* fn, const char* format, va_list args)) {
+  return lib_loader_->udev_set_log_fn(udev, log_fn);
+}
+
+void Udev1Loader::udev_set_log_priority(struct udev* udev, int priority) {
+  return lib_loader_->udev_set_log_priority(udev, priority);
+}
+
+void Udev1Loader::udev_unref(udev* udev) {
+  lib_loader_->udev_unref(udev);
+}
+
+}  // namespace device
diff --git a/device/udev_linux/udev1_loader.h b/device/udev_linux/udev1_loader.h
new file mode 100644
index 0000000..c765671
--- /dev/null
+++ b/device/udev_linux/udev1_loader.h
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_UDEV1_LOADER_H_
+#define DEVICE_UDEV_LINUX_UDEV1_LOADER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "device/udev_linux/udev_loader.h"
+
+class LibUdev1Loader;
+
+namespace device {
+
+class Udev1Loader : public UdevLoader {
+ public:
+  Udev1Loader();
+  ~Udev1Loader() override;
+
+ private:
+  bool Init() override;
+  const char* udev_device_get_action(udev_device* udev_device) override;
+  const char* udev_device_get_devnode(udev_device* udev_device) override;
+  udev_device* udev_device_get_parent(udev_device* udev_device) override;
+  udev_device* udev_device_get_parent_with_subsystem_devtype(
+      udev_device* udev_device,
+      const char* subsystem,
+      const char* devtype) override;
+  const char* udev_device_get_property_value(udev_device* udev_device,
+                                             const char* key) override;
+  const char* udev_device_get_subsystem(udev_device* udev_device) override;
+  const char* udev_device_get_sysattr_value(udev_device* udev_device,
+                                            const char* sysattr) override;
+  const char* udev_device_get_sysname(udev_device* udev_device) override;
+  const char* udev_device_get_syspath(udev_device* udev_device) override;
+  udev_device* udev_device_new_from_devnum(udev* udev,
+                                           char type,
+                                           dev_t devnum) override;
+  udev_device* udev_device_new_from_subsystem_sysname(
+      udev* udev,
+      const char* subsystem,
+      const char* sysname) override;
+  udev_device* udev_device_new_from_syspath(udev* udev,
+                                            const char* syspath) override;
+  void udev_device_unref(udev_device* udev_device) override;
+  int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
+                                         const char* subsystem) override;
+  udev_list_entry* udev_enumerate_get_list_entry(
+      udev_enumerate* udev_enumerate) override;
+  udev_enumerate* udev_enumerate_new(udev* udev) override;
+  int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) override;
+  void udev_enumerate_unref(udev_enumerate* udev_enumerate) override;
+  udev_list_entry* udev_list_entry_get_next(
+      udev_list_entry* list_entry) override;
+  const char* udev_list_entry_get_name(udev_list_entry* list_entry) override;
+  int udev_monitor_enable_receiving(udev_monitor* udev_monitor) override;
+  int udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor* udev_monitor,
+      const char* subsystem,
+      const char* devtype) override;
+  int udev_monitor_get_fd(udev_monitor* udev_monitor) override;
+  udev_monitor* udev_monitor_new_from_netlink(udev* udev,
+                                              const char* name) override;
+  udev_device* udev_monitor_receive_device(udev_monitor* udev_monitor) override;
+  void udev_monitor_unref(udev_monitor* udev_monitor) override;
+  udev* udev_new() override;
+  void udev_set_log_fn(
+      struct udev* udev,
+      void (*log_fn)(struct udev* udev, int priority,
+                     const char* file, int line,
+                     const char* fn, const char* format,
+                     va_list args)) override;
+  void udev_set_log_priority(struct udev* udev, int priority) override;
+  void udev_unref(udev* udev) override;
+
+  scoped_ptr<LibUdev1Loader> lib_loader_;
+
+  DISALLOW_COPY_AND_ASSIGN(Udev1Loader);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_UDEV_LINUX_UDEV1_LOADER_H_
diff --git a/device/udev_linux/udev_loader.cc b/device/udev_linux/udev_loader.cc
new file mode 100644
index 0000000..88b64e1
--- /dev/null
+++ b/device/udev_linux/udev_loader.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/udev_linux/udev_loader.h"
+
+#include "base/logging.h"
+#include "device/udev_linux/udev0_loader.h"
+#include "device/udev_linux/udev1_loader.h"
+
+namespace device {
+
+namespace {
+
+UdevLoader* g_udev_loader = NULL;
+
+}  // namespace
+
+// static
+UdevLoader* UdevLoader::Get() {
+  if (g_udev_loader)
+    return g_udev_loader;
+
+  scoped_ptr<UdevLoader> udev_loader;
+  udev_loader.reset(new Udev1Loader);
+  if (udev_loader->Init()) {
+    g_udev_loader = udev_loader.release();
+    return g_udev_loader;
+  }
+
+  udev_loader.reset(new Udev0Loader);
+  if (udev_loader->Init()) {
+    g_udev_loader = udev_loader.release();
+    return g_udev_loader;
+  }
+  CHECK(false);
+  return NULL;
+}
+
+UdevLoader::~UdevLoader() {
+}
+
+}  // namespace device
diff --git a/device/udev_linux/udev_loader.h b/device/udev_linux/udev_loader.h
new file mode 100644
index 0000000..9d3837d
--- /dev/null
+++ b/device/udev_linux/udev_loader.h
@@ -0,0 +1,95 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_UDEV_LOADER_H_
+#define DEVICE_UDEV_LINUX_UDEV_LOADER_H_
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if !defined(USE_UDEV)
+#error "USE_UDEV not defined"
+#endif
+
+struct udev;
+struct udev_device;
+struct udev_enumerate;
+struct udev_list_entry;
+struct udev_monitor;
+
+namespace device {
+
+// Interface to libudev. Accessed through the static Get() function, which
+// will try to load libudev1 first and then libudev0 on first use. If neither
+// libraries load successfully, the program will fail with a crash.
+//
+// All the methods have the same signatures as libudev's functions. e.g.
+// udev_monitor_get_fd(mon) simply becomes device::udev_monitor_get_fd(mon).
+class UdevLoader {
+ public:
+  static UdevLoader* Get();
+
+  virtual ~UdevLoader();
+
+  virtual bool Init() = 0;
+
+  virtual const char* udev_device_get_action(udev_device* udev_device) = 0;
+  virtual const char* udev_device_get_devnode(udev_device* udev_device) = 0;
+  virtual udev_device* udev_device_get_parent(udev_device* udev_device) = 0;
+  virtual udev_device* udev_device_get_parent_with_subsystem_devtype(
+      udev_device* udev_device,
+      const char* subsystem,
+      const char* devtype) = 0;
+  virtual const char* udev_device_get_property_value(udev_device* udev_device,
+                                                     const char* key) = 0;
+  virtual const char* udev_device_get_subsystem(udev_device* udev_device) = 0;
+  virtual const char* udev_device_get_sysattr_value(udev_device* udev_device,
+                                                    const char* sysattr) = 0;
+  virtual const char* udev_device_get_sysname(udev_device* udev_device) = 0;
+  virtual const char* udev_device_get_syspath(udev_device* udev_device) = 0;
+  virtual udev_device* udev_device_new_from_devnum(udev* udev,
+                                                   char type,
+                                                   dev_t devnum) = 0;
+  virtual udev_device* udev_device_new_from_subsystem_sysname(
+      udev* udev,
+      const char* subsystem,
+      const char* sysname) = 0;
+  virtual udev_device* udev_device_new_from_syspath(udev* udev,
+                                                    const char* syspath) = 0;
+  virtual void udev_device_unref(udev_device* udev_device) = 0;
+  virtual int udev_enumerate_add_match_subsystem(udev_enumerate* udev_enumerate,
+                                                 const char* subsystem) = 0;
+  virtual udev_list_entry* udev_enumerate_get_list_entry(
+      udev_enumerate* udev_enumerate) = 0;
+  virtual udev_enumerate* udev_enumerate_new(udev* udev) = 0;
+  virtual int udev_enumerate_scan_devices(udev_enumerate* udev_enumerate) = 0;
+  virtual void udev_enumerate_unref(udev_enumerate* udev_enumerate) = 0;
+  virtual udev_list_entry* udev_list_entry_get_next(
+      udev_list_entry* list_entry) = 0;
+  virtual const char* udev_list_entry_get_name(udev_list_entry* list_entry) = 0;
+  virtual int udev_monitor_enable_receiving(udev_monitor* udev_monitor) = 0;
+  virtual int udev_monitor_filter_add_match_subsystem_devtype(
+      udev_monitor* udev_monitor,
+      const char* subsystem,
+      const char* devtype) = 0;
+  virtual int udev_monitor_get_fd(udev_monitor* udev_monitor) = 0;
+  virtual udev_monitor* udev_monitor_new_from_netlink(udev* udev,
+                                                      const char* name) = 0;
+  virtual udev_device* udev_monitor_receive_device(
+      udev_monitor* udev_monitor) = 0;
+  virtual void udev_monitor_unref(udev_monitor* udev_monitor) = 0;
+  virtual udev* udev_new() = 0;
+  virtual void udev_set_log_fn(
+      struct udev* udev,
+      void (*log_fn)(struct udev* udev, int priority,
+                     const char* file, int line,
+                     const char* fn, const char* format, va_list args)) = 0;
+  virtual void udev_set_log_priority(struct udev* udev, int priority) = 0;
+  virtual void udev_unref(udev* udev) = 0;
+};
+
+}  // namespace device
+
+#endif  // DEVICE_UDEV_LINUX_UDEV_LOADER_H_
diff --git a/device/udev_linux/udev_unittest.cc b/device/udev_linux/udev_unittest.cc
new file mode 100644
index 0000000..244cd8e
--- /dev/null
+++ b/device/udev_linux/udev_unittest.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/udev_linux/udev.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(UdevTest, DecodeString) {
+  ASSERT_EQ("", UdevDecodeString(""));
+  ASSERT_EQ("\\", UdevDecodeString("\\x5c"));
+  ASSERT_EQ("\\x5", UdevDecodeString("\\x5"));
+  ASSERT_EQ("049f", UdevDecodeString("049f"));
+  ASSERT_EQ(
+      "HD Pro Webcam C920", UdevDecodeString("HD\\x20Pro\\x20Webcam\\x20C920"));
+  ASSERT_EQ("E-MU Systems,Inc.", UdevDecodeString("E-MU\\x20Systems\\x2cInc."));
+}
+
+}  // namespace device
diff --git a/gpu/command_buffer/service/gl_surface_mock.h b/gpu/command_buffer/service/gl_surface_mock.h
index 0652be6..97c8a09 100644
--- a/gpu/command_buffer/service/gl_surface_mock.h
+++ b/gpu/command_buffer/service/gl_surface_mock.h
@@ -5,8 +5,9 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GL_SURFACE_MOCK_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GL_SURFACE_MOCK_H_
 
-#include "ui/gl/gl_surface.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/gl/gl_surface.h"
 
 namespace gpu {
 
@@ -18,8 +19,8 @@
   MOCK_METHOD0(Destroy, void());
   MOCK_METHOD1(Resize, bool(const gfx::Size& size));
   MOCK_METHOD0(IsOffscreen, bool());
-  MOCK_METHOD0(SwapBuffers, bool());
-  MOCK_METHOD4(PostSubBuffer, bool(int x, int y, int width, int height));
+  MOCK_METHOD0(SwapBuffers, gfx::SwapResult());
+  MOCK_METHOD4(PostSubBuffer, gfx::SwapResult(int x, int y, int width, int height));
   MOCK_METHOD0(SupportsPostSubBuffer, bool());
   MOCK_METHOD0(GetSize, gfx::Size());
   MOCK_METHOD0(GetHandle, void*());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index f782d52..61ad037 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -8222,7 +8222,8 @@
     gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer(
         is_offscreen ? offscreen_size_ : surface_->GetSize());
   }
-  if (surface_->PostSubBuffer(c.x, c.y, c.width, c.height)) {
+  if (surface_->PostSubBuffer(c.x, c.y, c.width, c.height) !=
+      gfx::SwapResult::SWAP_FAILED) {
     return error::kNoError;
   } else {
     LOG(ERROR) << "Context lost because PostSubBuffer failed.";
@@ -10347,7 +10348,7 @@
         glFlush();
     }
   } else {
-    if (!surface_->SwapBuffers()) {
+    if (surface_->SwapBuffers() == gfx::SwapResult::SWAP_FAILED) {
       LOG(ERROR) << "Context lost because SwapBuffers failed.";
       if (!CheckResetStatus()) {
         MarkContextLost(error::kUnknown);
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 5556860..bbfa7ea 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -50,6 +50,13 @@
       "//mojo/nacl:mojo_nacl_tests",
     ]
   }
+
+  if (use_ozone) {
+    deps += [
+      "//ui/ozone",
+      "//ui/ozone/demo",
+    ]
+  }
 }
 
 group("tests") {
diff --git a/services/native_viewport/BUILD.gn b/services/native_viewport/BUILD.gn
index e3147d1..b07fc20 100644
--- a/services/native_viewport/BUILD.gn
+++ b/services/native_viewport/BUILD.gn
@@ -119,6 +119,11 @@
     sources -= [ "platform_viewport_x11.cc" ]
   }
 
+  if (use_ozone) {
+    sources += [ "platform_viewport_ozone.cc" ]
+    deps += [ "//ui/ozone" ]
+  }
+
   if (is_win) {
     deps += [ "//ui/platform_window/win" ]
   }
diff --git a/services/native_viewport/platform_viewport_ozone.cc b/services/native_viewport/platform_viewport_ozone.cc
new file mode 100644
index 0000000..69cba34
--- /dev/null
+++ b/services/native_viewport/platform_viewport_ozone.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/native_viewport/platform_viewport.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/converters/input_events/mojo_extended_key_event_data.h"
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace native_viewport {
+namespace {
+
+float ConvertUIWheelValueToMojoValue(int offset) {
+  // Mojo's event type takes a value between -1 and 1. Normalize by allowing
+  // up to 20 of ui's offset. This is a bit arbitrary.
+  return std::max(
+      -1.0f, std::min(1.0f, static_cast<float>(offset) /
+                                (20 * static_cast<float>(
+                                          ui::MouseWheelEvent::kWheelDelta))));
+}
+}  // namespace
+
+// TODO(spang): Deduplicate with PlatformViewportX11.. but there's a hack
+// in there that prevents this.
+class PlatformViewportOzone : public PlatformViewport,
+                              public ui::PlatformWindowDelegate {
+ public:
+  explicit PlatformViewportOzone(Delegate* delegate) : delegate_(delegate) {
+    ui::OzonePlatform::InitializeForUI();
+  }
+
+  ~PlatformViewportOzone() override {
+    // Destroy the platform-window while |this| is still alive.
+    platform_window_.reset();
+  }
+
+ private:
+  // Overridden from PlatformViewport:
+  void Init(const gfx::Rect& bounds) override {
+    platform_window_ =
+        ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
+
+    metrics_ = mojo::ViewportMetrics::New();
+    metrics_->size = mojo::Size::From(bounds.size());
+  }
+
+  void Show() override { platform_window_->Show(); }
+
+  void Hide() override { platform_window_->Hide(); }
+
+  void Close() override { platform_window_->Close(); }
+
+  gfx::Size GetSize() override {
+    return platform_window_->GetBounds().size();
+  }
+
+  void SetBounds(const gfx::Rect& bounds) override {
+    platform_window_->SetBounds(bounds);
+  }
+
+  // ui::PlatformWindowDelegate:
+  void OnBoundsChanged(const gfx::Rect& new_bounds) override {
+    metrics_->size = mojo::Size::From(new_bounds.size());
+    delegate_->OnMetricsChanged(metrics_.Clone());
+  }
+
+  void OnDamageRect(const gfx::Rect& damaged_region) override {}
+
+  void DispatchEvent(ui::Event* event) override {
+    mojo::EventPtr mojo_event(mojo::Event::From(*event));
+    if (event->IsMouseWheelEvent()) {
+      // Mojo's event type has a different meaning for wheel events. Convert
+      // between the two.
+      ui::MouseWheelEvent* wheel_event =
+          static_cast<ui::MouseWheelEvent*>(event);
+      DCHECK(mojo_event->pointer_data);
+      mojo_event->pointer_data->horizontal_wheel =
+          ConvertUIWheelValueToMojoValue(wheel_event->x_offset());
+      mojo_event->pointer_data->horizontal_wheel =
+          ConvertUIWheelValueToMojoValue(wheel_event->y_offset());
+    }
+    delegate_->OnEvent(mojo_event.Pass());
+
+    switch (event->type()) {
+      case ui::ET_MOUSE_PRESSED:
+      case ui::ET_TOUCH_PRESSED:
+        platform_window_->SetCapture();
+        break;
+      case ui::ET_MOUSE_RELEASED:
+      case ui::ET_TOUCH_RELEASED:
+        platform_window_->ReleaseCapture();
+        break;
+      default:
+        break;
+    }
+  }
+
+  void OnCloseRequest() override { platform_window_->Close(); }
+
+  void OnClosed() override { delegate_->OnDestroyed(); }
+
+  void OnWindowStateChanged(ui::PlatformWindowState state) override {}
+
+  void OnLostCapture() override {}
+
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+    delegate_->OnAcceleratedWidgetAvailable(widget);
+  }
+
+  void OnActivationChanged(bool active) override {}
+
+  scoped_ptr<ui::PlatformWindow> platform_window_;
+  Delegate* delegate_;
+  mojo::ViewportMetricsPtr metrics_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformViewportOzone);
+};
+
+// static
+scoped_ptr<PlatformViewport> PlatformViewport::Create(
+    mojo::ApplicationImpl* application_,
+    Delegate* delegate) {
+  return scoped_ptr<PlatformViewport>(
+      new PlatformViewportOzone(delegate)).Pass();
+}
+
+}  // namespace native_viewport
diff --git a/third_party/libudev/LICENSE b/third_party/libudev/LICENSE
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/third_party/libudev/LICENSE
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/third_party/libudev/OWNERS b/third_party/libudev/OWNERS
new file mode 100644
index 0000000..3a04629
--- /dev/null
+++ b/third_party/libudev/OWNERS
@@ -0,0 +1,2 @@
+reillyg@chromium.org
+thestig@chromium.org
diff --git a/third_party/libudev/README.chromium b/third_party/libudev/README.chromium
new file mode 100644
index 0000000..aede7ab
--- /dev/null
+++ b/third_party/libudev/README.chromium
@@ -0,0 +1,8 @@
+Name: libudev
+URL: http://www.freedesktop.org/software/systemd/libudev/
+Version: Varies
+Security Critical: no
+License: LGPL 2.1
+
+Description:
+Just the headers from libudev0 and libudev1.
diff --git a/third_party/libudev/libudev0.h b/third_party/libudev/libudev0.h
new file mode 100644
index 0000000..10e098d
--- /dev/null
+++ b/third_party/libudev/libudev0.h
@@ -0,0 +1,189 @@
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LIBUDEV_H_
+#define _LIBUDEV_H_
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * udev - library context
+ *
+ * reads the udev config and system environment
+ * allows custom logging
+ */
+struct udev;
+struct udev *udev_ref(struct udev *udev);
+void udev_unref(struct udev *udev);
+struct udev *udev_new(void);
+void udev_set_log_fn(struct udev *udev,
+                            void (*log_fn)(struct udev *udev,
+                                           int priority, const char *file, int line, const char *fn,
+                                           const char *format, va_list args));
+int udev_get_log_priority(struct udev *udev);
+void udev_set_log_priority(struct udev *udev, int priority);
+const char *udev_get_sys_path(struct udev *udev);
+const char *udev_get_dev_path(struct udev *udev);
+const char *udev_get_run_path(struct udev *udev);
+void *udev_get_userdata(struct udev *udev);
+void udev_set_userdata(struct udev *udev, void *userdata);
+
+/*
+ * udev_list
+ *
+ * access to libudev generated lists
+ */
+struct udev_list_entry;
+struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry);
+struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
+const char *udev_list_entry_get_name(struct udev_list_entry *list_entry);
+const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
+/**
+ * udev_list_entry_foreach:
+ * @list_entry: entry to store the current position
+ * @first_entry: first entry to start with
+ *
+ * Helper to iterate over all entries of a list.
+ */
+#define udev_list_entry_foreach(list_entry, first_entry) \
+        for (list_entry = first_entry; \
+             list_entry != NULL; \
+             list_entry = udev_list_entry_get_next(list_entry))
+
+/*
+ * udev_device
+ *
+ * access to sysfs/kernel devices
+ */
+struct udev_device;
+struct udev_device *udev_device_ref(struct udev_device *udev_device);
+void udev_device_unref(struct udev_device *udev_device);
+struct udev *udev_device_get_udev(struct udev_device *udev_device);
+struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath);
+struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
+struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname);
+struct udev_device *udev_device_new_from_environment(struct udev *udev);
+/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */
+struct udev_device *udev_device_get_parent(struct udev_device *udev_device);
+struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device,
+                                                                  const char *subsystem, const char *devtype);
+/* retrieve device properties */
+const char *udev_device_get_devpath(struct udev_device *udev_device);
+const char *udev_device_get_subsystem(struct udev_device *udev_device);
+const char *udev_device_get_devtype(struct udev_device *udev_device);
+const char *udev_device_get_syspath(struct udev_device *udev_device);
+const char *udev_device_get_sysname(struct udev_device *udev_device);
+const char *udev_device_get_sysnum(struct udev_device *udev_device);
+const char *udev_device_get_devnode(struct udev_device *udev_device);
+int udev_device_get_is_initialized(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
+const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key);
+const char *udev_device_get_driver(struct udev_device *udev_device);
+dev_t udev_device_get_devnum(struct udev_device *udev_device);
+const char *udev_device_get_action(struct udev_device *udev_device);
+unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
+unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
+const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
+int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
+
+/*
+ * udev_monitor
+ *
+ * access to kernel uevents and udev events
+ */
+struct udev_monitor;
+struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
+void udev_monitor_unref(struct udev_monitor *udev_monitor);
+struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
+/* kernel and udev generated events over netlink */
+struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
+/* custom socket (use netlink and filters instead) */
+struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path);
+/* bind socket */
+int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
+int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
+int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
+/* in-kernel socket filters to select messages that get delivered to a listener */
+int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
+                                                    const char *subsystem, const char *devtype);
+int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
+int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
+int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
+
+/*
+ * udev_enumerate
+ *
+ * search sysfs for specific devices and provide a sorted list
+ */
+struct udev_enumerate;
+struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
+void udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
+struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate);
+struct udev_enumerate *udev_enumerate_new(struct udev *udev);
+/* device properties filter */
+int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
+int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
+int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
+int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent);
+int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
+int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
+/* run enumeration with active filters */
+int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
+int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
+/* return device list */
+struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
+
+/*
+ * udev_queue
+ *
+ * access to the currently running udev events
+ */
+struct udev_queue;
+struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue);
+void udev_queue_unref(struct udev_queue *udev_queue);
+struct udev *udev_queue_get_udev(struct udev_queue *udev_queue);
+struct udev_queue *udev_queue_new(struct udev *udev);
+unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue);
+unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue);
+int udev_queue_get_udev_is_active(struct udev_queue *udev_queue);
+int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue);
+int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum);
+int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
+                                               unsigned long long int start, unsigned long long int end);
+struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue);
+
+/*
+ * udev_util
+ *
+ * udev specific utilities
+ */
+int udev_util_encode_string(const char *str, char *str_enc, size_t len);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/third_party/libudev/libudev1.h b/third_party/libudev/libudev1.h
new file mode 100644
index 0000000..799f470
--- /dev/null
+++ b/third_party/libudev/libudev1.h
@@ -0,0 +1,185 @@
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LIBUDEV_H_
+#define _LIBUDEV_H_
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * udev - library context
+ *
+ * reads the udev config and system environment
+ * allows custom logging
+ */
+struct udev;
+struct udev *udev_ref(struct udev *udev);
+struct udev *udev_unref(struct udev *udev);
+struct udev *udev_new(void);
+void udev_set_log_fn(struct udev *udev,
+                            void (*log_fn)(struct udev *udev,
+                                           int priority, const char *file, int line, const char *fn,
+                                           const char *format, va_list args));
+int udev_get_log_priority(struct udev *udev);
+void udev_set_log_priority(struct udev *udev, int priority);
+void *udev_get_userdata(struct udev *udev);
+void udev_set_userdata(struct udev *udev, void *userdata);
+
+/*
+ * udev_list
+ *
+ * access to libudev generated lists
+ */
+struct udev_list_entry;
+struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry);
+struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
+const char *udev_list_entry_get_name(struct udev_list_entry *list_entry);
+const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
+/**
+ * udev_list_entry_foreach:
+ * @list_entry: entry to store the current position
+ * @first_entry: first entry to start with
+ *
+ * Helper to iterate over all entries of a list.
+ */
+#define udev_list_entry_foreach(list_entry, first_entry) \
+        for (list_entry = first_entry; \
+             list_entry != NULL; \
+             list_entry = udev_list_entry_get_next(list_entry))
+
+/*
+ * udev_device
+ *
+ * access to sysfs/kernel devices
+ */
+struct udev_device;
+struct udev_device *udev_device_ref(struct udev_device *udev_device);
+struct udev_device *udev_device_unref(struct udev_device *udev_device);
+struct udev *udev_device_get_udev(struct udev_device *udev_device);
+struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath);
+struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
+struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname);
+struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id);
+struct udev_device *udev_device_new_from_environment(struct udev *udev);
+/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */
+struct udev_device *udev_device_get_parent(struct udev_device *udev_device);
+struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device,
+                                                                  const char *subsystem, const char *devtype);
+/* retrieve device properties */
+const char *udev_device_get_devpath(struct udev_device *udev_device);
+const char *udev_device_get_subsystem(struct udev_device *udev_device);
+const char *udev_device_get_devtype(struct udev_device *udev_device);
+const char *udev_device_get_syspath(struct udev_device *udev_device);
+const char *udev_device_get_sysname(struct udev_device *udev_device);
+const char *udev_device_get_sysnum(struct udev_device *udev_device);
+const char *udev_device_get_devnode(struct udev_device *udev_device);
+int udev_device_get_is_initialized(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
+const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key);
+const char *udev_device_get_driver(struct udev_device *udev_device);
+dev_t udev_device_get_devnum(struct udev_device *udev_device);
+const char *udev_device_get_action(struct udev_device *udev_device);
+unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
+unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
+const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
+int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
+
+/*
+ * udev_monitor
+ *
+ * access to kernel uevents and udev events
+ */
+struct udev_monitor;
+struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
+struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor);
+struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
+/* kernel and udev generated events over netlink */
+struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
+/* bind socket */
+int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
+int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
+int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
+/* in-kernel socket filters to select messages that get delivered to a listener */
+int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
+                                                    const char *subsystem, const char *devtype);
+int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
+int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
+int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
+
+/*
+ * udev_enumerate
+ *
+ * search sysfs for specific devices and provide a sorted list
+ */
+struct udev_enumerate;
+struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
+struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
+struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate);
+struct udev_enumerate *udev_enumerate_new(struct udev *udev);
+/* device properties filter */
+int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
+int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
+int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
+int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent);
+int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
+int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
+/* run enumeration with active filters */
+int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
+int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
+/* return device list */
+struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
+
+/*
+ * udev_queue
+ *
+ * access to the currently running udev events
+ */
+struct udev_queue;
+struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue);
+struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue);
+struct udev *udev_queue_get_udev(struct udev_queue *udev_queue);
+struct udev_queue *udev_queue_new(struct udev *udev);
+unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue);
+unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue);
+int udev_queue_get_udev_is_active(struct udev_queue *udev_queue);
+int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue);
+int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum);
+int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
+                                               unsigned long long int start, unsigned long long int end);
+struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue);
+
+/*
+ * udev_util
+ *
+ * udev specific utilities
+ */
+int udev_util_encode_string(const char *str, char *str_enc, size_t len);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
deleted file mode 100644
index df0f49d..0000000
--- a/ui/display/BUILD.gn
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/ui.gni")
-
-component("display") {
-  sources = [
-    "chromeos/display_configurator.cc",
-    "chromeos/display_configurator.h",
-    "chromeos/x11/display_configurator_x11.cc",
-    "chromeos/x11/display_mode_x11.cc",
-    "chromeos/x11/display_mode_x11.h",
-    "chromeos/x11/display_snapshot_x11.cc",
-    "chromeos/x11/display_snapshot_x11.h",
-    "chromeos/x11/display_util_x11.cc",
-    "chromeos/x11/display_util_x11.h",
-    "chromeos/x11/native_display_delegate_x11.cc",
-    "chromeos/x11/native_display_delegate_x11.h",
-    "chromeos/x11/native_display_event_dispatcher_x11.cc",
-    "chromeos/x11/native_display_event_dispatcher_x11.h",
-    "display_export.h",
-    "display_switches.cc",
-    "display_switches.h",
-  ]
-
-  defines = [ "DISPLAY_IMPLEMENTATION" ]
-
-  deps = [
-    "//base",
-    "//ui/display/util",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-  ]
-
-  if (use_x11) {
-    configs += [
-      "//build/config/linux:x11",
-      "//build/config/linux:xext",
-      "//build/config/linux:xi",
-      "//build/config/linux:xrandr",
-    ]
-    deps += [ "//ui/events/platform" ]
-  }
-}
-
-component("test_util") {
-  output_name = "display_test_util"
-  sources = [
-    "chromeos/test/test_display_snapshot.cc",
-    "chromeos/test/test_display_snapshot.h",
-  ]
-
-  defines = [ "DISPLAY_IMPLEMENTATION" ]
-
-  public_deps = [
-    ":display",
-  ]
-  deps = [
-    "//base",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-  ]
-}
-
-test("display_unittests") {
-  sources = [
-    "chromeos/display_configurator_unittest.cc",
-    "chromeos/x11/display_util_x11_unittest.cc",
-    "chromeos/x11/native_display_event_dispatcher_x11_unittest.cc",
-    "util/display_util_unittest.cc",
-    "util/edid_parser_unittest.cc",
-  ]
-
-  deps = [
-    ":test_util",
-    "//base",
-    "//base/test:run_all_unittests",
-    "//testing/gtest",
-    "//ui/display/util",
-    "//ui/gfx/geometry",
-  ]
-}
diff --git a/ui/display/types/BUILD.gn b/ui/display/types/BUILD.gn
index 4613119..6b5d1e3 100644
--- a/ui/display/types/BUILD.gn
+++ b/ui/display/types/BUILD.gn
@@ -11,6 +11,7 @@
     "display_snapshot.cc",
     "display_snapshot.h",
     "display_types_export.h",
+    "gamma_ramp_rgb_entry.h",
     "native_display_delegate.h",
     "native_display_observer.h",
   ]
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc
index efe401b..f19961d 100644
--- a/ui/display/types/display_snapshot.cc
+++ b/ui/display/types/display_snapshot.cc
@@ -7,7 +7,6 @@
 namespace ui {
 
 DisplaySnapshot::DisplaySnapshot(int64_t display_id,
-                                 bool has_proper_display_id,
                                  const gfx::Point& origin,
                                  const gfx::Size& physical_size,
                                  DisplayConnectionType type,
@@ -18,7 +17,6 @@
                                  const DisplayMode* current_mode,
                                  const DisplayMode* native_mode)
     : display_id_(display_id),
-      has_proper_display_id_(has_proper_display_id),
       origin_(origin),
       physical_size_(physical_size),
       type_(type),
@@ -27,7 +25,9 @@
       display_name_(display_name),
       modes_(modes),
       current_mode_(current_mode),
-      native_mode_(native_mode) {}
+      native_mode_(native_mode),
+      product_id_(kInvalidProductID) {
+}
 
 DisplaySnapshot::~DisplaySnapshot() {}
 
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h
index 7019208..c59ecde 100644
--- a/ui/display/types/display_snapshot.h
+++ b/ui/display/types/display_snapshot.h
@@ -20,7 +20,6 @@
 class DISPLAY_TYPES_EXPORT DisplaySnapshot {
  public:
   DisplaySnapshot(int64_t display_id,
-                  bool has_proper_display_id,
                   const gfx::Point& origin,
                   const gfx::Size& physical_size,
                   DisplayConnectionType type,
@@ -42,10 +41,10 @@
   std::string display_name() const { return display_name_; }
 
   int64_t display_id() const { return display_id_; }
-  bool has_proper_display_id() const { return has_proper_display_id_; }
 
   const DisplayMode* current_mode() const { return current_mode_; }
   const DisplayMode* native_mode() const { return native_mode_; }
+  int64_t product_id() const { return product_id_; }
 
   const std::vector<const DisplayMode*>& modes() const { return modes_; }
 
@@ -56,10 +55,12 @@
   // Returns a textual representation of this display state.
   virtual std::string ToString() const = 0;
 
+  // Used when no product id known.
+  static const int64_t kInvalidProductID = -1;
+
  protected:
   // Display id for this output.
   int64_t display_id_;
-  bool has_proper_display_id_;
 
   // Display's origin on the framebuffer.
   gfx::Point origin_;
@@ -82,6 +83,9 @@
   // "Best" mode supported by the output.
   const DisplayMode* native_mode_;
 
+  // Combination of manufacturer and product code.
+  int64_t product_id_;
+
   DISALLOW_COPY_AND_ASSIGN(DisplaySnapshot);
 };
 
diff --git a/ui/display/types/gamma_ramp_rgb_entry.h b/ui/display/types/gamma_ramp_rgb_entry.h
new file mode 100644
index 0000000..70405e1
--- /dev/null
+++ b/ui/display/types/gamma_ramp_rgb_entry.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_DISPLAY_TYPES_GAMMA_RAMP_RGB_ENTRY_H_
+#define UI_DISPLAY_TYPES_GAMMA_RAMP_RGB_ENTRY_H_
+
+#include <stdint.h>
+
+#include "ui/display/types/display_types_export.h"
+
+namespace ui {
+
+// Provides a single entry for a gamma correction table in a GPU.
+struct DISPLAY_TYPES_EXPORT GammaRampRGBEntry {
+  uint16_t r;
+  uint16_t g;
+  uint16_t b;
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_TYPES_GAMMA_RAMP_RGB_ENTRY_H_
diff --git a/ui/display/types/native_display_delegate.h b/ui/display/types/native_display_delegate.h
index 28c70e0..bf59dca 100644
--- a/ui/display/types/native_display_delegate.h
+++ b/ui/display/types/native_display_delegate.h
@@ -9,6 +9,7 @@
 
 #include <vector>
 
+#include "base/callback.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_types_export.h"
 
@@ -23,8 +24,21 @@
 
 class NativeDisplayObserver;
 
+struct GammaRampRGBEntry;
+
+typedef base::Callback<void(const std::vector<ui::DisplaySnapshot*>&)>
+    GetDisplaysCallback;
+typedef base::Callback<void(bool)> ConfigureCallback;
+typedef base::Callback<void(bool, ui::HDCPState)> GetHDCPStateCallback;
+typedef base::Callback<void(bool)> SetHDCPStateCallback;
+typedef base::Callback<void(bool)> DisplayControlCallback;
+
 // Interface for classes that perform display configuration actions on behalf
 // of DisplayConfigurator.
+// Implementations may perform calls asynchronously. In the case of functions
+// taking callbacks, the callbacks may be called asynchronously when the results
+// are available. The implementations must provide a strong guarantee that the
+// callbacks are always called.
 class DISPLAY_TYPES_EXPORT NativeDisplayDelegate {
  public:
   virtual ~NativeDisplayDelegate() {}
@@ -38,6 +52,13 @@
   // Released the display server and any resources allocated by GrabServer().
   virtual void UngrabServer() = 0;
 
+  // Take control of the display from any other controlling process.
+  virtual void TakeDisplayControl(const DisplayControlCallback& callback) = 0;
+
+  // Let others control the display.
+  virtual void RelinquishDisplayControl(
+      const DisplayControlCallback& callback) = 0;
+
   // Flushes all pending requests and waits for replies.
   virtual void SyncWithServer() = 0;
 
@@ -47,11 +68,9 @@
   // Enables DPMS and forces it to the "on" state.
   virtual void ForceDPMSOn() = 0;
 
-  // Returns information about the current outputs. This method may block for
-  // 60 milliseconds or more.
-  // NativeDisplayDelegate maintains ownership of the ui::DisplaySnapshot
-  // pointers.
-  virtual std::vector<ui::DisplaySnapshot*> GetDisplays() = 0;
+  // Queries for a list of fresh displays and returns them via |callback|.
+  // Note the query operation may be expensive and take over 60 milliseconds.
+  virtual void GetDisplays(const GetDisplaysCallback& callback) = 0;
 
   // Adds |mode| to |output|. |mode| must be a valid display mode pointer.
   virtual void AddMode(const ui::DisplaySnapshot& output,
@@ -59,21 +78,24 @@
 
   // Configures the display represented by |output| to use |mode| and positions
   // the display to |origin| in the framebuffer. |mode| can be NULL, which
-  // represents disabling the display. Returns true on success.
-  virtual bool Configure(const ui::DisplaySnapshot& output,
+  // represents disabling the display. The callback will return the status of
+  // the operation.
+  virtual void Configure(const ui::DisplaySnapshot& output,
                          const ui::DisplayMode* mode,
-                         const gfx::Point& origin) = 0;
+                         const gfx::Point& origin,
+                         const ConfigureCallback& callback) = 0;
 
   // Called to set the frame buffer (underlying XRR "screen") size.
   virtual void CreateFrameBuffer(const gfx::Size& size) = 0;
 
   // Gets HDCP state of output.
-  virtual bool GetHDCPState(const ui::DisplaySnapshot& output,
-                            ui::HDCPState* state) = 0;
+  virtual void GetHDCPState(const ui::DisplaySnapshot& output,
+                            const GetHDCPStateCallback& callback) = 0;
 
   // Sets HDCP state of output.
-  virtual bool SetHDCPState(const ui::DisplaySnapshot& output,
-                            ui::HDCPState state) = 0;
+  virtual void SetHDCPState(const ui::DisplaySnapshot& output,
+                            ui::HDCPState state,
+                            const SetHDCPStateCallback& callback) = 0;
 
   // Gets the available list of color calibrations.
   virtual std::vector<ui::ColorCalibrationProfile>
@@ -85,6 +107,10 @@
       const ui::DisplaySnapshot& output,
       ui::ColorCalibrationProfile new_profile) = 0;
 
+  // Set the gamma ramp for the display.
+  virtual bool SetGammaRamp(const ui::DisplaySnapshot& output,
+                            const std::vector<GammaRampRGBEntry>& lut) = 0;
+
   virtual void AddObserver(NativeDisplayObserver* observer) = 0;
 
   virtual void RemoveObserver(NativeDisplayObserver* observer) = 0;
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc
index b95eef4..2156c73 100644
--- a/ui/display/util/edid_parser.cc
+++ b/ui/display/util/edid_parser.cc
@@ -9,6 +9,7 @@
 #include "base/hash.h"
 #include "base/strings/string_util.h"
 #include "base/sys_byteorder.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace ui {
 
@@ -28,26 +29,41 @@
           (static_cast<int64_t>(product_code_hash) << 8) | output_index);
 }
 
+// Returns a 32-bit identifier for this model of display, using
+// |manufacturer_id| and |product_code|.
+uint32_t GetProductID(uint16_t manufacturer_id, uint16_t product_code) {
+  return ((static_cast<uint32_t>(manufacturer_id) << 16) |
+          (static_cast<uint32_t>(product_code)));
+}
+
 }  // namespace
 
 bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid,
                           uint8_t output_index,
-                          int64_t* display_id_out) {
+                          int64_t* display_id_out,
+                          int64_t* product_id_out) {
   uint16_t manufacturer_id = 0;
+  uint16_t product_code = 0;
   std::string product_name;
 
   // ParseOutputDeviceData fails if it doesn't have product_name.
-  ParseOutputDeviceData(edid, &manufacturer_id, &product_name);
+  ParseOutputDeviceData(edid, &manufacturer_id, &product_code, &product_name,
+                        nullptr, nullptr);
 
-  // Generates product specific value from product_name instead of product code.
-  // See crbug.com/240341
-  uint32_t product_code_hash = product_name.empty() ?
-      0 : base::Hash(product_name);
   if (manufacturer_id != 0) {
+    // Generates product specific value from product_name instead of product
+    // code.
+    // See crbug.com/240341
+    uint32_t product_code_hash =
+        product_name.empty() ? 0 : base::Hash(product_name);
     // An ID based on display's index will be assigned later if this call
     // fails.
     *display_id_out = GetID(
         manufacturer_id, product_code_hash, output_index);
+    // product_id is 64-bit signed so it can store -1 as kInvalidProductID and
+    // not match a valid product id which will all be in the lowest 32-bits.
+    if (product_id_out)
+      *product_id_out = GetProductID(manufacturer_id, product_code);
     return true;
   }
   return false;
@@ -55,14 +71,20 @@
 
 bool ParseOutputDeviceData(const std::vector<uint8_t>& edid,
                            uint16_t* manufacturer_id,
-                           std::string* human_readable_name) {
+                           uint16_t* product_code,
+                           std::string* human_readable_name,
+                           gfx::Size* active_pixel_out,
+                           gfx::Size* physical_display_size_out) {
   // See http://en.wikipedia.org/wiki/Extended_display_identification_data
   // for the details of EDID data format.  We use the following data:
   //   bytes 8-9: manufacturer EISA ID, in big-endian
+  //   bytes 10-11: manufacturer product code, in little-endian
   //   bytes 54-125: four descriptors (18-bytes each) which may contain
   //     the display name.
   const unsigned int kManufacturerOffset = 8;
   const unsigned int kManufacturerLength = 2;
+  const unsigned int kProductCodeOffset = 10;
+  const unsigned int kProductCodeLength = 2;
   const unsigned int kDescriptorOffset = 54;
   const unsigned int kNumDescriptors = 4;
   const unsigned int kDescriptorLength = 18;
@@ -71,7 +93,7 @@
 
   if (manufacturer_id) {
     if (edid.size() < kManufacturerOffset + kManufacturerLength) {
-      LOG(ERROR) << "too short EDID data: manifacturer id";
+      LOG(ERROR) << "too short EDID data: manufacturer id";
       return false;
     }
 
@@ -82,15 +104,67 @@
 #endif
   }
 
-  if (!human_readable_name)
-    return true;
+  if (product_code) {
+    if (edid.size() < kProductCodeOffset + kProductCodeLength) {
+      LOG(ERROR) << "too short EDID data: manufacturer product code";
+      return false;
+    }
 
-  human_readable_name->clear();
+    *product_code =
+        *reinterpret_cast<const uint16_t*>(&edid[kProductCodeOffset]);
+  }
+
+  if (human_readable_name)
+    human_readable_name->clear();
+
   for (unsigned int i = 0; i < kNumDescriptors; ++i) {
     if (edid.size() < kDescriptorOffset + (i + 1) * kDescriptorLength)
       break;
 
     size_t offset = kDescriptorOffset + i * kDescriptorLength;
+
+    // Detailed Timing Descriptor:
+    if (edid[offset] != 0 && edid[offset + 1] != 0) {
+      const int kMaxResolution = 10080;  // 8k display.
+
+      if (active_pixel_out) {
+        const int kHorizontalPixelLsbOffset = 2;
+        const int kHorizontalPixelMsbOffset = 4;
+        const int kVerticalPixelLsbOffset = 5;
+        const int kVerticalPixelMsbOffset = 7;
+
+        int h_lsb = edid[offset + kHorizontalPixelLsbOffset];
+        int h_msb = edid[offset + kHorizontalPixelMsbOffset];
+        int h_pixel = std::min(h_lsb + ((h_msb & 0xF0) << 4), kMaxResolution);
+
+        int v_lsb = edid[offset + kVerticalPixelLsbOffset];
+        int v_msb = edid[offset + kVerticalPixelMsbOffset];
+        int v_pixel = std::min(v_lsb + ((v_msb & 0xF0) << 4), kMaxResolution);
+
+        active_pixel_out->SetSize(h_pixel, v_pixel);
+        // EDID may contain multiple DTD. Use first one that
+        // contains the highest resolution.
+        active_pixel_out = nullptr;
+      }
+
+      if (physical_display_size_out) {
+        const int kHorizontalSizeLsbOffset = 12;
+        const int kVerticalSizeLsbOffset = 13;
+        const int kSizeMsbOffset = 14;
+
+        int h_lsb = edid[offset + kHorizontalSizeLsbOffset];
+        int v_lsb = edid[offset + kVerticalSizeLsbOffset];
+
+        int msb = edid[offset + kSizeMsbOffset];
+        int h_size = h_lsb + ((msb & 0xF0) << 4);
+        int v_size = v_lsb + ((msb & 0x0F) << 8);
+        physical_display_size_out->SetSize(h_size, v_size);
+        physical_display_size_out = nullptr;
+      }
+      continue;
+    }
+
+    // EDID Other Monitor Descriptors:
     // If the descriptor contains the display name, it has the following
     // structure:
     //   bytes 0-2, 4: \0
@@ -99,22 +173,26 @@
     // we should check bytes 0-2 and 4, since it may have other values in
     // case that the descriptor contains other type of data.
     if (edid[offset] == 0 && edid[offset + 1] == 0 && edid[offset + 2] == 0 &&
-        edid[offset + 3] == kMonitorNameDescriptor && edid[offset + 4] == 0) {
+        edid[offset + 3] == kMonitorNameDescriptor && edid[offset + 4] == 0 &&
+        human_readable_name) {
       std::string found_name(reinterpret_cast<const char*>(&edid[offset + 5]),
                              kDescriptorLength - 5);
       base::TrimWhitespaceASCII(
           found_name, base::TRIM_TRAILING, human_readable_name);
-      break;
+      continue;
     }
   }
 
   // Verify if the |human_readable_name| consists of printable characters only.
-  for (size_t i = 0; i < human_readable_name->size(); ++i) {
-    char c = (*human_readable_name)[i];
-    if (!isascii(c) || !isprint(c)) {
-      human_readable_name->clear();
-      LOG(ERROR) << "invalid EDID: human unreadable char in name";
-      return false;
+  // TODO(oshima|muka): Consider replacing unprintable chars with white space.
+  if (human_readable_name) {
+    for (size_t i = 0; i < human_readable_name->size(); ++i) {
+      char c = (*human_readable_name)[i];
+      if (!isascii(c) || !isprint(c)) {
+        human_readable_name->clear();
+        LOG(ERROR) << "invalid EDID: human unreadable char in name";
+        return false;
+      }
     }
   }
 
diff --git a/ui/display/util/edid_parser.h b/ui/display/util/edid_parser.h
index 4f12d40..385abe9 100644
--- a/ui/display/util/edid_parser.h
+++ b/ui/display/util/edid_parser.h
@@ -12,26 +12,36 @@
 
 #include "ui/display/util/display_util_export.h"
 
+namespace gfx {
+class Size;
+}
+
 // EDID (Extended Display Identification Data) is a format for monitor
 // metadata. This provides a parser for the data.
 
 namespace ui {
 
-// Generates the display id for the pair of |edid| and |index|, and store in
-// |display_id_out|. Returns true if the display id is successfully generated,
-// or false otherwise.
+// Generates the display id and product id for the pair of |edid| and |index|,
+// and store in |display_id_out| and |product_id_out|. Returns true if the
+// display id is successfully generated, or false otherwise.
 DISPLAY_UTIL_EXPORT bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid,
                                               uint8_t index,
-                                              int64_t* display_id_out);
+                                              int64_t* display_id_out,
+                                              int64_t* product_id_out);
 
-// Parses |edid| as EDID data and stores extracted data into |manufacturer_id|
-// and |human_readable_name| and returns true. NULL can be passed for unwanted
-// output parameters. Some devices (especially internal displays) may not have
-// the field for |human_readable_name|, and it will return true in that case.
+// Parses |edid| as EDID data and stores extracted data into |manufacturer_id|,
+// |product_code|, |human_readable_name|, |active_pixel_out| and
+// |physical_display_size_out|, then returns true. nullptr can be passed for
+// unwanted output parameters.  Some devices (especially internal displays) may
+// not have the field for |human_readable_name|, and it will return true in
+// that case.
 DISPLAY_UTIL_EXPORT bool ParseOutputDeviceData(
     const std::vector<uint8_t>& edid,
     uint16_t* manufacturer_id,
-    std::string* human_readable_name);
+    uint16_t* product_code,
+    std::string* human_readable_name,
+    gfx::Size* active_pixel_out,
+    gfx::Size* physical_display_size_out);
 
 DISPLAY_UTIL_EXPORT bool ParseOutputOverscanFlag(
     const std::vector<uint8_t>& edid,
diff --git a/ui/display/util/edid_parser_unittest.cc b/ui/display/util/edid_parser_unittest.cc
index ef55bd6..2e77cac 100644
--- a/ui/display/util/edid_parser_unittest.cc
+++ b/ui/display/util/edid_parser_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace ui {
 
@@ -93,6 +94,11 @@
     "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
     "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45";
 
+void Reset(gfx::Size* pixel, gfx::Size* size) {
+  pixel->SetSize(0, 0);
+  size->SetSize(0, 0);
+}
+
 }  // namespace
 
 TEST(EDIDParserTest, ParseOverscanFlag) {
@@ -146,42 +152,64 @@
 
 TEST(EDIDParserTest, ParseEDID) {
   uint16_t manufacturer_id = 0;
+  uint16_t product_code = 0;
   std::string human_readable_name;
   std::vector<uint8_t> edid(
       kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay));
-  EXPECT_TRUE(ParseOutputDeviceData(
-      edid, &manufacturer_id, &human_readable_name));
+  gfx::Size pixel;
+  gfx::Size size;
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code,
+                                    &human_readable_name, &pixel, &size));
   EXPECT_EQ(0x22f0u, manufacturer_id);
+  EXPECT_EQ(0x286cu, product_code);
   EXPECT_EQ("HP ZR30w", human_readable_name);
+  EXPECT_EQ("2560x1600", pixel.ToString());
+  EXPECT_EQ("641x400", size.ToString());
 
   manufacturer_id = 0;
+  product_code = 0;
   human_readable_name.clear();
+  Reset(&pixel, &size);
   edid.assign(kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay));
-  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, NULL));
+
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code,
+                                    nullptr, &pixel, &size));
   EXPECT_EQ(0x4ca3u, manufacturer_id);
+  EXPECT_EQ(0x3142u, product_code);
   EXPECT_EQ("", human_readable_name);
+  EXPECT_EQ("1280x800", pixel.ToString());
+  EXPECT_EQ("261x163", size.ToString());
 
   // Internal display doesn't have name.
-  EXPECT_TRUE(ParseOutputDeviceData(edid, NULL, &human_readable_name));
+  EXPECT_TRUE(ParseOutputDeviceData(edid, nullptr, nullptr,
+                                    &human_readable_name, &pixel, &size));
   EXPECT_TRUE(human_readable_name.empty());
 
   manufacturer_id = 0;
+  product_code = 0;
   human_readable_name.clear();
+  Reset(&pixel, &size);
   edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay));
-  EXPECT_TRUE(ParseOutputDeviceData(
-      edid, &manufacturer_id, &human_readable_name));
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code,
+                                    &human_readable_name, &pixel, &size));
   EXPECT_EQ(0x4c2du, manufacturer_id);
+  EXPECT_EQ(0x08feu, product_code);
   EXPECT_EQ("SAMSUNG", human_readable_name);
+  EXPECT_EQ("1920x1080", pixel.ToString());
+  EXPECT_EQ("160x90", size.ToString());
 }
 
 TEST(EDIDParserTest, ParseBrokenEDID) {
   uint16_t manufacturer_id = 0;
+  uint16_t product_code = 0;
   std::string human_readable_name;
   std::vector<uint8_t> edid;
 
+  gfx::Size dummy;
+
   // length == 0
-  EXPECT_FALSE(ParseOutputDeviceData(
-      edid, &manufacturer_id, &human_readable_name));
+  EXPECT_FALSE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code,
+                                     &human_readable_name, &dummy, &dummy));
 
   // name is broken. Copying kNormalDisplay and substitute its name data by
   // some control code.
@@ -190,40 +218,54 @@
   // display's name data is embedded in byte 95-107 in this specific example.
   // Fix here too when the contents of kNormalDisplay is altered.
   edid[97] = '\x1b';
-  EXPECT_FALSE(ParseOutputDeviceData(
-      edid, &manufacturer_id, &human_readable_name));
+  EXPECT_FALSE(ParseOutputDeviceData(edid, &manufacturer_id, nullptr,
+                                     &human_readable_name, &dummy, &dummy));
 
   // If |human_readable_name| isn't specified, it skips parsing the name.
   manufacturer_id = 0;
-  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, NULL));
+  product_code = 0;
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code,
+                                    nullptr, &dummy, &dummy));
   EXPECT_EQ(0x22f0u, manufacturer_id);
+  EXPECT_EQ(0x286cu, product_code);
 }
 
 TEST(EDIDParserTest, GetDisplayId) {
   // EDID of kLP2565A and B are slightly different but actually the same device.
   int64_t id1 = -1;
   int64_t id2 = -1;
+  int64_t product_id1 = -1;
+  int64_t product_id2 = -1;
   std::vector<uint8_t> edid(kLP2565A, kLP2565A + charsize(kLP2565A));
-  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id1));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id1, &product_id1));
   edid.assign(kLP2565B, kLP2565B + charsize(kLP2565B));
-  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id2));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id2, &product_id2));
   EXPECT_EQ(id1, id2);
+  // The product code in the two EDIDs varies.
+  EXPECT_NE(product_id1, product_id2);
+  EXPECT_EQ(0x22f02676, product_id1);
+  EXPECT_EQ(0x22f02675, product_id2);
   EXPECT_NE(-1, id1);
+  EXPECT_NE(-1, product_id1);
 }
 
 TEST(EDIDParserTest, GetDisplayIdFromInternal) {
   int64_t id = -1;
+  int64_t product_id = -1;
   std::vector<uint8_t> edid(
       kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay));
-  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id, &product_id));
   EXPECT_NE(-1, id);
+  EXPECT_NE(-1, product_id);
 }
 
 TEST(EDIDParserTest, GetDisplayIdFailure) {
   int64_t id = -1;
+  int64_t product_id = -1;
   std::vector<uint8_t> edid;
-  EXPECT_FALSE(GetDisplayIdFromEDID(edid, 0, &id));
+  EXPECT_FALSE(GetDisplayIdFromEDID(edid, 0, &id, &product_id));
   EXPECT_EQ(-1, id);
+  EXPECT_EQ(-1, product_id);
 }
 
 }   // namespace ui
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 536fd64..19513ef 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/ui.gni")
 import("//testing/test.gni")
+import("//ui/events/xkb.gni")
 
 static_library("dom_keycode_converter") {
   sources = [
@@ -71,7 +72,7 @@
     deps += [ "//ui/gfx/x" ]
   }
 
-  if (use_x11 || use_ozone) {
+  if (use_x11 || use_xkbcommon) {
     sources += [
       "keycodes/keyboard_code_conversion_xkb.cc",
       "keycodes/keyboard_code_conversion_xkb.h",
@@ -293,6 +294,7 @@
     "//ui/events/devices",
     "//ui/events/platform",
     "//ui/gfx:test_support",
+    "//testing/gmock:gmock",
   ]
 
   include_dirs = [ "//testing/gmock/include" ]
@@ -318,8 +320,10 @@
       "ozone/evdev/tablet_event_converter_evdev_unittest.cc",
       "ozone/evdev/touch_event_converter_evdev_unittest.cc",
       "ozone/evdev/touch_noise/touch_noise_finder_unittest.cc",
-      "ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc",
     ]
+    if (use_x11 || use_xkbcommon) {
+      sources += [ "ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc" ]
+    }
     deps += [
       "//ui/events/ozone:events_ozone",
       "//ui/events/ozone:events_ozone_evdev",
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 953220d..84b58bc 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -30,8 +30,8 @@
 #if defined(USE_X11)
 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
 #elif defined(USE_OZONE)
-#include "ui/events/ozone/layout/keyboard_layout_engine.h"
-#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine.h" // nogncheck
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" // nogncheck
 #endif
 
 namespace {
diff --git a/ui/events/ozone/BUILD.gn b/ui/events/ozone/BUILD.gn
index 4ddc579..7ee8ce4 100644
--- a/ui/events/ozone/BUILD.gn
+++ b/ui/events/ozone/BUILD.gn
@@ -5,11 +5,7 @@
 import("//build/config/features.gni")
 import("//build/config/linux/pkg_config.gni")
 import("//build/config/ui.gni")
-
-declare_args() {
-  # Optional system libraries.
-  use_xkbcommon = false
-}
+import("//ui/events/xkb.gni")
 
 if (use_xkbcommon) {
   pkg_config("xkbcommon") {
@@ -122,6 +118,7 @@
     "//ui/events/devices",
     "//ui/events/platform",
     "//ui/gfx",
+    "//ui/ozone:ozone_base",
   ]
 
   public_configs = [ ":evdev" ]
@@ -153,6 +150,7 @@
     "//base",
     "//ui/events:dom_keycode_converter",
     "//ui/events:events_base",
+    "//ui/ozone:ozone_base",
   ]
 
   public_configs = [ ":events_ozone_layout_config" ]
diff --git a/ui/events/ozone/device/udev/device_manager_udev.cc b/ui/events/ozone/device/udev/device_manager_udev.cc
index ca7daa2..4e28e39 100644
--- a/ui/events/ozone/device/udev/device_manager_udev.cc
+++ b/ui/events/ozone/device/udev/device_manager_udev.cc
@@ -162,10 +162,10 @@
 
   DeviceEvent::DeviceType device_type;
   if (!strcmp(subsystem, "input") &&
-      base::StartsWithASCII(path, "/dev/input/event", true))
+      StartsWithASCII(path, "/dev/input/event", true))
     device_type = DeviceEvent::INPUT;
   else if (!strcmp(subsystem, "drm") &&
-           base::StartsWithASCII(path, "/dev/dri/card", true))
+           StartsWithASCII(path, "/dev/dri/card", true))
     device_type = DeviceEvent::DISPLAY;
   else
     return nullptr;
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index 9d79b14..5350c0c 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -12,6 +12,7 @@
 #include "base/trace_event/trace_event.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/input_device.h"
+#include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/ozone/device/device_event.h"
 #include "ui/events/ozone/device/device_manager.h"
diff --git a/ui/events/ozone/evdev/input_injector_evdev_unittest.cc b/ui/events/ozone/evdev/input_injector_evdev_unittest.cc
index 88040c2..e57b5a9 100644
--- a/ui/events/ozone/evdev/input_injector_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/input_injector_evdev_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
 #include "ui/events/ozone/device/device_manager.h"
 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
 #include "ui/events/ozone/evdev/event_converter_test_util.h"
diff --git a/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc b/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc
index 83310a7..deeb36d 100644
--- a/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc
+++ b/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/strings/string16.h"
-#include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
diff --git a/ui/events/xkb.gni b/ui/events/xkb.gni
new file mode 100644
index 0000000..1bd10d4
--- /dev/null
+++ b/ui/events/xkb.gni
@@ -0,0 +1,8 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  # Optional system libraries.
+  use_xkbcommon = false
+}
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index b66f0ec..521042f 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -76,6 +76,7 @@
     "shadow_value.h",
     "skia_util.cc",
     "skia_util.h",
+    "swap_result.h",
     "transform.cc",
     "transform.h",
     "transform_util.cc",
diff --git a/ui/gfx/gpu_memory_buffer.cc b/ui/gfx/gpu_memory_buffer.cc
index be23d51..da80107 100644
--- a/ui/gfx/gpu_memory_buffer.cc
+++ b/ui/gfx/gpu_memory_buffer.cc
@@ -8,10 +8,11 @@
 
 GpuMemoryBufferHandle::GpuMemoryBufferHandle()
     : type(EMPTY_BUFFER),
+      id(0),
       handle(base::SharedMemory::NULLHandle())
-#if defined(USE_X11)
+#if defined(OS_MACOSX)
       ,
-      pixmap(0)
+      io_surface_id(0)
 #endif
 {
 }
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 51731d9..829865e 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -9,10 +9,6 @@
 #include "build/build_config.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(USE_X11)
-#include "ui/gfx/x/x11_types.h"
-#endif
-
 extern "C" typedef struct _ClientBuffer* ClientBuffer;
 
 namespace gfx {
@@ -22,27 +18,20 @@
   SHARED_MEMORY_BUFFER,
   IO_SURFACE_BUFFER,
   SURFACE_TEXTURE_BUFFER,
-  X11_PIXMAP_BUFFER,
   OZONE_NATIVE_BUFFER,
   GPU_MEMORY_BUFFER_TYPE_LAST = OZONE_NATIVE_BUFFER
 };
 
-struct GpuMemoryBufferId {
-  GpuMemoryBufferId() : primary_id(0), secondary_id(0) {}
-  GpuMemoryBufferId(int32 primary_id, int32 secondary_id)
-      : primary_id(primary_id), secondary_id(secondary_id) {}
-  int32 primary_id;
-  int32 secondary_id;
-};
+using GpuMemoryBufferId = int32;
 
 struct GFX_EXPORT GpuMemoryBufferHandle {
   GpuMemoryBufferHandle();
   bool is_null() const { return type == EMPTY_BUFFER; }
   GpuMemoryBufferType type;
+  GpuMemoryBufferId id;
   base::SharedMemoryHandle handle;
-  GpuMemoryBufferId global_id;
-#if defined(USE_X11)
-  XID pixmap;
+#if defined(OS_MACOSX)
+  uint32 io_surface_id;
 #endif
 };
 
@@ -67,7 +56,10 @@
 
   // The usage mode affects how a buffer can be used. Only buffers created with
   // MAP can be mapped into the client's address space and accessed by the CPU.
-  enum Usage { MAP, SCANOUT, USAGE_LAST = SCANOUT };
+  // PERSISTENT_MAP adds the additional condition that successive Map() calls
+  // (with Unmap() calls between) will return a pointer to the same memory
+  // contents.
+  enum Usage { MAP, PERSISTENT_MAP, SCANOUT, USAGE_LAST = SCANOUT };
 
   virtual ~GpuMemoryBuffer() {}
 
diff --git a/ui/gfx/swap_result.h b/ui/gfx/swap_result.h
new file mode 100644
index 0000000..32a6c5a
--- /dev/null
+++ b/ui/gfx/swap_result.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_SWAP_RESULT_H_
+#define UI_GFX_SWAP_RESULT_H_
+
+namespace gfx {
+
+enum class SwapResult {
+  SWAP_ACK,
+  SWAP_FAILED,
+  SWAP_NAK_RECREATE_BUFFERS,
+  SWAP_RESULT_LAST = SWAP_NAK_RECREATE_BUFFERS,
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_SWAP_RESULT_H_
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index a5ed5cc..e190b4d 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -181,8 +181,8 @@
     sources += [
       "gl_egl_api_implementation.cc",
       "gl_egl_api_implementation.h",
-      "gl_image_surface_texture.cc",
-      "gl_image_surface_texture.h",
+      "gl_image_linux_dma_buffer.cc",
+      "gl_image_linux_dma_buffer.h",
     ]
 
     defines += [
@@ -195,6 +195,12 @@
     deps += [ ":gl_jni_headers" ]
   }
   if (use_ozone) {
+    if (is_android) {
+      sources -= [
+        "gl_implementation_android.cc",
+        "gl_surface_android.cc",
+      ]
+    }
     sources += [
       "gl_context_ozone.cc",
       "gl_egl_api_implementation.cc",
diff --git a/ui/gl/gl_image_memory.cc b/ui/gl/gl_image_memory.cc
index 13e9d71..d5d54a3 100644
--- a/ui/gl/gl_image_memory.cc
+++ b/ui/gl/gl_image_memory.cc
@@ -7,9 +7,13 @@
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/scoped_binders.h"
 
+#if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
+    defined(USE_OZONE)
+#include "ui/gl/gl_surface_egl.h"
+#endif
+
 namespace gfx {
 namespace {
 
diff --git a/ui/gl/gl_image_surface_texture.cc b/ui/gl/gl_image_surface_texture.cc
deleted file mode 100644
index a321c4d..0000000
--- a/ui/gl/gl_image_surface_texture.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/gl/gl_image_surface_texture.h"
-
-#include "base/trace_event/trace_event.h"
-#include "ui/gl/android/surface_texture.h"
-
-namespace gfx {
-
-GLImageSurfaceTexture::GLImageSurfaceTexture(const gfx::Size& size)
-    : size_(size), texture_id_(0) {
-}
-
-GLImageSurfaceTexture::~GLImageSurfaceTexture() {
-  DCHECK(!surface_texture_.get());
-  DCHECK_EQ(0, texture_id_);
-}
-
-bool GLImageSurfaceTexture::Initialize(SurfaceTexture* surface_texture) {
-  DCHECK(!surface_texture_.get());
-  surface_texture_ = surface_texture;
-  return true;
-}
-
-void GLImageSurfaceTexture::Destroy(bool have_context) {
-  surface_texture_ = NULL;
-  texture_id_ = 0;
-}
-
-gfx::Size GLImageSurfaceTexture::GetSize() { return size_; }
-
-bool GLImageSurfaceTexture::BindTexImage(unsigned target) {
-  TRACE_EVENT0("gpu", "GLImageSurfaceTexture::BindTexImage");
-
-  if (target != GL_TEXTURE_EXTERNAL_OES) {
-    LOG(ERROR)
-        << "Surface texture can only be bound to TEXTURE_EXTERNAL_OES target";
-    return false;
-  }
-
-  GLint texture_id;
-  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
-  DCHECK(texture_id);
-
-  if (texture_id_ && texture_id_ != texture_id) {
-    LOG(ERROR) << "Surface texture can only be bound to one texture ID";
-    return false;
-  }
-
-  DCHECK(surface_texture_.get());
-  if (texture_id != texture_id_) {
-    // Note: Surface textures used as gpu memory buffers are created with an
-    // initial dummy texture id of 0. We need to call DetachFromGLContext() here
-    // to detach from the dummy texture before we can attach to a real texture
-    // id. DetachFromGLContext() will delete the texture for the current
-    // attachment point so it's important that this is never called when
-    // attached to a real texture id. Detaching from the dummy texture id should
-    // not cause any problems as the GL should silently ignore 0 when passed to
-    // glDeleteTextures.
-    DCHECK_EQ(0, texture_id_);
-    surface_texture_->DetachFromGLContext();
-
-    // This will attach the surface texture to the texture currently bound to
-    // GL_TEXTURE_EXTERNAL_OES target.
-    surface_texture_->AttachToGLContext();
-    texture_id_ = texture_id;
-  }
-
-  surface_texture_->UpdateTexImage();
-  return true;
-}
-
-bool GLImageSurfaceTexture::CopyTexImage(unsigned target) {
-  return false;
-}
-
-bool GLImageSurfaceTexture::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
-                                                 int z_order,
-                                                 OverlayTransform transform,
-                                                 const Rect& bounds_rect,
-                                                 const RectF& crop_rect) {
-  return false;
-}
-
-}  // namespace gfx
diff --git a/ui/gl/gl_image_surface_texture.h b/ui/gl/gl_image_surface_texture.h
deleted file mode 100644
index d2ff5b7..0000000
--- a/ui/gl/gl_image_surface_texture.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_GL_GL_IMAGE_SURFACE_TEXTURE_H_
-#define UI_GL_GL_IMAGE_SURFACE_TEXTURE_H_
-
-#include "base/memory/ref_counted.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_image.h"
-
-namespace gfx {
-class SurfaceTexture;
-
-class GL_EXPORT GLImageSurfaceTexture : public GLImage {
- public:
-  explicit GLImageSurfaceTexture(const gfx::Size& size);
-
-  bool Initialize(SurfaceTexture* surface_texture);
-
-  // Overridden from GLImage:
-  void Destroy(bool have_context) override;
-  gfx::Size GetSize() override;
-  bool BindTexImage(unsigned target) override;
-  void ReleaseTexImage(unsigned target) override {}
-  bool CopyTexImage(unsigned target) override;
-  void WillUseTexImage() override {}
-  void DidUseTexImage() override {}
-  void WillModifyTexImage() override {}
-  void DidModifyTexImage() override {}
-  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
-                            int z_order,
-                            OverlayTransform transform,
-                            const Rect& bounds_rect,
-                            const RectF& crop_rect) override;
-
- protected:
-  ~GLImageSurfaceTexture() override;
-
- private:
-  scoped_refptr<SurfaceTexture> surface_texture_;
-  const gfx::Size size_;
-  GLint texture_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(GLImageSurfaceTexture);
-};
-
-}  // namespace gfx
-
-#endif  // UI_GL_GL_IMAGE_SURFACE_TEXTURE_H_
diff --git a/ui/gl/gl_implementation_ozone.cc b/ui/gl/gl_implementation_ozone.cc
index 2a85720..715084f 100644
--- a/ui/gl/gl_implementation_ozone.cc
+++ b/ui/gl/gl_implementation_ozone.cc
@@ -44,9 +44,10 @@
     case kGLImplementationOSMesaGL:
       return InitializeStaticGLBindingsOSMesaGL();
     case kGLImplementationEGLGLES2:
-      if (!ui::SurfaceFactoryOzone::GetInstance()->LoadEGLGLES2Bindings(
-              base::Bind(&AddGLNativeLibrary),
-              base::Bind(&SetGLGetProcAddressProc)))
+      if (!ui::OzonePlatform::GetInstance()
+               ->GetSurfaceFactoryOzone()
+               ->LoadEGLGLES2Bindings(base::Bind(&AddGLNativeLibrary),
+                                      base::Bind(&SetGLGetProcAddressProc)))
         return false;
       SetGLImplementation(kGLImplementationEGLGLES2);
       InitializeStaticGLBindingsGL();
diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc
index 007bcaf..e5ac99d 100644
--- a/ui/gl/gl_surface.cc
+++ b/ui/gl/gl_surface.cc
@@ -197,13 +197,13 @@
 
 bool GLSurface::SwapBuffersAsync(const SwapCompletionCallback& callback) {
   DCHECK(!IsSurfaceless());
-  bool success = SwapBuffers();
-  callback.Run();
-  return success;
+  gfx::SwapResult result = SwapBuffers();
+  callback.Run(result);
+  return result == gfx::SwapResult::SWAP_ACK;
 }
 
-bool GLSurface::PostSubBuffer(int x, int y, int width, int height) {
-  return false;
+gfx::SwapResult GLSurface::PostSubBuffer(int x, int y, int width, int height) {
+  return gfx::SwapResult::SWAP_FAILED;
 }
 
 bool GLSurface::PostSubBufferAsync(int x,
@@ -211,9 +211,9 @@
                                    int width,
                                    int height,
                                    const SwapCompletionCallback& callback) {
-  bool success = PostSubBuffer(x, y, width, height);
-  callback.Run();
-  return success;
+  gfx::SwapResult result = PostSubBuffer(x, y, width, height);
+  callback.Run(result);
+  return result == gfx::SwapResult::SWAP_ACK;
 }
 
 bool GLSurface::OnMakeCurrent(GLContext* context) {
@@ -324,7 +324,7 @@
   return surface_->IsOffscreen();
 }
 
-bool GLSurfaceAdapter::SwapBuffers() {
+gfx::SwapResult GLSurfaceAdapter::SwapBuffers() {
   return surface_->SwapBuffers();
 }
 
@@ -333,7 +333,8 @@
   return surface_->SwapBuffersAsync(callback);
 }
 
-bool GLSurfaceAdapter::PostSubBuffer(int x, int y, int width, int height) {
+gfx::SwapResult GLSurfaceAdapter::PostSubBuffer(
+    int x, int y, int width, int height) {
   return surface_->PostSubBuffer(x, y, width, height);
 }
 
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h
index 73c085d..f6bba1b 100644
--- a/ui/gl/gl_surface.h
+++ b/ui/gl/gl_surface.h
@@ -15,6 +15,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/overlay_transform.h"
+#include "ui/gfx/swap_result.h"
 #include "ui/gl/gl_export.h"
 #include "ui/gl/gl_implementation.h"
 
@@ -67,7 +68,7 @@
 
   // Swaps front and back buffers. This has no effect for off-screen
   // contexts.
-  virtual bool SwapBuffers() = 0;
+  virtual gfx::SwapResult SwapBuffers() = 0;
 
   // Get the size of the surface.
   virtual gfx::Size GetSize() = 0;
@@ -82,7 +83,7 @@
   // FBO. Otherwise returns 0.
   virtual unsigned int GetBackingFrameBufferObject();
 
-  typedef base::Callback<void()> SwapCompletionCallback;
+  typedef base::Callback<void(gfx::SwapResult)> SwapCompletionCallback;
   // Swaps front and back buffers. This has no effect for off-screen
   // contexts. On some platforms, we want to send SwapBufferAck only after the
   // surface is displayed on screen. The callback can be used to delay sending
@@ -91,7 +92,7 @@
   virtual bool SwapBuffersAsync(const SwapCompletionCallback& callback);
 
   // Copy part of the backbuffer to the frontbuffer.
-  virtual bool PostSubBuffer(int x, int y, int width, int height);
+  virtual gfx::SwapResult PostSubBuffer(int x, int y, int width, int height);
 
   // Copy part of the backbuffer to the frontbuffer. On some platforms, we want
   // to send SwapBufferAck only after the surface is displayed on screen. The
@@ -222,9 +223,9 @@
   bool Recreate() override;
   bool DeferDraws() override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   bool SwapBuffersAsync(const SwapCompletionCallback& callback) override;
-  bool PostSubBuffer(int x, int y, int width, int height) override;
+  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
   bool PostSubBufferAsync(int x,
                           int y,
                           int width,
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 9e67979..23e1851 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -30,7 +30,8 @@
 #endif
 
 #if defined (USE_OZONE)
-#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h" // nogncheck
+#include "ui/ozone/public/surface_factory_ozone.h" // nogncheck
 #endif
 
 #if !defined(EGL_FIXED_SIZE_ANGLE)
@@ -142,7 +143,7 @@
           switches::kEnableUnsafeES3APIs)) {
     renderable_type = EGL_OPENGL_ES3_BIT;
   }
-  EGLint config_attribs[] = {
+  EGLint kConfigAttribs[] = {
     EGL_BUFFER_SIZE, configuration.alpha_bits +
                      configuration.red_bits +
                      configuration.green_bits +
@@ -161,16 +162,19 @@
   };
 
 #if defined(USE_OZONE)
-  config_attribs =
-      ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
-          config_attribs);
+  const EGLint* config_attribs = ui::OzonePlatform::GetInstance()
+    ->GetSurfaceFactoryOzone()
+    ->GetEGLSurfaceProperties(kConfigAttribs);
 #elif defined(USE_X11)
+  EGLint* config_attribs = kConfigAttribs;
   // Try matching the window depth with an alpha channel,
   // because we're worried the destination alpha width could
   // constrain blending precision.
   const int kBufferSizeOffset = 1;
   const int kAlphaSizeOffset = 3;
   config_attribs[kBufferSizeOffset] = win_attribs.depth;
+#else
+  const EGLint* config_attribs = kConfigAttribs;
 #endif
 
   EGLint num_configs;
@@ -446,7 +450,7 @@
   return false;
 }
 
-bool NativeViewGLSurfaceEGL::SwapBuffers() {
+gfx::SwapResult NativeViewGLSurfaceEGL::SwapBuffers() {
   TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
       "width", GetSize().width(),
       "height", GetSize().height());
@@ -454,10 +458,10 @@
   if (!eglSwapBuffers(GetDisplay(), surface_)) {
     DVLOG(1) << "eglSwapBuffers failed with error "
              << GetLastEGLErrorString();
-    return false;
+    return gfx::SwapResult::SWAP_FAILED;
   }
 
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
@@ -516,15 +520,15 @@
   return supports_post_sub_buffer_;
 }
 
-bool NativeViewGLSurfaceEGL::PostSubBuffer(
+gfx::SwapResult NativeViewGLSurfaceEGL::PostSubBuffer(
     int x, int y, int width, int height) {
   DCHECK(supports_post_sub_buffer_);
   if (!eglPostSubBufferNV(GetDisplay(), surface_, x, y, width, height)) {
     DVLOG(1) << "eglPostSubBufferNV failed with error "
              << GetLastEGLErrorString();
-    return false;
+    return gfx::SwapResult::SWAP_FAILED;
   }
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
@@ -614,9 +618,9 @@
   return true;
 }
 
-bool PbufferGLSurfaceEGL::SwapBuffers() {
+gfx::SwapResult PbufferGLSurfaceEGL::SwapBuffers() {
   NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
-  return false;
+  return gfx::SwapResult::SWAP_FAILED;
 }
 
 gfx::Size PbufferGLSurfaceEGL::GetSize() {
@@ -702,9 +706,9 @@
   return true;
 }
 
-bool SurfacelessEGL::SwapBuffers() {
+gfx::SwapResult SurfacelessEGL::SwapBuffers() {
   LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
-  return false;
+  return gfx::SwapResult::SWAP_FAILED;
 }
 
 gfx::Size SurfacelessEGL::GetSize() {
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 9bf2a2d..a18e6e0 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -62,11 +62,11 @@
   bool Resize(const gfx::Size& size) override;
   bool Recreate() override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   EGLSurface GetHandle() override;
   bool SupportsPostSubBuffer() override;
-  bool PostSubBuffer(int x, int y, int width, int height) override;
+  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
   VSyncProvider* GetVSyncProvider() override;
 
   // Create a NativeViewGLSurfaceEGL with an externally provided VSyncProvider.
@@ -104,7 +104,7 @@
   bool Initialize() override;
   void Destroy() override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   bool Resize(const gfx::Size& size) override;
   EGLSurface GetHandle() override;
@@ -135,7 +135,7 @@
   void Destroy() override;
   bool IsOffscreen() override;
   bool IsSurfaceless() const override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   bool Resize(const gfx::Size& size) override;
   EGLSurface GetHandle() override;
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index 2a52fc7..2740138 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -501,13 +501,13 @@
   return false;
 }
 
-bool NativeViewGLSurfaceGLX::SwapBuffers() {
+gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers() {
   TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
       "width", GetSize().width(),
       "height", GetSize().height());
 
   glXSwapBuffers(g_display, GetDrawableHandle());
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
@@ -586,11 +586,11 @@
   return config_;
 }
 
-bool NativeViewGLSurfaceGLX::PostSubBuffer(
+gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer(
     int x, int y, int width, int height) {
   DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
   glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
@@ -673,9 +673,9 @@
   return true;
 }
 
-bool PbufferGLSurfaceGLX::SwapBuffers() {
+gfx::SwapResult PbufferGLSurfaceGLX::SwapBuffers() {
   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
-  return false;
+  return gfx::SwapResult::SWAP_FAILED;
 }
 
 gfx::Size PbufferGLSurfaceGLX::GetSize() {
diff --git a/ui/gl/gl_surface_glx.h b/ui/gl/gl_surface_glx.h
index d8fa9a4..aa09418 100644
--- a/ui/gl/gl_surface_glx.h
+++ b/ui/gl/gl_surface_glx.h
@@ -62,12 +62,12 @@
   void Destroy() override;
   bool Resize(const gfx::Size& size) override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   void* GetHandle() override;
   bool SupportsPostSubBuffer() override;
   void* GetConfig() override;
-  bool PostSubBuffer(int x, int y, int width, int height) override;
+  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
   VSyncProvider* GetVSyncProvider() override;
 
  protected:
@@ -106,7 +106,7 @@
   bool Initialize() override;
   void Destroy() override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   void* GetHandle() override;
   void* GetConfig() override;
diff --git a/ui/gl/gl_surface_osmesa.cc b/ui/gl/gl_surface_osmesa.cc
index 2efe12b..b695224 100644
--- a/ui/gl/gl_surface_osmesa.cc
+++ b/ui/gl/gl_surface_osmesa.cc
@@ -94,9 +94,9 @@
   return true;
 }
 
-bool GLSurfaceOSMesa::SwapBuffers() {
+gfx::SwapResult GLSurfaceOSMesa::SwapBuffers() {
   NOTREACHED() << "Should not call SwapBuffers on an GLSurfaceOSMesa.";
-  return false;
+  return gfx::SwapResult::SWAP_FAILED;
 }
 
 gfx::Size GLSurfaceOSMesa::GetSize() {
@@ -117,7 +117,9 @@
 
 bool GLSurfaceOSMesaHeadless::IsOffscreen() { return false; }
 
-bool GLSurfaceOSMesaHeadless::SwapBuffers() { return true; }
+gfx::SwapResult GLSurfaceOSMesaHeadless::SwapBuffers() {
+  return gfx::SwapResult::SWAP_ACK;
+}
 
 GLSurfaceOSMesaHeadless::GLSurfaceOSMesaHeadless(
     const gfx::SurfaceConfiguration requested_configuration)
diff --git a/ui/gl/gl_surface_osmesa.h b/ui/gl/gl_surface_osmesa.h
index 7484063..b812d96 100644
--- a/ui/gl/gl_surface_osmesa.h
+++ b/ui/gl/gl_surface_osmesa.h
@@ -27,7 +27,7 @@
   void Destroy() override;
   bool Resize(const gfx::Size& new_size) override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   void* GetHandle() override;
   unsigned GetFormat() override;
@@ -53,7 +53,7 @@
       const gfx::SurfaceConfiguration requested_configuration);
 
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  SwapResult SwapBuffers() override;
   void* GetConfig() override;
 
  protected:
diff --git a/ui/gl/gl_surface_ozone.cc b/ui/gl/gl_surface_ozone.cc
index 800be17..c513e5f 100644
--- a/ui/gl/gl_surface_ozone.cc
+++ b/ui/gl/gl_surface_ozone.cc
@@ -9,6 +9,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/worker_pool.h"
 #include "ui/gfx/native_widget_types.h"
@@ -22,6 +23,7 @@
 #include "ui/gl/scoped_binders.h"
 #include "ui/gl/scoped_make_current.h"
 #include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #include "ui/ozone/public/surface_ozone_egl.h"
 
@@ -38,72 +40,25 @@
 class GL_EXPORT GLSurfaceOzoneEGL : public NativeViewGLSurfaceEGL {
  public:
   GLSurfaceOzoneEGL(scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
-                    AcceleratedWidget widget)
-      : NativeViewGLSurfaceEGL(ozone_surface->GetNativeWindow()),
-        ozone_surface_(ozone_surface.Pass()),
-        widget_(widget) {}
+                    AcceleratedWidget widget,
+                    const gfx::SurfaceConfiguration& requested_configuration);
 
-  bool Initialize() override {
-    return Initialize(ozone_surface_->CreateVSyncProvider());
-  }
-  bool Resize(const gfx::Size& size) override {
-    if (!ozone_surface_->ResizeNativeWindow(size)) {
-      if (!ReinitializeNativeSurface() ||
-          !ozone_surface_->ResizeNativeWindow(size))
-        return false;
-    }
-
-    return NativeViewGLSurfaceEGL::Resize(size);
-  }
-  bool SwapBuffers() override {
-    if (!NativeViewGLSurfaceEGL::SwapBuffers())
-      return false;
-
-    return ozone_surface_->OnSwapBuffers();
-  }
+  // GLSurface:
+  bool Initialize() override;
+  bool Resize(const gfx::Size& size) override;
+  gfx::SwapResult SwapBuffers() override;
   bool ScheduleOverlayPlane(int z_order,
                             OverlayTransform transform,
                             GLImage* image,
                             const Rect& bounds_rect,
-                            const RectF& crop_rect) override {
-    return image->ScheduleOverlayPlane(
-        widget_, z_order, transform, bounds_rect, crop_rect);
-  }
+                            const RectF& crop_rect) override;
 
  private:
   using NativeViewGLSurfaceEGL::Initialize;
 
-  ~GLSurfaceOzoneEGL() override {
-    Destroy();  // EGL surface must be destroyed before SurfaceOzone
-  }
+  ~GLSurfaceOzoneEGL() override;
 
-  bool ReinitializeNativeSurface() {
-    scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
-    GLContext* current_context = GLContext::GetCurrent();
-    bool was_current =
-        current_context && current_context->IsCurrent(this);
-    if (was_current) {
-      scoped_make_current.reset(
-          new ui::ScopedMakeCurrent(current_context, this));
-    }
-
-    Destroy();
-    ozone_surface_ =
-        ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget(
-            widget_).Pass();
-    if (!ozone_surface_) {
-      LOG(ERROR) << "Failed to create native surface.";
-      return false;
-    }
-
-    window_ = ozone_surface_->GetNativeWindow();
-    if (!Initialize()) {
-      LOG(ERROR) << "Failed to initialize.";
-      return false;
-    }
-
-    return true;
-  }
+  bool ReinitializeNativeSurface();
 
   // The native surface. Deleting this is allowed to free the EGLNativeWindow.
   scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface_;
@@ -112,133 +67,354 @@
   DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneEGL);
 };
 
+GLSurfaceOzoneEGL::GLSurfaceOzoneEGL(
+    scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
+    AcceleratedWidget widget,
+    const gfx::SurfaceConfiguration& requested_configuration)
+    : NativeViewGLSurfaceEGL(ozone_surface->GetNativeWindow(),
+                             requested_configuration),
+      ozone_surface_(ozone_surface.Pass()),
+      widget_(widget) {
+}
+
+bool GLSurfaceOzoneEGL::Initialize() {
+  return Initialize(ozone_surface_->CreateVSyncProvider());
+}
+
+bool GLSurfaceOzoneEGL::Resize(const gfx::Size& size) {
+  if (!ozone_surface_->ResizeNativeWindow(size)) {
+    if (!ReinitializeNativeSurface() ||
+        !ozone_surface_->ResizeNativeWindow(size))
+      return false;
+  }
+
+  return NativeViewGLSurfaceEGL::Resize(size);
+}
+
+gfx::SwapResult GLSurfaceOzoneEGL::SwapBuffers() {
+  gfx::SwapResult result = NativeViewGLSurfaceEGL::SwapBuffers();
+  if (result != gfx::SwapResult::SWAP_ACK)
+    return result;
+
+  return ozone_surface_->OnSwapBuffers() ? gfx::SwapResult::SWAP_ACK
+                                         : gfx::SwapResult::SWAP_FAILED;
+}
+
+bool GLSurfaceOzoneEGL::ScheduleOverlayPlane(int z_order,
+                                             OverlayTransform transform,
+                                             GLImage* image,
+                                             const Rect& bounds_rect,
+                                             const RectF& crop_rect) {
+  return image->ScheduleOverlayPlane(widget_, z_order, transform, bounds_rect,
+                                     crop_rect);
+}
+
+GLSurfaceOzoneEGL::~GLSurfaceOzoneEGL() {
+  Destroy();  // EGL surface must be destroyed before SurfaceOzone
+}
+
+bool GLSurfaceOzoneEGL::ReinitializeNativeSurface() {
+  scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
+  GLContext* current_context = GLContext::GetCurrent();
+  bool was_current = current_context && current_context->IsCurrent(this);
+  if (was_current) {
+    scoped_make_current.reset(new ui::ScopedMakeCurrent(current_context, this));
+  }
+
+  Destroy();
+  ozone_surface_ = ui::OzonePlatform::GetInstance()
+                       ->GetSurfaceFactoryOzone()
+                       ->CreateEGLSurfaceForWidget(widget_)
+                       .Pass();
+  if (!ozone_surface_) {
+    LOG(ERROR) << "Failed to create native surface.";
+    return false;
+  }
+
+  window_ = ozone_surface_->GetNativeWindow();
+  if (!Initialize()) {
+    LOG(ERROR) << "Failed to initialize.";
+    return false;
+  }
+
+  return true;
+}
+
 class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL {
  public:
-  GLSurfaceOzoneSurfaceless(scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
-                            AcceleratedWidget widget)
-      : SurfacelessEGL(gfx::Size()),
-        ozone_surface_(ozone_surface.Pass()),
-        widget_(widget),
-        has_implicit_external_sync_(
-            HasEGLExtension("EGL_ARM_implicit_external_sync")),
-        last_swap_buffers_result_(true),
-        weak_factory_(this) {}
+  GLSurfaceOzoneSurfaceless(
+    scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
+    AcceleratedWidget widget,
+    const gfx::SurfaceConfiguration& requested_configuration);
 
-  bool Initialize() override {
-    if (!SurfacelessEGL::Initialize())
-      return false;
-    vsync_provider_ = ozone_surface_->CreateVSyncProvider();
-    if (!vsync_provider_)
-      return false;
-    return true;
-  }
-  bool Resize(const gfx::Size& size) override {
-    if (!ozone_surface_->ResizeNativeWindow(size))
-      return false;
-
-    return SurfacelessEGL::Resize(size);
-  }
-  bool SwapBuffers() override {
-    glFlush();
-    // TODO: the following should be replaced by a per surface flush as it gets
-    // implemented in GL drivers.
-    if (has_implicit_external_sync_) {
-      EGLSyncKHR fence = InsertFence();
-      if (!fence)
-        return false;
-
-      EGLDisplay display = GetDisplay();
-      WaitForFence(display, fence);
-      eglDestroySyncKHR(display, fence);
-    } else if (ozone_surface_->IsUniversalDisplayLinkDevice()) {
-      glFinish();
-    }
-
-    return ozone_surface_->OnSwapBuffers();
-  }
+  // GLSurface:
+  bool Initialize() override;
+  bool Resize(const gfx::Size& size) override;
+  gfx::SwapResult SwapBuffers() override;
   bool ScheduleOverlayPlane(int z_order,
                             OverlayTransform transform,
                             GLImage* image,
                             const Rect& bounds_rect,
-                            const RectF& crop_rect) override {
-    return image->ScheduleOverlayPlane(
-        widget_, z_order, transform, bounds_rect, crop_rect);
-  }
-  bool IsOffscreen() override { return false; }
-  VSyncProvider* GetVSyncProvider() override { return vsync_provider_.get(); }
-  bool SupportsPostSubBuffer() override { return true; }
-  bool PostSubBuffer(int x, int y, int width, int height) override {
-    // The actual sub buffer handling is handled at higher layers.
-    SwapBuffers();
-    return true;
-  }
-  bool SwapBuffersAsync(const SwapCompletionCallback& callback) override {
-    glFlush();
-    // TODO: the following should be replaced by a per surface flush as it gets
-    // implemented in GL drivers.
-    if (has_implicit_external_sync_) {
-      // If last swap failed, don't try to schedule new ones.
-      if (!last_swap_buffers_result_) {
-        last_swap_buffers_result_ = true;
-        return false;
-      }
-
-      EGLSyncKHR fence = InsertFence();
-      if (!fence)
-        return false;
-
-      base::Closure fence_wait_task =
-          base::Bind(&WaitForFence, GetDisplay(), fence);
-
-      base::Closure fence_retired_callback =
-          base::Bind(&GLSurfaceOzoneSurfaceless::FenceRetired,
-                     weak_factory_.GetWeakPtr(), fence, callback);
-
-      base::WorkerPool::PostTaskAndReply(FROM_HERE, fence_wait_task,
-                                         fence_retired_callback, false);
-      return true;
-    } else if (ozone_surface_->IsUniversalDisplayLinkDevice()) {
-      glFinish();
-    }
-    return ozone_surface_->OnSwapBuffersAsync(callback);
-  }
+                            const RectF& crop_rect) override;
+  bool IsOffscreen() override;
+  VSyncProvider* GetVSyncProvider() override;
+  bool SupportsPostSubBuffer() override;
+  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
+  bool SwapBuffersAsync(const SwapCompletionCallback& callback) override;
   bool PostSubBufferAsync(int x,
                           int y,
                           int width,
                           int height,
-                          const SwapCompletionCallback& callback) override {
-    return SwapBuffersAsync(callback);
-  }
+                          const SwapCompletionCallback& callback) override;
 
  protected:
-  ~GLSurfaceOzoneSurfaceless() override {
-    Destroy();  // EGL surface must be destroyed before SurfaceOzone
-  }
+  struct Overlay {
+    Overlay(int z_order,
+            OverlayTransform transform,
+            GLImage* image,
+            const Rect& bounds_rect,
+            const RectF& crop_rect);
 
-  EGLSyncKHR InsertFence() {
-    const EGLint attrib_list[] = {EGL_SYNC_CONDITION_KHR,
-                                  EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM,
-                                  EGL_NONE};
-    return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR, attrib_list);
-  }
+    bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget) const;
 
-  void FenceRetired(EGLSyncKHR fence, const SwapCompletionCallback& callback) {
-    eglDestroySyncKHR(GetDisplay(), fence);
-    last_swap_buffers_result_ = ozone_surface_->OnSwapBuffersAsync(callback);
-  }
+    int z_order;
+    OverlayTransform transform;
+    scoped_refptr<GLImage> image;
+    Rect bounds_rect;
+    RectF crop_rect;
+  };
+
+  struct PendingFrame {
+    PendingFrame();
+
+    bool ScheduleOverlayPlanes(gfx::AcceleratedWidget widget);
+
+    bool ready;
+    std::vector<Overlay> overlays;
+    SwapCompletionCallback callback;
+  };
+
+  ~GLSurfaceOzoneSurfaceless() override;
+
+  void SubmitFrame();
+
+  EGLSyncKHR InsertFence();
+  void FenceRetired(EGLSyncKHR fence, PendingFrame* frame);
+
+  void SwapCompleted(const SwapCompletionCallback& callback,
+                     gfx::SwapResult result);
 
   // The native surface. Deleting this is allowed to free the EGLNativeWindow.
   scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface_;
   AcceleratedWidget widget_;
   scoped_ptr<VSyncProvider> vsync_provider_;
+  ScopedVector<PendingFrame> unsubmitted_frames_;
   bool has_implicit_external_sync_;
   bool last_swap_buffers_result_;
+  bool swap_buffers_pending_;
 
   base::WeakPtrFactory<GLSurfaceOzoneSurfaceless> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfaceless);
 };
 
+GLSurfaceOzoneSurfaceless::Overlay::Overlay(int z_order,
+                                            OverlayTransform transform,
+                                            GLImage* image,
+                                            const Rect& bounds_rect,
+                                            const RectF& crop_rect)
+    : z_order(z_order),
+      transform(transform),
+      image(image),
+      bounds_rect(bounds_rect),
+      crop_rect(crop_rect) {
+}
+
+bool GLSurfaceOzoneSurfaceless::Overlay::ScheduleOverlayPlane(
+    gfx::AcceleratedWidget widget) const {
+  return image->ScheduleOverlayPlane(widget, z_order, transform, bounds_rect,
+                                     crop_rect);
+}
+
+GLSurfaceOzoneSurfaceless::PendingFrame::PendingFrame() : ready(false) {
+}
+
+bool GLSurfaceOzoneSurfaceless::PendingFrame::ScheduleOverlayPlanes(
+    gfx::AcceleratedWidget widget) {
+  for (const auto& overlay : overlays)
+    if (!overlay.ScheduleOverlayPlane(widget))
+      return false;
+  return true;
+}
+
+GLSurfaceOzoneSurfaceless::GLSurfaceOzoneSurfaceless(
+    scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
+    AcceleratedWidget widget,
+    const gfx::SurfaceConfiguration& requested_configuration)
+    : SurfacelessEGL(gfx::Size(), requested_configuration),
+      ozone_surface_(ozone_surface.Pass()),
+      widget_(widget),
+      has_implicit_external_sync_(
+          HasEGLExtension("EGL_ARM_implicit_external_sync")),
+      last_swap_buffers_result_(true),
+      swap_buffers_pending_(false),
+      weak_factory_(this) {
+  unsubmitted_frames_.push_back(new PendingFrame());
+}
+
+bool GLSurfaceOzoneSurfaceless::Initialize() {
+  if (!SurfacelessEGL::Initialize())
+    return false;
+  vsync_provider_ = ozone_surface_->CreateVSyncProvider();
+  if (!vsync_provider_)
+    return false;
+  return true;
+}
+bool GLSurfaceOzoneSurfaceless::Resize(const gfx::Size& size) {
+  if (!ozone_surface_->ResizeNativeWindow(size))
+    return false;
+
+  return SurfacelessEGL::Resize(size);
+}
+gfx::SwapResult GLSurfaceOzoneSurfaceless::SwapBuffers() {
+  glFlush();
+  // TODO: the following should be replaced by a per surface flush as it gets
+  // implemented in GL drivers.
+  if (has_implicit_external_sync_) {
+    EGLSyncKHR fence = InsertFence();
+    if (!fence)
+      return SwapResult::SWAP_FAILED;
+
+    EGLDisplay display = GetDisplay();
+    WaitForFence(display, fence);
+    eglDestroySyncKHR(display, fence);
+  } else if (ozone_surface_->IsUniversalDisplayLinkDevice()) {
+    glFinish();
+  }
+
+  unsubmitted_frames_.back()->ScheduleOverlayPlanes(widget_);
+  unsubmitted_frames_.back()->overlays.clear();
+
+  return ozone_surface_->OnSwapBuffers() ? gfx::SwapResult::SWAP_ACK
+                                         : gfx::SwapResult::SWAP_FAILED;
+}
+bool GLSurfaceOzoneSurfaceless::ScheduleOverlayPlane(int z_order,
+                                                     OverlayTransform transform,
+                                                     GLImage* image,
+                                                     const Rect& bounds_rect,
+                                                     const RectF& crop_rect) {
+  unsubmitted_frames_.back()->overlays.push_back(
+      Overlay(z_order, transform, image, bounds_rect, crop_rect));
+  return true;
+}
+bool GLSurfaceOzoneSurfaceless::IsOffscreen() {
+  return false;
+}
+VSyncProvider* GLSurfaceOzoneSurfaceless::GetVSyncProvider() {
+  return vsync_provider_.get();
+}
+bool GLSurfaceOzoneSurfaceless::SupportsPostSubBuffer() {
+  return true;
+}
+gfx::SwapResult GLSurfaceOzoneSurfaceless::PostSubBuffer(int x,
+                                                         int y,
+                                                         int width,
+                                                         int height) {
+  // The actual sub buffer handling is handled at higher layers.
+  SwapBuffers();
+  return gfx::SwapResult::SWAP_ACK;
+}
+bool GLSurfaceOzoneSurfaceless::SwapBuffersAsync(
+    const SwapCompletionCallback& callback) {
+  // If last swap failed, don't try to schedule new ones.
+  if (!last_swap_buffers_result_)
+    return false;
+
+  glFlush();
+
+  SwapCompletionCallback surface_swap_callback =
+      base::Bind(&GLSurfaceOzoneSurfaceless::SwapCompleted,
+                 weak_factory_.GetWeakPtr(), callback);
+
+  PendingFrame* frame = unsubmitted_frames_.back();
+  frame->callback = surface_swap_callback;
+  unsubmitted_frames_.push_back(new PendingFrame());
+
+  // TODO: the following should be replaced by a per surface flush as it gets
+  // implemented in GL drivers.
+  if (has_implicit_external_sync_) {
+    EGLSyncKHR fence = InsertFence();
+    if (!fence)
+      return false;
+
+    base::Closure fence_wait_task =
+        base::Bind(&WaitForFence, GetDisplay(), fence);
+
+    base::Closure fence_retired_callback =
+        base::Bind(&GLSurfaceOzoneSurfaceless::FenceRetired,
+                   weak_factory_.GetWeakPtr(), fence, frame);
+
+    base::WorkerPool::PostTaskAndReply(FROM_HERE, fence_wait_task,
+                                       fence_retired_callback, false);
+    return true;
+  } else if (ozone_surface_->IsUniversalDisplayLinkDevice()) {
+    glFinish();
+  }
+
+  frame->ready = true;
+  SubmitFrame();
+  return last_swap_buffers_result_;
+}
+bool GLSurfaceOzoneSurfaceless::PostSubBufferAsync(
+    int x,
+    int y,
+    int width,
+    int height,
+    const SwapCompletionCallback& callback) {
+  return SwapBuffersAsync(callback);
+}
+
+GLSurfaceOzoneSurfaceless::~GLSurfaceOzoneSurfaceless() {
+  Destroy();  // EGL surface must be destroyed before SurfaceOzone
+}
+
+void GLSurfaceOzoneSurfaceless::SubmitFrame() {
+  DCHECK(!unsubmitted_frames_.empty());
+
+  if (unsubmitted_frames_.front()->ready && !swap_buffers_pending_) {
+    scoped_ptr<PendingFrame> frame(unsubmitted_frames_.front());
+    unsubmitted_frames_.weak_erase(unsubmitted_frames_.begin());
+    swap_buffers_pending_ = true;
+
+    last_swap_buffers_result_ =
+        frame->ScheduleOverlayPlanes(widget_) &&
+        ozone_surface_->OnSwapBuffersAsync(frame->callback);
+  }
+}
+
+EGLSyncKHR GLSurfaceOzoneSurfaceless::InsertFence() {
+  const EGLint attrib_list[] = {EGL_SYNC_CONDITION_KHR,
+                                EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM,
+                                EGL_NONE};
+  return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR, attrib_list);
+}
+
+void GLSurfaceOzoneSurfaceless::FenceRetired(EGLSyncKHR fence,
+                                             PendingFrame* frame) {
+  eglDestroySyncKHR(GetDisplay(), fence);
+  frame->ready = true;
+  SubmitFrame();
+}
+
+void GLSurfaceOzoneSurfaceless::SwapCompleted(
+    const SwapCompletionCallback& callback,
+    gfx::SwapResult result) {
+  callback.Run(result);
+  swap_buffers_pending_ = false;
+
+  SubmitFrame();
+}
+
 // This provides surface-like semantics implemented through surfaceless.
 // A framebuffer is bound automatically.
 class GL_EXPORT GLSurfaceOzoneSurfacelessSurfaceImpl
@@ -246,142 +422,41 @@
  public:
   GLSurfaceOzoneSurfacelessSurfaceImpl(
       scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
-      AcceleratedWidget widget)
-      : GLSurfaceOzoneSurfaceless(ozone_surface.Pass(), widget),
-        fbo_(0),
-        current_surface_(0) {
-    for (auto& texture : textures_)
-      texture = 0;
-  }
+      AcceleratedWidget widget,
+      const gfx::SurfaceConfiguration& requested_configuration);
 
-  unsigned int GetBackingFrameBufferObject() override { return fbo_; }
-
-  bool OnMakeCurrent(GLContext* context) override {
-    if (!fbo_) {
-      glGenFramebuffersEXT(1, &fbo_);
-      if (!fbo_)
-        return false;
-      glGenTextures(arraysize(textures_), textures_);
-      if (!CreatePixmaps())
-        return false;
-    }
-    BindFramebuffer();
-    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_);
-    return SurfacelessEGL::OnMakeCurrent(context);
-  }
-
-  bool Resize(const gfx::Size& size) override {
-    if (size == GetSize())
-      return true;
-    return GLSurfaceOzoneSurfaceless::Resize(size) && CreatePixmaps();
-  }
-
-  bool SupportsPostSubBuffer() override { return false; }
-
-  bool SwapBuffers() override {
-    if (!images_[current_surface_]->ScheduleOverlayPlane(
-            widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE,
-            gfx::Rect(GetSize()), gfx::RectF(1, 1)))
-      return false;
-    if (!GLSurfaceOzoneSurfaceless::SwapBuffers())
-      return false;
-    current_surface_ ^= 1;
-    BindFramebuffer();
-    return true;
-  }
-
-  bool SwapBuffersAsync(const SwapCompletionCallback& callback) override {
-    if (!images_[current_surface_]->ScheduleOverlayPlane(
-            widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE,
-            gfx::Rect(GetSize()), gfx::RectF(1, 1)))
-      return false;
-    if (!GLSurfaceOzoneSurfaceless::SwapBuffersAsync(callback))
-      return false;
-    current_surface_ ^= 1;
-    BindFramebuffer();
-    return true;
-  }
-
-  void Destroy() override {
-    GLContext* current_context = GLContext::GetCurrent();
-    DCHECK(current_context && current_context->IsCurrent(this));
-    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
-    if (fbo_) {
-      glDeleteTextures(arraysize(textures_), textures_);
-      for (auto& texture : textures_)
-        texture = 0;
-      glDeleteFramebuffersEXT(1, &fbo_);
-      fbo_ = 0;
-    }
-    for (auto image : images_) {
-      if (image)
-        image->Destroy(true);
-    }
-  }
+  // GLSurface:
+  unsigned int GetBackingFrameBufferObject() override;
+  bool OnMakeCurrent(GLContext* context) override;
+  bool Resize(const gfx::Size& size) override;
+  bool SupportsPostSubBuffer() override;
+  gfx::SwapResult SwapBuffers() override;
+  bool SwapBuffersAsync(const SwapCompletionCallback& callback) override;
+  void Destroy() override;
 
  private:
   class SurfaceImage : public GLImageLinuxDMABuffer {
    public:
-    SurfaceImage(const gfx::Size& size, unsigned internalformat)
-        : GLImageLinuxDMABuffer(size, internalformat) {}
+    SurfaceImage(const gfx::Size& size, unsigned internalformat);
 
     bool Initialize(scoped_refptr<ui::NativePixmap> pixmap,
-                    gfx::GpuMemoryBuffer::Format format) {
-      base::FileDescriptor handle(pixmap->GetDmaBufFd(), false);
-      if (!GLImageLinuxDMABuffer::Initialize(handle, format,
-                                             pixmap->GetDmaBufPitch()))
-        return false;
-      pixmap_ = pixmap;
-      return true;
-    }
+                    gfx::GpuMemoryBuffer::Format format);
     bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                               int z_order,
                               gfx::OverlayTransform transform,
                               const gfx::Rect& bounds_rect,
-                              const gfx::RectF& crop_rect) override {
-      return ui::SurfaceFactoryOzone::GetInstance()->ScheduleOverlayPlane(
-          widget, z_order, transform, pixmap_, bounds_rect, crop_rect);
-    }
+                              const gfx::RectF& crop_rect) override;
 
    private:
-    ~SurfaceImage() override {}
+    ~SurfaceImage() override;
 
     scoped_refptr<ui::NativePixmap> pixmap_;
   };
 
-  ~GLSurfaceOzoneSurfacelessSurfaceImpl() override {
-    DCHECK(!fbo_);
-    for (size_t i = 0; i < arraysize(textures_); i++)
-      DCHECK(!textures_[i]) << "texture " << i << " not released";
-  }
+  ~GLSurfaceOzoneSurfacelessSurfaceImpl() override;
 
-  void BindFramebuffer() {
-    ScopedFrameBufferBinder fb(fbo_);
-    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                              GL_TEXTURE_2D, textures_[current_surface_], 0);
-  }
-
-  bool CreatePixmaps() {
-    if (!fbo_)
-      return true;
-    for (size_t i = 0; i < arraysize(textures_); i++) {
-      scoped_refptr<ui::NativePixmap> pixmap =
-          ui::SurfaceFactoryOzone::GetInstance()->CreateNativePixmap(
-              widget_, GetSize(), ui::SurfaceFactoryOzone::RGBA_8888,
-              ui::SurfaceFactoryOzone::SCANOUT);
-      if (!pixmap)
-        return false;
-      scoped_refptr<SurfaceImage> image = new SurfaceImage(GetSize(), GL_RGBA);
-      if (!image->Initialize(pixmap, gfx::GpuMemoryBuffer::Format::BGRA_8888))
-        return false;
-      images_[i] = image;
-      // Bind image to texture.
-      ScopedTextureBinder binder(GL_TEXTURE_2D, textures_[i]);
-      if (!images_[i]->BindTexImage(GL_TEXTURE_2D))
-        return false;
-    }
-    return true;
-  }
+  void BindFramebuffer();
+  bool CreatePixmaps();
 
   GLuint fbo_;
   GLuint textures_[2];
@@ -390,6 +465,156 @@
   DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfacelessSurfaceImpl);
 };
 
+GLSurfaceOzoneSurfacelessSurfaceImpl::SurfaceImage::SurfaceImage(
+    const gfx::Size& size,
+    unsigned internalformat)
+    : GLImageLinuxDMABuffer(size, internalformat) {
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::SurfaceImage::Initialize(
+    scoped_refptr<ui::NativePixmap> pixmap,
+    gfx::GpuMemoryBuffer::Format format) {
+  base::FileDescriptor handle(pixmap->GetDmaBufFd(), false);
+  if (!GLImageLinuxDMABuffer::Initialize(handle, format,
+                                         pixmap->GetDmaBufPitch()))
+    return false;
+  pixmap_ = pixmap;
+  return true;
+}
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::SurfaceImage::ScheduleOverlayPlane(
+    gfx::AcceleratedWidget widget,
+    int z_order,
+    gfx::OverlayTransform transform,
+    const gfx::Rect& bounds_rect,
+    const gfx::RectF& crop_rect) {
+  return pixmap_->ScheduleOverlayPlane(widget, z_order, transform, bounds_rect,
+                                       crop_rect);
+}
+
+GLSurfaceOzoneSurfacelessSurfaceImpl::SurfaceImage::~SurfaceImage() {
+}
+
+GLSurfaceOzoneSurfacelessSurfaceImpl::GLSurfaceOzoneSurfacelessSurfaceImpl(
+    scoped_ptr<ui::SurfaceOzoneEGL> ozone_surface,
+    AcceleratedWidget widget,
+    const gfx::SurfaceConfiguration& requested_configuration)
+    : GLSurfaceOzoneSurfaceless(
+        ozone_surface.Pass(), widget, requested_configuration),
+      fbo_(0),
+      current_surface_(0) {
+  for (auto& texture : textures_)
+    texture = 0;
+}
+
+unsigned int
+GLSurfaceOzoneSurfacelessSurfaceImpl::GetBackingFrameBufferObject() {
+  return fbo_;
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::OnMakeCurrent(GLContext* context) {
+  if (!fbo_) {
+    glGenFramebuffersEXT(1, &fbo_);
+    if (!fbo_)
+      return false;
+    glGenTextures(arraysize(textures_), textures_);
+    if (!CreatePixmaps())
+      return false;
+  }
+  BindFramebuffer();
+  glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_);
+  return SurfacelessEGL::OnMakeCurrent(context);
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::Resize(const gfx::Size& size) {
+  if (size == GetSize())
+    return true;
+  return GLSurfaceOzoneSurfaceless::Resize(size) && CreatePixmaps();
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::SupportsPostSubBuffer() {
+  return false;
+}
+
+gfx::SwapResult GLSurfaceOzoneSurfacelessSurfaceImpl::SwapBuffers() {
+  if (!images_[current_surface_]->ScheduleOverlayPlane(
+          widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE,
+          gfx::Rect(GetSize()), gfx::RectF(1, 1)))
+    return gfx::SwapResult::SWAP_FAILED;
+  gfx::SwapResult result = GLSurfaceOzoneSurfaceless::SwapBuffers();
+  if (result != gfx::SwapResult::SWAP_ACK)
+    return result;
+  current_surface_ ^= 1;
+  BindFramebuffer();
+  return gfx::SwapResult::SWAP_ACK;
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::SwapBuffersAsync(
+    const SwapCompletionCallback& callback) {
+  if (!images_[current_surface_]->ScheduleOverlayPlane(
+          widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE,
+          gfx::Rect(GetSize()), gfx::RectF(1, 1)))
+    return false;
+  if (!GLSurfaceOzoneSurfaceless::SwapBuffersAsync(callback))
+    return false;
+  current_surface_ ^= 1;
+  BindFramebuffer();
+  return true;
+}
+
+void GLSurfaceOzoneSurfacelessSurfaceImpl::Destroy() {
+  GLContext* current_context = GLContext::GetCurrent();
+  DCHECK(current_context && current_context->IsCurrent(this));
+  glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
+  if (fbo_) {
+    glDeleteTextures(arraysize(textures_), textures_);
+    for (auto& texture : textures_)
+      texture = 0;
+    glDeleteFramebuffersEXT(1, &fbo_);
+    fbo_ = 0;
+  }
+  for (auto image : images_) {
+    if (image)
+      image->Destroy(true);
+  }
+}
+
+GLSurfaceOzoneSurfacelessSurfaceImpl::~GLSurfaceOzoneSurfacelessSurfaceImpl() {
+  DCHECK(!fbo_);
+  for (size_t i = 0; i < arraysize(textures_); i++)
+    DCHECK(!textures_[i]) << "texture " << i << " not released";
+}
+
+void GLSurfaceOzoneSurfacelessSurfaceImpl::BindFramebuffer() {
+  ScopedFrameBufferBinder fb(fbo_);
+  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                            textures_[current_surface_], 0);
+}
+
+bool GLSurfaceOzoneSurfacelessSurfaceImpl::CreatePixmaps() {
+  if (!fbo_)
+    return true;
+  for (size_t i = 0; i < arraysize(textures_); i++) {
+    scoped_refptr<ui::NativePixmap> pixmap =
+        ui::OzonePlatform::GetInstance()
+            ->GetSurfaceFactoryOzone()
+            ->CreateNativePixmap(widget_, GetSize(),
+                                 ui::SurfaceFactoryOzone::BGRA_8888,
+                                 ui::SurfaceFactoryOzone::SCANOUT);
+    if (!pixmap)
+      return false;
+    scoped_refptr<SurfaceImage> image =
+        new SurfaceImage(GetSize(), GL_BGRA_EXT);
+    if (!image->Initialize(pixmap, gfx::GpuMemoryBuffer::Format::BGRA_8888))
+      return false;
+    images_[i] = image;
+    // Bind image to texture.
+    ScopedTextureBinder binder(GL_TEXTURE_2D, textures_[i]);
+    if (!images_[i]->BindTexImage(GL_TEXTURE_2D))
+      return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 // static
@@ -412,18 +637,24 @@
 
 // static
 scoped_refptr<GLSurface> GLSurface::CreateSurfacelessViewGLSurface(
-    gfx::AcceleratedWidget window) {
+    gfx::AcceleratedWidget window,
+    const gfx::SurfaceConfiguration& requested_configuration) {
   if (GetGLImplementation() == kGLImplementationEGLGLES2 &&
       window != kNullAcceleratedWidget &&
       GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
-      ui::SurfaceFactoryOzone::GetInstance()->CanShowPrimaryPlaneAsOverlay()) {
+      ui::OzonePlatform::GetInstance()
+          ->GetSurfaceFactoryOzone()
+          ->CanShowPrimaryPlaneAsOverlay()) {
     scoped_ptr<ui::SurfaceOzoneEGL> surface_ozone =
-        ui::SurfaceFactoryOzone::GetInstance()
+        ui::OzonePlatform::GetInstance()
+            ->GetSurfaceFactoryOzone()
             ->CreateSurfacelessEGLSurfaceForWidget(window);
     if (!surface_ozone)
       return nullptr;
     scoped_refptr<GLSurface> surface;
-    surface = new GLSurfaceOzoneSurfaceless(surface_ozone.Pass(), window);
+    surface = new GLSurfaceOzoneSurfaceless(surface_ozone.Pass(),
+                                            window,
+                                            requested_configuration);
     if (surface->Initialize())
       return surface;
   }
@@ -433,9 +664,11 @@
 
 // static
 scoped_refptr<GLSurface> GLSurface::CreateViewGLSurface(
-    gfx::AcceleratedWidget window) {
+    gfx::AcceleratedWidget window,
+    const gfx::SurfaceConfiguration& requested_configuration) {
   if (GetGLImplementation() == kGLImplementationOSMesaGL) {
-    scoped_refptr<GLSurface> surface(new GLSurfaceOSMesaHeadless());
+    scoped_refptr<GLSurface> surface(
+      new GLSurfaceOSMesaHeadless(requested_configuration));
     if (!surface->Initialize())
       return NULL;
     return surface;
@@ -444,29 +677,34 @@
   if (window != kNullAcceleratedWidget) {
     scoped_refptr<GLSurface> surface;
     if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
-        ui::SurfaceFactoryOzone::GetInstance()
+        ui::OzonePlatform::GetInstance()
+            ->GetSurfaceFactoryOzone()
             ->CanShowPrimaryPlaneAsOverlay()) {
       scoped_ptr<ui::SurfaceOzoneEGL> surface_ozone =
-          ui::SurfaceFactoryOzone::GetInstance()
+          ui::OzonePlatform::GetInstance()
+              ->GetSurfaceFactoryOzone()
               ->CreateSurfacelessEGLSurfaceForWidget(window);
       if (!surface_ozone)
         return NULL;
-      surface = new GLSurfaceOzoneSurfacelessSurfaceImpl(surface_ozone.Pass(),
-                                                         window);
+      surface = new GLSurfaceOzoneSurfacelessSurfaceImpl(
+        surface_ozone.Pass(), window, requested_configuration);
     } else {
       scoped_ptr<ui::SurfaceOzoneEGL> surface_ozone =
-          ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget(
-              window);
+          ui::OzonePlatform::GetInstance()
+              ->GetSurfaceFactoryOzone()
+              ->CreateEGLSurfaceForWidget(window);
       if (!surface_ozone)
         return NULL;
 
-      surface = new GLSurfaceOzoneEGL(surface_ozone.Pass(), window);
+      surface = new GLSurfaceOzoneEGL(
+        surface_ozone.Pass(), window, requested_configuration);
     }
     if (!surface->Initialize())
       return NULL;
     return surface;
   } else {
-    scoped_refptr<GLSurface> surface = new GLSurfaceStub();
+    scoped_refptr<GLSurface> surface = new GLSurfaceStub(
+      requested_configuration);
     if (surface->Initialize())
       return surface;
   }
@@ -475,11 +713,12 @@
 
 // static
 scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface(
-    const gfx::Size& size) {
+    const gfx::Size& size,
+    const gfx::SurfaceConfiguration& requested_configuration) {
   switch (GetGLImplementation()) {
     case kGLImplementationOSMesaGL: {
-      scoped_refptr<GLSurface> surface(
-          new GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, size));
+      scoped_refptr<GLSurface> surface(new GLSurfaceOSMesa(
+        OSMesaSurfaceFormatBGRA, size, requested_configuration));
       if (!surface->Initialize())
         return NULL;
 
@@ -489,14 +728,16 @@
       scoped_refptr<GLSurface> surface;
       if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
           (size.width() == 0 && size.height() == 0)) {
-        surface = new SurfacelessEGL(size);
+        surface = new SurfacelessEGL(size, requested_configuration);
       } else
-        surface = new PbufferGLSurfaceEGL(size);
+        surface = new PbufferGLSurfaceEGL(size, requested_configuration);
 
       if (!surface->Initialize())
         return NULL;
       return surface;
     }
+    case kGLImplementationMockGL:
+      return new GLSurfaceStub(requested_configuration);
     default:
       NOTREACHED();
       return NULL;
@@ -504,7 +745,9 @@
 }
 
 EGLNativeDisplayType GetPlatformDefaultEGLNativeDisplay() {
-  return ui::SurfaceFactoryOzone::GetInstance()->GetNativeDisplay();
+  return ui::OzonePlatform::GetInstance()
+      ->GetSurfaceFactoryOzone()
+      ->GetNativeDisplay();
 }
 
 }  // namespace gfx
diff --git a/ui/gl/gl_surface_stub.cc b/ui/gl/gl_surface_stub.cc
index 70230fc..9c44023 100644
--- a/ui/gl/gl_surface_stub.cc
+++ b/ui/gl/gl_surface_stub.cc
@@ -18,8 +18,8 @@
   return false;
 }
 
-bool GLSurfaceStub::SwapBuffers() {
-  return true;
+gfx::SwapResult GLSurfaceStub::SwapBuffers() {
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 gfx::Size GLSurfaceStub::GetSize() {
diff --git a/ui/gl/gl_surface_stub.h b/ui/gl/gl_surface_stub.h
index f35e716..70a8936 100644
--- a/ui/gl/gl_surface_stub.h
+++ b/ui/gl/gl_surface_stub.h
@@ -20,7 +20,7 @@
   // Implement GLSurface.
   void Destroy() override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   gfx::Size GetSize() override;
   void* GetHandle() override;
 
diff --git a/ui/gl/gl_surface_x11.cc b/ui/gl/gl_surface_x11.cc
index efbaf60..8255a08 100644
--- a/ui/gl/gl_surface_x11.cc
+++ b/ui/gl/gl_surface_x11.cc
@@ -34,9 +34,9 @@
   void Destroy() override;
   bool Resize(const gfx::Size& new_size) override;
   bool IsOffscreen() override;
-  bool SwapBuffers() override;
+  gfx::SwapResult SwapBuffers() override;
   bool SupportsPostSubBuffer() override;
-  bool PostSubBuffer(int x, int y, int width, int height) override;
+  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
 
  protected:
   ~NativeViewGLSurfaceOSMesa() override;
@@ -186,7 +186,7 @@
   return false;
 }
 
-bool NativeViewGLSurfaceOSMesa::SwapBuffers() {
+gfx::SwapResult NativeViewGLSurfaceOSMesa::SwapBuffers() {
   TRACE_EVENT2("gpu", "NativeViewGLSurfaceOSMesa:RealSwapBuffers",
       "width", GetSize().width(),
       "height", GetSize().height());
@@ -196,7 +196,7 @@
   XWindowAttributes attributes;
   if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
     LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
-    return false;
+    return gfx::SwapResult::SWAP_FAILED;
   }
 
   // Copy the frame into the pixmap.
@@ -221,14 +221,14 @@
             0,
             0);
 
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() {
   return true;
 }
 
-bool NativeViewGLSurfaceOSMesa::PostSubBuffer(
+gfx::SwapResult NativeViewGLSurfaceOSMesa::PostSubBuffer(
     int x, int y, int width, int height) {
   gfx::Size size = GetSize();
 
@@ -238,7 +238,7 @@
   XWindowAttributes attributes;
   if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
     LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
-    return false;
+    return gfx::SwapResult::SWAP_FAILED;
   }
 
   // Copy the frame into the pixmap.
@@ -269,7 +269,7 @@
             x,
             y);
 
-  return true;
+  return gfx::SwapResult::SWAP_ACK;
 }
 
 NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() {
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
new file mode 100644
index 0000000..117988b
--- /dev/null
+++ b/ui/ozone/BUILD.gn
@@ -0,0 +1,201 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/ozone/ozone.gni")
+import("//testing/test.gni")
+
+# The list of platforms that will be built.
+ozone_platforms = []
+
+# Extra dependencies to pull into ui/ozone for built platforms.
+ozone_platform_deps = []
+
+# Extra dependencies to pull into ozone_unittests for built platforms.
+ozone_platform_test_deps = []
+
+if (ozone_platform_egltest) {
+  ozone_platforms += [ "egltest" ]
+  ozone_platform_deps += [ "platform/egltest" ]
+}
+
+if (ozone_platform_test) {
+  ozone_platforms += [ "test" ]
+  ozone_platform_deps += [ "platform/test" ]
+}
+
+if (ozone_platform_caca) {
+  ozone_platforms += [ "caca" ]
+  ozone_platform_deps += [ "platform/caca" ]
+}
+
+if (ozone_platform_dri || ozone_platform_drm) {
+  ozone_platforms += [
+    "dri",
+    "drm",
+  ]
+  ozone_platform_deps += [
+    "platform/drm",
+    "platform/drm:drm_common",
+  ]
+  ozone_platform_test_deps += [ "platform/drm:drm_unittests" ]
+}
+
+if (ozone_platform_gbm) {
+  ozone_platforms += [ "gbm" ]
+  ozone_platform_deps += [
+    "platform/drm:drm_common",
+    "platform/drm:gbm",
+  ]
+}
+
+platform_list_cc_file = "$target_gen_dir/platform_list.cc"
+platform_list_h_file = "$target_gen_dir/platform_list.h"
+platform_list_txt_file = "$target_gen_dir/platform_list.txt"
+constructor_list_cc_file = "$target_gen_dir/constructor_list.cc"
+
+# GYP version: ui/ozone/ozone.gyp:ozone_base
+component("ozone_base") {
+  sources = [
+    "public/cursor_factory_ozone.cc",
+    "public/cursor_factory_ozone.h",
+    "public/gpu_platform_support.cc",
+    "public/gpu_platform_support.h",
+    "public/gpu_platform_support_host.cc",
+    "public/gpu_platform_support_host.h",
+    "public/input_controller.cc",
+    "public/input_controller.h",
+    "public/overlay_candidates_ozone.cc",
+    "public/overlay_candidates_ozone.h",
+    "public/overlay_manager_ozone.h",
+    "public/surface_factory_ozone.cc",
+    "public/surface_factory_ozone.h",
+    "public/surface_ozone_canvas.h",
+    "public/surface_ozone_egl.cc",
+    "public/surface_ozone_egl.h",
+    "public/system_input_injector.h",
+  ]
+
+  defines = [ "OZONE_BASE_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/events:events_base",
+    "//ui/events:dom_keycode_converter",
+    "//ui/gfx/geometry",
+    "//ui/gfx",
+  ]
+}
+
+component("ozone") {
+  sources = [
+    "common/display_mode_proxy.cc",
+    "common/display_mode_proxy.h",
+    "common/display_snapshot_proxy.cc",
+    "common/display_snapshot_proxy.h",
+    "common/display_util.cc",
+    "common/display_util.h",
+    "common/egl_util.cc",
+    "common/egl_util.h",
+    "common/gpu/ozone_gpu_message_params.cc",
+    "common/gpu/ozone_gpu_message_params.h",
+    "common/native_display_delegate_ozone.cc",
+    "common/native_display_delegate_ozone.h",
+    "common/stub_overlay_manager.cc",
+    "common/stub_overlay_manager.h",
+    "platform_selection.cc",
+    "platform_selection.h",
+    "public/ozone_platform.cc",
+    "public/ozone_platform.h",
+    "public/ozone_switches.cc",
+    "public/ozone_switches.h",
+    constructor_list_cc_file,
+    platform_list_cc_file,
+    platform_list_h_file,
+  ]
+
+  defines = [ "OZONE_IMPLEMENTATION" ]
+
+  deps =
+      [
+        ":generate_constructor_list",
+        ":generate_ozone_platform_list",
+        ":ozone_base",
+        "//base",
+        "//skia",
+        "//ui/display/types",
+        "//ui/display/util",
+        "//ui/events",
+        "//ui/events/devices",
+        "//ui/events/ozone:events_ozone",
+        "//ui/gfx",
+        "//ui/gfx/geometry",
+
+        # TODO(GYP) the GYP version has a way to add additional dependencies via
+        # build flags.
+      ] + ozone_platform_deps
+
+  allow_circular_includes_from = [
+    "platform/egltest",
+
+    #"platform/drm",
+    "platform/drm:drm_common",
+    "platform/drm:gbm",
+    "//ui/events/ozone:events_ozone",
+  ]
+}
+
+# GYP version: ui/ozone/ozone.gyp:generate_ozone_platform_list
+action("generate_ozone_platform_list") {
+  script = "generate_ozone_platform_list.py"
+  outputs = [
+    platform_list_cc_file,
+    platform_list_h_file,
+    platform_list_txt_file,
+  ]
+
+  args =
+      [
+        "--output_cc=" + rebase_path(platform_list_cc_file, root_build_dir),
+        "--output_h=" + rebase_path(platform_list_h_file, root_build_dir),
+        "--output_txt=" + rebase_path(platform_list_txt_file, root_build_dir),
+        "--default=$ozone_platform",
+      ] + ozone_platforms
+}
+
+# GYP version: ui/ozone/ozone.gyp:generate_constructor_list
+action("generate_constructor_list") {
+  script = "generate_constructor_list.py"
+
+  inputs = [
+    platform_list_txt_file,
+  ]
+  outputs = [
+    constructor_list_cc_file,
+  ]
+
+  args = [
+    "--platform_list=" + rebase_path(platform_list_txt_file, root_build_dir),
+    "--output_cc=" + rebase_path(constructor_list_cc_file, root_build_dir),
+    "--namespace=ui",
+    "--typename=OzonePlatform",
+    "--include=\"ui/ozone/public/ozone_platform.h\"",
+  ]
+
+  deps = [
+    ":generate_ozone_platform_list",
+  ]
+}
+
+test("ozone_unittests") {
+  sources = [
+    "run_all_unittests.cc",
+  ]
+
+  deps = [
+           "//base/test:test_support",
+           "//testing/gtest",
+           "//ui/gfx/geometry",
+         ] + ozone_platform_test_deps
+}
diff --git a/ui/ozone/PRESUBMIT.py b/ui/ozone/PRESUBMIT.py
new file mode 100644
index 0000000..6b822c9
--- /dev/null
+++ b/ui/ozone/PRESUBMIT.py
@@ -0,0 +1,14 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for ozone.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
+  return results
diff --git a/ui/ozone/common/README b/ui/ozone/common/README
new file mode 100644
index 0000000..1ab9e67
--- /dev/null
+++ b/ui/ozone/common/README
@@ -0,0 +1,2 @@
+This directory contains code that is used by multiple platforms, but is not part
+of the public platform API. This code should not be used outside of ui/ozone.
diff --git a/ui/ozone/common/display_mode_proxy.cc b/ui/ozone/common/display_mode_proxy.cc
new file mode 100644
index 0000000..6890b33
--- /dev/null
+++ b/ui/ozone/common/display_mode_proxy.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/display_mode_proxy.h"
+
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+namespace ui {
+
+DisplayModeProxy::DisplayModeProxy(const DisplayMode_Params& params)
+    : DisplayMode(params.size, params.is_interlaced, params.refresh_rate) {
+}
+
+DisplayModeProxy::~DisplayModeProxy() {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/display_mode_proxy.h b/ui/ozone/common/display_mode_proxy.h
new file mode 100644
index 0000000..f9211c1
--- /dev/null
+++ b/ui/ozone/common/display_mode_proxy.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_DISPLAY_MODE_PROXY_H_
+#define UI_OZONE_COMMON_DISPLAY_MODE_PROXY_H_
+
+#include "ui/display/types/display_mode.h"
+
+namespace ui {
+
+struct DisplayMode_Params;
+
+class DisplayModeProxy : public DisplayMode {
+ public:
+  DisplayModeProxy(const DisplayMode_Params& params);
+  ~DisplayModeProxy() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisplayModeProxy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_DISPLAY_MODE_PROXY_H_
diff --git a/ui/ozone/common/display_snapshot_proxy.cc b/ui/ozone/common/display_snapshot_proxy.cc
new file mode 100644
index 0000000..4f6556c
--- /dev/null
+++ b/ui/ozone/common/display_snapshot_proxy.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/display_snapshot_proxy.h"
+
+#include "ui/ozone/common/display_mode_proxy.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+namespace ui {
+
+namespace {
+
+bool SameModes(const DisplayMode_Params& lhs, const DisplayMode_Params& rhs) {
+  return lhs.size == rhs.size && lhs.is_interlaced == rhs.is_interlaced &&
+         lhs.refresh_rate == rhs.refresh_rate;
+}
+
+// Exclude 4K@60kHz becaseu this doesn't work in most devices/configuration now.
+// TODO(marcheu|oshima): Revisit this. crbug.com/39397
+bool IsModeBlackListed(const DisplayMode_Params& mode_params) {
+  return mode_params.size.width() >= 3840 && mode_params.size.width() >= 2160 &&
+         mode_params.refresh_rate >= 60.0f;
+}
+
+}  // namespace
+
+DisplaySnapshotProxy::DisplaySnapshotProxy(const DisplaySnapshot_Params& params)
+    : DisplaySnapshot(params.display_id,
+                      params.origin,
+                      params.physical_size,
+                      params.type,
+                      params.is_aspect_preserving_scaling,
+                      params.has_overscan,
+                      params.display_name,
+                      std::vector<const DisplayMode*>(),
+                      NULL,
+                      NULL),
+      string_representation_(params.string_representation) {
+  for (size_t i = 0; i < params.modes.size(); ++i) {
+    const DisplayMode_Params& mode_params = params.modes[i];
+    if (IsModeBlackListed(mode_params))
+      continue;
+    modes_.push_back(new DisplayModeProxy(mode_params));
+
+    if (params.has_current_mode &&
+        SameModes(params.modes[i], params.current_mode))
+      current_mode_ = modes_.back();
+
+    if (params.has_native_mode &&
+        SameModes(params.modes[i], params.native_mode))
+      native_mode_ = modes_.back();
+  }
+
+  product_id_ = params.product_id;
+}
+
+DisplaySnapshotProxy::~DisplaySnapshotProxy() {
+}
+
+std::string DisplaySnapshotProxy::ToString() const {
+  return string_representation_;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/display_snapshot_proxy.h b/ui/ozone/common/display_snapshot_proxy.h
new file mode 100644
index 0000000..158f584
--- /dev/null
+++ b/ui/ozone/common/display_snapshot_proxy.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_
+#define UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_
+
+#include "ui/display/types/display_snapshot.h"
+
+namespace ui {
+
+struct DisplaySnapshot_Params;
+
+class DisplaySnapshotProxy : public DisplaySnapshot {
+ public:
+  DisplaySnapshotProxy(const DisplaySnapshot_Params& params);
+  ~DisplaySnapshotProxy() override;
+
+  // DisplaySnapshot override:
+  std::string ToString() const override;
+
+ private:
+  std::string string_representation_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotProxy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_
diff --git a/ui/ozone/common/display_util.cc b/ui/ozone/common/display_util.cc
new file mode 100644
index 0000000..ffa4670
--- /dev/null
+++ b/ui/ozone/common/display_util.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/display_util.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/util/edid_parser.h"
+#include "ui/display/util/edid_parser.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+namespace {
+
+const int64_t kDummyDisplayId = 1;
+const int64_t kDummyProductId = 1;
+
+}  // namespace
+
+FindDisplayById::FindDisplayById(int64_t display_id) : display_id_(display_id) {
+}
+
+bool FindDisplayById::operator()(const DisplaySnapshot_Params& display) const {
+  return display.display_id == display_id_;
+}
+
+DisplayMode_Params GetDisplayModeParams(const DisplayMode& mode) {
+  DisplayMode_Params params;
+  params.size = mode.size();
+  params.is_interlaced = mode.is_interlaced();
+  params.refresh_rate = mode.refresh_rate();
+
+  return params;
+}
+
+DisplaySnapshot_Params GetDisplaySnapshotParams(
+    const DisplaySnapshot& display) {
+  DisplaySnapshot_Params params;
+  params.display_id = display.display_id();
+  params.origin = display.origin();
+  params.physical_size = display.physical_size();
+  params.type = display.type();
+  params.is_aspect_preserving_scaling = display.is_aspect_preserving_scaling();
+  params.has_overscan = display.has_overscan();
+  params.display_name = display.display_name();
+  for (size_t i = 0; i < display.modes().size(); ++i)
+    params.modes.push_back(GetDisplayModeParams(*display.modes()[i]));
+
+  params.has_current_mode = display.current_mode() != NULL;
+  if (params.has_current_mode)
+    params.current_mode = GetDisplayModeParams(*display.current_mode());
+
+  params.has_native_mode = display.native_mode() != NULL;
+  if (params.has_native_mode)
+    params.native_mode = GetDisplayModeParams(*display.native_mode());
+
+  params.product_id = display.product_id();
+  params.string_representation = display.ToString();
+
+  return params;
+}
+
+bool CreateSnapshotFromCommandLine(DisplaySnapshot_Params* snapshot_out) {
+  base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+  std::string spec =
+      cmd->GetSwitchValueASCII(switches::kOzoneInitialDisplayBounds);
+  std::string physical_spec =
+      cmd->GetSwitchValueASCII(switches::kOzoneInitialDisplayPhysicalSizeMm);
+
+  int width = 0;
+  int height = 0;
+  if (spec.empty() || sscanf(spec.c_str(), "%dx%d", &width, &height) < 2 ||
+      width == 0 || height == 0) {
+    return false;
+  }
+
+  int physical_width = 0;
+  int physical_height = 0;
+  sscanf(physical_spec.c_str(), "%dx%d", &physical_width, &physical_height);
+
+  DisplayMode_Params mode_param;
+  mode_param.size = gfx::Size(width, height);
+  mode_param.refresh_rate = 60;
+
+  snapshot_out->display_id = kDummyDisplayId;
+  snapshot_out->modes.push_back(mode_param);
+  snapshot_out->type = DISPLAY_CONNECTION_TYPE_INTERNAL;
+  snapshot_out->physical_size = gfx::Size(physical_width, physical_height);
+  snapshot_out->has_current_mode = true;
+  snapshot_out->current_mode = mode_param;
+  snapshot_out->has_native_mode = true;
+  snapshot_out->native_mode = mode_param;
+  snapshot_out->product_id = kDummyProductId;
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/display_util.h b/ui/ozone/common/display_util.h
new file mode 100644
index 0000000..6289fdd
--- /dev/null
+++ b/ui/ozone/common/display_util.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_DISPLAY_UTIL_H_
+#define UI_OZONE_COMMON_DISPLAY_UTIL_H_
+
+#include <vector>
+
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace ui {
+
+class DisplayMode;
+class DisplaySnapshot;
+
+// Conforms to the std::UnaryPredicate interface such that it can be used to
+// find a display with |display_id| in std:: containers (ie: std::vector).
+class FindDisplayById {
+ public:
+  explicit FindDisplayById(int64_t display_id);
+
+  bool operator()(const DisplaySnapshot_Params& display) const;
+
+ private:
+  int64_t display_id_;
+};
+
+DisplayMode_Params GetDisplayModeParams(const DisplayMode& mode);
+DisplaySnapshot_Params GetDisplaySnapshotParams(const DisplaySnapshot& display);
+
+// Create a display using the Ozone command line parameters.
+// Return false if the command line flags are not specified.
+bool CreateSnapshotFromCommandLine(DisplaySnapshot_Params* snapshot_out);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_DISPLAY_UTIL_H_
diff --git a/ui/ozone/common/egl_util.cc b/ui/ozone/common/egl_util.cc
new file mode 100644
index 0000000..8000360
--- /dev/null
+++ b/ui/ozone/common/egl_util.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/egl_util.h"
+
+#include "base/files/file_path.h"
+
+namespace ui {
+namespace {
+
+const char kDefaultEglSoname[] = "libEGL.so.1";
+const char kDefaultGlesSoname[] = "libGLESv2.so.2";
+
+}  // namespace
+
+bool LoadDefaultEGLGLES2Bindings(
+    SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
+    SurfaceFactoryOzone::SetGLGetProcAddressProcCallback
+        set_gl_get_proc_address) {
+  return LoadEGLGLES2Bindings(add_gl_library, set_gl_get_proc_address,
+                              kDefaultEglSoname, kDefaultGlesSoname);
+}
+
+bool LoadEGLGLES2Bindings(
+    SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
+    SurfaceFactoryOzone::SetGLGetProcAddressProcCallback
+        set_gl_get_proc_address,
+    const char* egl_library_name,
+    const char* gles_library_name) {
+  base::NativeLibraryLoadError error;
+  base::NativeLibrary gles_library =
+      base::LoadNativeLibrary(base::FilePath(gles_library_name), &error);
+  if (!gles_library) {
+    LOG(WARNING) << "Failed to load GLES library: " << error.ToString();
+    return false;
+  }
+
+  base::NativeLibrary egl_library =
+      base::LoadNativeLibrary(base::FilePath(egl_library_name), &error);
+  if (!egl_library) {
+    LOG(WARNING) << "Failed to load EGL library: " << error.ToString();
+    base::UnloadNativeLibrary(gles_library);
+    return false;
+  }
+
+  SurfaceFactoryOzone::GLGetProcAddressProc get_proc_address =
+      reinterpret_cast<SurfaceFactoryOzone::GLGetProcAddressProc>(
+          base::GetFunctionPointerFromNativeLibrary(egl_library,
+                                                    "eglGetProcAddress"));
+  if (!get_proc_address) {
+    LOG(ERROR) << "eglGetProcAddress not found.";
+    base::UnloadNativeLibrary(egl_library);
+    base::UnloadNativeLibrary(gles_library);
+    return false;
+  }
+
+  set_gl_get_proc_address.Run(get_proc_address);
+  add_gl_library.Run(egl_library);
+  add_gl_library.Run(gles_library);
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/egl_util.h b/ui/ozone/common/egl_util.h
new file mode 100644
index 0000000..5cdfa42
--- /dev/null
+++ b/ui/ozone/common/egl_util.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_EGL_UTIL_H_
+#define UI_OZONE_COMMON_EGL_UTIL_H_
+
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+bool LoadDefaultEGLGLES2Bindings(
+    SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
+    SurfaceFactoryOzone::SetGLGetProcAddressProcCallback
+        set_gl_get_proc_address);
+
+bool LoadEGLGLES2Bindings(
+    SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library,
+    SurfaceFactoryOzone::SetGLGetProcAddressProcCallback
+        set_gl_get_proc_address,
+    const char* egl_library_name,
+    const char* gles_library_name);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_EGL_UTIL_H_
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.cc b/ui/ozone/common/gpu/ozone_gpu_message_params.cc
new file mode 100644
index 0000000..8460b53
--- /dev/null
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+#include "ui/gfx/geometry/rect_conversions.h"
+
+namespace ui {
+
+DisplayMode_Params::DisplayMode_Params() {
+}
+
+DisplayMode_Params::~DisplayMode_Params() {}
+
+DisplaySnapshot_Params::DisplaySnapshot_Params() {
+}
+
+DisplaySnapshot_Params::~DisplaySnapshot_Params() {}
+
+OverlayCheck_Params::OverlayCheck_Params() {
+}
+
+OverlayCheck_Params::OverlayCheck_Params(
+    const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate)
+    : buffer_size(candidate.buffer_size),
+      transform(candidate.transform),
+      format(candidate.format),
+      display_rect(gfx::ToNearestRect(candidate.display_rect)),
+      plane_z_order(candidate.plane_z_order) {
+}
+
+OverlayCheck_Params::~OverlayCheck_Params() {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.h b/ui/ozone/common/gpu/ozone_gpu_message_params.h
new file mode 100644
index 0000000..3c87890
--- /dev/null
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_GPU_OZONE_GPU_MESSAGE_PARAMS_H_
+#define UI_OZONE_COMMON_GPU_OZONE_GPU_MESSAGE_PARAMS_H_
+
+#include <string>
+#include <vector>
+
+#include "ui/display/types/display_constants.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+struct OZONE_EXPORT DisplayMode_Params {
+  DisplayMode_Params();
+  ~DisplayMode_Params();
+
+  gfx::Size size;
+  bool is_interlaced = false;
+  float refresh_rate = 0.0f;
+};
+
+struct OZONE_EXPORT DisplaySnapshot_Params {
+  DisplaySnapshot_Params();
+  ~DisplaySnapshot_Params();
+
+  int64_t display_id = 0;
+  gfx::Point origin;
+  gfx::Size physical_size;
+  DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_NONE;
+  bool is_aspect_preserving_scaling = false;
+  bool has_overscan = false;
+  std::string display_name;
+  std::vector<DisplayMode_Params> modes;
+  bool has_current_mode = false;
+  DisplayMode_Params current_mode;
+  bool has_native_mode = false;
+  DisplayMode_Params native_mode;
+  int64_t product_id = 0;
+  std::string string_representation;
+};
+
+struct OZONE_EXPORT OverlayCheck_Params {
+  OverlayCheck_Params();
+  OverlayCheck_Params(
+      const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate);
+  ~OverlayCheck_Params();
+
+  gfx::Size buffer_size;
+  gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_INVALID;
+  SurfaceFactoryOzone::BufferFormat format = SurfaceFactoryOzone::UNKNOWN;
+  gfx::Rect display_rect;
+  int plane_z_order = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_GPU_OZONE_GPU_MESSAGE_PARAMS_H_
diff --git a/ui/ozone/common/native_display_delegate_ozone.cc b/ui/ozone/common/native_display_delegate_ozone.cc
new file mode 100644
index 0000000..a7f7d1f
--- /dev/null
+++ b/ui/ozone/common/native_display_delegate_ozone.cc
@@ -0,0 +1,126 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+
+#include "base/logging.h"
+#include "ui/ozone/common/display_snapshot_proxy.h"
+#include "ui/ozone/common/display_util.h"
+
+namespace ui {
+
+NativeDisplayDelegateOzone::NativeDisplayDelegateOzone() {
+}
+
+NativeDisplayDelegateOzone::~NativeDisplayDelegateOzone() {
+}
+
+void NativeDisplayDelegateOzone::Initialize() {
+  DisplaySnapshot_Params params;
+  if (CreateSnapshotFromCommandLine(&params)) {
+    DCHECK_NE(DISPLAY_CONNECTION_TYPE_NONE, params.type);
+    displays_.push_back(new DisplaySnapshotProxy(params));
+  }
+}
+
+void NativeDisplayDelegateOzone::GrabServer() {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::UngrabServer() {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::TakeDisplayControl(
+    const DisplayControlCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(false);
+}
+
+void NativeDisplayDelegateOzone::RelinquishDisplayControl(
+    const DisplayControlCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(false);
+}
+
+void NativeDisplayDelegateOzone::SyncWithServer() {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::SetBackgroundColor(uint32_t color_argb) {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::ForceDPMSOn() {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::GetDisplays(
+    const GetDisplaysCallback& callback) {
+  callback.Run(displays_.get());
+}
+
+void NativeDisplayDelegateOzone::AddMode(const ui::DisplaySnapshot& output,
+                                         const ui::DisplayMode* mode) {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::Configure(const ui::DisplaySnapshot& output,
+                                           const ui::DisplayMode* mode,
+                                           const gfx::Point& origin,
+                                           const ConfigureCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(true);
+}
+
+void NativeDisplayDelegateOzone::CreateFrameBuffer(const gfx::Size& size) {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::GetHDCPState(
+    const ui::DisplaySnapshot& output,
+    const GetHDCPStateCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(false, HDCP_STATE_UNDESIRED);
+}
+
+void NativeDisplayDelegateOzone::SetHDCPState(
+    const ui::DisplaySnapshot& output,
+    ui::HDCPState state,
+    const SetHDCPStateCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(false);
+}
+
+std::vector<ui::ColorCalibrationProfile>
+NativeDisplayDelegateOzone::GetAvailableColorCalibrationProfiles(
+    const ui::DisplaySnapshot& output) {
+  NOTIMPLEMENTED();
+  return std::vector<ui::ColorCalibrationProfile>();
+}
+
+bool NativeDisplayDelegateOzone::SetColorCalibrationProfile(
+    const ui::DisplaySnapshot& output,
+    ui::ColorCalibrationProfile new_profile) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool NativeDisplayDelegateOzone::SetGammaRamp(
+    const ui::DisplaySnapshot& output,
+    const std::vector<GammaRampRGBEntry>& lut) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void NativeDisplayDelegateOzone::AddObserver(NativeDisplayObserver* observer) {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateOzone::RemoveObserver(
+    NativeDisplayObserver* observer) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/native_display_delegate_ozone.h b/ui/ozone/common/native_display_delegate_ozone.h
new file mode 100644
index 0000000..e057ea0
--- /dev/null
+++ b/ui/ozone/common/native_display_delegate_ozone.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_NATIVE_DISPLAY_DELEGATE_OZONE_H_
+#define UI_OZONE_COMMON_NATIVE_DISPLAY_DELEGATE_OZONE_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/display/types/native_display_delegate.h"
+
+namespace ui {
+
+class NativeDisplayDelegateOzone : public NativeDisplayDelegate {
+ public:
+  NativeDisplayDelegateOzone();
+  ~NativeDisplayDelegateOzone() override;
+
+  // NativeDisplayDelegate overrides:
+  void Initialize() override;
+  void GrabServer() override;
+  void UngrabServer() override;
+  void TakeDisplayControl(const DisplayControlCallback& callback) override;
+  void RelinquishDisplayControl(
+      const DisplayControlCallback& callback) override;
+  void SyncWithServer() override;
+  void SetBackgroundColor(uint32_t color_argb) override;
+  void ForceDPMSOn() override;
+  void GetDisplays(const GetDisplaysCallback& callback) override;
+  void AddMode(const ui::DisplaySnapshot& output,
+               const ui::DisplayMode* mode) override;
+  void Configure(const ui::DisplaySnapshot& output,
+                 const ui::DisplayMode* mode,
+                 const gfx::Point& origin,
+                 const ConfigureCallback& callback) override;
+  void CreateFrameBuffer(const gfx::Size& size) override;
+  void GetHDCPState(const ui::DisplaySnapshot& output,
+                    const GetHDCPStateCallback& callback) override;
+  void SetHDCPState(const ui::DisplaySnapshot& output,
+                    ui::HDCPState state,
+                    const SetHDCPStateCallback& callback) override;
+  std::vector<ui::ColorCalibrationProfile> GetAvailableColorCalibrationProfiles(
+      const ui::DisplaySnapshot& output) override;
+  bool SetColorCalibrationProfile(
+      const ui::DisplaySnapshot& output,
+      ui::ColorCalibrationProfile new_profile) override;
+  bool SetGammaRamp(const ui::DisplaySnapshot& output,
+                    const std::vector<GammaRampRGBEntry>& lut) override;
+
+  void AddObserver(NativeDisplayObserver* observer) override;
+  void RemoveObserver(NativeDisplayObserver* observer) override;
+
+ private:
+  ScopedVector<DisplaySnapshot> displays_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateOzone);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_NATIVE_DISPLAY_DELEGATE_OZONE_H_
diff --git a/ui/ozone/common/stub_overlay_manager.cc b/ui/ozone/common/stub_overlay_manager.cc
new file mode 100644
index 0000000..45f8a5f
--- /dev/null
+++ b/ui/ozone/common/stub_overlay_manager.cc
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/common/stub_overlay_manager.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+namespace ui {
+
+StubOverlayManager::StubOverlayManager() {
+}
+
+StubOverlayManager::~StubOverlayManager() {
+}
+
+scoped_ptr<OverlayCandidatesOzone> StubOverlayManager::CreateOverlayCandidates(
+    gfx::AcceleratedWidget w) {
+  return nullptr;
+}
+
+bool StubOverlayManager::CanShowPrimaryPlaneAsOverlay() {
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/common/stub_overlay_manager.h b/ui/ozone/common/stub_overlay_manager.h
new file mode 100644
index 0000000..ba72ba0
--- /dev/null
+++ b/ui/ozone/common/stub_overlay_manager.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_COMMON_STUB_OVERLAY_MANAGER_H_
+#define UI_OZONE_COMMON_STUB_OVERLAY_MANAGER_H_
+
+#include "ui/ozone/public/overlay_manager_ozone.h"
+
+namespace ui {
+
+class StubOverlayManager : public OverlayManagerOzone {
+ public:
+  StubOverlayManager();
+  ~StubOverlayManager() override;
+
+  // OverlayManagerOzone:
+  scoped_ptr<OverlayCandidatesOzone> CreateOverlayCandidates(
+      gfx::AcceleratedWidget w) override;
+  bool CanShowPrimaryPlaneAsOverlay() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StubOverlayManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_COMMON_STUB_OVERLAY_MANAGER_H_
diff --git a/ui/ozone/demo/BUILD.gn b/ui/ozone/demo/BUILD.gn
new file mode 100644
index 0000000..ad78f75
--- /dev/null
+++ b/ui/ozone/demo/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("demo") {
+  deps = [
+    ":ozone_demo",
+  ]
+}
+
+executable("ozone_demo") {
+  sources = [
+    "gl_renderer.cc",
+    "gl_renderer.h",
+    "ozone_demo.cc",
+    "renderer.h",
+    "renderer_base.cc",
+    "renderer_base.h",
+    "software_renderer.cc",
+    "software_renderer.h",
+    "surfaceless_gl_renderer.cc",
+    "surfaceless_gl_renderer.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/events",
+    "//ui/events:dom_keycode_converter",
+    "//ui/events/ozone:events_ozone_layout",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//ui/ozone",
+    "//ui/ozone/gpu",
+    "//ui/ozone:ozone_base",
+    "//ui/display/types",
+    "//ui/platform_window",
+  ]
+}
diff --git a/ui/ozone/demo/gl_renderer.cc b/ui/ozone/demo/gl_renderer.cc
new file mode 100644
index 0000000..73592e4
--- /dev/null
+++ b/ui/ozone/demo/gl_renderer.cc
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/demo/gl_renderer.h"
+
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+
+namespace ui {
+
+GlRenderer::GlRenderer(gfx::AcceleratedWidget widget, const gfx::Size& size)
+    : RendererBase(widget, size), weak_ptr_factory_(this) {
+}
+
+GlRenderer::~GlRenderer() {
+}
+
+bool GlRenderer::Initialize() {
+  surface_ = CreateSurface();
+  if (!surface_.get()) {
+    LOG(ERROR) << "Failed to create GL surface";
+    return false;
+  }
+
+  context_ = gfx::GLContext::CreateGLContext(NULL, surface_.get(),
+                                             gfx::PreferIntegratedGpu);
+  if (!context_.get()) {
+    LOG(ERROR) << "Failed to create GL context";
+    return false;
+  }
+
+  if (!surface_->Resize(size_)) {
+    LOG(ERROR) << "Failed to resize GL surface";
+    return false;
+  }
+
+  if (!context_->MakeCurrent(surface_.get())) {
+    LOG(ERROR) << "Failed to make GL context current";
+    return false;
+  }
+
+  PostRenderFrameTask(gfx::SwapResult::SWAP_ACK);
+  return true;
+}
+
+void GlRenderer::RenderFrame() {
+  float fraction = NextFraction();
+
+  context_->MakeCurrent(surface_.get());
+
+  glViewport(0, 0, size_.width(), size_.height());
+  glClearColor(1 - fraction, fraction, 0.0, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  if (!surface_->SwapBuffersAsync(base::Bind(&GlRenderer::PostRenderFrameTask,
+                                             weak_ptr_factory_.GetWeakPtr())))
+    LOG(FATAL) << "Failed to swap buffers";
+}
+
+void GlRenderer::PostRenderFrameTask(gfx::SwapResult result) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(&GlRenderer::RenderFrame, weak_ptr_factory_.GetWeakPtr()));
+}
+
+scoped_refptr<gfx::GLSurface> GlRenderer::CreateSurface() {
+  gfx::SurfaceConfiguration config;
+  return gfx::GLSurface::CreateViewGLSurface(widget_, config);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/demo/gl_renderer.h b/ui/ozone/demo/gl_renderer.h
new file mode 100644
index 0000000..80e556b
--- /dev/null
+++ b/ui/ozone/demo/gl_renderer.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_DEMO_GL_RENDERER_H_
+#define UI_OZONE_DEMO_GL_RENDERER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/demo/renderer_base.h"
+
+namespace gfx {
+class GLContext;
+class GLSurface;
+}  // namespace gfx
+
+namespace ui {
+
+class GlRenderer : public RendererBase {
+ public:
+  GlRenderer(gfx::AcceleratedWidget widget, const gfx::Size& size);
+  ~GlRenderer() override;
+
+  void PostRenderFrameTask(gfx::SwapResult result);
+
+  // Renderer:
+  bool Initialize() override;
+
+ protected:
+  virtual void RenderFrame();
+  virtual scoped_refptr<gfx::GLSurface> CreateSurface();
+
+  scoped_refptr<gfx::GLSurface> surface_;
+  scoped_refptr<gfx::GLContext> context_;
+
+ private:
+  base::WeakPtrFactory<GlRenderer> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GlRenderer);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_DEMO_GL_RENDERER_H_
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
new file mode 100644
index 0000000..534b914
--- /dev/null
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -0,0 +1,346 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/ozone/demo/gl_renderer.h"
+#include "ui/ozone/demo/software_renderer.h"
+#include "ui/ozone/demo/surfaceless_gl_renderer.h"
+#include "ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+const int kTestWindowWidth = 800;
+const int kTestWindowHeight = 600;
+
+const char kDisableGpu[] = "disable-gpu";
+
+const char kWindowSize[] = "window-size";
+
+class DemoWindow;
+
+class RendererFactory {
+ public:
+  enum RendererType {
+    GL,
+    SURFACELESS_GL,
+    SOFTWARE,
+  };
+
+  RendererFactory();
+  ~RendererFactory();
+
+  bool Initialize();
+  scoped_ptr<ui::Renderer> CreateRenderer(gfx::AcceleratedWidget widget,
+                                          const gfx::Size& size);
+
+ private:
+  RendererType type_ = SOFTWARE;
+
+  // Helper for applications that do GL on main thread.
+  ui::OzoneGpuTestHelper gpu_helper_;
+
+  // Used by the surfaceless renderers to allocate buffers.
+  ui::GpuMemoryBufferFactoryOzoneNativeBuffer buffer_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererFactory);
+};
+
+class WindowManager : public ui::NativeDisplayObserver {
+ public:
+  WindowManager(const base::Closure& quit_closure);
+  ~WindowManager() override;
+
+  void Quit();
+
+  void AddWindow(DemoWindow* window);
+  void RemoveWindow(DemoWindow* window);
+
+ private:
+  void OnDisplaysAquired(const std::vector<ui::DisplaySnapshot*>& displays);
+  void OnDisplayConfigured(const gfx::Rect& bounds, bool success);
+
+  // ui::NativeDisplayDelegate:
+  void OnConfigurationChanged() override;
+
+  scoped_ptr<ui::NativeDisplayDelegate> delegate_;
+  base::Closure quit_closure_;
+  RendererFactory renderer_factory_;
+  ScopedVector<DemoWindow> windows_;
+
+  // Flags used to keep track of the current state of display configuration.
+  //
+  // True if configuring the displays. In this case a new display configuration
+  // isn't started.
+  bool is_configuring_ = false;
+
+  // If |is_configuring_| is true and another display configuration event
+  // happens, the event is deferred. This is set to true and a display
+  // configuration will be scheduled after the current one finishes.
+  bool should_configure_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowManager);
+};
+
+class DemoWindow : public ui::PlatformWindowDelegate {
+ public:
+  DemoWindow(WindowManager* window_manager,
+             RendererFactory* renderer_factory,
+             const gfx::Rect& bounds)
+      : window_manager_(window_manager),
+        renderer_factory_(renderer_factory),
+        weak_ptr_factory_(this) {
+    platform_window_ =
+        ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
+  }
+  ~DemoWindow() override {}
+
+  gfx::AcceleratedWidget GetAcceleratedWidget() {
+    // TODO(spang): We should start rendering asynchronously.
+    DCHECK_NE(widget_, gfx::kNullAcceleratedWidget)
+        << "Widget not available synchronously";
+    return widget_;
+  }
+
+  gfx::Size GetSize() { return platform_window_->GetBounds().size(); }
+
+  void Start() {
+    LOG(INFO) << "DemoWindow::Start";
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&DemoWindow::StartOnGpu, weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void Quit() {
+    window_manager_->Quit();
+  }
+
+  // PlatformWindowDelegate:
+  void OnBoundsChanged(const gfx::Rect& new_bounds) override {}
+  void OnDamageRect(const gfx::Rect& damaged_region) override {}
+  void DispatchEvent(ui::Event* event) override {
+    if (event->IsKeyEvent() &&
+        static_cast<ui::KeyEvent*>(event)->code() == ui::DomCode::KEY_Q)
+      Quit();
+  }
+  void OnCloseRequest() override { Quit(); }
+  void OnClosed() override {}
+  void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
+  void OnLostCapture() override {}
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+    LOG(INFO) << "OnAcceleratedWidgetAvailable " << widget;
+    DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
+    widget_ = widget;
+  }
+  void OnActivationChanged(bool active) override {}
+
+ private:
+  // Since we pretend to have a GPU process, we should also pretend to
+  // initialize the GPU resources via a posted task.
+  void StartOnGpu() {
+    LOG(INFO) << "StartOnGPU";
+    gfx::GLSurface::InitializeOneOff();
+
+    renderer_ =
+        renderer_factory_->CreateRenderer(GetAcceleratedWidget(), GetSize());
+    renderer_->Initialize();
+  }
+
+  WindowManager* window_manager_;      // Not owned.
+  RendererFactory* renderer_factory_;  // Not owned.
+
+  scoped_ptr<ui::Renderer> renderer_;
+
+  // Window-related state.
+  scoped_ptr<ui::PlatformWindow> platform_window_;
+  gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
+
+  base::WeakPtrFactory<DemoWindow> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DemoWindow);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// RendererFactory implementation:
+
+RendererFactory::RendererFactory() {
+}
+
+RendererFactory::~RendererFactory() {
+}
+
+bool RendererFactory::Initialize() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(kDisableGpu) &&
+      gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
+                             base::ThreadTaskRunnerHandle::Get())) {
+    if (command_line->HasSwitch(switches::kOzoneUseSurfaceless)) {
+      type_ = SURFACELESS_GL;
+    } else {
+      type_ = GL;
+    }
+  } else {
+    type_ = SOFTWARE;
+  }
+
+  return true;
+}
+
+scoped_ptr<ui::Renderer> RendererFactory::CreateRenderer(
+    gfx::AcceleratedWidget widget,
+    const gfx::Size& size) {
+  switch (type_) {
+    case GL:
+      return make_scoped_ptr(new ui::GlRenderer(widget, size));
+    case SURFACELESS_GL:
+      return make_scoped_ptr(
+          new ui::SurfacelessGlRenderer(widget, size, &buffer_factory_));
+    case SOFTWARE:
+      return make_scoped_ptr(new ui::SoftwareRenderer(widget, size));
+  }
+
+  return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowManager implementation:
+
+WindowManager::WindowManager(const base::Closure& quit_closure)
+    : delegate_(
+          ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()),
+      quit_closure_(quit_closure) {
+  if (!renderer_factory_.Initialize())
+    LOG(FATAL) << "Failed to initialize renderer factory";
+
+  LOG(INFO) << "WindowManager() " << delegate_;
+
+  if (delegate_) {
+    delegate_->AddObserver(this);
+    delegate_->Initialize();
+    OnConfigurationChanged();
+  } else {
+    LOG(WARNING) << "No display delegate; falling back to test window";
+    int width = kTestWindowWidth;
+    int height = kTestWindowHeight;
+    sscanf(base::CommandLine::ForCurrentProcess()
+               ->GetSwitchValueASCII(kWindowSize)
+               .c_str(),
+           "%dx%d", &width, &height);
+
+    DemoWindow* window = new DemoWindow(this, &renderer_factory_,
+                                        gfx::Rect(gfx::Size(width, height)));
+    window->Start();
+  }
+}
+
+WindowManager::~WindowManager() {
+  if (delegate_)
+    delegate_->RemoveObserver(this);
+}
+
+void WindowManager::Quit() {
+  quit_closure_.Run();
+}
+
+void WindowManager::OnConfigurationChanged() {
+  if (is_configuring_) {
+    should_configure_ = true;
+    return;
+  }
+
+  is_configuring_ = true;
+  delegate_->GrabServer();
+  delegate_->GetDisplays(
+      base::Bind(&WindowManager::OnDisplaysAquired, base::Unretained(this)));
+}
+
+void WindowManager::OnDisplaysAquired(
+    const std::vector<ui::DisplaySnapshot*>& displays) {
+  windows_.clear();
+
+  gfx::Point origin;
+  for (auto display : displays) {
+    if (!display->native_mode()) {
+      LOG(ERROR) << "Display " << display->display_id()
+                 << " doesn't have a native mode";
+      continue;
+    }
+
+    delegate_->Configure(
+        *display, display->native_mode(), origin,
+        base::Bind(&WindowManager::OnDisplayConfigured, base::Unretained(this),
+                   gfx::Rect(origin, display->native_mode()->size())));
+    origin.Offset(display->native_mode()->size().width(), 0);
+  }
+  delegate_->UngrabServer();
+  is_configuring_ = false;
+
+  if (should_configure_) {
+    should_configure_ = false;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&WindowManager::OnConfigurationChanged,
+                              base::Unretained(this)));
+  }
+}
+
+void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) {
+  LOG(INFO) << "OnDisplayConfigured " << success;
+  if (success) {
+    scoped_ptr<DemoWindow> window(
+        new DemoWindow(this, &renderer_factory_, bounds));
+    window->Start();
+    windows_.push_back(window.Pass());
+  } else {
+    LOG(ERROR) << "Failed to configure display at " << bounds.ToString();
+  }
+}
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  base::AtExitManager exit_manager;
+
+  // Initialize logging so we can enable VLOG messages.
+  logging::LoggingSettings settings;
+  logging::InitLogging(settings);
+
+  // Build UI thread message loop. This is used by platform
+  // implementations for event polling & running background tasks.
+  base::MessageLoopForUI message_loop;
+
+  ui::OzonePlatform::InitializeForUI();
+  LOG(INFO) << "InitializeForUI complete";
+
+  ui::OzonePlatform::InitializeForGPU();
+  LOG(INFO) << "InitializeForGPU complete";
+
+  ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()
+      ->SetCurrentLayoutByName("us");
+
+  base::RunLoop run_loop;
+
+  WindowManager window_manager(run_loop.QuitClosure());
+
+  LOG(INFO) << "demo entering run loop";
+  run_loop.Run();
+  LOG(INFO) << "demo returning from run loop";
+
+  return 0;
+}
diff --git a/ui/ozone/demo/renderer.h b/ui/ozone/demo/renderer.h
new file mode 100644
index 0000000..30d86af
--- /dev/null
+++ b/ui/ozone/demo/renderer.h
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_DEMO_RENDERER_H_
+#define UI_OZONE_DEMO_RENDERER_H_
+
+namespace ui {
+
+class Renderer {
+ public:
+  virtual ~Renderer() {}
+
+  virtual bool Initialize() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_DEMO_RENDERER_H_
diff --git a/ui/ozone/demo/renderer_base.cc b/ui/ozone/demo/renderer_base.cc
new file mode 100644
index 0000000..9d7d6e2
--- /dev/null
+++ b/ui/ozone/demo/renderer_base.cc
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/demo/renderer_base.h"
+
+namespace ui {
+
+namespace {
+const int kAnimationSteps = 240;
+}  // namespace
+
+RendererBase::RendererBase(gfx::AcceleratedWidget widget, const gfx::Size& size)
+    : widget_(widget), size_(size) {
+}
+
+RendererBase::~RendererBase() {
+}
+
+float RendererBase::NextFraction() {
+  float fraction = (sinf(iteration_ * 2 * M_PI / kAnimationSteps) + 1) / 2;
+
+  iteration_++;
+  iteration_ %= kAnimationSteps;
+
+  return fraction;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/demo/renderer_base.h b/ui/ozone/demo/renderer_base.h
new file mode 100644
index 0000000..8d7acd5
--- /dev/null
+++ b/ui/ozone/demo/renderer_base.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_DEMO_RENDERER_BASE_H_
+#define UI_OZONE_DEMO_RENDERER_BASE_H_
+
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/demo/renderer.h"
+
+namespace ui {
+
+class RendererBase : public Renderer {
+ public:
+  RendererBase(gfx::AcceleratedWidget widget, const gfx::Size& size);
+  ~RendererBase() override;
+
+ protected:
+  float NextFraction();
+
+  gfx::AcceleratedWidget widget_;
+  gfx::Size size_;
+
+  int iteration_ = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_DEMO_RENDERER_BASE_H_
diff --git a/ui/ozone/demo/software_renderer.cc b/ui/ozone/demo/software_renderer.cc
new file mode 100644
index 0000000..162d5e0
--- /dev/null
+++ b/ui/ozone/demo/software_renderer.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/demo/software_renderer.h"
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+namespace {
+
+const int kFrameDelayMilliseconds = 16;
+
+}  // namespace
+
+SoftwareRenderer::SoftwareRenderer(gfx::AcceleratedWidget widget,
+                                   const gfx::Size& size)
+    : RendererBase(widget, size),
+      vsync_period_(base::TimeDelta::FromMilliseconds(kFrameDelayMilliseconds)),
+      weak_ptr_factory_(this) {
+}
+
+SoftwareRenderer::~SoftwareRenderer() {
+}
+
+bool SoftwareRenderer::Initialize() {
+  software_surface_ = ui::OzonePlatform::GetInstance()
+                          ->GetSurfaceFactoryOzone()
+                          ->CreateCanvasForWidget(widget_);
+  if (!software_surface_) {
+    LOG(ERROR) << "Failed to create software surface";
+    return false;
+  }
+
+  software_surface_->ResizeCanvas(size_);
+  vsync_provider_ = software_surface_->CreateVSyncProvider();
+  RenderFrame();
+  return true;
+}
+
+void SoftwareRenderer::RenderFrame() {
+  float fraction = NextFraction();
+
+  skia::RefPtr<SkSurface> surface = software_surface_->GetSurface();
+
+  SkColor color =
+      SkColorSetARGB(0xff, 0, 0xff * fraction, 0xff * (1 - fraction));
+
+  surface->getCanvas()->clear(color);
+
+  software_surface_->PresentCanvas(gfx::Rect(size_));
+
+  if (vsync_provider_) {
+    vsync_provider_->GetVSyncParameters(
+        base::Bind(&SoftwareRenderer::UpdateVSyncParameters,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  timer_.Start(FROM_HERE, vsync_period_, this, &SoftwareRenderer::RenderFrame);
+}
+
+void SoftwareRenderer::UpdateVSyncParameters(const base::TimeTicks timebase,
+                                             const base::TimeDelta interval) {
+  vsync_period_ = interval;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/demo/software_renderer.h b/ui/ozone/demo/software_renderer.h
new file mode 100644
index 0000000..dcb4645
--- /dev/null
+++ b/ui/ozone/demo/software_renderer.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_DEMO_SOFTWARE_RENDERER_H_
+#define UI_OZONE_DEMO_SOFTWARE_RENDERER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "ui/ozone/demo/renderer_base.h"
+
+namespace gfx {
+class VSyncProvider;
+}  // namespace gfx
+
+namespace ui {
+
+class SurfaceOzoneCanvas;
+
+class SoftwareRenderer : public RendererBase {
+ public:
+  SoftwareRenderer(gfx::AcceleratedWidget widget, const gfx::Size& size);
+  ~SoftwareRenderer() override;
+
+  // Renderer:
+  bool Initialize() override;
+
+ private:
+  void RenderFrame();
+
+  void UpdateVSyncParameters(const base::TimeTicks timebase,
+                             const base::TimeDelta interval);
+
+  scoped_ptr<SurfaceOzoneCanvas> software_surface_;
+
+  scoped_ptr<gfx::VSyncProvider> vsync_provider_;
+
+  // Timer for animation.
+  base::RepeatingTimer<SoftwareRenderer> timer_;
+
+  base::TimeDelta vsync_period_;
+
+  base::WeakPtrFactory<SoftwareRenderer> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SoftwareRenderer);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_DEMO_SOFTWARE_RENDERER_H_
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc
new file mode 100644
index 0000000..f6f7060
--- /dev/null
+++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/demo/surfaceless_gl_renderer.h"
+
+#include "base/bind.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h"
+
+namespace ui {
+
+SurfacelessGlRenderer::BufferWrapper::BufferWrapper() {
+}
+
+SurfacelessGlRenderer::BufferWrapper::~BufferWrapper() {
+  if (gl_fb_)
+    glDeleteFramebuffersEXT(1, &gl_fb_);
+
+  if (gl_tex_) {
+    image_->ReleaseTexImage(GL_TEXTURE_2D);
+    glDeleteTextures(1, &gl_tex_);
+    image_->Destroy(true);
+  }
+}
+
+bool SurfacelessGlRenderer::BufferWrapper::Initialize(
+    GpuMemoryBufferFactoryOzoneNativeBuffer* buffer_factory,
+    gfx::AcceleratedWidget widget,
+    const gfx::Size& size) {
+  glGenFramebuffersEXT(1, &gl_fb_);
+  glGenTextures(1, &gl_tex_);
+
+  static int buffer_id_generator = 1;
+  int id = buffer_id_generator++;
+
+  buffer_factory->CreateGpuMemoryBuffer(
+      id, size, gfx::GpuMemoryBuffer::RGBX_8888, gfx::GpuMemoryBuffer::SCANOUT,
+      1, widget);
+  image_ = buffer_factory->CreateImageForGpuMemoryBuffer(
+      id, size, gfx::GpuMemoryBuffer::RGBX_8888, GL_RGB, 1);
+  // Now that we have a reference to |image_|; we can just remove it from the
+  // factory mapping.
+  buffer_factory->DestroyGpuMemoryBuffer(id, widget);
+
+  if (!image_) {
+    LOG(ERROR) << "Failed to create GL image";
+    return false;
+  }
+
+  glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
+  glBindTexture(GL_TEXTURE_2D, gl_tex_);
+  image_->BindTexImage(GL_TEXTURE_2D);
+
+  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                            gl_tex_, 0);
+  if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+    LOG(ERROR) << "Failed to create framebuffer "
+               << glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+    return false;
+  }
+
+  widget_ = widget;
+  size_ = size;
+
+  return true;
+}
+
+void SurfacelessGlRenderer::BufferWrapper::BindFramebuffer() {
+  glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
+}
+
+void SurfacelessGlRenderer::BufferWrapper::SchedulePlane() {
+  image_->ScheduleOverlayPlane(widget_, 0, gfx::OVERLAY_TRANSFORM_NONE,
+                               gfx::Rect(size_), gfx::RectF(0, 0, 1, 1));
+}
+
+SurfacelessGlRenderer::SurfacelessGlRenderer(
+    gfx::AcceleratedWidget widget,
+    const gfx::Size& size,
+    GpuMemoryBufferFactoryOzoneNativeBuffer* buffer_factory)
+    : GlRenderer(widget, size),
+      buffer_factory_(buffer_factory),
+      weak_ptr_factory_(this) {
+}
+
+SurfacelessGlRenderer::~SurfacelessGlRenderer() {
+  // Need to make current when deleting the framebuffer resources allocated in
+  // the buffers.
+  context_->MakeCurrent(surface_.get());
+}
+
+bool SurfacelessGlRenderer::Initialize() {
+  if (!GlRenderer::Initialize())
+    return false;
+
+  for (size_t i = 0; i < arraysize(buffers_); ++i)
+    if (!buffers_[i].Initialize(buffer_factory_, widget_, size_))
+      return false;
+
+  PostRenderFrameTask(gfx::SwapResult::SWAP_ACK);
+  return true;
+}
+
+void SurfacelessGlRenderer::RenderFrame() {
+  float fraction = NextFraction();
+
+  context_->MakeCurrent(surface_.get());
+  buffers_[back_buffer_].BindFramebuffer();
+
+  glViewport(0, 0, size_.width(), size_.height());
+  glClearColor(1 - fraction, fraction, 0.0, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  buffers_[back_buffer_].SchedulePlane();
+  back_buffer_ ^= 1;
+  if (!surface_->SwapBuffersAsync(base::Bind(&GlRenderer::PostRenderFrameTask,
+                                             weak_ptr_factory_.GetWeakPtr())))
+    LOG(FATAL) << "Failed to swap buffers";
+}
+
+scoped_refptr<gfx::GLSurface> SurfacelessGlRenderer::CreateSurface() {
+  gfx::SurfaceConfiguration config;
+  return gfx::GLSurface::CreateSurfacelessViewGLSurface(widget_, config);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.h b/ui/ozone/demo/surfaceless_gl_renderer.h
new file mode 100644
index 0000000..de5a689
--- /dev/null
+++ b/ui/ozone/demo/surfaceless_gl_renderer.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
+#define UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
+
+#include "ui/ozone/demo/gl_renderer.h"
+
+namespace gfx {
+class GLImage;
+}  // namespace gfx
+
+namespace ui {
+
+class GpuMemoryBufferFactoryOzoneNativeBuffer;
+
+class SurfacelessGlRenderer : public GlRenderer {
+ public:
+  SurfacelessGlRenderer(
+      gfx::AcceleratedWidget widget,
+      const gfx::Size& size,
+      GpuMemoryBufferFactoryOzoneNativeBuffer* buffer_factory);
+  ~SurfacelessGlRenderer() override;
+
+  // Renderer:
+  bool Initialize() override;
+
+ private:
+  // GlRenderer:
+  void RenderFrame() override;
+  scoped_refptr<gfx::GLSurface> CreateSurface() override;
+
+  class BufferWrapper {
+   public:
+    BufferWrapper();
+    ~BufferWrapper();
+
+    bool Initialize(GpuMemoryBufferFactoryOzoneNativeBuffer* buffer_factory,
+                    gfx::AcceleratedWidget widget,
+                    const gfx::Size& size);
+    void BindFramebuffer();
+    void SchedulePlane();
+
+   private:
+    gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
+    gfx::Size size_;
+
+    scoped_refptr<gfx::GLImage> image_;
+    unsigned int gl_fb_ = 0;
+    unsigned int gl_tex_ = 0;
+  };
+
+  GpuMemoryBufferFactoryOzoneNativeBuffer* buffer_factory_;
+
+  BufferWrapper buffers_[2];
+  int back_buffer_ = 0;
+
+  base::WeakPtrFactory<SurfacelessGlRenderer> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfacelessGlRenderer);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_
diff --git a/ui/ozone/generate_constructor_list.py b/ui/ozone/generate_constructor_list.py
new file mode 100755
index 0000000..02945b3
--- /dev/null
+++ b/ui/ozone/generate_constructor_list.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Code generator for PlatformObject<> constructor list.
+
+This script takes as arguments a list of platform names as a text file and
+a list of types and generates a C++ source file containing a list of
+the constructors for that object in platform order.
+
+Example Output: ./ui/ozone/generate_constructor_list.py \
+                    --platform test \
+                    --platform dri \
+                    --export OZONE_EXPORT \
+                    --namespace ui \
+                    --typename OzonePlatform \
+                    --include '"ui/ozone/ozone_platform.h"'
+
+  // DO NOT MODIFY. GENERATED BY generate_constructor_list.py
+
+  #include "ui/ozone/platform_object_internal.h"
+
+  #include "ui/ozone/ozone_platform.h"
+
+  namespace ui {
+
+  OzonePlatform* CreateOzonePlatformTest();
+  OzonePlatform* CreateOzonePlatformDri();
+
+  }  // namespace ui
+
+  namespace ui {
+
+  typedef ui::OzonePlatform* (*OzonePlatformConstructor)();
+
+  template <> const OzonePlatformConstructor
+  PlatformConstructorList<ui::OzonePlatform>::kConstructors[] = {
+    &ui::CreateOzonePlatformTest,
+    &ui::CreateOzonePlatformDri,
+  };
+
+  template class OZONE_EXPORT PlatformObject<ui::OzonePlatform>;
+
+  }  // namespace ui
+"""
+
+import optparse
+import os
+import collections
+import re
+import sys
+import string
+
+
+def GetTypedefName(typename):
+  """Determine typedef name of constructor for typename.
+
+  This is just typename + "Constructor".
+  """
+
+  return typename + 'Constructor'
+
+
+def GetConstructorName(typename, platform):
+  """Determine name of static constructor function from platform name.
+
+  This is just "Create" + typename + platform.
+  """
+
+  return 'Create' + typename + string.capitalize(platform)
+
+
+def GenerateConstructorList(out, namespace, export, typenames, platforms,
+                            includes):
+  """Generate static array containing a list of constructors."""
+
+  out.write('// DO NOT MODIFY. GENERATED BY generate_constructor_list.py\n')
+  out.write('\n')
+
+  out.write('#include "ui/ozone/platform_object_internal.h"\n')
+  out.write('\n')
+
+  for include in includes:
+    out.write('#include %(include)s\n' % {'include': include})
+  out.write('\n')
+
+  out.write('namespace %(namespace)s {\n' % {'namespace': namespace})
+  out.write('\n')
+
+  # Declarations of constructor functions.
+  for typename in typenames:
+    for platform in platforms:
+      constructor = GetConstructorName(typename, platform)
+      out.write('%(typename)s* %(constructor)s();\n'
+               % {'typename': typename,
+                  'constructor': constructor})
+    out.write('\n')
+
+  out.write('}  // namespace %(namespace)s\n' % {'namespace': namespace})
+  out.write('\n')
+
+  out.write('namespace ui {\n')
+  out.write('\n')
+
+  # Handy typedefs for constructor types.
+  for typename in typenames:
+    out.write('typedef %(typename)s* (*%(typedef)s)();\n'
+              % {'typename': namespace + '::' + typename,
+                 'typedef': GetTypedefName(typename)})
+  out.write('\n')
+
+  # The actual constructor lists.
+  for typename in typenames:
+    out.write('template <> const %(typedef)s\n'
+              % {'typedef': GetTypedefName(typename)})
+    out.write('PlatformConstructorList<%(typename)s>::kConstructors[] = {\n'
+              % {'typename': namespace + '::' + typename})
+    for platform in platforms:
+      constructor = GetConstructorName(typename, platform)
+      out.write('  &%(namespace)s::%(constructor)s,\n'
+                % {'namespace': namespace, 'constructor': constructor})
+    out.write('};\n')
+    out.write('\n')
+
+  # Exported template instantiation.
+  for typename in typenames:
+    out.write('template class %(export)s PlatformObject<%(typename)s>;\n'
+              % {'export': export, 'typename': namespace + '::' + typename})
+  out.write('\n')
+
+  out.write('}  // namespace ui\n')
+  out.write('\n')
+
+
+def main(argv):
+  parser = optparse.OptionParser()
+  parser.add_option('--namespace', default='ozone')
+  parser.add_option('--export', default='OZONE_EXPORT')
+  parser.add_option('--platform_list')
+  parser.add_option('--output_cc')
+  parser.add_option('--include', action='append', default=[])
+  parser.add_option('--platform', action='append', default=[])
+  parser.add_option('--typename', action='append', default=[])
+  options, _ = parser.parse_args(argv)
+
+  platforms = list(options.platform)
+  typenames = list(options.typename)
+  includes = list(options.include)
+
+  if options.platform_list:
+    platforms = open(options.platform_list, 'r').read().strip().split('\n')
+
+  if not platforms:
+    sys.stderr.write('No platforms are selected!')
+    sys.exit(1)
+
+  # Write to standard output or file specified by --output_cc.
+  out_cc = sys.stdout
+  if options.output_cc:
+    out_cc = open(options.output_cc, 'wb')
+
+  GenerateConstructorList(out_cc, options.namespace, options.export,
+                          typenames, platforms, includes)
+
+  if options.output_cc:
+    out_cc.close()
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/ui/ozone/generate_ozone_platform_list.py b/ui/ozone/generate_ozone_platform_list.py
new file mode 100755
index 0000000..d47c398
--- /dev/null
+++ b/ui/ozone/generate_ozone_platform_list.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Code generator for Ozone platform list.
+
+This script takes as arguments a list of platform names and generates a C++
+source file containing a list of those platforms.
+
+Each platform gets an integer identifier that is used to find objects for that
+platform (particularly constructors for platform-specific objects).
+
+Example Output: ./generate_ozone_platform_list.py --default wayland dri wayland
+
+  // platform_list.txt
+
+  wayland
+  dri
+
+  // platform_list.h
+
+  #ifndef UI_OZONE_PLATFORM_LIST_H_
+  #define UI_OZONE_PLATFORM_LIST_H_
+
+  namespace ui {
+
+  const int kPlatformWayland = 0;
+  const int kPlatformDri = 1;
+
+  extern const char *kPlatformNames[kPlatformCount];
+
+  }  // namespace ui
+
+  // platform_list.cc
+
+  #include "ui/ozone/platform_list.h"
+
+  namespace ui {
+
+  const char *kPlatformNames[] = {
+   "wayland", // kPlatformWayland
+   "dri", // kPlatformDri
+  };
+
+  }  // namespace ui
+
+  #endif  // UI_OZONE_PLATFORM_LIST_H_
+
+"""
+
+import optparse
+import os
+import collections
+import re
+import sys
+import string
+
+
+def GetConstantName(name):
+  """Determine name of static constructor function from platform name.
+
+  We just capitalize the platform name and prepend "CreateOzonePlatform".
+  """
+
+  return 'kPlatform' + string.capitalize(name)
+
+
+def GeneratePlatformListText(out, platforms):
+  """Generate text file with list of platform names, in platform id order."""
+
+  for platform in platforms:
+    out.write(platform)
+    out.write('\n')
+
+  out.write('\n')
+
+
+def GeneratePlatformListHeader(out, platforms):
+  """Generate ids of ozone platforms & declaration of static names array."""
+
+  out.write('// DO NOT MODIFY. GENERATED BY generate_ozone_platform_list.py\n')
+  out.write('\n')
+
+  out.write('#ifndef UI_OZONE_PLATFORM_LIST_H_\n')
+  out.write('#define UI_OZONE_PLATFORM_LIST_H_\n')
+  out.write('\n')
+
+  out.write('namespace ui {\n')
+  out.write('\n')
+
+  # Prototypes for platform initializers.
+  for plat_id, plat_name in enumerate(platforms):
+    out.write('const int %s = %d;\n' % (GetConstantName(plat_name), plat_id))
+  out.write('\n')
+
+  # Platform count.
+  out.write('const int kPlatformCount = %d;\n' % len(platforms))
+  out.write('\n')
+
+  # Declaration for names list.
+  out.write('extern const char* kPlatformNames[kPlatformCount];\n')
+  out.write('\n')
+
+  out.write('}  // namespace ui\n')
+  out.write('\n')
+
+  out.write('#endif  // UI_OZONE_PLATFORM_LIST_H_\n')
+  out.write('\n')
+
+
+def GeneratePlatformListSource(out, platforms):
+  """Generate static array containing a list of ozone platforms."""
+
+  out.write('// DO NOT MODIFY. GENERATED BY generate_ozone_platform_list.py\n')
+  out.write('\n')
+
+  out.write('#include "ui/ozone/platform_list.h"\n')
+  out.write('\n')
+
+  out.write('namespace ui {\n')
+  out.write('\n')
+
+  # Definition of names list.
+  out.write('const char* kPlatformNames[] = {\n')
+
+  # Prototypes for platform initializers.
+  for plat_name in platforms:
+    out.write('    "%s",  // %s\n' % (plat_name, GetConstantName(plat_name)))
+  out.write('};\n')
+  out.write('\n')
+
+  out.write('}  // namespace ui\n')
+  out.write('\n')
+
+
+def main(argv):
+  parser = optparse.OptionParser()
+  parser.add_option('--output_cc')
+  parser.add_option('--output_h')
+  parser.add_option('--output_txt')
+  parser.add_option('--default')
+  options, platforms = parser.parse_args(argv)
+
+  # Reorder the platforms when --default is specified.
+  # The default platform must appear first in the platform list.
+  if options.default and options.default in platforms:
+    platforms.remove(options.default)
+    platforms.insert(0, options.default)
+
+  # Write to standard output or file specified by --output_{cc,h}.
+  out_cc = sys.stdout
+  out_h = sys.stdout
+  out_txt = sys.stdout
+  if options.output_cc:
+    out_cc = open(options.output_cc, 'wb')
+  if options.output_h:
+    out_h = open(options.output_h, 'wb')
+  if options.output_txt:
+    out_txt = open(options.output_txt, 'wb')
+
+  GeneratePlatformListText(out_txt, platforms)
+  GeneratePlatformListHeader(out_h, platforms)
+  GeneratePlatformListSource(out_cc, platforms)
+
+  if options.output_cc:
+    out_cc.close()
+  if options.output_h:
+    out_h.close()
+  if options.output_txt:
+    out_txt.close()
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/ui/ozone/gpu/BUILD.gn b/ui/ozone/gpu/BUILD.gn
new file mode 100644
index 0000000..90e06bc
--- /dev/null
+++ b/ui/ozone/gpu/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+component("gpu") {
+  output_name = "ozone_gpu"
+
+  sources = [
+    "gpu_memory_buffer_factory_ozone_native_buffer.cc",
+    "gpu_memory_buffer_factory_ozone_native_buffer.h",
+  ]
+
+  defines = [ "OZONE_GPU_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//ui/ozone",
+    "//ui/ozone:ozone_base",
+  ]
+}
diff --git a/ui/ozone/gpu/README b/ui/ozone/gpu/README
new file mode 100644
index 0000000..7f96a44
--- /dev/null
+++ b/ui/ozone/gpu/README
@@ -0,0 +1 @@
+This component contains support code for content/common/gpu.
diff --git a/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.cc b/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.cc
new file mode 100644
index 0000000..0a08873
--- /dev/null
+++ b/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.cc
@@ -0,0 +1,211 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h"
+
+#include "base/logging.h"
+#include "ui/gl/gl_image_egl.h"
+#include "ui/gl/gl_image_linux_dma_buffer.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+namespace {
+class GLImageOzoneNativePixmap : public gfx::GLImageEGL {
+ public:
+  explicit GLImageOzoneNativePixmap(const gfx::Size& size) : GLImageEGL(size) {}
+
+  void Destroy(bool have_context) override {
+    gfx::GLImageEGL::Destroy(have_context);
+    pixmap_ = nullptr;
+  }
+
+  bool Initialize(NativePixmap* pixmap) {
+    EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+    if (!Initialize(EGL_NATIVE_PIXMAP_KHR, pixmap->GetEGLClientBuffer(), attrs))
+      return false;
+    pixmap_ = pixmap;
+    return true;
+  }
+
+  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                            int z_order,
+                            gfx::OverlayTransform transform,
+                            const gfx::Rect& bounds_rect,
+                            const gfx::RectF& crop_rect) override {
+    return pixmap_->ScheduleOverlayPlane(widget, z_order, transform,
+                                         bounds_rect, crop_rect);
+  }
+
+ protected:
+  ~GLImageOzoneNativePixmap() override {}
+
+ private:
+  using gfx::GLImageEGL::Initialize;
+  scoped_refptr<NativePixmap> pixmap_;
+};
+
+class GLImageOzoneNativePixmapDmaBuf : public gfx::GLImageLinuxDMABuffer {
+ public:
+  explicit GLImageOzoneNativePixmapDmaBuf(const gfx::Size& size,
+                                          unsigned internalformat)
+      : GLImageLinuxDMABuffer(size, internalformat) {}
+
+  void Destroy(bool have_context) override {
+    gfx::GLImageLinuxDMABuffer::Destroy(have_context);
+    pixmap_ = nullptr;
+  }
+
+  bool Initialize(NativePixmap* pixmap, gfx::GpuMemoryBuffer::Format format) {
+    base::FileDescriptor handle(pixmap->GetDmaBufFd(), false);
+    if (!GLImageLinuxDMABuffer::Initialize(handle, format,
+                                           pixmap->GetDmaBufPitch()))
+      return false;
+    pixmap_ = pixmap;
+    return true;
+  }
+
+  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                            int z_order,
+                            gfx::OverlayTransform transform,
+                            const gfx::Rect& bounds_rect,
+                            const gfx::RectF& crop_rect) override {
+    return pixmap_->ScheduleOverlayPlane(widget, z_order, transform,
+                                         bounds_rect, crop_rect);
+  }
+
+ protected:
+  ~GLImageOzoneNativePixmapDmaBuf() override {}
+
+ private:
+  scoped_refptr<NativePixmap> pixmap_;
+};
+
+SurfaceFactoryOzone::BufferFormat GetOzoneFormatFor(
+    gfx::GpuMemoryBuffer::Format format) {
+  switch (format) {
+    case gfx::GpuMemoryBuffer::BGRA_8888:
+      return SurfaceFactoryOzone::BGRA_8888;
+    case gfx::GpuMemoryBuffer::RGBX_8888:
+      return SurfaceFactoryOzone::RGBX_8888;
+    case gfx::GpuMemoryBuffer::ATC:
+    case gfx::GpuMemoryBuffer::ATCIA:
+    case gfx::GpuMemoryBuffer::DXT1:
+    case gfx::GpuMemoryBuffer::DXT5:
+    case gfx::GpuMemoryBuffer::ETC1:
+    case gfx::GpuMemoryBuffer::RGBA_8888:
+      NOTREACHED();
+      return SurfaceFactoryOzone::BGRA_8888;
+  }
+
+  NOTREACHED();
+  return SurfaceFactoryOzone::BGRA_8888;
+}
+
+SurfaceFactoryOzone::BufferUsage GetOzoneUsageFor(
+    gfx::GpuMemoryBuffer::Usage usage) {
+  switch (usage) {
+    case gfx::GpuMemoryBuffer::MAP:
+      return SurfaceFactoryOzone::MAP;
+    case gfx::GpuMemoryBuffer::PERSISTENT_MAP:
+      return SurfaceFactoryOzone::PERSISTENT_MAP;
+    case gfx::GpuMemoryBuffer::SCANOUT:
+      return SurfaceFactoryOzone::SCANOUT;
+  }
+
+  NOTREACHED();
+  return SurfaceFactoryOzone::MAP;
+}
+
+std::pair<uint32_t, uint32_t> GetIndex(gfx::GpuMemoryBufferId id,
+                                       int client_id) {
+  return std::pair<uint32_t, uint32_t>(id, client_id);
+}
+}  // namespace
+
+GpuMemoryBufferFactoryOzoneNativeBuffer::
+    GpuMemoryBufferFactoryOzoneNativeBuffer() {
+}
+
+GpuMemoryBufferFactoryOzoneNativeBuffer::
+    ~GpuMemoryBufferFactoryOzoneNativeBuffer() {
+}
+
+bool GpuMemoryBufferFactoryOzoneNativeBuffer::CreateGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id,
+    const gfx::Size& size,
+    gfx::GpuMemoryBuffer::Format format,
+    gfx::GpuMemoryBuffer::Usage usage,
+    int client_id,
+    gfx::PluginWindowHandle surface_handle) {
+  scoped_refptr<NativePixmap> pixmap =
+      OzonePlatform::GetInstance()
+          ->GetSurfaceFactoryOzone()
+          ->CreateNativePixmap(surface_handle, size, GetOzoneFormatFor(format),
+                               GetOzoneUsageFor(usage));
+  if (!pixmap.get()) {
+    LOG(ERROR) << "Failed to create pixmap " << size.width() << "x"
+               << size.height() << " format " << format << ", usage " << usage;
+    return false;
+  }
+  base::AutoLock lock(native_pixmap_map_lock_);
+  native_pixmap_map_[GetIndex(id, client_id)] = pixmap;
+  return true;
+}
+
+void GpuMemoryBufferFactoryOzoneNativeBuffer::DestroyGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id,
+    int client_id) {
+  base::AutoLock lock(native_pixmap_map_lock_);
+  native_pixmap_map_.erase(GetIndex(id, client_id));
+}
+
+scoped_refptr<gfx::GLImage>
+GpuMemoryBufferFactoryOzoneNativeBuffer::CreateImageForGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id,
+    const gfx::Size& size,
+    gfx::GpuMemoryBuffer::Format format,
+    unsigned internalformat,
+    int client_id) {
+  NativePixmap* pixmap = nullptr;
+  {
+    base::AutoLock lock(native_pixmap_map_lock_);
+    BufferToPixmapMap::iterator it =
+        native_pixmap_map_.find(GetIndex(id, client_id));
+    if (it == native_pixmap_map_.end()) {
+      return scoped_refptr<gfx::GLImage>();
+    }
+    pixmap = it->second.get();
+  }
+  return CreateImageForPixmap(pixmap, size, format, internalformat);
+}
+
+scoped_refptr<gfx::GLImage>
+GpuMemoryBufferFactoryOzoneNativeBuffer::CreateImageForPixmap(
+    scoped_refptr<NativePixmap> pixmap,
+    const gfx::Size& size,
+    gfx::GpuMemoryBuffer::Format format,
+    unsigned internalformat) {
+  if (pixmap->GetEGLClientBuffer()) {
+    scoped_refptr<GLImageOzoneNativePixmap> image =
+        new GLImageOzoneNativePixmap(size);
+    if (!image->Initialize(pixmap.get())) {
+      return scoped_refptr<gfx::GLImage>();
+    }
+    return image;
+  }
+  if (pixmap->GetDmaBufFd() > 0) {
+    scoped_refptr<GLImageOzoneNativePixmapDmaBuf> image =
+        new GLImageOzoneNativePixmapDmaBuf(size, internalformat);
+    if (!image->Initialize(pixmap.get(), format)) {
+      return scoped_refptr<gfx::GLImage>();
+    }
+    return image;
+  }
+  return scoped_refptr<gfx::GLImage>();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h b/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h
new file mode 100644
index 0000000..c24dbb9
--- /dev/null
+++ b/ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_GPU_GPU_MEMORY_BUFFER_FACTORY_OZONE_NATIVE_BUFFER_H_
+#define UI_OZONE_GPU_GPU_MEMORY_BUFFER_FACTORY_OZONE_NATIVE_BUFFER_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/gpu/ozone_gpu_export.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace gfx {
+class GLImage;
+}
+
+namespace ui {
+class NativePixmap;
+
+class OZONE_GPU_EXPORT GpuMemoryBufferFactoryOzoneNativeBuffer {
+  typedef std::map<std::pair<uint32_t, uint32_t>, scoped_refptr<NativePixmap> >
+      BufferToPixmapMap;
+
+ public:
+  GpuMemoryBufferFactoryOzoneNativeBuffer();
+  virtual ~GpuMemoryBufferFactoryOzoneNativeBuffer();
+
+  // Creates a GPU memory buffer identified by |id|.
+  // It can be called on any thread.
+  bool CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                             const gfx::Size& size,
+                             gfx::GpuMemoryBuffer::Format format,
+                             gfx::GpuMemoryBuffer::Usage usage,
+                             int client_id,
+                             gfx::PluginWindowHandle surface_handle);
+
+  // Destroys GPU memory buffer identified by |id|.
+  // It can be called on any thread.
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, int client_id);
+
+  // Creates a GLImage instance for GPU memory buffer identified by |id|.
+  scoped_refptr<gfx::GLImage> CreateImageForGpuMemoryBuffer(
+      gfx::GpuMemoryBufferId id,
+      const gfx::Size& size,
+      gfx::GpuMemoryBuffer::Format format,
+      unsigned internalformat,
+      int client_id);
+
+  static scoped_refptr<gfx::GLImage> CreateImageForPixmap(
+      scoped_refptr<NativePixmap> pixmap,
+      const gfx::Size& size,
+      gfx::GpuMemoryBuffer::Format format,
+      unsigned internalformat);
+
+ private:
+  BufferToPixmapMap native_pixmap_map_;
+  base::Lock native_pixmap_map_lock_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_GPU_GPU_MEMORY_BUFFER_FACTORY_OZONE_NATIVE_BUFFER_H_
diff --git a/ui/ozone/gpu/ozone_gpu_export.h b/ui/ozone/gpu/ozone_gpu_export.h
new file mode 100644
index 0000000..cc4a7bc
--- /dev/null
+++ b/ui/ozone/gpu/ozone_gpu_export.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_GPU_OZONE_GPU_EXPORT_H_
+#define UI_OZONE_GPU_OZONE_GPU_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(OZONE_GPU_IMPLEMENTATION)
+#define OZONE_GPU_EXPORT __declspec(dllexport)
+#else
+#define OZONE_GPU_EXPORT __declspec(dllimport)
+#endif  // defined(OZONE_GPU_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(OZONE_GPU_IMPLEMENTATION)
+#define OZONE_GPU_EXPORT __attribute__((visibility("default")))
+#else
+#define OZONE_GPU_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define OZONE_GPU_EXPORT
+#endif
+
+#endif  // UI_OZONE_GPU_OZONE_GPU_EXPORT_H_
diff --git a/ui/ozone/ozone.gni b/ui/ozone/ozone.gni
new file mode 100644
index 0000000..92b1dee
--- /dev/null
+++ b/ui/ozone/ozone.gni
@@ -0,0 +1,33 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  # Select platforms automatically. Turn this off for manual control.
+  ozone_auto_platforms = false
+}
+
+declare_args() {
+  # The platform that will be active by default.
+  ozone_platform = ""
+
+  # Enable individual platforms.
+  ozone_platform_caca = false
+  ozone_platform_dri = false
+  ozone_platform_drm = false
+  ozone_platform_egltest = true
+  ozone_platform_gbm = true
+  ozone_platform_test = false
+
+  if (ozone_auto_platforms) {
+    # Use test as the default platform.
+    ozone_platform = "test"
+
+    # Build all platforms whose deps are in install-build-deps.sh.
+    # Only these platforms will be compile tested by buildbots.
+    ozone_platform_dri = true
+    ozone_platform_drm = true
+    ozone_platform_test = true
+    ozone_platform_egltest = true
+  }
+}
diff --git a/ui/ozone/ozone_base_export.h b/ui/ozone/ozone_base_export.h
new file mode 100644
index 0000000..d3df39b
--- /dev/null
+++ b/ui/ozone/ozone_base_export.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_OZONE_BASE_EXPORT_H_
+#define UI_OZONE_OZONE_BASE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(OZONE_BASE_IMPLEMENTATION)
+#define OZONE_BASE_EXPORT __declspec(dllexport)
+#else
+#define OZONE_BASE_EXPORT __declspec(dllimport)
+#endif  // defined(OZONE_BASE_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(OZONE_BASE_IMPLEMENTATION)
+#define OZONE_BASE_EXPORT __attribute__((visibility("default")))
+#else
+#define OZONE_BASE_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define OZONE_BASE_EXPORT
+#endif
+
+#endif  // UI_OZONE_OZONE_BASE_EXPORT_H_
diff --git a/ui/ozone/ozone_export.h b/ui/ozone/ozone_export.h
new file mode 100644
index 0000000..cc587a5
--- /dev/null
+++ b/ui/ozone/ozone_export.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_OZONE_EXPORT_H_
+#define UI_OZONE_OZONE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(OZONE_IMPLEMENTATION)
+#define OZONE_EXPORT __declspec(dllexport)
+#else
+#define OZONE_EXPORT __declspec(dllimport)
+#endif  // defined(OZONE_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(OZONE_IMPLEMENTATION)
+#define OZONE_EXPORT __attribute__((visibility("default")))
+#else
+#define OZONE_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define OZONE_EXPORT
+#endif
+
+#endif  // UI_OZONE_OZONE_EXPORT_H_
diff --git a/ui/ozone/platform/README b/ui/ozone/platform/README
new file mode 100644
index 0000000..cc91d3b
--- /dev/null
+++ b/ui/ozone/platform/README
@@ -0,0 +1,2 @@
+This directory contains implementations of platforms. Each platform implements
+the interfaces from ui/ozone/public for the rest of chromium.
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
new file mode 100644
index 0000000..ef0d786
--- /dev/null
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -0,0 +1,212 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/linux/pkg_config.gni")
+import("//ui/ozone/ozone.gni")
+
+declare_args() {
+  use_mesa_platform_null = false
+  use_drm_atomic = false
+}
+
+pkg_config("libdrm") {
+  packages = [ "libdrm" ]
+}
+
+source_set("drm_common") {
+  sources = [
+    "common/drm_gpu_test_helper_inprocess.cc",
+    "common/drm_util.cc",
+    "common/drm_util.h",
+    "common/inprocess_messages.cc",
+    "common/inprocess_messages.h",
+    "common/scoped_drm_types.cc",
+    "common/scoped_drm_types.h",
+    "gpu/crtc_controller.cc",
+    "gpu/crtc_controller.h",
+    "gpu/drm_buffer.cc",
+    "gpu/drm_buffer.h",
+    "gpu/drm_console_buffer.cc",
+    "gpu/drm_console_buffer.h",
+    "gpu/drm_device.cc",
+    "gpu/drm_device.h",
+    "gpu/drm_device_generator.cc",
+    "gpu/drm_device_generator.h",
+    "gpu/drm_device_manager.cc",
+    "gpu/drm_device_manager.h",
+    "gpu/drm_display.cc",
+    "gpu/drm_display.h",
+    "gpu/drm_gpu_display_manager.cc",
+    "gpu/drm_gpu_display_manager.h",
+    "gpu/drm_gpu_platform_support.cc",
+    "gpu/drm_gpu_platform_support.h",
+    "gpu/drm_gpu_platform_support_inprocess.cc",
+    "gpu/drm_surface.cc",
+    "gpu/drm_surface.h",
+    "gpu/drm_surface_factory.cc",
+    "gpu/drm_surface_factory.h",
+    "gpu/drm_vsync_provider.cc",
+    "gpu/drm_vsync_provider.h",
+    "gpu/drm_window.cc",
+    "gpu/drm_window.h",
+    "gpu/hardware_display_controller.cc",
+    "gpu/hardware_display_controller.h",
+    "gpu/hardware_display_plane.cc",
+    "gpu/hardware_display_plane.h",
+    "gpu/hardware_display_plane_manager.cc",
+    "gpu/hardware_display_plane_manager.h",
+    "gpu/hardware_display_plane_manager_legacy.cc",
+    "gpu/hardware_display_plane_manager_legacy.h",
+    "gpu/overlay_plane.cc",
+    "gpu/overlay_plane.h",
+    "gpu/page_flip_request.cc",
+    "gpu/page_flip_request.h",
+    "gpu/scanout_buffer.h",
+    "gpu/screen_manager.cc",
+    "gpu/screen_manager.h",
+    "host/channel_observer.h",
+    "host/drm_cursor.cc",
+    "host/drm_cursor.h",
+    "host/drm_device_handle.cc",
+    "host/drm_device_handle.h",
+    "host/drm_display_host.cc",
+    "host/drm_display_host.h",
+    "host/drm_display_host_manager.cc",
+    "host/drm_display_host_manager.h",
+    "host/drm_gpu_platform_support_host.cc",
+    "host/drm_gpu_platform_support_host.h",
+    "host/drm_gpu_platform_support_host_inprocess.cc",
+    "host/drm_native_display_delegate.cc",
+    "host/drm_native_display_delegate.h",
+    "host/drm_overlay_candidates_host.cc",
+    "host/drm_overlay_candidates_host.h",
+    "host/drm_overlay_manager.cc",
+    "host/drm_overlay_manager.h",
+    "host/drm_window_host.cc",
+    "host/drm_window_host.h",
+    "host/drm_window_host_manager.cc",
+    "host/drm_window_host_manager.h",
+  ]
+
+  defines = [ "OZONE_IMPLEMENTATION" ]
+
+  if (use_drm_atomic) {
+    defines += [ "USE_DRM_ATOMIC" ]
+    sources += [
+      "gpu/hardware_display_plane_atomic.cc",
+      "gpu/hardware_display_plane_atomic.h",
+      "gpu/hardware_display_plane_manager_atomic.cc",
+      "gpu/hardware_display_plane_manager_atomic.h",
+    ]
+  }
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/base",
+    "//ui/display/types",
+    "//ui/display/util",
+    "//ui/events",
+    "//ui/events/devices",
+    "//ui/events/ozone:events_ozone",
+    "//ui/events/ozone:events_ozone_evdev",
+    "//ui/events/ozone:events_ozone_layout",
+    "//ui/events/platform",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/ozone:ozone_base",
+    "//ui/platform_window",
+  ]
+
+  public_configs = [ ":libdrm" ]
+}
+
+if (ozone_platform_dri || ozone_platform_drm) {
+  source_set("drm") {
+    sources = [
+      "ozone_platform_drm.cc",
+      "ozone_platform_drm.h",
+    ]
+
+    deps = [
+      ":drm_common",
+      "//base",
+      "//skia",
+      "//ui/base",
+      "//ui/ozone:ozone_base",
+      "//ui/events/ozone:events_ozone",
+      "//ui/events/ozone:events_ozone_evdev",
+      "//ui/events/ozone:events_ozone_layout",
+    ]
+  }
+
+  source_set("drm_unittests") {
+    testonly = true
+    sources = [
+      "gpu/drm_surface_unittest.cc",
+      "gpu/drm_window_unittest.cc",
+      "gpu/hardware_display_controller_unittest.cc",
+      "gpu/hardware_display_plane_manager_unittest.cc",
+      "gpu/screen_manager_unittest.cc",
+      "test/mock_drm_device.cc",
+      "test/mock_drm_device.h",
+    ]
+
+    deps = [
+      "//base",
+      "//skia",
+      "//testing/gtest",
+      "//ui/ozone:ozone_base",
+      "//ui/ozone/platform/drm:drm_common",
+    ]
+
+    public_configs = [ ":libdrm" ]
+  }
+}
+
+if (ozone_platform_gbm) {
+  pkg_config("libgbm") {
+    packages = [ "gbm" ]
+  }
+
+  source_set("gbm") {
+    sources = [
+      "gpu/gbm_buffer.cc",
+      "gpu/gbm_buffer.h",
+      "gpu/gbm_buffer_base.cc",
+      "gpu/gbm_buffer_base.h",
+      "gpu/gbm_device.cc",
+      "gpu/gbm_device.h",
+      "gpu/gbm_surface.cc",
+      "gpu/gbm_surface.h",
+      "gpu/gbm_surface_factory.cc",
+      "gpu/gbm_surface_factory.h",
+      "gpu/gbm_surfaceless.cc",
+      "gpu/gbm_surfaceless.h",
+      "ozone_platform_gbm.cc",
+      "ozone_platform_gbm.h",
+    ]
+
+    deps = [
+      ":drm_common",
+      "//base",
+      "//skia",
+      "//ui/base",
+      "//ui/events/ozone:events_ozone",
+      "//ui/events/ozone:events_ozone_evdev",
+      "//ui/events/ozone:events_ozone_layout",
+      "//ui/gfx/geometry",
+      "//ui/ozone:ozone_base",
+    ]
+
+    public_configs = [
+      ":libgbm",
+      "//third_party/khronos:khronos_headers",
+    ]
+
+    if (use_mesa_platform_null) {
+      defines += [ "USE_MESA_PLATFORM_NULL" ]
+    }
+  }
+}
diff --git a/ui/ozone/platform/drm/common/drm_gpu_test_helper_inprocess.cc b/ui/ozone/platform/drm/common/drm_gpu_test_helper_inprocess.cc
new file mode 100644
index 0000000..06ec8d4
--- /dev/null
+++ b/ui/ozone/platform/drm/common/drm_gpu_test_helper_inprocess.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+namespace {
+
+const int kGpuProcessHostId = 1;
+
+}  // namespace
+
+static void DispatchToGpuPlatformSupportHostTask(Message* msg) {
+  auto support = static_cast<DrmGpuPlatformSupportHost*>(
+    ui::OzonePlatform::GetInstance()->GetGpuPlatformSupportHost());
+  auto inprocess = static_cast<DrmGpuPlatformSupportHostInprocess*>(
+    support->get_delegate());
+  inprocess->OnMessageReceived(*msg);
+  delete msg;
+}
+
+class FakeGpuProcess {
+ public:
+  FakeGpuProcess(
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
+      : ui_task_runner_(ui_task_runner) {}
+  ~FakeGpuProcess() {}
+
+  void Init() {
+    base::Callback<void(Message*)> sender =
+      base::Bind(&DispatchToGpuPlatformSupportHostTask);
+
+    auto delegate = new DrmGpuPlatformSupportInprocess();
+    delegate->OnChannelEstablished(ui_task_runner_, sender);
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+};
+
+static void DispatchToGpuPlatformSupportTask(Message* msg) {
+  auto support = static_cast<DrmGpuPlatformSupport*>(
+    ui::OzonePlatform::GetInstance()->GetGpuPlatformSupport());
+  auto inprocess = static_cast<DrmGpuPlatformSupportInprocess*>(
+    support->get_delegate());
+  inprocess->OnMessageReceived(*msg);
+  delete msg;
+}
+
+class FakeGpuProcessHost {
+ public:
+  FakeGpuProcessHost(
+      const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner)
+      : gpu_task_runner_(gpu_task_runner) {}
+  ~FakeGpuProcessHost() {}
+
+  void Init() {
+    base::Callback<void(Message*)> sender =
+      base::Bind(&DispatchToGpuPlatformSupportTask);
+
+    auto host_support_inprocess = new DrmGpuPlatformSupportHostInprocess();
+    host_support_inprocess->OnChannelEstablished(
+      kGpuProcessHostId, gpu_task_runner_, sender);
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
+};
+
+OzoneGpuTestHelper::OzoneGpuTestHelper() {
+}
+
+OzoneGpuTestHelper::~OzoneGpuTestHelper() {
+}
+
+bool OzoneGpuTestHelper::Initialize(
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+    const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner) {
+
+  fake_gpu_process_.reset(new FakeGpuProcess(ui_task_runner));
+  fake_gpu_process_->Init();
+
+  fake_gpu_process_host_.reset(new FakeGpuProcessHost(gpu_task_runner));
+  fake_gpu_process_host_->Init();
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
new file mode 100644
index 0000000..3e0804e
--- /dev/null
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -0,0 +1,268 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/common/drm_util.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <xf86drmMode.h>
+
+#include "ui/display/types/display_constants.h"
+#include "ui/display/util/edid_parser.h"
+
+#if !defined(DRM_MODE_CONNECTOR_DSI)
+#define DRM_MODE_CONNECTOR_DSI 16
+#endif
+
+namespace ui {
+
+namespace {
+
+bool IsCrtcInUse(uint32_t crtc,
+                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+  for (size_t i = 0; i < displays.size(); ++i) {
+    if (crtc == displays[i]->crtc()->crtc_id)
+      return true;
+  }
+
+  return false;
+}
+
+uint32_t GetCrtc(int fd,
+                 drmModeConnector* connector,
+                 drmModeRes* resources,
+                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+  // If the connector already has an encoder try to re-use.
+  if (connector->encoder_id) {
+    ScopedDrmEncoderPtr encoder(drmModeGetEncoder(fd, connector->encoder_id));
+    if (encoder && encoder->crtc_id && !IsCrtcInUse(encoder->crtc_id, displays))
+      return encoder->crtc_id;
+  }
+
+  // Try to find an encoder for the connector.
+  for (int i = 0; i < connector->count_encoders; ++i) {
+    ScopedDrmEncoderPtr encoder(drmModeGetEncoder(fd, connector->encoders[i]));
+    if (!encoder)
+      continue;
+
+    for (int j = 0; j < resources->count_crtcs; ++j) {
+      // Check if the encoder is compatible with this CRTC
+      if (!(encoder->possible_crtcs & (1 << j)) ||
+          IsCrtcInUse(resources->crtcs[j], displays))
+        continue;
+
+      return resources->crtcs[j];
+    }
+  }
+
+  return 0;
+}
+
+// Computes the refresh rate for the specific mode. If we have enough
+// information use the mode timings to compute a more exact value otherwise
+// fallback to using the mode's vertical refresh rate (the kernel computes this
+// the same way, however there is a loss in precision since |vrefresh| is sent
+// as an integer).
+float GetRefreshRate(const drmModeModeInfo& mode) {
+  if (!mode.htotal || !mode.vtotal)
+    return mode.vrefresh;
+
+  float clock = mode.clock;
+  float htotal = mode.htotal;
+  float vtotal = mode.vtotal;
+
+  return (clock * 1000.0f) / (htotal * vtotal);
+}
+
+DisplayConnectionType GetDisplayType(drmModeConnector* connector) {
+  switch (connector->connector_type) {
+    case DRM_MODE_CONNECTOR_VGA:
+      return DISPLAY_CONNECTION_TYPE_VGA;
+    case DRM_MODE_CONNECTOR_DVII:
+    case DRM_MODE_CONNECTOR_DVID:
+    case DRM_MODE_CONNECTOR_DVIA:
+      return DISPLAY_CONNECTION_TYPE_DVI;
+    case DRM_MODE_CONNECTOR_LVDS:
+    case DRM_MODE_CONNECTOR_eDP:
+    case DRM_MODE_CONNECTOR_DSI:
+      return DISPLAY_CONNECTION_TYPE_INTERNAL;
+    case DRM_MODE_CONNECTOR_DisplayPort:
+      return DISPLAY_CONNECTION_TYPE_DISPLAYPORT;
+    case DRM_MODE_CONNECTOR_HDMIA:
+    case DRM_MODE_CONNECTOR_HDMIB:
+      return DISPLAY_CONNECTION_TYPE_HDMI;
+    default:
+      return DISPLAY_CONNECTION_TYPE_UNKNOWN;
+  }
+}
+
+int GetDrmProperty(int fd,
+                   drmModeConnector* connector,
+                   const std::string& name,
+                   ScopedDrmPropertyPtr* property) {
+  for (int i = 0; i < connector->count_props; ++i) {
+    ScopedDrmPropertyPtr tmp(drmModeGetProperty(fd, connector->props[i]));
+    if (!tmp)
+      continue;
+
+    if (name == tmp->name) {
+      *property = tmp.Pass();
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+std::string GetNameForEnumValue(drmModePropertyRes* property, uint32_t value) {
+  for (int i = 0; i < property->count_enums; ++i)
+    if (property->enums[i].value == value)
+      return property->enums[i].name;
+
+  return std::string();
+}
+
+ScopedDrmPropertyBlobPtr GetDrmPropertyBlob(int fd,
+                                            drmModeConnector* connector,
+                                            const std::string& name) {
+  ScopedDrmPropertyPtr property;
+  int index = GetDrmProperty(fd, connector, name, &property);
+  if (index < 0)
+    return nullptr;
+
+  if (property->flags & DRM_MODE_PROP_BLOB) {
+    return ScopedDrmPropertyBlobPtr(
+        drmModeGetPropertyBlob(fd, connector->prop_values[index]));
+  }
+
+  return nullptr;
+}
+
+bool IsAspectPreserving(int fd, drmModeConnector* connector) {
+  ScopedDrmPropertyPtr property;
+  int index = GetDrmProperty(fd, connector, "scaling mode", &property);
+  if (index < 0)
+    return false;
+
+  return (GetNameForEnumValue(property.get(), connector->prop_values[index]) ==
+          "Full aspect");
+}
+
+}  // namespace
+
+HardwareDisplayControllerInfo::HardwareDisplayControllerInfo(
+    ScopedDrmConnectorPtr connector,
+    ScopedDrmCrtcPtr crtc)
+    : connector_(connector.Pass()), crtc_(crtc.Pass()) {
+}
+
+HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() {
+}
+
+ScopedVector<HardwareDisplayControllerInfo> GetAvailableDisplayControllerInfos(
+    int fd) {
+  ScopedDrmResourcesPtr resources(drmModeGetResources(fd));
+  DCHECK(resources) << "Failed to get DRM resources";
+  ScopedVector<HardwareDisplayControllerInfo> displays;
+
+  for (int i = 0; i < resources->count_connectors; ++i) {
+    ScopedDrmConnectorPtr connector(
+        drmModeGetConnector(fd, resources->connectors[i]));
+
+    if (!connector || connector->connection != DRM_MODE_CONNECTED ||
+        connector->count_modes == 0)
+      continue;
+
+    uint32_t crtc_id = GetCrtc(fd, connector.get(), resources.get(), displays);
+    if (!crtc_id)
+      continue;
+
+    ScopedDrmCrtcPtr crtc(drmModeGetCrtc(fd, crtc_id));
+    displays.push_back(
+        new HardwareDisplayControllerInfo(connector.Pass(), crtc.Pass()));
+  }
+
+  return displays.Pass();
+}
+
+bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs) {
+  return lhs.clock == rhs.clock && lhs.hdisplay == rhs.hdisplay &&
+         lhs.vdisplay == rhs.vdisplay && lhs.vrefresh == rhs.vrefresh &&
+         lhs.hsync_start == rhs.hsync_start && lhs.hsync_end == rhs.hsync_end &&
+         lhs.htotal == rhs.htotal && lhs.hskew == rhs.hskew &&
+         lhs.vsync_start == rhs.vsync_start && lhs.vsync_end == rhs.vsync_end &&
+         lhs.vtotal == rhs.vtotal && lhs.vscan == rhs.vscan &&
+         lhs.flags == rhs.flags && strcmp(lhs.name, rhs.name) == 0;
+}
+
+DisplayMode_Params CreateDisplayModeParams(const drmModeModeInfo& mode) {
+  DisplayMode_Params params;
+  params.size = gfx::Size(mode.hdisplay, mode.vdisplay);
+  params.is_interlaced = mode.flags & DRM_MODE_FLAG_INTERLACE;
+  params.refresh_rate = GetRefreshRate(mode);
+
+  return params;
+}
+
+DisplaySnapshot_Params CreateDisplaySnapshotParams(
+    HardwareDisplayControllerInfo* info,
+    int fd,
+    size_t display_index,
+    const gfx::Point& origin) {
+  DisplaySnapshot_Params params;
+  params.display_id = display_index;
+  params.origin = origin;
+  params.physical_size =
+      gfx::Size(info->connector()->mmWidth, info->connector()->mmHeight);
+  params.type = GetDisplayType(info->connector());
+  params.is_aspect_preserving_scaling =
+      IsAspectPreserving(fd, info->connector());
+
+  ScopedDrmPropertyBlobPtr edid_blob(
+      GetDrmPropertyBlob(fd, info->connector(), "EDID"));
+
+  if (edid_blob) {
+    std::vector<uint8_t> edid(
+        static_cast<uint8_t*>(edid_blob->data),
+        static_cast<uint8_t*>(edid_blob->data) + edid_blob->length);
+
+    if (!GetDisplayIdFromEDID(edid, display_index, &params.display_id,
+                              &params.product_id))
+      params.display_id = display_index;
+
+    ParseOutputDeviceData(edid, nullptr, nullptr, &params.display_name, nullptr,
+                          nullptr);
+    ParseOutputOverscanFlag(edid, &params.has_overscan);
+  } else {
+    VLOG(1) << "Failed to get EDID blob for connector "
+            << info->connector()->connector_id;
+  }
+
+  for (int i = 0; i < info->connector()->count_modes; ++i) {
+    const drmModeModeInfo& mode = info->connector()->modes[i];
+    params.modes.push_back(CreateDisplayModeParams(mode));
+
+    if (info->crtc()->mode_valid && SameMode(info->crtc()->mode, mode)) {
+      params.has_current_mode = true;
+      params.current_mode = params.modes.back();
+    }
+
+    if (mode.type & DRM_MODE_TYPE_PREFERRED) {
+      params.has_native_mode = true;
+      params.native_mode = params.modes.back();
+    }
+  }
+
+  // If no preferred mode is found then use the first one. Using the first one
+  // since it should be the best mode.
+  if (!params.has_native_mode && !params.modes.empty()) {
+    params.has_native_mode = true;
+    params.native_mode = params.modes.front();
+  }
+
+  return params;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
new file mode 100644
index 0000000..e761aba
--- /dev/null
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
+#define UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+// Representation of the information required to initialize and configure a
+// native display.
+class HardwareDisplayControllerInfo {
+ public:
+  HardwareDisplayControllerInfo(ScopedDrmConnectorPtr connector,
+                                ScopedDrmCrtcPtr crtc);
+  ~HardwareDisplayControllerInfo();
+
+  drmModeConnector* connector() const { return connector_.get(); }
+  drmModeCrtc* crtc() const { return crtc_.get(); }
+
+ private:
+  ScopedDrmConnectorPtr connector_;
+  ScopedDrmCrtcPtr crtc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerInfo);
+};
+
+// Looks-up and parses the native display configurations returning all available
+// displays.
+ScopedVector<HardwareDisplayControllerInfo> GetAvailableDisplayControllerInfos(
+    int fd);
+
+bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);
+
+DisplayMode_Params CreateDisplayModeParams(const drmModeModeInfo& mode);
+
+// |info| provides the DRM information related to the display, |fd| is the
+// connection to the DRM device and |index| provides a unique identifier for the
+// display. |index| will be used to generate the display id (it may be the id if
+// the monitor's EDID lacks the necessary identifiers).
+DisplaySnapshot_Params CreateDisplaySnapshotParams(
+    HardwareDisplayControllerInfo* info,
+    int fd,
+    size_t display_index,
+    const gfx::Point& origin);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
diff --git a/ui/ozone/platform/drm/common/inprocess_messages.cc b/ui/ozone/platform/drm/common/inprocess_messages.cc
new file mode 100644
index 0000000..43642f6
--- /dev/null
+++ b/ui/ozone/platform/drm/common/inprocess_messages.cc
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/common/inprocess_messages.h"
+
+namespace ui {
+
+OzoneHostMsg_UpdateNativeDisplays::OzoneHostMsg_UpdateNativeDisplays(
+  const std::vector<DisplaySnapshot_Params>& _displays)
+  : Message(OZONE_HOST_MSG__UPDATE_NATIVE_DISPLAYS),
+    displays(_displays) {
+}
+
+OzoneHostMsg_UpdateNativeDisplays::~OzoneHostMsg_UpdateNativeDisplays() {
+}
+
+} // namespace
diff --git a/ui/ozone/platform/drm/common/inprocess_messages.h b/ui/ozone/platform/drm/common/inprocess_messages.h
new file mode 100644
index 0000000..f31fc7a
--- /dev/null
+++ b/ui/ozone/platform/drm/common/inprocess_messages.h
@@ -0,0 +1,109 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_COMMON_INPROCESS_MESSAGES_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_COMMON_INPROCESS_MESSAGES_H_
+
+#include "base/file_descriptor_posix.h"
+#include "base/files/file_path.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+namespace ui {
+
+enum MessageId {
+  OZONE_GPU_MSG__CREATE_WINDOW = 1000,
+  OZONE_GPU_MSG__WINDOW_BOUNDS_CHANGED,
+  OZONE_GPU_MSG__ADD_GRAPHICS_DEVICE,
+  OZONE_GPU_MSG__REFRESH_NATIVE_DISPLAYS,
+  OZONE_GPU_MSG__CONFIGURE_NATIVE_DISPLAY,
+
+  OZONE_HOST_MSG__UPDATE_NATIVE_DISPLAYS = 2000,
+  OZONE_HOST_MSG__DISPLAY_CONFIGURED,
+  OZONE_HOST_MSG__HDCP_STATE_RECEIVED,
+};
+
+class Message {
+ public:
+  Message(MessageId _id)
+    : id(_id) {
+  }
+  const MessageId id;
+};
+
+class OzoneGpuMsg_CreateWindow : public Message {
+ public:
+  OzoneGpuMsg_CreateWindow(const gfx::AcceleratedWidget& _widget)
+    : Message(OZONE_GPU_MSG__CREATE_WINDOW),
+      widget(_widget) {
+  }
+  const gfx::AcceleratedWidget widget;
+};
+
+class OzoneGpuMsg_WindowBoundsChanged : public Message {
+ public:
+  OzoneGpuMsg_WindowBoundsChanged(const gfx::AcceleratedWidget& _widget,
+                                  const gfx::Rect& _bounds)
+    : Message(OZONE_GPU_MSG__WINDOW_BOUNDS_CHANGED),
+      widget(_widget), bounds(_bounds) {
+  }
+  const gfx::AcceleratedWidget widget;
+  const gfx::Rect bounds;
+};
+
+class OzoneGpuMsg_AddGraphicsDevice : public Message {
+ public:
+  OzoneGpuMsg_AddGraphicsDevice(const base::FilePath& _path,
+                                const base::FileDescriptor& _fd)
+    : Message(OZONE_GPU_MSG__ADD_GRAPHICS_DEVICE),
+      path(_path), fd(_fd) {
+  }
+  const base::FilePath path;
+  const base::FileDescriptor fd;
+};
+
+class OzoneGpuMsg_RefreshNativeDisplays : public Message {
+ public:
+  OzoneGpuMsg_RefreshNativeDisplays()
+    : Message(OZONE_GPU_MSG__REFRESH_NATIVE_DISPLAYS) {
+  }
+};
+
+class OzoneGpuMsg_ConfigureNativeDisplay : public Message {
+ public:
+  OzoneGpuMsg_ConfigureNativeDisplay(int64_t _id,
+                                     const DisplayMode_Params& _mode,
+                                     const gfx::Point& _originhost)
+    : Message(OZONE_GPU_MSG__CONFIGURE_NATIVE_DISPLAY)
+    , id(_id), mode(_mode), originhost(_originhost) {
+  }
+  const int64_t id;
+  const DisplayMode_Params mode;
+  const gfx::Point originhost;
+};
+
+
+class OzoneHostMsg_UpdateNativeDisplays : public Message {
+ public:
+  OzoneHostMsg_UpdateNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& _displays);
+  ~OzoneHostMsg_UpdateNativeDisplays();
+
+  const std::vector<DisplaySnapshot_Params> displays;
+};
+
+class OzoneHostMsg_DisplayConfigured : public Message {
+ public:
+  OzoneHostMsg_DisplayConfigured(int64_t _id, bool _result)
+    : Message(OZONE_HOST_MSG__DISPLAY_CONFIGURED)
+    , id(_id), result(_result) {
+  }
+  const int64_t id;
+  const bool result;
+};
+
+} // namespace
+
+#endif
diff --git a/ui/ozone/platform/drm/common/scoped_drm_types.cc b/ui/ozone/platform/drm/common/scoped_drm_types.cc
new file mode 100644
index 0000000..9a1456e
--- /dev/null
+++ b/ui/ozone/platform/drm/common/scoped_drm_types.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+
+#include <xf86drmMode.h>
+
+namespace ui {
+
+void DrmResourcesDeleter::operator()(drmModeRes* resources) const {
+  drmModeFreeResources(resources);
+}
+
+void DrmConnectorDeleter::operator()(drmModeConnector* connector) const {
+  drmModeFreeConnector(connector);
+}
+
+void DrmCrtcDeleter::operator()(drmModeCrtc* crtc) const {
+  drmModeFreeCrtc(crtc);
+}
+
+void DrmEncoderDeleter::operator()(drmModeEncoder* encoder) const {
+  drmModeFreeEncoder(encoder);
+}
+
+void DrmObjectPropertiesDeleter::operator()(
+    drmModeObjectProperties* properties) const {
+  drmModeFreeObjectProperties(properties);
+}
+
+void DrmPlaneDeleter::operator()(drmModePlane* plane) const {
+  drmModeFreePlane(plane);
+}
+
+void DrmPlaneResDeleter::operator()(drmModePlaneRes* plane) const {
+  drmModeFreePlaneResources(plane);
+}
+
+void DrmPropertyDeleter::operator()(drmModePropertyRes* property) const {
+  drmModeFreeProperty(property);
+}
+
+#if defined(USE_DRM_ATOMIC)
+void DrmPropertySetDeleter::operator()(drmModePropertySet* property) const {
+  drmModePropertySetFree(property);
+}
+#endif  // defined(USE_DRM_ATOMIC)
+
+void DrmPropertyBlobDeleter::operator()(
+    drmModePropertyBlobRes* property) const {
+  drmModeFreePropertyBlob(property);
+}
+
+void DrmFramebufferDeleter::operator()(drmModeFB* framebuffer) const {
+  drmModeFreeFB(framebuffer);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/common/scoped_drm_types.h b/ui/ozone/platform/drm/common/scoped_drm_types.h
new file mode 100644
index 0000000..02f1eb5
--- /dev/null
+++ b/ui/ozone/platform/drm/common/scoped_drm_types.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_COMMON_SCOPED_DRM_TYPES_H_
+#define UI_OZONE_PLATFORM_DRM_COMMON_SCOPED_DRM_TYPES_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/ozone_export.h"
+
+typedef struct _drmModeConnector drmModeConnector;
+typedef struct _drmModeCrtc drmModeCrtc;
+typedef struct _drmModeEncoder drmModeEncoder;
+typedef struct _drmModeFB drmModeFB;
+typedef struct _drmModeObjectProperties drmModeObjectProperties;
+typedef struct _drmModePlane drmModePlane;
+typedef struct _drmModePlaneRes drmModePlaneRes;
+typedef struct _drmModeProperty drmModePropertyRes;
+typedef struct _drmModePropertySet drmModePropertySet;
+typedef struct _drmModePropertyBlob drmModePropertyBlobRes;
+typedef struct _drmModeRes drmModeRes;
+
+namespace ui {
+
+struct OZONE_EXPORT DrmResourcesDeleter {
+  void operator()(drmModeRes* resources) const;
+};
+struct OZONE_EXPORT DrmConnectorDeleter {
+  void operator()(drmModeConnector* connector) const;
+};
+struct OZONE_EXPORT DrmCrtcDeleter {
+  void operator()(drmModeCrtc* crtc) const;
+};
+struct OZONE_EXPORT DrmEncoderDeleter {
+  void operator()(drmModeEncoder* encoder) const;
+};
+struct OZONE_EXPORT DrmObjectPropertiesDeleter {
+  void operator()(drmModeObjectProperties* properties) const;
+};
+struct OZONE_EXPORT DrmPlaneDeleter {
+  void operator()(drmModePlane* plane) const;
+};
+struct OZONE_EXPORT DrmPlaneResDeleter {
+  void operator()(drmModePlaneRes* plane_res) const;
+};
+struct OZONE_EXPORT DrmPropertyDeleter {
+  void operator()(drmModePropertyRes* property) const;
+};
+#if defined(USE_DRM_ATOMIC)
+struct OZONE_EXPORT DrmPropertySetDeleter {
+  void operator()(drmModePropertySet* property) const;
+};
+#endif  // defined(USE_DRM_ATOMIC)
+struct OZONE_EXPORT DrmPropertyBlobDeleter {
+  void operator()(drmModePropertyBlobRes* property) const;
+};
+struct OZONE_EXPORT DrmFramebufferDeleter {
+  void operator()(drmModeFB* framebuffer) const;
+};
+
+typedef scoped_ptr<drmModeRes, DrmResourcesDeleter> ScopedDrmResourcesPtr;
+typedef scoped_ptr<drmModeConnector, DrmConnectorDeleter> ScopedDrmConnectorPtr;
+typedef scoped_ptr<drmModeCrtc, DrmCrtcDeleter> ScopedDrmCrtcPtr;
+typedef scoped_ptr<drmModeEncoder, DrmEncoderDeleter> ScopedDrmEncoderPtr;
+typedef scoped_ptr<drmModeObjectProperties, DrmObjectPropertiesDeleter>
+    ScopedDrmObjectPropertyPtr;
+typedef scoped_ptr<drmModePlane, DrmPlaneDeleter> ScopedDrmPlanePtr;
+typedef scoped_ptr<drmModePlaneRes, DrmPlaneResDeleter> ScopedDrmPlaneResPtr;
+typedef scoped_ptr<drmModePropertyRes, DrmPropertyDeleter> ScopedDrmPropertyPtr;
+#if defined(USE_DRM_ATOMIC)
+typedef scoped_ptr<drmModePropertySet, DrmPropertySetDeleter>
+    ScopedDrmPropertySetPtr;
+#endif  // defined(USE_DRM_ATOMIC)
+typedef scoped_ptr<drmModePropertyBlobRes, DrmPropertyBlobDeleter>
+    ScopedDrmPropertyBlobPtr;
+typedef scoped_ptr<drmModeFB, DrmFramebufferDeleter> ScopedDrmFramebufferPtr;
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_COMMON_SCOPED_DRM_TYPES_H_
diff --git a/ui/ozone/platform/drm/gpu/crtc_controller.cc b/ui/ozone/platform/drm/gpu/crtc_controller.cc
new file mode 100644
index 0000000..34390a2
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/crtc_controller.cc
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/page_flip_request.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+CrtcController::CrtcController(const scoped_refptr<DrmDevice>& drm,
+                               uint32_t crtc,
+                               uint32_t connector)
+    : drm_(drm), crtc_(crtc), connector_(connector) {
+}
+
+CrtcController::~CrtcController() {
+  if (!is_disabled_) {
+    SetCursor(nullptr);
+    drm_->DisableCrtc(crtc_);
+    SignalPageFlipRequest();
+  }
+}
+
+bool CrtcController::Modeset(const OverlayPlane& plane, drmModeModeInfo mode) {
+  if (!drm_->SetCrtc(crtc_, plane.buffer->GetFramebufferId(),
+                     std::vector<uint32_t>(1, connector_), &mode)) {
+    PLOG(ERROR) << "Failed to modeset: crtc=" << crtc_
+                << " connector=" << connector_
+                << " framebuffer_id=" << plane.buffer->GetFramebufferId()
+                << " mode=" << mode.hdisplay << "x" << mode.vdisplay << "@"
+                << mode.vrefresh;
+    return false;
+  }
+
+  mode_ = mode;
+  pending_planes_.clear();
+  is_disabled_ = false;
+
+  // drmModeSetCrtc has an immediate effect, so we can assume that the current
+  // planes have been updated. However if a page flip is still pending, set the
+  // pending planes to the same values so that the callback keeps the correct
+  // state.
+  current_planes_ = std::vector<OverlayPlane>(1, plane);
+  if (page_flip_request_.get())
+    pending_planes_ = current_planes_;
+
+  ResetCursor();
+
+  return true;
+}
+
+bool CrtcController::Disable() {
+  if (is_disabled_)
+    return true;
+
+  is_disabled_ = true;
+  return drm_->DisableCrtc(crtc_);
+}
+
+bool CrtcController::SchedulePageFlip(
+    HardwareDisplayPlaneList* plane_list,
+    const OverlayPlaneList& overlays,
+    bool test_only,
+    scoped_refptr<PageFlipRequest> page_flip_request) {
+  DCHECK(!page_flip_request_.get() || test_only);
+  DCHECK(!is_disabled_);
+  const OverlayPlane* primary = OverlayPlane::GetPrimaryPlane(overlays);
+  if (!primary) {
+    LOG(ERROR) << "No primary plane to display on crtc " << crtc_;
+    page_flip_request->Signal(gfx::SwapResult::SWAP_ACK);
+    return true;
+  }
+  DCHECK(primary->buffer.get());
+
+  if (primary->buffer->GetSize() != gfx::Size(mode_.hdisplay, mode_.vdisplay)) {
+    VLOG(2) << "Trying to pageflip a buffer with the wrong size. Expected "
+            << mode_.hdisplay << "x" << mode_.vdisplay << " got "
+            << primary->buffer->GetSize().ToString() << " for"
+            << " crtc=" << crtc_ << " connector=" << connector_;
+    page_flip_request->Signal(gfx::SwapResult::SWAP_ACK);
+    return true;
+  }
+
+  if (!drm_->plane_manager()->AssignOverlayPlanes(plane_list, overlays, crtc_,
+                                                  this)) {
+    PLOG(ERROR) << "Failed to assign overlay planes for crtc " << crtc_;
+    page_flip_request->Signal(gfx::SwapResult::SWAP_FAILED);
+    return false;
+  }
+
+  if (test_only) {
+    page_flip_request->Signal(gfx::SwapResult::SWAP_ACK);
+  } else {
+    pending_planes_ = overlays;
+    page_flip_request_ = page_flip_request;
+  }
+
+  return true;
+}
+
+void CrtcController::PageFlipFailed() {
+  pending_planes_.clear();
+  SignalPageFlipRequest();
+}
+
+void CrtcController::OnPageFlipEvent(unsigned int frame,
+                                     unsigned int seconds,
+                                     unsigned int useconds) {
+  time_of_last_flip_ =
+      static_cast<uint64_t>(seconds) * base::Time::kMicrosecondsPerSecond +
+      useconds;
+
+  current_planes_.clear();
+  current_planes_.swap(pending_planes_);
+
+  SignalPageFlipRequest();
+}
+
+bool CrtcController::SetCursor(const scoped_refptr<ScanoutBuffer>& buffer) {
+  DCHECK(!is_disabled_ || !buffer);
+  cursor_buffer_ = buffer;
+
+  return ResetCursor();
+}
+
+bool CrtcController::MoveCursor(const gfx::Point& location) {
+  DCHECK(!is_disabled_);
+  return drm_->MoveCursor(crtc_, location);
+}
+
+bool CrtcController::ResetCursor() {
+  uint32_t handle = 0;
+  gfx::Size size;
+
+  if (cursor_buffer_) {
+    handle = cursor_buffer_->GetHandle();
+    size = cursor_buffer_->GetSize();
+  }
+
+  bool status = drm_->SetCursor(crtc_, handle, size);
+  if (!status) {
+    PLOG(ERROR) << "drmModeSetCursor: device " << drm_->device_path().value()
+                << " crtc " << crtc_ << " handle " << handle << " size "
+                << size.ToString();
+  }
+
+  return status;
+}
+
+void CrtcController::SignalPageFlipRequest() {
+  if (page_flip_request_.get()) {
+    // If another frame is queued up and available immediately, calling Signal()
+    // may result in a call to SchedulePageFlip(), which will override
+    // page_flip_request_ and possibly release the ref. Stash previous request
+    // locally to  avoid deleting the object we are making a call on.
+    scoped_refptr<PageFlipRequest> last_request;
+    last_request.swap(page_flip_request_);
+    last_request->Signal(gfx::SwapResult::SWAP_ACK);
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/crtc_controller.h b/ui/ozone/platform/drm/gpu/crtc_controller.h
new file mode 100644
index 0000000..636fe89
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/crtc_controller.h
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_CRTC_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_CRTC_CONTROLLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+
+namespace ui {
+
+class DrmDevice;
+class PageFlipRequest;
+
+// Wrapper around a CRTC.
+//
+// One CRTC can be paired up with one or more connectors. The simplest
+// configuration represents one CRTC driving one monitor, while pairing up a
+// CRTC with multiple connectors results in hardware mirroring.
+class OZONE_EXPORT CrtcController
+    : public base::SupportsWeakPtr<CrtcController> {
+ public:
+  CrtcController(const scoped_refptr<DrmDevice>& drm,
+                 uint32_t crtc,
+                 uint32_t connector);
+  ~CrtcController();
+
+  drmModeModeInfo mode() const { return mode_; }
+  uint32_t crtc() const { return crtc_; }
+  uint32_t connector() const { return connector_; }
+  const scoped_refptr<DrmDevice>& drm() const { return drm_; }
+  bool is_disabled() const { return is_disabled_; }
+  uint64_t time_of_last_flip() const { return time_of_last_flip_; }
+
+  // Perform the initial modesetting operation using |plane| as the buffer for
+  // the primary plane. The CRTC configuration is specified by |mode|.
+  bool Modeset(const OverlayPlane& plane, drmModeModeInfo mode);
+
+  // Disables the controller.
+  bool Disable();
+
+  // Schedule a page flip event and present the overlays in |planes|.
+  bool SchedulePageFlip(HardwareDisplayPlaneList* plane_list,
+                        const OverlayPlaneList& planes,
+                        bool test_only,
+                        scoped_refptr<PageFlipRequest> page_flip_request);
+
+  // Called if the page flip for this CRTC fails after being scheduled.
+  void PageFlipFailed();
+
+  // Called when the page flip event occurred. The event is provided by the
+  // kernel when a VBlank event finished. This allows the controller to
+  // update internal state and propagate the update to the surface.
+  // The tuple (seconds, useconds) represents the event timestamp. |seconds|
+  // represents the number of seconds while |useconds| represents the
+  // microseconds (< 1 second) in the timestamp.
+  void OnPageFlipEvent(unsigned int frame,
+                       unsigned int seconds,
+                       unsigned int useconds);
+
+  bool SetCursor(const scoped_refptr<ScanoutBuffer>& buffer);
+  bool MoveCursor(const gfx::Point& location);
+
+ private:
+  bool ResetCursor();
+
+  void SignalPageFlipRequest();
+
+  scoped_refptr<DrmDevice> drm_;
+
+  HardwareDisplayPlaneManager* overlay_plane_manager_;  // Not owned.
+
+  // Buffers need to be declared first so that they are destroyed last. Needed
+  // since the controllers may reference the buffers.
+  OverlayPlaneList current_planes_;
+  OverlayPlaneList pending_planes_;
+  scoped_refptr<ScanoutBuffer> cursor_buffer_;
+  scoped_refptr<PageFlipRequest> page_flip_request_;
+
+  uint32_t crtc_;
+
+  // TODO(dnicoara) Add support for hardware mirroring (multiple connectors).
+  uint32_t connector_;
+
+  drmModeModeInfo mode_;
+
+  // Keeps track of the CRTC state. If a surface has been bound, then the value
+  // is set to false. Otherwise it is true.
+  bool is_disabled_ = true;
+
+  // The time of the last page flip event as reported by the kernel callback.
+  uint64_t time_of_last_flip_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CrtcController);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_CRTC_CONTROLLER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.cc b/ui/ozone/platform/drm/gpu/drm_buffer.cc
new file mode 100644
index 0000000..0ab1f61
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_buffer.cc
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+namespace {
+
+// Modesetting cannot happen from a buffer with transparencies. Return the size
+// of a pixel without alpha.
+uint8_t GetColorDepth(SkColorType type) {
+  switch (type) {
+    case kUnknown_SkColorType:
+    case kAlpha_8_SkColorType:
+      return 0;
+    case kIndex_8_SkColorType:
+      return 8;
+    case kRGB_565_SkColorType:
+      return 16;
+    case kARGB_4444_SkColorType:
+      return 12;
+    case kN32_SkColorType:
+      return 24;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+}  // namespace
+
+DrmBuffer::DrmBuffer(const scoped_refptr<DrmDevice>& drm) : drm_(drm) {
+}
+
+DrmBuffer::~DrmBuffer() {
+  surface_.clear();
+
+  if (framebuffer_ && !drm_->RemoveFramebuffer(framebuffer_))
+    PLOG(ERROR) << "DrmBuffer: RemoveFramebuffer: fb " << framebuffer_;
+
+  if (mmap_base_ && !drm_->UnmapDumbBuffer(mmap_base_, mmap_size_))
+    PLOG(ERROR) << "DrmBuffer: UnmapDumbBuffer: handle " << handle_;
+
+  if (handle_ && !drm_->DestroyDumbBuffer(handle_))
+    PLOG(ERROR) << "DrmBuffer: DestroyDumbBuffer: handle " << handle_;
+}
+
+bool DrmBuffer::Initialize(const SkImageInfo& info,
+                           bool should_register_framebuffer) {
+  if (!drm_->CreateDumbBuffer(info, &handle_, &stride_)) {
+    PLOG(ERROR) << "DrmBuffer: CreateDumbBuffer: width " << info.width()
+                << " height " << info.height();
+    return false;
+  }
+
+  mmap_size_ = info.getSafeSize(stride_);
+  if (!drm_->MapDumbBuffer(handle_, mmap_size_, &mmap_base_)) {
+    PLOG(ERROR) << "DrmBuffer: MapDumbBuffer: handle " << handle_;
+    return false;
+  }
+
+  if (should_register_framebuffer &&
+      !drm_->AddFramebuffer(
+          info.width(), info.height(), GetColorDepth(info.colorType()),
+          info.bytesPerPixel() << 3, stride_, handle_, &framebuffer_)) {
+    PLOG(ERROR) << "DrmBuffer: AddFramebuffer: handle " << handle_;
+    return false;
+  }
+
+  surface_ =
+      skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_));
+  if (!surface_) {
+    LOG(ERROR) << "DrmBuffer: Failed to create SkSurface: handle " << handle_;
+    return false;
+  }
+
+  return true;
+}
+
+SkCanvas* DrmBuffer::GetCanvas() const {
+  return surface_->getCanvas();
+}
+
+uint32_t DrmBuffer::GetFramebufferId() const {
+  return framebuffer_;
+}
+
+uint32_t DrmBuffer::GetHandle() const {
+  return handle_;
+}
+
+gfx::Size DrmBuffer::GetSize() const {
+  return gfx::Size(surface_->width(), surface_->height());
+}
+
+DrmBufferGenerator::DrmBufferGenerator() {
+}
+
+DrmBufferGenerator::~DrmBufferGenerator() {
+}
+
+scoped_refptr<ScanoutBuffer> DrmBufferGenerator::Create(
+    const scoped_refptr<DrmDevice>& drm,
+    const gfx::Size& size) {
+  scoped_refptr<DrmBuffer> buffer(new DrmBuffer(drm));
+  SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+  if (!buffer->Initialize(info, true /* should_register_framebuffer */))
+    return NULL;
+
+  return buffer;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.h b/ui/ozone/platform/drm/gpu/drm_buffer.h
new file mode 100644
index 0000000..84556ad
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_buffer.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_
+
+#include "base/macros.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+class DrmDevice;
+
+// Wrapper for a DRM allocated buffer. Keeps track of the native properties of
+// the buffer and wraps the pixel memory into a SkSurface which can be used to
+// draw into using Skia.
+class OZONE_EXPORT DrmBuffer : public ScanoutBuffer {
+ public:
+  DrmBuffer(const scoped_refptr<DrmDevice>& drm);
+
+  // Allocates the backing pixels and wraps them in |surface_|. |info| is used
+  // to describe the buffer characteristics (size, color format).
+  // |should_register_framebuffer| is used to distinguish the buffers that are
+  // used for modesetting.
+  bool Initialize(const SkImageInfo& info, bool should_register_framebuffer);
+
+  SkCanvas* GetCanvas() const;
+
+  // ScanoutBuffer:
+  uint32_t GetFramebufferId() const override;
+  uint32_t GetHandle() const override;
+  gfx::Size GetSize() const override;
+
+ protected:
+  ~DrmBuffer() override;
+
+  scoped_refptr<DrmDevice> drm_;
+
+  // Length of a row of pixels.
+  uint32_t stride_ = 0;
+
+  // Buffer handle used by the DRM allocator.
+  uint32_t handle_ = 0;
+
+  // Base address for memory mapping.
+  void* mmap_base_ = 0;
+
+  // Size for memory mapping.
+  size_t mmap_size_ = 0;
+
+  // Buffer ID used by the DRM modesettings API. This is set when the buffer is
+  // registered with the CRTC.
+  uint32_t framebuffer_ = 0;
+
+  // Wrapper around the native pixel memory.
+  skia::RefPtr<SkSurface> surface_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmBuffer);
+};
+
+class OZONE_EXPORT DrmBufferGenerator : public ScanoutBufferGenerator {
+ public:
+  DrmBufferGenerator();
+  ~DrmBufferGenerator() override;
+
+  // ScanoutBufferGenerator:
+  scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                      const gfx::Size& size) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DrmBufferGenerator);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_console_buffer.cc b/ui/ozone/platform/drm/gpu/drm_console_buffer.cc
new file mode 100644
index 0000000..464c380
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_console_buffer.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_console_buffer.h"
+
+#include <sys/mman.h>
+#include <xf86drmMode.h>
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+DrmConsoleBuffer::DrmConsoleBuffer(const scoped_refptr<DrmDevice>& drm,
+                                   uint32_t framebuffer)
+    : drm_(drm), framebuffer_(framebuffer) {
+}
+
+DrmConsoleBuffer::~DrmConsoleBuffer() {
+  if (mmap_base_)
+    if (munmap(mmap_base_, mmap_size_))
+      PLOG(ERROR) << "munmap";
+
+  if (handle_ && !drm_->CloseBufferHandle(handle_))
+    PLOG(ERROR) << "DrmConsoleBuffer: CloseBufferHandle: handle " << handle_;
+}
+
+bool DrmConsoleBuffer::Initialize() {
+  ScopedDrmFramebufferPtr fb(drm_->GetFramebuffer(framebuffer_));
+
+  if (!fb)
+    return false;
+
+  handle_ = fb->handle;
+  stride_ = fb->pitch;
+  SkImageInfo info = SkImageInfo::MakeN32Premul(fb->width, fb->height);
+
+  mmap_size_ = info.getSafeSize(stride_);
+
+  if (!drm_->MapDumbBuffer(fb->handle, mmap_size_, &mmap_base_)) {
+    mmap_base_ = NULL;
+    return false;
+  }
+
+  surface_ =
+      skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_));
+  if (!surface_)
+    return false;
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_console_buffer.h b/ui/ozone/platform/drm/gpu/drm_console_buffer.h
new file mode 100644
index 0000000..27dc038
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_console_buffer.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+class SkCanvas;
+
+namespace ui {
+
+class DrmDevice;
+
+// Wrapper for the console buffer. This is the buffer that is allocated by
+// default by the system and is used when no application is controlling the
+// CRTC. Keeps track of the native properties of the buffer and wraps the pixel
+// memory into a SkSurface which can be used to draw into using Skia.
+class DrmConsoleBuffer {
+ public:
+  DrmConsoleBuffer(const scoped_refptr<DrmDevice>& drm, uint32_t framebuffer);
+  ~DrmConsoleBuffer();
+
+  SkCanvas* canvas() { return surface_->getCanvas(); }
+  skia::RefPtr<SkImage> image() {
+    return skia::AdoptRef(surface_->newImageSnapshot());
+  }
+
+  // Memory map the backing pixels and wrap them in |surface_|.
+  bool Initialize();
+
+ protected:
+  scoped_refptr<DrmDevice> drm_;
+
+  // Wrapper around the native pixel memory.
+  skia::RefPtr<SkSurface> surface_;
+
+  // Length of a row of pixels.
+  uint32_t stride_ = 0;
+
+  // Buffer handle used by the DRM allocator.
+  uint32_t handle_ = 0;
+
+  // Buffer ID used by the DRM modesettings API.
+  uint32_t framebuffer_ = 0;
+
+  // Memory map base address.
+  void* mmap_base_ = nullptr;
+
+  // Memory map size.
+  size_t mmap_size_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmConsoleBuffer);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc
new file mode 100644
index 0000000..1d80b68
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -0,0 +1,618 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/display/types/gamma_ramp_rgb_entry.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
+
+#if defined(USE_DRM_ATOMIC)
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
+#endif
+
+namespace ui {
+
+namespace {
+
+typedef base::Callback<void(uint32_t /* frame */,
+                            uint32_t /* seconds */,
+                            uint32_t /* useconds */,
+                            uint64_t /* id */)> DrmEventHandler;
+
+bool DrmCreateDumbBuffer(int fd,
+                         const SkImageInfo& info,
+                         uint32_t* handle,
+                         uint32_t* stride) {
+  struct drm_mode_create_dumb request;
+  memset(&request, 0, sizeof(request));
+  request.width = info.width();
+  request.height = info.height();
+  request.bpp = info.bytesPerPixel() << 3;
+  request.flags = 0;
+
+  if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
+    VPLOG(2) << "Cannot create dumb buffer";
+    return false;
+  }
+
+  // The driver may choose to align the last row as well. We don't care about
+  // the last alignment bits since they aren't used for display purposes, so
+  // just check that the expected size is <= to what the driver allocated.
+  DCHECK_LE(info.getSafeSize(request.pitch), request.size);
+
+  *handle = request.handle;
+  *stride = request.pitch;
+  return true;
+}
+
+bool DrmDestroyDumbBuffer(int fd, uint32_t handle) {
+  struct drm_mode_destroy_dumb destroy_request;
+  memset(&destroy_request, 0, sizeof(destroy_request));
+  destroy_request.handle = handle;
+  return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
+}
+
+bool ProcessDrmEvent(int fd, const DrmEventHandler& callback) {
+  char buffer[1024];
+  int len = read(fd, buffer, sizeof(buffer));
+  if (len == 0)
+    return false;
+
+  if (len < static_cast<int>(sizeof(drm_event))) {
+    PLOG(ERROR) << "Failed to read DRM event";
+    return false;
+  }
+
+  int idx = 0;
+  while (idx < len) {
+    DCHECK_LE(static_cast<int>(sizeof(drm_event)), len - idx);
+    drm_event event;
+    memcpy(&event, &buffer[idx], sizeof(event));
+    switch (event.type) {
+      case DRM_EVENT_FLIP_COMPLETE: {
+        DCHECK_LE(static_cast<int>(sizeof(drm_event_vblank)), len - idx);
+        drm_event_vblank vblank;
+        memcpy(&vblank, &buffer[idx], sizeof(vblank));
+        callback.Run(vblank.sequence, vblank.tv_sec, vblank.tv_usec,
+                     vblank.user_data);
+      } break;
+      case DRM_EVENT_VBLANK:
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    idx += event.length;
+  }
+
+  return true;
+}
+
+bool CanQueryForResources(int fd) {
+  drm_mode_card_res resources;
+  memset(&resources, 0, sizeof(resources));
+  // If there is no error getting DRM resources then assume this is a
+  // modesetting device.
+  return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources);
+}
+
+}  // namespace
+
+class DrmDevice::PageFlipManager
+    : public base::RefCountedThreadSafe<DrmDevice::PageFlipManager> {
+ public:
+  PageFlipManager() : next_id_(0) {}
+
+  void OnPageFlip(uint32_t frame,
+                  uint32_t seconds,
+                  uint32_t useconds,
+                  uint64_t id) {
+    auto it =
+        std::find_if(callbacks_.begin(), callbacks_.end(), FindCallback(id));
+    if (it == callbacks_.end()) {
+      LOG(WARNING) << "Could not find callback for page flip id=" << id;
+      return;
+    }
+
+    DrmDevice::PageFlipCallback callback = it->callback;
+    callbacks_.erase(it);
+    callback.Run(frame, seconds, useconds);
+  }
+
+  uint64_t GetNextId() { return next_id_++; }
+
+  void RegisterCallback(uint64_t id,
+                        const DrmDevice::PageFlipCallback& callback) {
+    callbacks_.push_back({id, callback});
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<DrmDevice::PageFlipManager>;
+  ~PageFlipManager() {}
+
+  struct PageFlip {
+    uint64_t id;
+    DrmDevice::PageFlipCallback callback;
+  };
+
+  struct FindCallback {
+    FindCallback(uint64_t id) : id(id) {}
+
+    bool operator()(const PageFlip& flip) const { return flip.id == id; }
+
+    uint64_t id;
+  };
+
+  uint64_t next_id_;
+
+  std::vector<PageFlip> callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(PageFlipManager);
+};
+
+class DrmDevice::IOWatcher
+    : public base::RefCountedThreadSafe<DrmDevice::IOWatcher>,
+      public base::MessagePumpLibevent::Watcher {
+ public:
+  IOWatcher(int fd,
+            const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+            const scoped_refptr<DrmDevice::PageFlipManager>& page_flip_manager)
+      : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+        io_task_runner_(io_task_runner),
+        page_flip_manager_(page_flip_manager),
+        paused_(true),
+        fd_(fd) {}
+
+  void SetPaused(bool paused) {
+    if (paused_ == paused)
+      return;
+
+    paused_ = paused;
+    base::WaitableEvent done(false, false);
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&IOWatcher::SetPausedOnIO, this, &done));
+    done.Wait();
+  }
+
+  void Shutdown() {
+    if (!paused_)
+      io_task_runner_->PostTask(FROM_HERE,
+                                base::Bind(&IOWatcher::UnregisterOnIO, this));
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<IOWatcher>;
+
+  ~IOWatcher() override {}
+
+  void RegisterOnIO() {
+    DCHECK(base::MessageLoopForIO::IsCurrent());
+    base::MessageLoopForIO::current()->WatchFileDescriptor(
+        fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this);
+  }
+
+  void UnregisterOnIO() {
+    DCHECK(base::MessageLoopForIO::IsCurrent());
+    controller_.StopWatchingFileDescriptor();
+  }
+
+  void SetPausedOnIO(base::WaitableEvent* done) {
+    DCHECK(base::MessageLoopForIO::IsCurrent());
+    if (paused_)
+      UnregisterOnIO();
+    else
+      RegisterOnIO();
+    done->Signal();
+  }
+
+  void OnPageFlipOnIO(uint32_t frame,
+                      uint32_t seconds,
+                      uint32_t useconds,
+                      uint64_t id) {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&DrmDevice::PageFlipManager::OnPageFlip, page_flip_manager_,
+                   frame, seconds, useconds, id));
+  }
+
+  // base::MessagePumpLibevent::Watcher overrides:
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    DCHECK(base::MessageLoopForIO::IsCurrent());
+    TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
+
+    if (!ProcessDrmEvent(
+            fd, base::Bind(&DrmDevice::IOWatcher::OnPageFlipOnIO, this)))
+      UnregisterOnIO();
+  }
+
+  void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  scoped_refptr<DrmDevice::PageFlipManager> page_flip_manager_;
+
+  base::MessagePumpLibevent::FileDescriptorWatcher controller_;
+
+  bool paused_;
+  int fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOWatcher);
+};
+
+DrmDevice::DrmDevice(const base::FilePath& device_path, base::File file)
+    : device_path_(device_path),
+      file_(file.Pass()),
+      page_flip_manager_(new PageFlipManager()) {
+}
+
+DrmDevice::~DrmDevice() {
+  if (watcher_)
+    watcher_->Shutdown();
+}
+
+bool DrmDevice::Initialize(bool use_atomic) {
+  // Ignore devices that cannot perform modesetting.
+  if (!CanQueryForResources(file_.GetPlatformFile())) {
+    VLOG(2) << "Cannot query for resources for '" << device_path_.value()
+            << "'";
+    return false;
+  }
+
+#if defined(USE_DRM_ATOMIC)
+  // Use atomic only if the build, kernel & flags all allow it.
+  if (use_atomic && SetCapability(DRM_CLIENT_CAP_ATOMIC, 1))
+    plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic());
+#endif  // defined(USE_DRM_ATOMIC)
+
+  if (!plane_manager_)
+    plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy());
+  if (!plane_manager_->Initialize(this)) {
+    LOG(ERROR) << "Failed to initialize the plane manager for "
+               << device_path_.value();
+    plane_manager_.reset();
+    return false;
+  }
+
+  return true;
+}
+
+void DrmDevice::InitializeTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+  DCHECK(!task_runner_);
+  task_runner_ = task_runner;
+  watcher_ =
+      new IOWatcher(file_.GetPlatformFile(), task_runner_, page_flip_manager_);
+}
+
+ScopedDrmCrtcPtr DrmDevice::GetCrtc(uint32_t crtc_id) {
+  DCHECK(file_.IsValid());
+  return ScopedDrmCrtcPtr(drmModeGetCrtc(file_.GetPlatformFile(), crtc_id));
+}
+
+bool DrmDevice::SetCrtc(uint32_t crtc_id,
+                        uint32_t framebuffer,
+                        std::vector<uint32_t> connectors,
+                        drmModeModeInfo* mode) {
+  DCHECK(file_.IsValid());
+  DCHECK(!connectors.empty());
+  DCHECK(mode);
+
+  TRACE_EVENT2("drm", "DrmDevice::SetCrtc", "crtc", crtc_id, "size",
+               gfx::Size(mode->hdisplay, mode->vdisplay).ToString());
+  return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0,
+                         vector_as_array(&connectors), connectors.size(), mode);
+}
+
+bool DrmDevice::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) {
+  DCHECK(file_.IsValid());
+  // If there's no buffer then the CRTC was disabled.
+  if (!crtc->buffer_id)
+    return DisableCrtc(crtc->crtc_id);
+
+  DCHECK(!connectors.empty());
+
+  TRACE_EVENT1("drm", "DrmDevice::RestoreCrtc", "crtc", crtc->crtc_id);
+  return !drmModeSetCrtc(file_.GetPlatformFile(), crtc->crtc_id,
+                         crtc->buffer_id, crtc->x, crtc->y,
+                         vector_as_array(&connectors), connectors.size(),
+                         &crtc->mode);
+}
+
+bool DrmDevice::DisableCrtc(uint32_t crtc_id) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::DisableCrtc", "crtc", crtc_id);
+  return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, 0, 0, 0, NULL, 0,
+                         NULL);
+}
+
+ScopedDrmConnectorPtr DrmDevice::GetConnector(uint32_t connector_id) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::GetConnector", "connector", connector_id);
+  return ScopedDrmConnectorPtr(
+      drmModeGetConnector(file_.GetPlatformFile(), connector_id));
+}
+
+bool DrmDevice::AddFramebuffer(uint32_t width,
+                               uint32_t height,
+                               uint8_t depth,
+                               uint8_t bpp,
+                               uint32_t stride,
+                               uint32_t handle,
+                               uint32_t* framebuffer) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handle);
+  return !drmModeAddFB(file_.GetPlatformFile(), width, height, depth, bpp,
+                       stride, handle, framebuffer);
+}
+
+bool DrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::RemoveFramebuffer", "framebuffer",
+               framebuffer);
+  return !drmModeRmFB(file_.GetPlatformFile(), framebuffer);
+}
+
+bool DrmDevice::PageFlip(uint32_t crtc_id,
+                         uint32_t framebuffer,
+                         bool is_sync,
+                         const PageFlipCallback& callback) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id, "framebuffer",
+               framebuffer);
+
+  if (watcher_)
+    watcher_->SetPaused(is_sync);
+
+  // NOTE: Calling drmModeSetCrtc will immediately update the state, though
+  // callbacks to already scheduled page flips will be honored by the kernel.
+  uint64_t id = page_flip_manager_->GetNextId();
+  if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer,
+                       DRM_MODE_PAGE_FLIP_EVENT, reinterpret_cast<void*>(id))) {
+    // If successful the payload will be removed by a PageFlip event.
+    page_flip_manager_->RegisterCallback(id, callback);
+
+    // If the flip was requested synchronous or if no watcher has been installed
+    // yet, then synchronously handle the page flip events.
+    if (is_sync || !watcher_) {
+      TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile());
+
+      ProcessDrmEvent(
+          file_.GetPlatformFile(),
+          base::Bind(&PageFlipManager::OnPageFlip, page_flip_manager_));
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+bool DrmDevice::PageFlipOverlay(uint32_t crtc_id,
+                                uint32_t framebuffer,
+                                const gfx::Rect& location,
+                                const gfx::Rect& source,
+                                int overlay_plane) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT2("drm", "DrmDevice::PageFlipOverlay", "crtc", crtc_id,
+               "framebuffer", framebuffer);
+  return !drmModeSetPlane(file_.GetPlatformFile(), overlay_plane, crtc_id,
+                          framebuffer, 0, location.x(), location.y(),
+                          location.width(), location.height(), source.x(),
+                          source.y(), source.width(), source.height());
+}
+
+ScopedDrmFramebufferPtr DrmDevice::GetFramebuffer(uint32_t framebuffer) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::GetFramebuffer", "framebuffer", framebuffer);
+  return ScopedDrmFramebufferPtr(
+      drmModeGetFB(file_.GetPlatformFile(), framebuffer));
+}
+
+ScopedDrmPropertyPtr DrmDevice::GetProperty(drmModeConnector* connector,
+                                            const char* name) {
+  TRACE_EVENT2("drm", "DrmDevice::GetProperty", "connector",
+               connector->connector_id, "name", name);
+  for (int i = 0; i < connector->count_props; ++i) {
+    ScopedDrmPropertyPtr property(
+        drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
+    if (!property)
+      continue;
+
+    if (strcmp(property->name, name) == 0)
+      return property.Pass();
+  }
+
+  return ScopedDrmPropertyPtr();
+}
+
+bool DrmDevice::SetProperty(uint32_t connector_id,
+                            uint32_t property_id,
+                            uint64_t value) {
+  DCHECK(file_.IsValid());
+  return !drmModeConnectorSetProperty(file_.GetPlatformFile(), connector_id,
+                                      property_id, value);
+}
+
+bool DrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
+  DCHECK(file_.IsValid());
+  return !drmGetCap(file_.GetPlatformFile(), capability, value);
+}
+
+ScopedDrmPropertyBlobPtr DrmDevice::GetPropertyBlob(drmModeConnector* connector,
+                                                    const char* name) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT2("drm", "DrmDevice::GetPropertyBlob", "connector",
+               connector->connector_id, "name", name);
+  for (int i = 0; i < connector->count_props; ++i) {
+    ScopedDrmPropertyPtr property(
+        drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
+    if (!property)
+      continue;
+
+    if (strcmp(property->name, name) == 0 &&
+        property->flags & DRM_MODE_PROP_BLOB)
+      return ScopedDrmPropertyBlobPtr(drmModeGetPropertyBlob(
+          file_.GetPlatformFile(), connector->prop_values[i]));
+  }
+
+  return ScopedDrmPropertyBlobPtr();
+}
+
+bool DrmDevice::SetCursor(uint32_t crtc_id,
+                          uint32_t handle,
+                          const gfx::Size& size) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::SetCursor", "handle", handle);
+  return !drmModeSetCursor(file_.GetPlatformFile(), crtc_id, handle,
+                           size.width(), size.height());
+}
+
+bool DrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
+  DCHECK(file_.IsValid());
+  return !drmModeMoveCursor(file_.GetPlatformFile(), crtc_id, point.x(),
+                            point.y());
+}
+
+bool DrmDevice::CreateDumbBuffer(const SkImageInfo& info,
+                                 uint32_t* handle,
+                                 uint32_t* stride) {
+  DCHECK(file_.IsValid());
+
+  TRACE_EVENT0("drm", "DrmDevice::CreateDumbBuffer");
+  return DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride);
+}
+
+bool DrmDevice::DestroyDumbBuffer(uint32_t handle) {
+  DCHECK(file_.IsValid());
+  TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle);
+  return DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle);
+}
+
+bool DrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
+  struct drm_mode_map_dumb map_request;
+  memset(&map_request, 0, sizeof(map_request));
+  map_request.handle = handle;
+  if (drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_MODE_MAP_DUMB,
+               &map_request)) {
+    PLOG(ERROR) << "Cannot prepare dumb buffer for mapping";
+    return false;
+  }
+
+  *pixels = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                 file_.GetPlatformFile(), map_request.offset);
+  if (*pixels == MAP_FAILED) {
+    PLOG(ERROR) << "Cannot mmap dumb buffer";
+    return false;
+  }
+
+  return true;
+}
+
+bool DrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
+  return !munmap(pixels, size);
+}
+
+bool DrmDevice::CloseBufferHandle(uint32_t handle) {
+  struct drm_gem_close close_request;
+  memset(&close_request, 0, sizeof(close_request));
+  close_request.handle = handle;
+  return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_GEM_CLOSE,
+                   &close_request);
+}
+
+bool DrmDevice::CommitProperties(drmModePropertySet* properties,
+                                 uint32_t flags,
+                                 bool is_sync,
+                                 bool test_only,
+                                 const PageFlipCallback& callback) {
+#if defined(USE_DRM_ATOMIC)
+  if (test_only)
+    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+  else
+    flags |= DRM_MODE_PAGE_FLIP_EVENT;
+  uint64_t id = page_flip_manager_->GetNextId();
+  if (!drmModePropertySetCommit(file_.GetPlatformFile(), flags,
+                                reinterpret_cast<void*>(id), properties)) {
+    if (test_only)
+      return true;
+    page_flip_manager_->RegisterCallback(id, callback);
+
+    // If the flip was requested synchronous or if no watcher has been installed
+    // yet, then synchronously handle the page flip events.
+    if (is_sync || !watcher_) {
+      TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile());
+
+      ProcessDrmEvent(
+          file_.GetPlatformFile(),
+          base::Bind(&PageFlipManager::OnPageFlip, page_flip_manager_));
+    }
+    return true;
+  }
+#endif  // defined(USE_DRM_ATOMIC)
+  return false;
+}
+
+bool DrmDevice::SetCapability(uint64_t capability, uint64_t value) {
+  DCHECK(file_.IsValid());
+  return !drmSetClientCap(file_.GetPlatformFile(), capability, value);
+}
+
+bool DrmDevice::SetMaster() {
+  TRACE_EVENT1("drm", "DrmDevice::SetMaster", "path", device_path_.value());
+  DCHECK(file_.IsValid());
+  return (drmSetMaster(file_.GetPlatformFile()) == 0);
+}
+
+bool DrmDevice::DropMaster() {
+  TRACE_EVENT1("drm", "DrmDevice::DropMaster", "path", device_path_.value());
+  DCHECK(file_.IsValid());
+  return (drmDropMaster(file_.GetPlatformFile()) == 0);
+}
+
+bool DrmDevice::SetGammaRamp(uint32_t crtc_id,
+                             const std::vector<GammaRampRGBEntry>& lut) {
+  ScopedDrmCrtcPtr crtc = GetCrtc(crtc_id);
+
+  // TODO(robert.bradford) resample the incoming ramp to match what the kernel
+  // expects.
+  if (static_cast<size_t>(crtc->gamma_size) != lut.size()) {
+    LOG(ERROR) << "Gamma table size mismatch: supplied " << lut.size()
+               << " expected " << crtc->gamma_size;
+  }
+
+  std::vector<uint16_t> r, g, b;
+  r.reserve(lut.size());
+  g.reserve(lut.size());
+  b.reserve(lut.size());
+
+  for (size_t i = 0; i < lut.size(); ++i) {
+    r.push_back(lut[i].r);
+    g.push_back(lut[i].g);
+    b.push_back(lut[i].b);
+  }
+
+  DCHECK(file_.IsValid());
+  TRACE_EVENT0("drm", "DrmDevice::SetGamma");
+  return (drmModeCrtcSetGamma(file_.GetPlatformFile(), crtc_id, r.size(), &r[0],
+                              &g[0], &b[0]) == 0);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h
new file mode 100644
index 0000000..012dde7
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -0,0 +1,211 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+
+typedef struct _drmEventContext drmEventContext;
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+struct SkImageInfo;
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace ui {
+
+class HardwareDisplayPlaneManager;
+struct GammaRampRGBEntry;
+
+// Wraps DRM calls into a nice interface. Used to provide different
+// implementations of the DRM calls. For the actual implementation the DRM API
+// would be called. In unit tests this interface would be stubbed.
+class OZONE_EXPORT DrmDevice : public base::RefCountedThreadSafe<DrmDevice> {
+ public:
+  typedef base::Callback<void(unsigned int /* frame */,
+                              unsigned int /* seconds */,
+                              unsigned int /* useconds */)> PageFlipCallback;
+
+  DrmDevice(const base::FilePath& device_path, base::File file);
+
+  // Open device.
+  virtual bool Initialize(bool use_atomic);
+
+  // |task_runner| will be used to asynchronously page flip.
+  virtual void InitializeTaskRunner(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+  // Get the CRTC state. This is generally used to save state before using the
+  // CRTC. When the user finishes using the CRTC, the user should restore the
+  // CRTC to it's initial state. Use |SetCrtc| to restore the state.
+  virtual ScopedDrmCrtcPtr GetCrtc(uint32_t crtc_id);
+
+  // Used to configure CRTC with ID |crtc_id| to use the connector in
+  // |connectors|. The CRTC will be configured with mode |mode| and will display
+  // the framebuffer with ID |framebuffer|. Before being able to display the
+  // framebuffer, it should be registered with the CRTC using |AddFramebuffer|.
+  virtual bool SetCrtc(uint32_t crtc_id,
+                       uint32_t framebuffer,
+                       std::vector<uint32_t> connectors,
+                       drmModeModeInfo* mode);
+
+  // Used to set a specific configuration to the CRTC. Normally this function
+  // would be called with a CRTC saved state (from |GetCrtc|) to restore it to
+  // its original configuration.
+  virtual bool SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors);
+
+  virtual bool DisableCrtc(uint32_t crtc_id);
+
+  // Returns the connector properties for |connector_id|.
+  virtual ScopedDrmConnectorPtr GetConnector(uint32_t connector_id);
+
+  // Register a buffer with the CRTC. On successful registration, the CRTC will
+  // assign a framebuffer ID to |framebuffer|.
+  virtual bool AddFramebuffer(uint32_t width,
+                              uint32_t height,
+                              uint8_t depth,
+                              uint8_t bpp,
+                              uint32_t stride,
+                              uint32_t handle,
+                              uint32_t* framebuffer);
+
+  // Deregister the given |framebuffer|.
+  virtual bool RemoveFramebuffer(uint32_t framebuffer);
+
+  // Get the DRM details associated with |framebuffer|.
+  virtual ScopedDrmFramebufferPtr GetFramebuffer(uint32_t framebuffer);
+
+  // Schedules a pageflip for CRTC |crtc_id|. This function will return
+  // immediately. Upon completion of the pageflip event, the CRTC will be
+  // displaying the buffer with ID |framebuffer| and will have a DRM event
+  // queued on |fd_|.
+  virtual bool PageFlip(uint32_t crtc_id,
+                        uint32_t framebuffer,
+                        bool is_sync,
+                        const PageFlipCallback& callback);
+
+  // Schedule an overlay to be show during the page flip for CRTC |crtc_id|.
+  // |source| location from |framebuffer| will be shown on overlay
+  // |overlay_plane|, in the bounds specified by |location| on the screen.
+  virtual bool PageFlipOverlay(uint32_t crtc_id,
+                               uint32_t framebuffer,
+                               const gfx::Rect& location,
+                               const gfx::Rect& source,
+                               int overlay_plane);
+
+  // Returns the property with name |name| associated with |connector|. Returns
+  // NULL if property not found. If the returned value is valid, it must be
+  // released using FreeProperty().
+  virtual ScopedDrmPropertyPtr GetProperty(drmModeConnector* connector,
+                                           const char* name);
+
+  // Sets the value of property with ID |property_id| to |value|. The property
+  // is applied to the connector with ID |connector_id|.
+  virtual bool SetProperty(uint32_t connector_id,
+                           uint32_t property_id,
+                           uint64_t value);
+
+  // Can be used to query device/driver |capability|. Sets the value of
+  // |capability to |value|. Returns true in case of a succesful query.
+  virtual bool GetCapability(uint64_t capability, uint64_t* value);
+
+  // Return a binary blob associated with |connector|. The binary blob is
+  // associated with the property with name |name|. Return NULL if the property
+  // could not be found or if the property does not have a binary blob. If valid
+  // the returned object must be freed using FreePropertyBlob().
+  virtual ScopedDrmPropertyBlobPtr GetPropertyBlob(drmModeConnector* connector,
+                                                   const char* name);
+
+  // Set the cursor to be displayed in CRTC |crtc_id|. (width, height) is the
+  // cursor size pointed by |handle|.
+  virtual bool SetCursor(uint32_t crtc_id,
+                         uint32_t handle,
+                         const gfx::Size& size);
+
+  // Move the cursor on CRTC |crtc_id| to (x, y);
+  virtual bool MoveCursor(uint32_t crtc_id, const gfx::Point& point);
+
+  virtual bool CreateDumbBuffer(const SkImageInfo& info,
+                                uint32_t* handle,
+                                uint32_t* stride);
+
+  virtual bool DestroyDumbBuffer(uint32_t handle);
+
+  virtual bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels);
+
+  virtual bool UnmapDumbBuffer(void* pixels, size_t size);
+
+  virtual bool CloseBufferHandle(uint32_t handle);
+
+  virtual bool CommitProperties(drmModePropertySet* properties,
+                                uint32_t flags,
+                                bool is_sync,
+                                bool test_only,
+                                const PageFlipCallback& callback);
+
+  // Set the gamma ramp for |crtc_id| to reflect the ramps in |lut|.
+  virtual bool SetGammaRamp(uint32_t crtc_id,
+                            const std::vector<GammaRampRGBEntry>& lut);
+
+  virtual bool SetCapability(uint64_t capability, uint64_t value);
+
+  // Drm master related
+  virtual bool SetMaster();
+  virtual bool DropMaster();
+
+  int get_fd() const { return file_.GetPlatformFile(); }
+
+  base::FilePath device_path() const { return device_path_; }
+
+  HardwareDisplayPlaneManager* plane_manager() { return plane_manager_.get(); }
+
+ protected:
+  friend class base::RefCountedThreadSafe<DrmDevice>;
+
+  virtual ~DrmDevice();
+
+  scoped_ptr<HardwareDisplayPlaneManager> plane_manager_;
+
+ private:
+  class IOWatcher;
+  class PageFlipManager;
+
+  // Path to DRM device.
+  const base::FilePath device_path_;
+
+  // DRM device.
+  base::File file_;
+
+  // Helper thread to perform IO listener operations.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Watcher for |fd_| listening for page flip events.
+  scoped_refptr<IOWatcher> watcher_;
+
+  scoped_refptr<PageFlipManager> page_flip_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDevice);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_device_generator.cc b/ui/ozone/platform/drm/gpu/drm_device_generator.cc
new file mode 100644
index 0000000..205269f
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device_generator.cc
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+DrmDeviceGenerator::DrmDeviceGenerator() {
+}
+
+DrmDeviceGenerator::~DrmDeviceGenerator() {
+}
+
+scoped_refptr<DrmDevice> DrmDeviceGenerator::CreateDevice(
+    const base::FilePath& device_path,
+    base::File file) {
+  scoped_refptr<DrmDevice> drm = new DrmDevice(device_path, file.Pass());
+  if (drm->Initialize(false))
+    return drm;
+
+  return nullptr;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_device_generator.h b/ui/ozone/platform/drm/gpu/drm_device_generator.h
new file mode 100644
index 0000000..aebf9b8
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device_generator.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_GENERATOR_H_
+
+#include "base/files/file.h"
+#include "base/memory/ref_counted.h"
+
+namespace ui {
+
+class DrmDevice;
+
+class DrmDeviceGenerator {
+ public:
+  DrmDeviceGenerator();
+  virtual ~DrmDeviceGenerator();
+
+  // Creates a DRM device for |file|. |device_path| describes the location of
+  // the DRM device.
+  virtual scoped_refptr<DrmDevice> CreateDevice(
+      const base::FilePath& device_path,
+      base::File file);
+
+ public:
+  DISALLOW_COPY_AND_ASSIGN(DrmDeviceGenerator);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_device_manager.cc b/ui/ozone/platform/drm/gpu/drm_device_manager.cc
new file mode 100644
index 0000000..b01664a
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device_manager.cc
@@ -0,0 +1,126 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+
+#include "base/file_descriptor_posix.h"
+#include "base/single_thread_task_runner.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+
+namespace ui {
+
+namespace {
+
+class FindByDevicePath {
+ public:
+  explicit FindByDevicePath(const base::FilePath& path) : path_(path) {}
+
+  bool operator()(const scoped_refptr<DrmDevice>& device) {
+    return device->device_path() == path_;
+  }
+
+ private:
+  base::FilePath path_;
+};
+
+}  // namespace
+
+DrmDeviceManager::DrmDeviceManager(
+    scoped_ptr<DrmDeviceGenerator> drm_device_generator)
+    : drm_device_generator_(drm_device_generator.Pass()) {
+}
+
+DrmDeviceManager::~DrmDeviceManager() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(drm_device_map_.empty());
+}
+
+bool DrmDeviceManager::AddDrmDevice(const base::FilePath& path,
+                                    const base::FileDescriptor& fd) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::File file(fd.fd);
+  auto it =
+      std::find_if(devices_.begin(), devices_.end(), FindByDevicePath(path));
+  if (it != devices_.end()) {
+    VLOG(2) << "Got request to add existing device: " << path.value();
+    return false;
+  }
+
+  scoped_refptr<DrmDevice> device =
+      drm_device_generator_->CreateDevice(path, file.Pass());
+  if (!device) {
+    LOG(ERROR) << "Could not initialize DRM device for " << path.value();
+    return false;
+  }
+
+  if (io_task_runner_)
+    device->InitializeTaskRunner(io_task_runner_);
+
+  if (!primary_device_)
+    primary_device_ = device;
+
+  devices_.push_back(device);
+  return true;
+}
+
+void DrmDeviceManager::RemoveDrmDevice(const base::FilePath& path) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto it =
+      std::find_if(devices_.begin(), devices_.end(), FindByDevicePath(path));
+  if (it == devices_.end()) {
+    VLOG(2) << "Got request to remove non-existent device: " << path.value();
+    return;
+  }
+
+  DCHECK_NE(primary_device_, *it);
+  devices_.erase(it);
+}
+
+void DrmDeviceManager::InitializeIOTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!io_task_runner_);
+  io_task_runner_ = task_runner;
+  for (const auto& device : devices_)
+    device->InitializeTaskRunner(io_task_runner_);
+}
+
+void DrmDeviceManager::UpdateDrmDevice(gfx::AcceleratedWidget widget,
+                                       const scoped_refptr<DrmDevice>& device) {
+  base::AutoLock lock(lock_);
+  drm_device_map_[widget] = device;
+}
+
+void DrmDeviceManager::RemoveDrmDevice(gfx::AcceleratedWidget widget) {
+  base::AutoLock lock(lock_);
+  auto it = drm_device_map_.find(widget);
+  if (it != drm_device_map_.end())
+    drm_device_map_.erase(it);
+}
+
+scoped_refptr<DrmDevice> DrmDeviceManager::GetDrmDevice(
+    gfx::AcceleratedWidget widget) {
+  base::AutoLock lock(lock_);
+  if (widget == gfx::kNullAcceleratedWidget)
+    return primary_device_;
+
+  auto it = drm_device_map_.find(widget);
+  DCHECK(it != drm_device_map_.end())
+      << "Attempting to get device for unknown widget " << widget;
+  // If the widget isn't associated with a display (headless mode) we can
+  // allocate buffers from any controller since they will never be scanned out.
+  // Use the primary DRM device as a fallback when allocating these buffers.
+  if (!it->second)
+    return primary_device_;
+
+  return it->second;
+}
+
+const DrmDeviceVector& DrmDeviceManager::GetDrmDevices() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return devices_;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_device_manager.h b/ui/ozone/platform/drm/gpu/drm_device_manager.h
new file mode 100644
index 0000000..34fe613
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_device_manager.h
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_MANAGER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace base {
+class FilePath;
+struct FileDescriptor;
+class SingleThreadTaskRunner;
+}
+
+namespace ui {
+
+class DrmDevice;
+class DrmDeviceGenerator;
+
+typedef std::vector<scoped_refptr<DrmDevice>> DrmDeviceVector;
+
+// Tracks the mapping between widgets and the DRM devices used to allocate
+// buffers for the window represented by the widget.
+// Note: All calls are protected by a lock since
+// GetDrmDevice(gfx::AcceleratedWidget) may be called on the IO thread.
+class OZONE_EXPORT DrmDeviceManager {
+ public:
+  DrmDeviceManager(scoped_ptr<DrmDeviceGenerator> drm_device_generator);
+  ~DrmDeviceManager();
+
+  // The first device registered is assumed to be the primary device.
+  bool AddDrmDevice(const base::FilePath& path, const base::FileDescriptor& fd);
+  void RemoveDrmDevice(const base::FilePath& path);
+
+  void InitializeIOTaskRunner(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+  // Updates the device associated with |widget|.
+  void UpdateDrmDevice(gfx::AcceleratedWidget widget,
+                       const scoped_refptr<DrmDevice>& device);
+
+  // Removes the device associated with |widget|.
+  void RemoveDrmDevice(gfx::AcceleratedWidget widget);
+
+  // Returns the device associated with |widget|. If there is no association
+  // returns |primary_device_|.
+  scoped_refptr<DrmDevice> GetDrmDevice(gfx::AcceleratedWidget widget);
+
+  const DrmDeviceVector& GetDrmDevices() const;
+
+ private:
+  // With the exception of GetDrmDevice() all functions must be called on GPU
+  // main.
+  base::ThreadChecker thread_checker_;
+
+  scoped_ptr<DrmDeviceGenerator> drm_device_generator_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  DrmDeviceVector devices_;
+
+  std::map<gfx::AcceleratedWidget, scoped_refptr<DrmDevice>> drm_device_map_;
+
+  // This device represents the primary graphics device and is used when:
+  // 1) 'widget == kNullAcceleratedWidget' when the API requesting a buffer has
+  // no knowledge of the surface/display it belongs to (currently this happens
+  // for video buffers), or
+  // 2) in order to allocate buffers for unmatched surfaces (surfaces without a
+  // display; ie: when in headless mode).
+  scoped_refptr<DrmDevice> primary_device_;
+
+  // This class is accessed from the main thread and the IO thread. This lock
+  // protects access to the device map.
+  base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDeviceManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_DEVICE_MANAGER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
new file mode 100644
index 0000000..4e949ec
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -0,0 +1,179 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_display.h"
+
+#include <xf86drmMode.h>
+
+#include "ui/display/types/gamma_ramp_rgb_entry.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+
+namespace ui {
+
+namespace {
+
+const char kContentProtection[] = "Content Protection";
+
+struct ContentProtectionMapping {
+  const char* name;
+  HDCPState state;
+};
+
+const ContentProtectionMapping kContentProtectionStates[] = {
+    {"Undesired", HDCP_STATE_UNDESIRED},
+    {"Desired", HDCP_STATE_DESIRED},
+    {"Enabled", HDCP_STATE_ENABLED}};
+
+// Converts |state| to the DRM value associated with the it.
+uint32_t GetContentProtectionValue(drmModePropertyRes* property,
+                                   HDCPState state) {
+  std::string name;
+  for (size_t i = 0; i < arraysize(kContentProtectionStates); ++i) {
+    if (kContentProtectionStates[i].state == state) {
+      name = kContentProtectionStates[i].name;
+      break;
+    }
+  }
+
+  for (int i = 0; i < property->count_enums; ++i)
+    if (name == property->enums[i].name)
+      return i;
+
+  NOTREACHED();
+  return 0;
+}
+
+std::string GetEnumNameForProperty(drmModeConnector* connector,
+                                   drmModePropertyRes* property) {
+  for (int prop_idx = 0; prop_idx < connector->count_props; ++prop_idx) {
+    if (connector->props[prop_idx] != property->prop_id)
+      continue;
+
+    for (int enum_idx = 0; enum_idx < property->count_enums; ++enum_idx) {
+      const drm_mode_property_enum& property_enum = property->enums[enum_idx];
+      if (property_enum.value == connector->prop_values[prop_idx])
+        return property_enum.name;
+    }
+  }
+
+  NOTREACHED();
+  return std::string();
+}
+
+gfx::Size GetDrmModeSize(const drmModeModeInfo& mode) {
+  return gfx::Size(mode.hdisplay, mode.vdisplay);
+}
+
+std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) {
+  std::vector<drmModeModeInfo> modes;
+  for (int i = 0; i < connector->count_modes; ++i)
+    modes.push_back(connector->modes[i]);
+
+  return modes;
+}
+
+}  // namespace
+
+DrmDisplay::DrmDisplay(ScreenManager* screen_manager,
+                       const scoped_refptr<DrmDevice>& drm)
+    : screen_manager_(screen_manager), drm_(drm) {
+}
+
+DrmDisplay::~DrmDisplay() {
+}
+
+DisplaySnapshot_Params DrmDisplay::Update(HardwareDisplayControllerInfo* info,
+                                          size_t display_index) {
+  DisplaySnapshot_Params params =
+      CreateDisplaySnapshotParams(info, drm_->get_fd(), display_index, origin_);
+  crtc_ = info->crtc()->crtc_id;
+  connector_ = info->connector()->connector_id;
+  display_id_ = params.display_id;
+  modes_ = GetDrmModeVector(info->connector());
+  return params;
+}
+
+bool DrmDisplay::Configure(const drmModeModeInfo* mode,
+                           const gfx::Point& origin) {
+  VLOG(1) << "DRM configuring: device=" << drm_->device_path().value()
+          << " crtc=" << crtc_ << " connector=" << connector_
+          << " origin=" << origin.ToString()
+          << " size=" << (mode ? GetDrmModeSize(*mode).ToString() : "0x0");
+
+  if (mode) {
+    if (!screen_manager_->ConfigureDisplayController(drm_, crtc_, connector_,
+                                                     origin, *mode)) {
+      VLOG(1) << "Failed to configure: device=" << drm_->device_path().value()
+              << " crtc=" << crtc_ << " connector=" << connector_;
+      return false;
+    }
+  } else {
+    if (!screen_manager_->DisableDisplayController(drm_, crtc_)) {
+      VLOG(1) << "Failed to disable device=" << drm_->device_path().value()
+              << " crtc=" << crtc_;
+      return false;
+    }
+  }
+
+  origin_ = origin;
+  return true;
+}
+
+bool DrmDisplay::GetHDCPState(HDCPState* state) {
+  ScopedDrmConnectorPtr connector(drm_->GetConnector(connector_));
+  if (!connector) {
+    PLOG(ERROR) << "Failed to get connector " << connector_;
+    return false;
+  }
+
+  ScopedDrmPropertyPtr hdcp_property(
+      drm_->GetProperty(connector.get(), kContentProtection));
+  if (!hdcp_property) {
+    PLOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
+    return false;
+  }
+
+  std::string name =
+      GetEnumNameForProperty(connector.get(), hdcp_property.get());
+  for (size_t i = 0; i < arraysize(kContentProtectionStates); ++i) {
+    if (name == kContentProtectionStates[i].name) {
+      *state = kContentProtectionStates[i].state;
+      VLOG(3) << "HDCP state: " << *state << " (" << name << ")";
+      return true;
+    }
+  }
+
+  LOG(ERROR) << "Unknown content protection value '" << name << "'";
+  return false;
+}
+
+bool DrmDisplay::SetHDCPState(HDCPState state) {
+  ScopedDrmConnectorPtr connector(drm_->GetConnector(connector_));
+  if (!connector) {
+    PLOG(ERROR) << "Failed to get connector " << connector_;
+    return false;
+  }
+
+  ScopedDrmPropertyPtr hdcp_property(
+      drm_->GetProperty(connector.get(), kContentProtection));
+  if (!hdcp_property) {
+    LOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
+    return false;
+  }
+
+  return drm_->SetProperty(
+      connector_, hdcp_property->prop_id,
+      GetContentProtectionValue(hdcp_property.get(), state));
+}
+
+void DrmDisplay::SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut) {
+  if (!drm_->SetGammaRamp(crtc_, lut)) {
+    LOG(ERROR) << "Failed to set gamma ramp for display: crtc_id = " << crtc_
+               << " size = " << lut.size();
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
new file mode 100644
index 0000000..40cd343
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DISPLAY_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DISPLAY_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+namespace ui {
+
+class DrmDevice;
+class HardwareDisplayControllerInfo;
+class ScreenManager;
+
+struct GammaRampRGBEntry;
+
+class DrmDisplay {
+ public:
+  DrmDisplay(ScreenManager* screen_manager,
+             const scoped_refptr<DrmDevice>& drm);
+  ~DrmDisplay();
+
+  int64_t display_id() const { return display_id_; }
+  scoped_refptr<DrmDevice> drm() const { return drm_; }
+  uint32_t crtc() const { return crtc_; }
+  uint32_t connector() const { return connector_; }
+  const std::vector<drmModeModeInfo>& modes() const { return modes_; }
+
+  DisplaySnapshot_Params Update(HardwareDisplayControllerInfo* info,
+                                size_t display_index);
+
+  bool Configure(const drmModeModeInfo* mode, const gfx::Point& origin);
+  bool GetHDCPState(HDCPState* state);
+  bool SetHDCPState(HDCPState state);
+  void SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut);
+
+ private:
+  ScreenManager* screen_manager_;  // Not owned.
+
+  int64_t display_id_ = -1;
+  scoped_refptr<DrmDevice> drm_;
+  uint32_t crtc_ = 0;
+  uint32_t connector_ = 0;
+  std::vector<drmModeModeInfo> modes_;
+  gfx::Point origin_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDisplay);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_DISPLAY_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
new file mode 100644
index 0000000..7d7bba4
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -0,0 +1,230 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
+
+#include "ui/display/types/gamma_ramp_rgb_entry.h"
+#include "ui/ozone/common/display_util.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_display.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+
+namespace ui {
+
+namespace {
+
+class DisplayComparator {
+ public:
+  explicit DisplayComparator(const DrmDisplay* display)
+      : drm_(display->drm()),
+        crtc_(display->crtc()),
+        connector_(display->connector()) {}
+
+  DisplayComparator(const scoped_refptr<DrmDevice>& drm,
+                    uint32_t crtc,
+                    uint32_t connector)
+      : drm_(drm), crtc_(crtc), connector_(connector) {}
+
+  bool operator()(const DrmDisplay* other) const {
+    return drm_ == other->drm() && connector_ == other->connector() &&
+           crtc_ == other->crtc();
+  }
+
+ private:
+  scoped_refptr<DrmDevice> drm_;
+  uint32_t crtc_;
+  uint32_t connector_;
+};
+
+bool FindMatchingMode(const std::vector<drmModeModeInfo> modes,
+                      const DisplayMode_Params& mode_params,
+                      drmModeModeInfo* mode) {
+  for (const drmModeModeInfo& m : modes) {
+    DisplayMode_Params params = CreateDisplayModeParams(m);
+    if (mode_params.size == params.size &&
+        mode_params.refresh_rate == params.refresh_rate &&
+        mode_params.is_interlaced == params.is_interlaced) {
+      *mode = m;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace
+
+DrmGpuDisplayManager::DrmGpuDisplayManager(ScreenManager* screen_manager,
+                                           DrmDeviceManager* drm_device_manager)
+    : screen_manager_(screen_manager), drm_device_manager_(drm_device_manager) {
+}
+
+DrmGpuDisplayManager::~DrmGpuDisplayManager() {
+}
+
+std::vector<DisplaySnapshot_Params> DrmGpuDisplayManager::GetDisplays() {
+  ScopedVector<DrmDisplay> old_displays(displays_.Pass());
+  std::vector<DisplaySnapshot_Params> params_list;
+
+  const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
+  // Unique identifier used to create the display id.
+  size_t index = 0;
+  for (const auto& drm : devices) {
+    ScopedVector<HardwareDisplayControllerInfo> display_infos =
+        GetAvailableDisplayControllerInfos(drm->get_fd());
+    for (auto* display_info : display_infos) {
+      auto it = std::find_if(
+          old_displays.begin(), old_displays.end(),
+          DisplayComparator(drm, display_info->crtc()->crtc_id,
+                            display_info->connector()->connector_id));
+      if (it != old_displays.end()) {
+        displays_.push_back(*it);
+        old_displays.weak_erase(it);
+      } else {
+        displays_.push_back(new DrmDisplay(screen_manager_, drm));
+      }
+
+      params_list.push_back(displays_.back()->Update(display_info, index++));
+    }
+  }
+
+  NotifyScreenManager(displays_.get(), old_displays.get());
+  return params_list;
+}
+
+bool DrmGpuDisplayManager::TakeDisplayControl() {
+  const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
+  bool status = true;
+  for (const auto& drm : devices)
+    status &= drm->SetMaster();
+
+  // Roll-back any successful operation.
+  if (!status) {
+    LOG(ERROR) << "Failed to take control of the display";
+    RelinquishDisplayControl();
+  }
+
+  return status;
+}
+
+void DrmGpuDisplayManager::RelinquishDisplayControl() {
+  const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
+  for (const auto& drm : devices)
+    drm->DropMaster();
+}
+
+bool DrmGpuDisplayManager::ConfigureDisplay(
+    int64_t display_id,
+    const DisplayMode_Params& mode_param,
+    const gfx::Point& origin) {
+  DrmDisplay* display = FindDisplay(display_id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << display_id;
+    return false;
+  }
+
+  drmModeModeInfo mode;
+  bool mode_found = FindMatchingMode(display->modes(), mode_param, &mode);
+  if (!mode_found) {
+    // If the display doesn't have the mode natively, then lookup the mode from
+    // other displays and try using it on the current display (some displays
+    // support panel fitting and they can use different modes even if the mode
+    // isn't explicitly declared).
+    for (DrmDisplay* other : displays_) {
+      mode_found = FindMatchingMode(other->modes(), mode_param, &mode);
+      if (mode_found)
+        break;
+    }
+  }
+
+  if (!mode_found) {
+    LOG(ERROR) << "Failed to find mode: size=" << mode_param.size.ToString()
+               << " is_interlaced=" << mode_param.is_interlaced
+               << " refresh_rate=" << mode_param.refresh_rate;
+    return false;
+  }
+
+  return display->Configure(&mode, origin);
+}
+
+bool DrmGpuDisplayManager::DisableDisplay(int64_t display_id) {
+  DrmDisplay* display = FindDisplay(display_id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << display_id;
+    return false;
+  }
+
+  return display->Configure(nullptr, gfx::Point());
+}
+
+bool DrmGpuDisplayManager::GetHDCPState(int64_t display_id, HDCPState* state) {
+  DrmDisplay* display = FindDisplay(display_id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << display_id;
+    return false;
+  }
+
+  return display->GetHDCPState(state);
+}
+
+bool DrmGpuDisplayManager::SetHDCPState(int64_t display_id, HDCPState state) {
+  DrmDisplay* display = FindDisplay(display_id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << display_id;
+    return false;
+  }
+
+  return display->SetHDCPState(state);
+}
+
+void DrmGpuDisplayManager::SetGammaRamp(
+    int64_t display_id,
+    const std::vector<GammaRampRGBEntry>& lut) {
+  DrmDisplay* display = FindDisplay(display_id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << display_id;
+    return;
+  }
+
+  display->SetGammaRamp(lut);
+}
+
+DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
+  for (DrmDisplay* display : displays_)
+    if (display->display_id() == display_id)
+      return display;
+
+  return nullptr;
+}
+
+void DrmGpuDisplayManager::NotifyScreenManager(
+    const std::vector<DrmDisplay*>& new_displays,
+    const std::vector<DrmDisplay*>& old_displays) const {
+  for (size_t i = 0; i < old_displays.size(); ++i) {
+    const std::vector<DrmDisplay*>::const_iterator it =
+        std::find_if(new_displays.begin(), new_displays.end(),
+                     DisplayComparator(old_displays[i]));
+
+    if (it == new_displays.end()) {
+      screen_manager_->RemoveDisplayController(old_displays[i]->drm(),
+                                               old_displays[i]->crtc());
+    }
+  }
+
+  for (size_t i = 0; i < new_displays.size(); ++i) {
+    const std::vector<DrmDisplay*>::const_iterator it =
+        std::find_if(old_displays.begin(), old_displays.end(),
+                     DisplayComparator(new_displays[i]));
+
+    if (it == old_displays.end()) {
+      screen_manager_->AddDisplayController(new_displays[i]->drm(),
+                                            new_displays[i]->crtc(),
+                                            new_displays[i]->connector());
+    }
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
new file mode 100644
index 0000000..97ebaa7
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_H_
+
+#include "base/memory/scoped_vector.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+
+namespace ui {
+
+class DrmDeviceManager;
+class DrmDisplay;
+class ScreenManager;
+
+struct GammaRampRGBEntry;
+
+class DrmGpuDisplayManager {
+ public:
+  DrmGpuDisplayManager(ScreenManager* screen_manager,
+                       DrmDeviceManager* drm_device_manager);
+  ~DrmGpuDisplayManager();
+
+  // Returns a list of the connected displays. When this is called the list of
+  // displays is refreshed.
+  std::vector<DisplaySnapshot_Params> GetDisplays();
+
+  // Takes/releases the control of the DRM devices.
+  bool TakeDisplayControl();
+  void RelinquishDisplayControl();
+
+  bool ConfigureDisplay(int64_t id,
+                        const DisplayMode_Params& mode,
+                        const gfx::Point& origin);
+  bool DisableDisplay(int64_t id);
+  bool GetHDCPState(int64_t display_id, HDCPState* state);
+  bool SetHDCPState(int64_t display_id, HDCPState state);
+  void SetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut);
+
+ private:
+  DrmDisplay* FindDisplay(int64_t display_id);
+
+  // Notify ScreenManager of all the displays that were present before the
+  // update but are gone after the update.
+  void NotifyScreenManager(const std::vector<DrmDisplay*>& new_displays,
+                           const std::vector<DrmDisplay*>& old_displays) const;
+
+  ScreenManager* screen_manager_;  // Not owned.
+  DrmDeviceManager* drm_device_manager_;  // Not owned.
+
+  ScopedVector<DrmDisplay> displays_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmGpuDisplayManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.cc b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.cc
new file mode 100644
index 0000000..680749b
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.cc
@@ -0,0 +1,139 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h"
+
+#include "base/bind.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+
+namespace ui {
+
+DrmGpuPlatformSupport::DrmGpuPlatformSupport(
+    DrmDeviceManager* drm_device_manager,
+    ScreenManager* screen_manager,
+    ScanoutBufferGenerator* buffer_generator,
+    scoped_ptr<DrmGpuDisplayManager> display_manager)
+    : drm_device_manager_(drm_device_manager),
+      screen_manager_(screen_manager),
+      display_manager_(display_manager.Pass()),
+      delegate_(nullptr) {
+}
+
+DrmGpuPlatformSupport::~DrmGpuPlatformSupport() {
+}
+
+void DrmGpuPlatformSupport::SetDelegate(
+    DrmGpuPlatformSupportDelegate* delegate) {
+  DCHECK(!delegate_);
+  delegate_ = delegate;
+}
+
+void DrmGpuPlatformSupport::AddHandler(scoped_ptr<GpuPlatformSupport> handler) {
+  handlers_.push_back(handler.Pass());
+}
+
+void DrmGpuPlatformSupport::OnChannelEstablished() {
+  for (size_t i = 0; i < handlers_.size(); ++i)
+    handlers_[i]->OnChannelEstablished();
+}
+
+void DrmGpuPlatformSupport::OnCreateWindow(gfx::AcceleratedWidget widget) {
+  scoped_ptr<DrmWindow> delegate(
+      new DrmWindow(widget, drm_device_manager_, screen_manager_));
+  delegate->Initialize();
+  screen_manager_->AddWindow(widget, delegate.Pass());
+}
+
+void DrmGpuPlatformSupport::OnDestroyWindow(gfx::AcceleratedWidget widget) {
+  scoped_ptr<DrmWindow> delegate = screen_manager_->RemoveWindow(widget);
+  delegate->Shutdown();
+}
+
+void DrmGpuPlatformSupport::OnWindowBoundsChanged(gfx::AcceleratedWidget widget,
+                                                  const gfx::Rect& bounds) {
+  screen_manager_->GetWindow(widget)->OnBoundsChanged(bounds);
+}
+
+void DrmGpuPlatformSupport::OnRefreshNativeDisplays() {
+  DCHECK(delegate_);
+  delegate_->UpdateNativeDisplays(display_manager()->GetDisplays());
+}
+
+void DrmGpuPlatformSupport::OnCursorSet(gfx::AcceleratedWidget widget,
+                                        const std::vector<SkBitmap>& bitmaps,
+                                        const gfx::Point& location,
+                                        int frame_delay_ms) {
+  screen_manager_->GetWindow(widget)
+      ->SetCursor(bitmaps, location, frame_delay_ms);
+}
+
+void DrmGpuPlatformSupport::OnCursorMove(gfx::AcceleratedWidget widget,
+                                         const gfx::Point& location) {
+  screen_manager_->GetWindow(widget)->MoveCursor(location);
+}
+
+void DrmGpuPlatformSupport::OnCheckOverlayCapabilities(
+    gfx::AcceleratedWidget widget,
+    const std::vector<OverlayCheck_Params>& overlays) {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::OnConfigureNativeDisplay(
+    int64_t id,
+    const DisplayMode_Params& mode_param,
+    const gfx::Point& origin) {
+  DCHECK(delegate_);
+  delegate_->DisplayConfigured(
+    id, display_manager_->ConfigureDisplay(id, mode_param, origin));
+}
+
+void DrmGpuPlatformSupport::OnDisableNativeDisplay(int64_t id) {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::OnTakeDisplayControl() {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::OnRelinquishDisplayControl() {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::OnAddGraphicsDevice(
+    const base::FilePath& path,
+    const base::FileDescriptor& fd) {
+  drm_device_manager_->AddDrmDevice(path, fd);
+}
+
+void DrmGpuPlatformSupport::OnRemoveGraphicsDevice(const base::FilePath& path) {
+  drm_device_manager_->RemoveDrmDevice(path);
+}
+
+void DrmGpuPlatformSupport::OnSetGammaRamp(
+    int64_t id,
+    const std::vector<GammaRampRGBEntry>& lut) {
+  display_manager_->SetGammaRamp(id, lut);
+}
+
+void DrmGpuPlatformSupport::OnGetHDCPState(int64_t display_id) {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::OnSetHDCPState(int64_t display_id,
+                                           HDCPState state) {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupport::SetIOTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) {
+  drm_device_manager_->InitializeIOTaskRunner(io_task_runner);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h
new file mode 100644
index 0000000..27eb819
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_PLATFORM_SUPPORT_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_PLATFORM_SUPPORT_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+
+class SkBitmap;
+
+namespace base {
+class FilePath;
+class SingleThreadTaskRunner;
+struct FileDescriptor;
+}
+
+namespace gfx {
+class Point;
+class Rect;
+}
+
+namespace ui {
+
+class DrmDeviceManager;
+class DrmGpuDisplayManager;
+class DrmSurfaceFactory;
+class DrmWindow;
+class ScreenManager;
+class ScanoutBufferGenerator;
+
+struct DisplayMode_Params;
+struct DisplaySnapshot_Params;
+struct OverlayCheck_Params;
+struct GammaRampRGBEntry;
+
+class DrmGpuPlatformSupportDelegate {
+public:
+  DrmGpuPlatformSupportDelegate() {};
+  virtual ~DrmGpuPlatformSupportDelegate() {};
+
+  virtual void UpdateNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& displays) = 0;
+  virtual void DisplayConfigured(int64_t id, bool result) = 0;
+};
+
+class DrmGpuPlatformSupport : public GpuPlatformSupport {
+ public:
+  DrmGpuPlatformSupport(DrmDeviceManager* drm_device_manager,
+                        ScreenManager* screen_manager,
+                        ScanoutBufferGenerator* buffer_generator,
+                        scoped_ptr<DrmGpuDisplayManager> display_manager);
+  ~DrmGpuPlatformSupport() override;
+
+  void SetDelegate(DrmGpuPlatformSupportDelegate* delegate);
+
+  DrmGpuPlatformSupportDelegate* get_delegate() const {
+    return delegate_;
+  }
+
+  void AddHandler(scoped_ptr<GpuPlatformSupport> handler);
+
+  // GpuPlatformSupport:
+  void OnChannelEstablished() override;
+
+  void OnCreateWindow(gfx::AcceleratedWidget widget);
+  void OnDestroyWindow(gfx::AcceleratedWidget widget);
+  void OnWindowBoundsChanged(gfx::AcceleratedWidget widget,
+                             const gfx::Rect& bounds);
+  void OnCursorSet(gfx::AcceleratedWidget widget,
+                   const std::vector<SkBitmap>& bitmaps,
+                   const gfx::Point& location,
+                   int frame_delay_ms);
+  void OnCursorMove(gfx::AcceleratedWidget widget, const gfx::Point& location);
+  void OnCheckOverlayCapabilities(
+      gfx::AcceleratedWidget widget,
+      const std::vector<OverlayCheck_Params>& overlays);
+
+  DrmGpuDisplayManager* display_manager() {
+    return display_manager_.get();
+  }
+
+  void OnRefreshNativeDisplays();
+  void OnConfigureNativeDisplay(int64_t id,
+                                const DisplayMode_Params& mode,
+                                const gfx::Point& origin);
+  void OnDisableNativeDisplay(int64_t id);
+  void OnTakeDisplayControl();
+  void OnRelinquishDisplayControl();
+  void OnAddGraphicsDevice(const base::FilePath& path,
+                           const base::FileDescriptor& fd);
+  void OnRemoveGraphicsDevice(const base::FilePath& path);
+  void OnGetHDCPState(int64_t display_id);
+  void OnSetHDCPState(int64_t display_id, HDCPState state);
+  void OnSetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut);
+
+  void SetIOTaskRunner(
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
+
+  DrmDeviceManager* drm_device_manager_;  // Not owned.
+  ScreenManager* screen_manager_;         // Not owned.
+
+  scoped_ptr<DrmGpuDisplayManager> display_manager_;
+  ScopedVector<GpuPlatformSupport> handlers_;
+  DrmGpuPlatformSupportDelegate* delegate_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_PLATFORM_SUPPORT_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.cc b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.cc
new file mode 100644
index 0000000..a467480
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+DrmGpuPlatformSupportInprocess::DrmGpuPlatformSupportInprocess()
+  : platform_support_(static_cast<ui::DrmGpuPlatformSupport*>(
+      ui::OzonePlatform::GetInstance()->GetGpuPlatformSupport())) {
+  platform_support_->SetDelegate(this);
+}
+
+DrmGpuPlatformSupportInprocess::~DrmGpuPlatformSupportInprocess() {
+}
+
+void DrmGpuPlatformSupportInprocess::OnChannelEstablished(
+  scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+  base::Callback<void(Message*)> send_callback) {
+
+  send_runner_ = send_runner;
+  send_callback_ = send_callback;
+
+  platform_support_->OnChannelEstablished();
+}
+
+bool DrmGpuPlatformSupportInprocess::OnMessageReceived(const Message& message) {
+  bool handled = true;
+
+  switch (message.id) {
+    case OZONE_GPU_MSG__CREATE_WINDOW:
+      platform_support_->OnCreateWindow(
+        static_cast<const OzoneGpuMsg_CreateWindow*>(&message)->widget);
+      break;
+
+    case OZONE_GPU_MSG__WINDOW_BOUNDS_CHANGED: {
+      auto message_params = static_cast<
+        const OzoneGpuMsg_WindowBoundsChanged*>(&message);
+      platform_support_->OnWindowBoundsChanged(message_params->widget,
+                                               message_params->bounds);
+      break;
+    }
+
+    case OZONE_GPU_MSG__ADD_GRAPHICS_DEVICE: {
+      auto message_params = static_cast<
+        const OzoneGpuMsg_AddGraphicsDevice*>(&message);
+      platform_support_->OnAddGraphicsDevice(message_params->path,
+                                             message_params->fd);
+      break;
+    }
+
+    case OZONE_GPU_MSG__REFRESH_NATIVE_DISPLAYS:
+      platform_support_->OnRefreshNativeDisplays();
+      break;
+
+    case OZONE_GPU_MSG__CONFIGURE_NATIVE_DISPLAY: {
+      auto message_params = static_cast<
+        const OzoneGpuMsg_ConfigureNativeDisplay*>(&message);
+      platform_support_->OnConfigureNativeDisplay(
+        message_params->id, message_params->mode, message_params->originhost);
+      break;
+    }
+
+    default:
+      handled = false;
+  }
+
+  return handled;
+}
+
+void DrmGpuPlatformSupportInprocess::UpdateNativeDisplays(
+  const std::vector<DisplaySnapshot_Params>& displays) {
+  Send(new OzoneHostMsg_UpdateNativeDisplays(displays));
+}
+
+void DrmGpuPlatformSupportInprocess::DisplayConfigured(int64_t id,
+                                                       bool result) {
+  Send(new OzoneHostMsg_DisplayConfigured(id, result));
+}
+
+bool DrmGpuPlatformSupportInprocess::Send(Message* message) {
+  if (send_runner_->PostTask(FROM_HERE, base::Bind(send_callback_, message)))
+    return true;
+
+  delete message;
+  return false;
+}
+
+} // namespace
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.h b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.h
new file mode 100644
index 0000000..45692e3
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_platform_support_inprocess.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_PLATFORM_SUPPORT_INPROCESS_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_PLATFORM_SUPPORT_INPROCESS_H_
+
+#include "base/callback.h"
+#include "base/single_thread_task_runner.h"
+#include "ui/ozone/platform/drm/common/inprocess_messages.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h"
+
+namespace ui {
+
+class DrmGpuPlatformSupportInprocess : public DrmGpuPlatformSupportDelegate {
+ public:
+  DrmGpuPlatformSupportInprocess();
+  ~DrmGpuPlatformSupportInprocess() override;
+
+  void OnChannelEstablished(
+    scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+    base::Callback<void(Message*)> send_callback);
+
+  bool OnMessageReceived(const Message& message);
+  bool Send(Message* message);
+
+  void UpdateNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& displays) override;
+  void DisplayConfigured(int64_t id, bool result) override;
+
+ private:
+  DrmGpuPlatformSupport* platform_support_;
+  scoped_refptr<base::SingleThreadTaskRunner> send_runner_;
+  base::Callback<void(Message*)> send_callback_;
+};
+
+} // namespace
+
+#endif
diff --git a/ui/ozone/platform/drm/gpu/drm_surface.cc b/ui/ozone/platform/drm/gpu/drm_surface.cc
new file mode 100644
index 0000000..707f3c9
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_surface.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_surface.h"
+
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+
+namespace ui {
+
+namespace {
+
+scoped_refptr<DrmBuffer> AllocateBuffer(const scoped_refptr<DrmDevice>& drm,
+                                        const gfx::Size& size) {
+  scoped_refptr<DrmBuffer> buffer(new DrmBuffer(drm));
+  SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+
+  bool initialized =
+      buffer->Initialize(info, true /* should_register_framebuffer */);
+  DCHECK(initialized) << "Failed to create drm buffer.";
+
+  return buffer;
+}
+
+}  // namespace
+
+DrmSurface::DrmSurface(DrmWindow* window_delegate)
+    : window_delegate_(window_delegate),
+      weak_ptr_factory_(this) {
+}
+
+DrmSurface::~DrmSurface() {
+}
+
+skia::RefPtr<SkSurface> DrmSurface::GetSurface() {
+  return surface_;
+}
+
+void DrmSurface::ResizeCanvas(const gfx::Size& viewport_size) {
+  SkImageInfo info = SkImageInfo::MakeN32(
+      viewport_size.width(), viewport_size.height(), kOpaque_SkAlphaType);
+  surface_ = skia::AdoptRef(SkSurface::NewRaster(info));
+
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return;
+
+  // For the display buffers use the mode size since a |viewport_size| smaller
+  // than the display size will not scanout.
+  front_buffer_ = AllocateBuffer(controller->GetAllocationDrmDevice(),
+                                 controller->GetModeSize());
+  back_buffer_ = AllocateBuffer(controller->GetAllocationDrmDevice(),
+                                controller->GetModeSize());
+}
+
+void DrmSurface::PresentCanvas(const gfx::Rect& damage) {
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+
+  // Create a snapshot of the requested drawing. If we get here again before
+  // presenting, just add the additional damage.
+  pending_image_damage_.Union(damage);
+  pending_image_ = skia::AdoptRef(surface_->newImageSnapshot());
+
+  if (!pending_pageflip_)
+    SchedulePageFlip();
+}
+
+scoped_ptr<gfx::VSyncProvider> DrmSurface::CreateVSyncProvider() {
+  return make_scoped_ptr(new DrmVSyncProvider(window_delegate_));
+}
+
+void DrmSurface::SchedulePageFlip() {
+  DCHECK(back_buffer_);
+  SkCanvas* canvas = back_buffer_->GetCanvas();
+
+  // The DrmSurface is double buffered, so the current back buffer is
+  // missing the previous update. Expand damage region.
+  SkRect real_damage =
+      RectToSkRect(UnionRects(pending_image_damage_, last_damage_));
+
+  // Copy damage region.
+  canvas->drawImageRect(pending_image_.get(), &real_damage, real_damage, NULL);
+  last_damage_ = pending_image_damage_;
+
+  pending_image_.clear();
+  pending_image_damage_ = gfx::Rect();
+
+  window_delegate_->QueueOverlayPlane(OverlayPlane(back_buffer_));
+
+  // Update our front buffer pointer.
+  std::swap(front_buffer_, back_buffer_);
+  pending_pageflip_ = window_delegate_->SchedulePageFlip(
+      false /* is_sync */,
+      base::Bind(&DrmSurface::OnPageFlip, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DrmSurface::OnPageFlip(gfx::SwapResult result) {
+  pending_pageflip_ = false;
+  if (!pending_image_)
+    return;
+
+  SchedulePageFlip();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_surface.h b/ui/ozone/platform/drm/gpu/drm_surface.h
new file mode 100644
index 0000000..65fe969
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_surface.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+class SkImage;
+class SkSurface;
+
+namespace ui {
+
+class DrmBuffer;
+class DrmWindow;
+class HardwareDisplayController;
+
+class OZONE_EXPORT DrmSurface : public SurfaceOzoneCanvas {
+ public:
+  DrmSurface(DrmWindow* window_delegate);
+  ~DrmSurface() override;
+
+  // SurfaceOzoneCanvas:
+  skia::RefPtr<SkSurface> GetSurface() override;
+  void ResizeCanvas(const gfx::Size& viewport_size) override;
+  void PresentCanvas(const gfx::Rect& damage) override;
+  scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override;
+
+ private:
+  void SchedulePageFlip();
+
+  // Callback for SchedulePageFlip(). This will signal when the page flip event
+  // has completed.
+  void OnPageFlip(gfx::SwapResult result);
+
+  DrmWindow* window_delegate_;
+
+  // The actual buffers used for painting.
+  scoped_refptr<DrmBuffer> front_buffer_;
+  scoped_refptr<DrmBuffer> back_buffer_;
+
+  skia::RefPtr<SkSurface> surface_;
+  gfx::Rect last_damage_;
+
+  // Keep track of the requested image and damage for the last presentation.
+  // This will be used to update the scanout buffers once the previous page flip
+  // events completes.
+  skia::RefPtr<SkImage> pending_image_;
+  gfx::Rect pending_image_damage_;
+  bool pending_pageflip_ = false;
+
+  base::WeakPtrFactory<DrmSurface> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmSurface);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_surface_factory.cc b/ui/ozone/platform/drm/gpu/drm_surface_factory.cc
new file mode 100644
index 0000000..fc64d25
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_surface_factory.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_surface_factory.h"
+
+#include <errno.h>
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_surface.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+
+DrmSurfaceFactory::DrmSurfaceFactory(ScreenManager* screen_manager)
+    : screen_manager_(screen_manager) {
+}
+
+DrmSurfaceFactory::~DrmSurfaceFactory() {
+}
+
+scoped_ptr<ui::SurfaceOzoneCanvas> DrmSurfaceFactory::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return make_scoped_ptr(new DrmSurface(screen_manager_->GetWindow(widget)));
+}
+
+bool DrmSurfaceFactory::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_surface_factory.h b/ui/ozone/platform/drm/gpu/drm_surface_factory.h
new file mode 100644
index 0000000..223a7b9
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_surface_factory.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_FACTORY_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+class ScreenManager;
+class SurfaceOzoneCanvas;
+
+// SurfaceFactoryOzone implementation on top of DRM/KMS using dumb buffers.
+// This implementation is used in conjunction with the software rendering
+// path.
+class DrmSurfaceFactory : public SurfaceFactoryOzone {
+ public:
+  DrmSurfaceFactory(ScreenManager* screen_manager);
+  ~DrmSurfaceFactory() override;
+
+  // SurfaceFactoryOzone:
+  scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget widget) override;
+  bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) override;
+
+ protected:
+  ScreenManager* screen_manager_;  // Not owned.
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmSurfaceFactory);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_SURFACE_FACTORY_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_surface_unittest.cc b/ui/ozone/platform/drm/gpu/drm_surface_unittest.cc
new file mode 100644
index 0000000..4498f1f
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_surface_unittest.cc
@@ -0,0 +1,191 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_surface.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+
+namespace {
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
+const uint32_t kDefaultCrtc = 1;
+const uint32_t kDefaultConnector = 2;
+const size_t kPlanesPerCrtc = 1;
+const uint32_t kDefaultCursorSize = 64;
+
+}  // namespace
+
+class DrmSurfaceTest : public testing::Test {
+ public:
+  DrmSurfaceTest() {}
+
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  scoped_ptr<base::MessageLoop> message_loop_;
+  scoped_refptr<ui::MockDrmDevice> drm_;
+  scoped_ptr<ui::DrmBufferGenerator> buffer_generator_;
+  scoped_ptr<ui::ScreenManager> screen_manager_;
+  scoped_ptr<ui::DrmDeviceManager> drm_device_manager_;
+  scoped_ptr<ui::DrmSurface> surface_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DrmSurfaceTest);
+};
+
+void DrmSurfaceTest::SetUp() {
+  message_loop_.reset(new base::MessageLoopForUI);
+  std::vector<uint32_t> crtcs;
+  crtcs.push_back(kDefaultCrtc);
+  drm_ = new ui::MockDrmDevice(false, crtcs, kPlanesPerCrtc);
+  buffer_generator_.reset(new ui::DrmBufferGenerator());
+  screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
+  screen_manager_->AddDisplayController(drm_, kDefaultCrtc, kDefaultConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(), kDefaultMode);
+
+  drm_device_manager_.reset(new ui::DrmDeviceManager(nullptr));
+  scoped_ptr<ui::DrmWindow> window(new ui::DrmWindow(
+      kDefaultWidgetHandle, drm_device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  window->OnBoundsChanged(
+      gfx::Rect(gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay)));
+  screen_manager_->AddWindow(kDefaultWidgetHandle, window.Pass());
+
+  surface_.reset(
+      new ui::DrmSurface(screen_manager_->GetWindow(kDefaultWidgetHandle)));
+  surface_->ResizeCanvas(
+      gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay));
+}
+
+void DrmSurfaceTest::TearDown() {
+  surface_.reset();
+  scoped_ptr<ui::DrmWindow> window =
+      screen_manager_->RemoveWindow(kDefaultWidgetHandle);
+  window->Shutdown();
+  drm_ = nullptr;
+  message_loop_.reset();
+}
+
+TEST_F(DrmSurfaceTest, CheckFBIDOnSwap) {
+  surface_->PresentCanvas(gfx::Rect());
+  drm_->RunCallbacks();
+  // Framebuffer ID 1 is allocated in SetUp for the buffer used to modeset.
+  EXPECT_EQ(3u, drm_->current_framebuffer());
+  surface_->PresentCanvas(gfx::Rect());
+  drm_->RunCallbacks();
+  EXPECT_EQ(2u, drm_->current_framebuffer());
+}
+
+TEST_F(DrmSurfaceTest, CheckSurfaceContents) {
+  SkPaint paint;
+  paint.setColor(SK_ColorWHITE);
+  SkRect rect =
+      SkRect::MakeWH(kDefaultMode.hdisplay / 2, kDefaultMode.vdisplay / 2);
+  surface_->GetSurface()->getCanvas()->drawRect(rect, paint);
+  surface_->PresentCanvas(
+      gfx::Rect(0, 0, kDefaultMode.hdisplay / 2, kDefaultMode.vdisplay / 2));
+  drm_->RunCallbacks();
+
+  SkBitmap image;
+  std::vector<skia::RefPtr<SkSurface>> framebuffers;
+  for (const auto& buffer : drm_->buffers()) {
+    // Skip cursor buffers.
+    if (buffer->width() == kDefaultCursorSize &&
+        buffer->height() == kDefaultCursorSize)
+      continue;
+
+    framebuffers.push_back(buffer);
+  }
+
+  // Buffer 0 is the modesetting buffer, buffer 1 is the frontbuffer and buffer
+  // 2 is the backbuffer.
+  EXPECT_EQ(3u, framebuffers.size());
+
+  image.setInfo(framebuffers[2]->getCanvas()->imageInfo());
+  EXPECT_TRUE(framebuffers[2]->getCanvas()->readPixels(&image, 0, 0));
+
+  EXPECT_EQ(kDefaultMode.hdisplay, image.width());
+  EXPECT_EQ(kDefaultMode.vdisplay, image.height());
+
+  // Make sure the updates are correctly propagated to the native surface.
+  for (int i = 0; i < image.height(); ++i) {
+    for (int j = 0; j < image.width(); ++j) {
+      if (j < kDefaultMode.hdisplay / 2 && i < kDefaultMode.vdisplay / 2)
+        EXPECT_EQ(SK_ColorWHITE, image.getColor(j, i));
+      else
+        EXPECT_EQ(SK_ColorBLACK, image.getColor(j, i));
+    }
+  }
+}
+
+TEST_F(DrmSurfaceTest, CheckSurfaceContentsAfter2QueuedPresents) {
+  gfx::Rect rect;
+  // Present an empty buffer but don't respond with the page flip event since we
+  // want to make sure the following presents will aggregate correctly.
+  surface_->PresentCanvas(rect);
+
+  SkPaint paint;
+  paint.setColor(SK_ColorWHITE);
+  rect.SetRect(0, 0, kDefaultMode.hdisplay / 2, kDefaultMode.vdisplay / 2);
+  surface_->GetSurface()->getCanvas()->drawRect(RectToSkRect(rect), paint);
+  surface_->PresentCanvas(rect);
+
+  paint.setColor(SK_ColorRED);
+  rect.SetRect(0, kDefaultMode.vdisplay / 2, kDefaultMode.hdisplay / 2,
+               kDefaultMode.vdisplay / 2);
+  surface_->GetSurface()->getCanvas()->drawRect(RectToSkRect(rect), paint);
+  surface_->PresentCanvas(rect);
+
+  drm_->RunCallbacks();
+
+  SkBitmap image;
+  std::vector<skia::RefPtr<SkSurface>> framebuffers;
+  for (const auto& buffer : drm_->buffers()) {
+    // Skip cursor buffers.
+    if (buffer->width() == kDefaultCursorSize &&
+        buffer->height() == kDefaultCursorSize)
+      continue;
+
+    framebuffers.push_back(buffer);
+  }
+
+  // Buffer 0 is the modesetting buffer, buffer 1 is the backbuffer and buffer
+  // 2 is the frontbuffer.
+  EXPECT_EQ(3u, framebuffers.size());
+
+  image.setInfo(framebuffers[1]->getCanvas()->imageInfo());
+  EXPECT_TRUE(framebuffers[1]->getCanvas()->readPixels(&image, 0, 0));
+
+  EXPECT_EQ(kDefaultMode.hdisplay, image.width());
+  EXPECT_EQ(kDefaultMode.vdisplay, image.height());
+
+  // Make sure the updates are correctly propagated to the native surface.
+  for (int i = 0; i < image.height(); ++i) {
+    for (int j = 0; j < image.width(); ++j) {
+      if (j < kDefaultMode.hdisplay / 2 && i < kDefaultMode.vdisplay / 2)
+        EXPECT_EQ(SK_ColorWHITE, image.getColor(j, i));
+      else if (j < kDefaultMode.hdisplay / 2)
+        EXPECT_EQ(SK_ColorRED, image.getColor(j, i));
+      else
+        EXPECT_EQ(SK_ColorBLACK, image.getColor(j, i));
+    }
+  }
+}
diff --git a/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc b/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc
new file mode 100644
index 0000000..bb846b5
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h"
+
+#include "base/time/time.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+
+namespace ui {
+
+DrmVSyncProvider::DrmVSyncProvider(DrmWindow* window_delegate)
+    : window_delegate_(window_delegate) {
+}
+
+DrmVSyncProvider::~DrmVSyncProvider() {
+}
+
+void DrmVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) {
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return;
+
+  // The value is invalid, so we can't update the parameters.
+  if (controller->GetTimeOfLastFlip() == 0 ||
+      controller->get_mode().vrefresh == 0)
+    return;
+
+  // Stores the time of the last refresh.
+  base::TimeTicks timebase =
+      base::TimeTicks::FromInternalValue(controller->GetTimeOfLastFlip());
+  // Stores the refresh rate.
+  base::TimeDelta interval =
+      base::TimeDelta::FromSeconds(1) / controller->get_mode().vrefresh;
+
+  callback.Run(timebase, interval);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_vsync_provider.h b/ui/ozone/platform/drm/gpu/drm_vsync_provider.h
new file mode 100644
index 0000000..dba2980
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_vsync_provider.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_
+#define UI_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_
+
+#include "ui/gfx/vsync_provider.h"
+
+namespace ui {
+
+class DrmWindow;
+
+class DrmVSyncProvider : public gfx::VSyncProvider {
+ public:
+  DrmVSyncProvider(DrmWindow* window_delegate);
+  ~DrmVSyncProvider() override;
+
+  void GetVSyncParameters(const UpdateVSyncCallback& callback) override;
+
+ private:
+  DrmWindow* window_delegate_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(DrmVSyncProvider);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_window.cc b/ui/ozone/platform/drm/gpu/drm_window.cc
new file mode 100644
index 0000000..3dda06c
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_window.cc
@@ -0,0 +1,244 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+
+#include "base/trace_event/trace_event.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+
+namespace ui {
+
+namespace {
+
+#ifndef DRM_CAP_CURSOR_WIDTH
+#define DRM_CAP_CURSOR_WIDTH 0x8
+#endif
+
+#ifndef DRM_CAP_CURSOR_HEIGHT
+#define DRM_CAP_CURSOR_HEIGHT 0x9
+#endif
+
+void EmptyFlipCallback(gfx::SwapResult) {
+}
+
+void UpdateCursorImage(DrmBuffer* cursor, const SkBitmap& image) {
+  SkRect damage;
+  image.getBounds(&damage);
+
+  // Clear to transparent in case |image| is smaller than the canvas.
+  SkCanvas* canvas = cursor->GetCanvas();
+  canvas->clear(SK_ColorTRANSPARENT);
+
+  SkRect clip;
+  clip.set(0, 0, canvas->getDeviceSize().width(),
+           canvas->getDeviceSize().height());
+  canvas->clipRect(clip, SkRegion::kReplace_Op);
+  canvas->drawBitmapRectToRect(image, &damage, damage);
+}
+
+}  // namespace
+
+DrmWindow::DrmWindow(gfx::AcceleratedWidget widget,
+                     DrmDeviceManager* device_manager,
+                     ScreenManager* screen_manager)
+    : widget_(widget),
+      device_manager_(device_manager),
+      screen_manager_(screen_manager) {
+}
+
+DrmWindow::~DrmWindow() {
+}
+
+void DrmWindow::Initialize() {
+  TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_);
+
+  device_manager_->UpdateDrmDevice(widget_, nullptr);
+}
+
+void DrmWindow::Shutdown() {
+  TRACE_EVENT1("drm", "DrmWindow::Shutdown", "widget", widget_);
+  device_manager_->RemoveDrmDevice(widget_);
+}
+
+gfx::AcceleratedWidget DrmWindow::GetAcceleratedWidget() {
+  return widget_;
+}
+
+HardwareDisplayController* DrmWindow::GetController() {
+  return controller_;
+}
+
+void DrmWindow::OnBoundsChanged(const gfx::Rect& bounds) {
+  TRACE_EVENT2("drm", "DrmWindow::OnBoundsChanged", "widget", widget_, "bounds",
+               bounds.ToString());
+  bounds_ = bounds;
+  if (bounds_.size() != bounds.size())
+    last_submitted_planes_.clear();
+
+  screen_manager_->UpdateControllerToWindowMapping();
+}
+
+void DrmWindow::SetCursor(const std::vector<SkBitmap>& bitmaps,
+                          const gfx::Point& location,
+                          int frame_delay_ms) {
+  cursor_bitmaps_ = bitmaps;
+  cursor_location_ = location;
+  cursor_frame_ = 0;
+  cursor_frame_delay_ms_ = frame_delay_ms;
+  cursor_timer_.Stop();
+
+  if (cursor_frame_delay_ms_)
+    cursor_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_),
+        this, &DrmWindow::OnCursorAnimationTimeout);
+
+  ResetCursor(false);
+}
+
+void DrmWindow::SetCursorWithoutAnimations(const std::vector<SkBitmap>& bitmaps,
+                                           const gfx::Point& location) {
+  cursor_bitmaps_ = bitmaps;
+  cursor_location_ = location;
+  cursor_frame_ = 0;
+  cursor_frame_delay_ms_ = 0;
+  ResetCursor(false);
+}
+
+void DrmWindow::MoveCursor(const gfx::Point& location) {
+  cursor_location_ = location;
+
+  if (controller_)
+    controller_->MoveCursor(location);
+}
+
+void DrmWindow::QueueOverlayPlane(const OverlayPlane& plane) {
+  pending_planes_.push_back(plane);
+}
+
+bool DrmWindow::SchedulePageFlip(bool is_sync,
+                                 const SwapCompletionCallback& callback) {
+  last_submitted_planes_.clear();
+  last_submitted_planes_.swap(pending_planes_);
+  last_swap_sync_ = is_sync;
+
+  if (controller_) {
+    return controller_->SchedulePageFlip(last_submitted_planes_, is_sync, false,
+                                         callback);
+  }
+
+  callback.Run(gfx::SwapResult::SWAP_ACK);
+  return true;
+}
+
+bool DrmWindow::TestPageFlip(const std::vector<OverlayCheck_Params>& overlays,
+                             ScanoutBufferGenerator* buffer_generator) {
+  if (!controller_)
+    return true;
+  for (const auto& overlay : overlays) {
+    // It is possible that the cc rect we get actually falls off the edge of
+    // the screen. Usually this is prevented via things like status bars
+    // blocking overlaying or cc clipping it, but in case it wasn't properly
+    // clipped (since GL will render this situation fine) just ignore it here.
+    // This should be an extremely rare occurrance.
+    if (overlay.plane_z_order != 0 && !bounds().Contains(overlay.display_rect))
+      return false;
+  }
+
+  scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice();
+  OverlayPlaneList planes;
+  for (const auto& overlay : overlays) {
+    gfx::Size size =
+        (overlay.plane_z_order == 0) ? bounds().size() : overlay.buffer_size;
+    scoped_refptr<ScanoutBuffer> buffer = buffer_generator->Create(drm, size);
+    if (!buffer)
+      return false;
+    planes.push_back(OverlayPlane(buffer, overlay.plane_z_order,
+                                  overlay.transform, overlay.display_rect,
+                                  gfx::RectF(gfx::Size(1, 1))));
+  }
+  return controller_->SchedulePageFlip(planes, true, true,
+                                       base::Bind(&EmptyFlipCallback));
+}
+
+const OverlayPlane* DrmWindow::GetLastModesetBuffer() {
+  return OverlayPlane::GetPrimaryPlane(last_submitted_planes_);
+}
+
+void DrmWindow::ResetCursor(bool bitmap_only) {
+  if (!controller_)
+    return;
+
+  if (cursor_bitmaps_.size()) {
+    // Draw new cursor into backbuffer.
+    UpdateCursorImage(cursor_buffers_[cursor_frontbuffer_ ^ 1].get(),
+                      cursor_bitmaps_[cursor_frame_]);
+
+    // Reset location & buffer.
+    if (!bitmap_only)
+      controller_->MoveCursor(cursor_location_);
+    controller_->SetCursor(cursor_buffers_[cursor_frontbuffer_ ^ 1]);
+    cursor_frontbuffer_ ^= 1;
+  } else {
+    // No cursor set.
+    controller_->UnsetCursor();
+  }
+}
+
+void DrmWindow::OnCursorAnimationTimeout() {
+  cursor_frame_++;
+  cursor_frame_ %= cursor_bitmaps_.size();
+
+  ResetCursor(true);
+}
+
+void DrmWindow::SetController(HardwareDisplayController* controller) {
+  if (controller_ == controller)
+    return;
+
+  controller_ = controller;
+  device_manager_->UpdateDrmDevice(
+      widget_, controller ? controller->GetAllocationDrmDevice() : nullptr);
+
+  UpdateCursorBuffers();
+  // We changed displays, so we want to update the cursor as well.
+  ResetCursor(false /* bitmap_only */);
+}
+
+void DrmWindow::UpdateCursorBuffers() {
+  if (!controller_) {
+    for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
+      cursor_buffers_[i] = nullptr;
+    }
+  } else {
+    scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice();
+
+    uint64_t cursor_width = 64;
+    uint64_t cursor_height = 64;
+    drm->GetCapability(DRM_CAP_CURSOR_WIDTH, &cursor_width);
+    drm->GetCapability(DRM_CAP_CURSOR_HEIGHT, &cursor_height);
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(cursor_width, cursor_height);
+    for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
+      cursor_buffers_[i] = new DrmBuffer(drm);
+      // Don't register a framebuffer for cursors since they are special (they
+      // aren't modesetting buffers and drivers may fail to register them due to
+      // their small sizes).
+      if (!cursor_buffers_[i]->Initialize(
+              info, false /* should_register_framebuffer */)) {
+        LOG(FATAL) << "Failed to initialize cursor buffer";
+        return;
+      }
+    }
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_window.h b/ui/ozone/platform/drm/gpu/drm_window.h
new file mode 100644
index 0000000..fa5e5bd
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_window.h
@@ -0,0 +1,139 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_WINDOW_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_WINDOW_H_
+
+#include <vector>
+
+#include "base/timer/timer.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+#include "ui/ozone/platform/drm/gpu/page_flip_request.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Point;
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+class DrmBuffer;
+class DrmDeviceManager;
+class HardwareDisplayController;
+struct OverlayCheck_Params;
+class ScanoutBufferGenerator;
+class ScreenManager;
+
+// A delegate of the platform window (DrmWindow) on the GPU process. This is
+// used to keep track of window state changes such that each platform window is
+// correctly associated with a display.
+// A window is associated with the display whose bounds contains the window
+// bounds. If there's no suitable display, the window is disconnected and its
+// contents will not be visible.
+class OZONE_EXPORT DrmWindow {
+ public:
+  DrmWindow(gfx::AcceleratedWidget widget,
+            DrmDeviceManager* device_manager,
+            ScreenManager* screen_manager);
+
+  ~DrmWindow();
+
+  gfx::Rect bounds() const { return bounds_; }
+
+  void Initialize();
+
+  void Shutdown();
+
+  // Returns the accelerated widget associated with the delegate.
+  gfx::AcceleratedWidget GetAcceleratedWidget();
+
+  // Returns the current controller the window is displaying on. Callers should
+  // not cache the result as the controller may change as the window is moved.
+  HardwareDisplayController* GetController();
+
+  void SetController(HardwareDisplayController* controller);
+
+  // Called when the window is resized/moved.
+  void OnBoundsChanged(const gfx::Rect& bounds);
+
+  // Update the HW cursor bitmap & move to specified location. If
+  // the bitmap is empty, the cursor is hidden.
+  void SetCursor(const std::vector<SkBitmap>& bitmaps,
+                 const gfx::Point& location,
+                 int frame_delay_ms);
+
+  // Update the HW cursor bitmap & move to specified location. If
+  // the bitmap is empty, the cursor is hidden.
+  void SetCursorWithoutAnimations(const std::vector<SkBitmap>& bitmaps,
+                                  const gfx::Point& location);
+
+  // Move the HW cursor to the specified location.
+  void MoveCursor(const gfx::Point& location);
+
+  // Queue overlay planes and page flips.
+  // If hardware display controller is available, forward the information
+  // immediately, otherwise queue up on the window and forward when the hardware
+  // is once again ready.
+  void QueueOverlayPlane(const OverlayPlane& plane);
+
+  bool SchedulePageFlip(bool is_sync, const SwapCompletionCallback& callback);
+  bool TestPageFlip(const std::vector<OverlayCheck_Params>& planes,
+                    ScanoutBufferGenerator* buffer_generator);
+
+  // Returns the last buffer associated with this window.
+  const OverlayPlane* GetLastModesetBuffer();
+
+ private:
+  // Draw the last set cursor & update the cursor plane.
+  void ResetCursor(bool bitmap_only);
+
+  // Draw next frame in an animated cursor.
+  void OnCursorAnimationTimeout();
+
+  // When |controller_| changes this is called to reallocate the cursor buffers
+  // since the allocation DRM device may have changed.
+  void UpdateCursorBuffers();
+
+  gfx::AcceleratedWidget widget_;
+
+  DrmDeviceManager* device_manager_;  // Not owned.
+  ScreenManager* screen_manager_;     // Not owned.
+
+  // The current bounds of the window.
+  gfx::Rect bounds_;
+
+  // The controller associated with the current window. This may be nullptr if
+  // the window isn't over an active display.
+  HardwareDisplayController* controller_ = nullptr;
+
+  base::RepeatingTimer<DrmWindow> cursor_timer_;
+
+  scoped_refptr<DrmBuffer> cursor_buffers_[2];
+  int cursor_frontbuffer_ = 0;
+
+  std::vector<SkBitmap> cursor_bitmaps_;
+  gfx::Point cursor_location_;
+  int cursor_frame_ = 0;
+  int cursor_frame_delay_ms_ = 0;
+
+  // Planes and flips currently being queued in the absence of hardware display
+  // controller.
+  OverlayPlaneList pending_planes_;
+  OverlayPlaneList last_submitted_planes_;
+  bool last_swap_sync_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmWindow);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_WINDOW_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
new file mode 100644
index 0000000..abb3129
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_surface.h"
+#include "ui/ozone/platform/drm/gpu/drm_surface_factory.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace {
+
+// Mode of size 6x4.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
+const uint32_t kDefaultCrtc = 1;
+const uint32_t kDefaultConnector = 2;
+const int kDefaultCursorSize = 64;
+
+std::vector<skia::RefPtr<SkSurface>> GetCursorBuffers(
+    const scoped_refptr<ui::MockDrmDevice> drm) {
+  std::vector<skia::RefPtr<SkSurface>> cursor_buffers;
+  for (const skia::RefPtr<SkSurface>& cursor_buffer : drm->buffers()) {
+    if (cursor_buffer->width() == kDefaultCursorSize &&
+        cursor_buffer->height() == kDefaultCursorSize) {
+      cursor_buffers.push_back(cursor_buffer);
+    }
+  }
+
+  return cursor_buffers;
+}
+
+SkBitmap AllocateBitmap(const gfx::Size& size) {
+  SkBitmap image;
+  SkImageInfo info = SkImageInfo::Make(size.width(), size.height(),
+                                       kN32_SkColorType, kPremul_SkAlphaType);
+  image.allocPixels(info);
+  image.eraseColor(SK_ColorWHITE);
+  return image;
+}
+
+}  // namespace
+
+class DrmWindowTest : public testing::Test {
+ public:
+  DrmWindowTest() {}
+
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  scoped_ptr<base::MessageLoop> message_loop_;
+  scoped_refptr<ui::MockDrmDevice> drm_;
+  scoped_ptr<ui::DrmBufferGenerator> buffer_generator_;
+  scoped_ptr<ui::ScreenManager> screen_manager_;
+  scoped_ptr<ui::DrmDeviceManager> drm_device_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DrmWindowTest);
+};
+
+void DrmWindowTest::SetUp() {
+  message_loop_.reset(new base::MessageLoopForUI);
+  drm_ = new ui::MockDrmDevice();
+  buffer_generator_.reset(new ui::DrmBufferGenerator());
+  screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
+  screen_manager_->AddDisplayController(drm_, kDefaultCrtc, kDefaultConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(), kDefaultMode);
+
+  drm_device_manager_.reset(new ui::DrmDeviceManager(nullptr));
+
+  scoped_ptr<ui::DrmWindow> window_delegate(new ui::DrmWindow(
+      kDefaultWidgetHandle, drm_device_manager_.get(), screen_manager_.get()));
+  window_delegate->Initialize();
+  window_delegate->OnBoundsChanged(
+      gfx::Rect(gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay)));
+  screen_manager_->AddWindow(kDefaultWidgetHandle, window_delegate.Pass());
+}
+
+void DrmWindowTest::TearDown() {
+  scoped_ptr<ui::DrmWindow> delegate =
+      screen_manager_->RemoveWindow(kDefaultWidgetHandle);
+  delegate->Shutdown();
+  message_loop_.reset();
+}
+
+TEST_F(DrmWindowTest, SetCursorImage) {
+  const gfx::Size cursor_size(6, 4);
+  screen_manager_->GetWindow(kDefaultWidgetHandle)
+      ->SetCursor(std::vector<SkBitmap>(1, AllocateBitmap(cursor_size)),
+                  gfx::Point(4, 2), 0);
+
+  SkBitmap cursor;
+  std::vector<skia::RefPtr<SkSurface>> cursor_buffers = GetCursorBuffers(drm_);
+  EXPECT_EQ(2u, cursor_buffers.size());
+
+  // Buffers 1 is the cursor backbuffer we just drew in.
+  cursor.setInfo(cursor_buffers[1]->getCanvas()->imageInfo());
+  EXPECT_TRUE(cursor_buffers[1]->getCanvas()->readPixels(&cursor, 0, 0));
+
+  // Check that the frontbuffer is displaying the right image as set above.
+  for (int i = 0; i < cursor.height(); ++i) {
+    for (int j = 0; j < cursor.width(); ++j) {
+      if (j < cursor_size.width() && i < cursor_size.height())
+        EXPECT_EQ(SK_ColorWHITE, cursor.getColor(j, i));
+      else
+        EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
+                  cursor.getColor(j, i));
+    }
+  }
+}
+
+TEST_F(DrmWindowTest, CheckCursorSurfaceAfterChangingDevice) {
+  const gfx::Size cursor_size(6, 4);
+  screen_manager_->GetWindow(kDefaultWidgetHandle)
+      ->SetCursor(std::vector<SkBitmap>(1, AllocateBitmap(cursor_size)),
+                  gfx::Point(4, 2), 0);
+
+  // Add another device.
+  scoped_refptr<ui::MockDrmDevice> drm = new ui::MockDrmDevice();
+  screen_manager_->AddDisplayController(drm, kDefaultCrtc, kDefaultConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm, kDefaultCrtc, kDefaultConnector,
+      gfx::Point(0, kDefaultMode.vdisplay), kDefaultMode);
+
+  // Move window to the display on the new device.
+  screen_manager_->GetWindow(kDefaultWidgetHandle)
+      ->OnBoundsChanged(gfx::Rect(0, kDefaultMode.vdisplay,
+                                  kDefaultMode.hdisplay,
+                                  kDefaultMode.vdisplay));
+
+  EXPECT_EQ(2u, GetCursorBuffers(drm).size());
+  // Make sure the cursor is showing on the new display.
+  EXPECT_NE(0u, drm->get_cursor_handle_for_crtc(kDefaultCrtc));
+}
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
new file mode 100644
index 0000000..8da5e5d
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
+
+#include <drm.h>
+#include <fcntl.h>
+#include <gbm.h>
+#include <xf86drm.h>
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/gbm_device.h"
+
+namespace ui {
+
+namespace {
+
+int GetGbmFormatFromBufferFormat(SurfaceFactoryOzone::BufferFormat fmt) {
+  switch (fmt) {
+    case SurfaceFactoryOzone::BGRA_8888:
+      return GBM_BO_FORMAT_ARGB8888;
+    case SurfaceFactoryOzone::RGBX_8888:
+      return GBM_BO_FORMAT_XRGB8888;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+}  // namespace
+
+GbmBuffer::GbmBuffer(const scoped_refptr<GbmDevice>& gbm,
+                     gbm_bo* bo,
+                     bool scanout)
+    : GbmBufferBase(gbm, bo, scanout) {
+}
+
+GbmBuffer::~GbmBuffer() {
+  if (bo())
+    gbm_bo_destroy(bo());
+}
+
+// static
+scoped_refptr<GbmBuffer> GbmBuffer::CreateBuffer(
+    const scoped_refptr<GbmDevice>& gbm,
+    SurfaceFactoryOzone::BufferFormat format,
+    const gfx::Size& size,
+    bool scanout) {
+  TRACE_EVENT2("drm", "GbmBuffer::CreateBuffer", "device",
+               gbm->device_path().value(), "size", size.ToString());
+  unsigned flags = GBM_BO_USE_RENDERING;
+  if (scanout)
+    flags |= GBM_BO_USE_SCANOUT;
+  gbm_bo* bo = gbm_bo_create(gbm->device(), size.width(), size.height(),
+                             GetGbmFormatFromBufferFormat(format), flags);
+  if (!bo)
+    return NULL;
+
+  scoped_refptr<GbmBuffer> buffer(new GbmBuffer(gbm, bo, scanout));
+  if (scanout && !buffer->GetFramebufferId())
+    return NULL;
+
+  return buffer;
+}
+
+GbmPixmap::GbmPixmap(const scoped_refptr<GbmBuffer>& buffer,
+                     ScreenManager* screen_manager)
+    : buffer_(buffer), screen_manager_(screen_manager) {
+}
+
+bool GbmPixmap::Initialize() {
+  // We want to use the GBM API because it's going to call into libdrm
+  // which might do some optimizations on buffer allocation,
+  // especially when sharing buffers via DMABUF.
+  dma_buf_ = gbm_bo_get_fd(buffer_->bo());
+  if (dma_buf_ < 0) {
+    PLOG(ERROR) << "Failed to export buffer to dma_buf";
+    return false;
+  }
+  return true;
+}
+
+GbmPixmap::~GbmPixmap() {
+  if (dma_buf_ > 0)
+    close(dma_buf_);
+}
+
+void* GbmPixmap::GetEGLClientBuffer() {
+  return nullptr;
+}
+
+int GbmPixmap::GetDmaBufFd() {
+  return dma_buf_;
+}
+
+int GbmPixmap::GetDmaBufPitch() {
+  return gbm_bo_get_stride(buffer_->bo());
+}
+
+bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                                     int plane_z_order,
+                                     gfx::OverlayTransform plane_transform,
+                                     const gfx::Rect& display_bounds,
+                                     const gfx::RectF& crop_rect) {
+  screen_manager_->GetWindow(widget)->QueueOverlayPlane(OverlayPlane(
+      buffer_, plane_z_order, plane_transform, display_bounds, crop_rect));
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.h b/ui/ozone/platform/drm/gpu/gbm_buffer.h
new file mode 100644
index 0000000..3f0c5d7
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.h
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/drm/gpu/gbm_buffer_base.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+struct gbm_bo;
+
+namespace ui {
+
+class GbmDevice;
+
+class GbmBuffer : public GbmBufferBase {
+ public:
+  static scoped_refptr<GbmBuffer> CreateBuffer(
+      const scoped_refptr<GbmDevice>& gbm,
+      SurfaceFactoryOzone::BufferFormat format,
+      const gfx::Size& size,
+      bool scanout);
+
+ private:
+  GbmBuffer(const scoped_refptr<GbmDevice>& gbm, gbm_bo* bo, bool scanout);
+  ~GbmBuffer() override;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmBuffer);
+};
+
+class GbmPixmap : public NativePixmap {
+ public:
+  GbmPixmap(const scoped_refptr<GbmBuffer>& buffer,
+            ScreenManager* screen_manager);
+  bool Initialize();
+
+  // NativePixmap:
+  void* GetEGLClientBuffer() override;
+  int GetDmaBufFd() override;
+  int GetDmaBufPitch() override;
+  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                            int plane_z_order,
+                            gfx::OverlayTransform plane_transform,
+                            const gfx::Rect& display_bounds,
+                            const gfx::RectF& crop_rect) override;
+
+  scoped_refptr<GbmBuffer> buffer() { return buffer_; }
+
+ private:
+  ~GbmPixmap() override;
+
+  scoped_refptr<GbmBuffer> buffer_;
+  int dma_buf_ = -1;
+
+  ScreenManager* screen_manager_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(GbmPixmap);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
new file mode 100644
index 0000000..c283a64
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_buffer_base.h"
+
+#include <gbm.h>
+
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+namespace {
+
+// Pixel configuration for the current buffer format.
+// TODO(dnicoara) These will need to change once we query the hardware for
+// supported configurations.
+const uint8_t kColorDepth = 24;
+const uint8_t kPixelDepth = 32;
+
+}  // namespace
+
+GbmBufferBase::GbmBufferBase(const scoped_refptr<DrmDevice>& drm,
+                             gbm_bo* bo,
+                             bool scanout)
+    : drm_(drm), bo_(bo) {
+  if (scanout &&
+      !drm_->AddFramebuffer(gbm_bo_get_width(bo), gbm_bo_get_height(bo),
+                            kColorDepth, kPixelDepth, gbm_bo_get_stride(bo),
+                            gbm_bo_get_handle(bo).u32, &framebuffer_))
+    PLOG(ERROR) << "Failed to register buffer";
+}
+
+GbmBufferBase::~GbmBufferBase() {
+  if (framebuffer_)
+    drm_->RemoveFramebuffer(framebuffer_);
+}
+
+uint32_t GbmBufferBase::GetFramebufferId() const {
+  return framebuffer_;
+}
+
+uint32_t GbmBufferBase::GetHandle() const {
+  return gbm_bo_get_handle(bo_).u32;
+}
+
+gfx::Size GbmBufferBase::GetSize() const {
+  return gfx::Size(gbm_bo_get_width(bo_), gbm_bo_get_height(bo_));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer_base.h b/ui/ozone/platform/drm/gpu/gbm_buffer_base.h
new file mode 100644
index 0000000..d0872fa
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer_base.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_BASE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_BASE_H_
+
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+struct gbm_bo;
+
+namespace ui {
+
+class DrmDevice;
+
+// Wrapper for a GBM buffer. The base class provides common functionality
+// required to prepare the buffer for scanout. It does not provide any ownership
+// of the buffer. Implementations of this base class should deal with buffer
+// ownership.
+class GbmBufferBase : public ScanoutBuffer {
+ public:
+  gbm_bo* bo() const { return bo_; }
+
+  // ScanoutBuffer:
+  uint32_t GetFramebufferId() const override;
+  uint32_t GetHandle() const override;
+  gfx::Size GetSize() const override;
+
+ protected:
+  GbmBufferBase(const scoped_refptr<DrmDevice>& drm, gbm_bo* bo, bool scanout);
+  ~GbmBufferBase() override;
+
+ private:
+  scoped_refptr<DrmDevice> drm_;
+  gbm_bo* bo_;
+  uint32_t framebuffer_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmBufferBase);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_BUFFER_BASE_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_device.cc b/ui/ozone/platform/drm/gpu/gbm_device.cc
new file mode 100644
index 0000000..dc36ba3
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_device.cc
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_device.h"
+
+#include <gbm.h>
+
+namespace ui {
+
+GbmDevice::GbmDevice(const base::FilePath& device_path, base::File file)
+    : DrmDevice(device_path, file.Pass()) {
+}
+
+GbmDevice::~GbmDevice() {
+  if (device_)
+    gbm_device_destroy(device_);
+}
+
+bool GbmDevice::Initialize(bool use_atomic) {
+  if (!DrmDevice::Initialize(use_atomic))
+    return false;
+
+  device_ = gbm_create_device(get_fd());
+  if (!device_) {
+    PLOG(ERROR) << "Unable to initialize GBM for " << device_path().value();
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_device.h b/ui/ozone/platform/drm/gpu/gbm_device.h
new file mode 100644
index 0000000..29cb9df
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_device.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_DEVICE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_DEVICE_H_
+
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+struct gbm_device;
+
+namespace ui {
+
+class GbmDevice : public DrmDevice {
+ public:
+  GbmDevice(const base::FilePath& device_path, base::File file);
+
+  gbm_device* device() const { return device_; }
+
+  // DrmDevice implementation:
+  bool Initialize(bool use_atomic) override;
+
+ private:
+  ~GbmDevice() override;
+
+  gbm_device* device_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmDevice);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_DEVICE_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface.cc b/ui/ozone/platform/drm/gpu/gbm_surface.cc
new file mode 100644
index 0000000..08a32f4
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surface.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_surface.h"
+
+#include <gbm.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/gbm_buffer_base.h"
+#include "ui/ozone/platform/drm/gpu/gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+namespace {
+
+void DoNothing(gfx::SwapResult) {
+}
+
+class GbmSurfaceBuffer : public GbmBufferBase {
+ public:
+  static scoped_refptr<GbmSurfaceBuffer> CreateBuffer(
+      const scoped_refptr<DrmDevice>& drm,
+      gbm_bo* buffer);
+  static scoped_refptr<GbmSurfaceBuffer> GetBuffer(gbm_bo* buffer);
+
+ private:
+  GbmSurfaceBuffer(const scoped_refptr<DrmDevice>& drm, gbm_bo* bo);
+  ~GbmSurfaceBuffer() override;
+
+  static void Destroy(gbm_bo* buffer, void* data);
+
+  // This buffer is special and is released by GBM at any point in time (as
+  // long as it isn't being used). Since GBM should be the only one to
+  // release this buffer, keep a self-reference in order to keep this alive.
+  // When GBM calls Destroy(..) the self-reference will dissapear and this will
+  // be destroyed.
+  scoped_refptr<GbmSurfaceBuffer> self_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceBuffer);
+};
+
+GbmSurfaceBuffer::GbmSurfaceBuffer(const scoped_refptr<DrmDevice>& drm,
+                                   gbm_bo* bo)
+    : GbmBufferBase(drm, bo, true) {
+  if (GetFramebufferId()) {
+    self_ = this;
+    gbm_bo_set_user_data(bo, this, GbmSurfaceBuffer::Destroy);
+  }
+}
+
+GbmSurfaceBuffer::~GbmSurfaceBuffer() {
+}
+
+// static
+scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::CreateBuffer(
+    const scoped_refptr<DrmDevice>& drm,
+    gbm_bo* buffer) {
+  scoped_refptr<GbmSurfaceBuffer> scoped_buffer(
+      new GbmSurfaceBuffer(drm, buffer));
+  if (!scoped_buffer->GetFramebufferId())
+    return NULL;
+
+  return scoped_buffer;
+}
+
+// static
+scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::GetBuffer(gbm_bo* buffer) {
+  return scoped_refptr<GbmSurfaceBuffer>(
+      static_cast<GbmSurfaceBuffer*>(gbm_bo_get_user_data(buffer)));
+}
+
+// static
+void GbmSurfaceBuffer::Destroy(gbm_bo* buffer, void* data) {
+  GbmSurfaceBuffer* scoped_buffer = static_cast<GbmSurfaceBuffer*>(data);
+  scoped_buffer->self_ = NULL;
+}
+
+}  // namespace
+
+GbmSurface::GbmSurface(DrmWindow* window_delegate,
+                       const scoped_refptr<GbmDevice>& gbm)
+    : GbmSurfaceless(window_delegate, NULL),
+      gbm_(gbm),
+      native_surface_(NULL),
+      current_buffer_(NULL),
+      weak_factory_(this) {
+}
+
+GbmSurface::~GbmSurface() {
+  if (current_buffer_)
+    gbm_surface_release_buffer(native_surface_, current_buffer_);
+
+  if (native_surface_)
+    gbm_surface_destroy(native_surface_);
+}
+
+bool GbmSurface::Initialize() {
+  // If we're initializing the surface without a controller (possible on startup
+  // where the surface creation can happen before the native window delegate
+  // IPCs arrive), initialize the size to a valid value such that surface
+  // creation doesn't fail.
+  gfx::Size size(1, 1);
+  if (window_delegate_->GetController()) {
+    size = window_delegate_->GetController()->GetModeSize();
+  }
+  // TODO(dnicoara) Check underlying system support for pixel format.
+  native_surface_ = gbm_surface_create(
+      gbm_->device(), size.width(), size.height(), GBM_BO_FORMAT_XRGB8888,
+      GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+
+  if (!native_surface_)
+    return false;
+
+  size_ = size;
+  return true;
+}
+
+intptr_t GbmSurface::GetNativeWindow() {
+  DCHECK(native_surface_);
+  return reinterpret_cast<intptr_t>(native_surface_);
+}
+
+bool GbmSurface::ResizeNativeWindow(const gfx::Size& viewport_size) {
+  if (size_ == viewport_size)
+    return true;
+
+  return false;
+}
+
+bool GbmSurface::OnSwapBuffers() {
+  return OnSwapBuffersAsync(base::Bind(&DoNothing));
+}
+
+bool GbmSurface::OnSwapBuffersAsync(const SwapCompletionCallback& callback) {
+  DCHECK(native_surface_);
+
+  gbm_bo* pending_buffer = gbm_surface_lock_front_buffer(native_surface_);
+  scoped_refptr<GbmSurfaceBuffer> primary =
+      GbmSurfaceBuffer::GetBuffer(pending_buffer);
+  if (!primary.get()) {
+    primary = GbmSurfaceBuffer::CreateBuffer(gbm_, pending_buffer);
+    if (!primary.get()) {
+      LOG(ERROR) << "Failed to associate the buffer with the controller";
+      callback.Run(gfx::SwapResult::SWAP_FAILED);
+      return false;
+    }
+  }
+
+  // The primary buffer is a special case.
+  window_delegate_->QueueOverlayPlane(OverlayPlane(primary));
+
+  if (!GbmSurfaceless::OnSwapBuffersAsync(
+          base::Bind(&GbmSurface::OnSwapBuffersCallback,
+                     weak_factory_.GetWeakPtr(), callback, pending_buffer))) {
+    callback.Run(gfx::SwapResult::SWAP_FAILED);
+    return false;
+  }
+
+  return true;
+}
+
+void GbmSurface::OnSwapBuffersCallback(const SwapCompletionCallback& callback,
+                                       gbm_bo* pending_buffer,
+                                       gfx::SwapResult result) {
+  // If there was a frontbuffer, it is no longer active. Release it back to GBM.
+  if (current_buffer_)
+    gbm_surface_release_buffer(native_surface_, current_buffer_);
+
+  current_buffer_ = pending_buffer;
+  callback.Run(result);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface.h b/ui/ozone/platform/drm/gpu/gbm_surface.h
new file mode 100644
index 0000000..45757ec
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surface.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+struct gbm_bo;
+struct gbm_surface;
+
+namespace ui {
+
+class DrmBuffer;
+class DrmWindow;
+class GbmDevice;
+
+// Extends the GBM surfaceless functionality and adds an implicit surface for
+// the primary plane. Arbitrary buffers can still be allocated and displayed as
+// overlay planes, however the primary plane is associated with the native
+// surface and is updated via an EGLSurface.
+class GbmSurface : public GbmSurfaceless {
+ public:
+  GbmSurface(DrmWindow* window_delegate, const scoped_refptr<GbmDevice>& gbm);
+  ~GbmSurface() override;
+
+  bool Initialize();
+
+  // GbmSurfaceless:
+  intptr_t GetNativeWindow() override;
+  bool ResizeNativeWindow(const gfx::Size& viewport_size) override;
+  bool OnSwapBuffers() override;
+  bool OnSwapBuffersAsync(const SwapCompletionCallback& callback) override;
+
+ private:
+  void OnSwapBuffersCallback(const SwapCompletionCallback& callback,
+                             gbm_bo* pending_buffer,
+                             gfx::SwapResult result);
+
+  scoped_refptr<GbmDevice> gbm_;
+
+  // The native GBM surface. In EGL this represents the EGLNativeWindowType.
+  gbm_surface* native_surface_;
+
+  // Buffer currently used for scanout.
+  gbm_bo* current_buffer_;
+
+  gfx::Size size_;
+
+  base::WeakPtrFactory<GbmSurface> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurface);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
new file mode 100644
index 0000000..f482861
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
+
+#include <gbm.h>
+
+#include "base/files/file_path.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/ozone/common/egl_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/gbm_surface.h"
+#include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+
+GbmSurfaceFactory::GbmSurfaceFactory(bool allow_surfaceless)
+    : DrmSurfaceFactory(NULL), allow_surfaceless_(allow_surfaceless) {
+}
+
+GbmSurfaceFactory::~GbmSurfaceFactory() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void GbmSurfaceFactory::InitializeGpu(DrmDeviceManager* drm_device_manager,
+                                      ScreenManager* screen_manager) {
+  drm_device_manager_ = drm_device_manager;
+  screen_manager_ = screen_manager;
+}
+
+intptr_t GbmSurfaceFactory::GetNativeDisplay() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto primary_device = drm_device_manager_->GetDrmDevice(
+    gfx::kNullAcceleratedWidget);
+  DCHECK(primary_device.get());
+  auto gbm_device = static_cast<GbmDevice*>(primary_device.get());
+  return reinterpret_cast<EGLNativeDisplayType>(gbm_device->device());
+}
+
+const int32* GbmSurfaceFactory::GetEGLSurfaceProperties(
+    const int32* desired_list) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  static const int32 kConfigAttribs[] = {EGL_BUFFER_SIZE,
+                                         32,
+                                         EGL_ALPHA_SIZE,
+                                         8,
+                                         EGL_BLUE_SIZE,
+                                         8,
+                                         EGL_GREEN_SIZE,
+                                         8,
+                                         EGL_RED_SIZE,
+                                         8,
+                                         EGL_RENDERABLE_TYPE,
+                                         EGL_OPENGL_ES2_BIT,
+                                         EGL_SURFACE_TYPE,
+                                         EGL_WINDOW_BIT,
+                                         EGL_NONE};
+
+  return kConfigAttribs;
+}
+
+bool GbmSurfaceFactory::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return LoadDefaultEGLGLES2Bindings(add_gl_library, set_gl_get_proc_address);
+}
+
+scoped_ptr<SurfaceOzoneCanvas> GbmSurfaceFactory::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  LOG(FATAL) << "Software rendering mode is not supported with GBM platform";
+  return nullptr;
+}
+
+scoped_ptr<SurfaceOzoneEGL> GbmSurfaceFactory::CreateEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  scoped_refptr<GbmDevice> gbm = GetGbmDevice(widget);
+  DCHECK(gbm);
+
+  scoped_ptr<GbmSurface> surface(
+      new GbmSurface(screen_manager_->GetWindow(widget), gbm));
+  if (!surface->Initialize())
+    return nullptr;
+
+  return surface.Pass();
+}
+
+scoped_ptr<SurfaceOzoneEGL>
+GbmSurfaceFactory::CreateSurfacelessEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!allow_surfaceless_)
+    return nullptr;
+
+  return make_scoped_ptr(new GbmSurfaceless(screen_manager_->GetWindow(widget),
+                                            drm_device_manager_));
+}
+
+scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmap(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    BufferFormat format,
+    BufferUsage usage) {
+  if (usage == MAP)
+    return nullptr;
+
+  scoped_refptr<GbmDevice> gbm = GetGbmDevice(widget);
+  DCHECK(gbm);
+
+  scoped_refptr<GbmBuffer> buffer =
+      GbmBuffer::CreateBuffer(gbm, format, size, true);
+  if (!buffer.get())
+    return nullptr;
+
+  scoped_refptr<GbmPixmap> pixmap(new GbmPixmap(buffer, screen_manager_));
+  if (!pixmap->Initialize())
+    return nullptr;
+
+  return pixmap;
+}
+
+bool GbmSurfaceFactory::CanShowPrimaryPlaneAsOverlay() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return allow_surfaceless_;
+}
+
+bool GbmSurfaceFactory::CanCreateNativePixmap(BufferUsage usage) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  switch (usage) {
+    case MAP:
+      return false;
+    case PERSISTENT_MAP:
+      return false;
+    case SCANOUT:
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+scoped_refptr<GbmDevice> GbmSurfaceFactory::GetGbmDevice(
+    gfx::AcceleratedWidget widget) {
+  return static_cast<GbmDevice*>(
+      drm_device_manager_->GetDrmDevice(widget).get());
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
new file mode 100644
index 0000000..0bc01b3
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_FACTORY_H_
+
+#include "ui/ozone/platform/drm/gpu/drm_surface_factory.h"
+
+namespace ui {
+
+class DrmDeviceManager;
+class DrmWindow;
+class GbmDevice;
+class ScreenManager;
+
+class GbmSurfaceFactory : public DrmSurfaceFactory {
+ public:
+  GbmSurfaceFactory(bool allow_surfaceless);
+  ~GbmSurfaceFactory() override;
+
+  void InitializeGpu(DrmDeviceManager* drm_device_manager,
+                     ScreenManager* screen_manager);
+
+  // DrmSurfaceFactory:
+  intptr_t GetNativeDisplay() override;
+  const int32_t* GetEGLSurfaceProperties(const int32_t* desired_list) override;
+  bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) override;
+  scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget widget) override;
+  scoped_ptr<ui::SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+      gfx::AcceleratedWidget w) override;
+  scoped_ptr<SurfaceOzoneEGL> CreateSurfacelessEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget) override;
+  scoped_refptr<ui::NativePixmap> CreateNativePixmap(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      BufferFormat format,
+      BufferUsage usage) override;
+  bool CanShowPrimaryPlaneAsOverlay() override;
+  bool CanCreateNativePixmap(BufferUsage usage) override;
+
+ private:
+  scoped_refptr<GbmDevice> GetGbmDevice(gfx::AcceleratedWidget widget);
+
+  bool allow_surfaceless_;
+
+  DrmDeviceManager* drm_device_manager_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceFactory);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_FACTORY_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
new file mode 100644
index 0000000..244b8ab
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+
+namespace ui {
+namespace {
+void EmptyPageFlipCallback(gfx::SwapResult result) {
+}
+}  // namespace
+
+GbmSurfaceless::GbmSurfaceless(DrmWindow* window_delegate,
+                               DrmDeviceManager* drm_device_manager)
+    : window_delegate_(window_delegate),
+      drm_device_manager_(drm_device_manager) {
+}
+
+GbmSurfaceless::~GbmSurfaceless() {
+}
+
+intptr_t GbmSurfaceless::GetNativeWindow() {
+  NOTREACHED();
+  return 0;
+}
+
+bool GbmSurfaceless::ResizeNativeWindow(const gfx::Size& viewport_size) {
+  return true;
+}
+
+bool GbmSurfaceless::OnSwapBuffers() {
+  return window_delegate_->SchedulePageFlip(true /* is_sync */,
+                                            base::Bind(&EmptyPageFlipCallback));
+}
+
+bool GbmSurfaceless::OnSwapBuffersAsync(
+    const SwapCompletionCallback& callback) {
+  return window_delegate_->SchedulePageFlip(false /* is_sync */, callback);
+}
+
+scoped_ptr<gfx::VSyncProvider> GbmSurfaceless::CreateVSyncProvider() {
+  return make_scoped_ptr(new DrmVSyncProvider(window_delegate_));
+}
+
+bool GbmSurfaceless::IsUniversalDisplayLinkDevice() {
+  if (!drm_device_manager_)
+    return false;
+  scoped_refptr<DrmDevice> drm_primary =
+      drm_device_manager_->GetDrmDevice(gfx::kNullAcceleratedWidget);
+  DCHECK(drm_primary);
+
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return false;
+  scoped_refptr<DrmDevice> drm = controller->GetAllocationDrmDevice();
+  DCHECK(drm);
+
+  return drm_primary != drm;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
new file mode 100644
index 0000000..c890d9d
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACELESS_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACELESS_H_
+
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace gfx {
+class Size;
+}  // namespace gfx
+
+namespace ui {
+
+class DrmDeviceManager;
+class DrmWindow;
+
+// In surfaceless mode drawing and displaying happens directly through
+// NativePixmap buffers. CC would call into SurfaceFactoryOzone to allocate the
+// buffers and then call ScheduleOverlayPlane(..) to schedule the buffer for
+// presentation.
+class GbmSurfaceless : public SurfaceOzoneEGL {
+ public:
+  GbmSurfaceless(DrmWindow* window_delegate,
+                 DrmDeviceManager* drm_device_manager);
+  ~GbmSurfaceless() override;
+
+  // SurfaceOzoneEGL:
+  intptr_t GetNativeWindow() override;
+  bool ResizeNativeWindow(const gfx::Size& viewport_size) override;
+  bool OnSwapBuffers() override;
+  bool OnSwapBuffersAsync(const SwapCompletionCallback& callback) override;
+  scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override;
+  bool IsUniversalDisplayLinkDevice() override;
+
+ protected:
+  DrmWindow* window_delegate_;
+  DrmDeviceManager* drm_device_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceless);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACELESS_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
new file mode 100644
index 0000000..5ee4819
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
@@ -0,0 +1,215 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+
+#include <drm.h>
+#include <string.h>
+#include <xf86drm.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/page_flip_request.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace ui {
+
+HardwareDisplayController::HardwareDisplayController(
+    scoped_ptr<CrtcController> controller,
+    const gfx::Point& origin)
+    : origin_(origin),
+      mode_(controller->mode()),
+      is_disabled_(controller->is_disabled()) {
+  AddCrtc(controller.Pass());
+}
+
+HardwareDisplayController::~HardwareDisplayController() {
+  // Reset the cursor.
+  UnsetCursor();
+}
+
+bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
+                                        drmModeModeInfo mode) {
+  TRACE_EVENT0("drm", "HDC::Modeset");
+  DCHECK(primary.buffer.get());
+  bool status = true;
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    status &= crtc_controllers_[i]->Modeset(primary, mode);
+
+  is_disabled_ = false;
+  mode_ = mode;
+
+  return status;
+}
+
+void HardwareDisplayController::Disable() {
+  TRACE_EVENT0("drm", "HDC::Disable");
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    crtc_controllers_[i]->Disable();
+
+
+  is_disabled_ = true;
+}
+
+bool HardwareDisplayController::SchedulePageFlip(
+    const OverlayPlaneList& plane_list,
+    bool is_sync,
+    bool test_only,
+    const PageFlipCallback& callback) {
+  TRACE_EVENT0("drm", "HDC::SchedulePageFlip");
+
+  DCHECK(!is_disabled_);
+
+  // Ignore requests with no planes to schedule.
+  if (plane_list.empty()) {
+    callback.Run(gfx::SwapResult::SWAP_ACK);
+    return true;
+  }
+
+  scoped_refptr<PageFlipRequest> page_flip_request =
+      new PageFlipRequest(crtc_controllers_.size(), callback);
+
+  OverlayPlaneList pending_planes = plane_list;
+  std::sort(pending_planes.begin(), pending_planes.end(),
+            [](const OverlayPlane& l, const OverlayPlane& r) {
+              return l.z_order < r.z_order;
+            });
+  if (pending_planes.front().z_order != 0)
+    return false;
+
+  for (const auto& planes : owned_hardware_planes_)
+    planes.first->plane_manager()->BeginFrame(planes.second);
+
+  bool status = true;
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i) {
+    status &= crtc_controllers_[i]->SchedulePageFlip(
+        owned_hardware_planes_.get(crtc_controllers_[i]->drm().get()),
+        pending_planes, test_only, page_flip_request);
+  }
+
+  for (const auto& planes : owned_hardware_planes_) {
+    if (!planes.first->plane_manager()->Commit(planes.second, is_sync,
+                                               test_only)) {
+      status = false;
+    }
+  }
+
+  return status;
+}
+
+bool HardwareDisplayController::SetCursor(
+    const scoped_refptr<ScanoutBuffer>& buffer) {
+  bool status = true;
+
+  if (is_disabled_)
+    return true;
+
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    status &= crtc_controllers_[i]->SetCursor(buffer);
+
+  return status;
+}
+
+bool HardwareDisplayController::UnsetCursor() {
+  bool status = true;
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    status &= crtc_controllers_[i]->SetCursor(nullptr);
+
+  return status;
+}
+
+bool HardwareDisplayController::MoveCursor(const gfx::Point& location) {
+  if (is_disabled_)
+    return true;
+
+  bool status = true;
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    status &= crtc_controllers_[i]->MoveCursor(location);
+
+  return status;
+}
+
+void HardwareDisplayController::AddCrtc(scoped_ptr<CrtcController> controller) {
+  owned_hardware_planes_.add(
+      controller->drm().get(),
+      scoped_ptr<HardwareDisplayPlaneList>(new HardwareDisplayPlaneList()));
+  crtc_controllers_.push_back(controller.Pass());
+}
+
+scoped_ptr<CrtcController> HardwareDisplayController::RemoveCrtc(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t crtc) {
+  for (ScopedVector<CrtcController>::iterator it = crtc_controllers_.begin();
+       it != crtc_controllers_.end(); ++it) {
+    if ((*it)->drm() == drm && (*it)->crtc() == crtc) {
+      scoped_ptr<CrtcController> controller(*it);
+      crtc_controllers_.weak_erase(it);
+      // Remove entry from |owned_hardware_planes_| iff no other crtcs share it.
+      bool found = false;
+      for (ScopedVector<CrtcController>::iterator it =
+               crtc_controllers_.begin();
+           it != crtc_controllers_.end(); ++it) {
+        if ((*it)->drm() == controller->drm()) {
+          found = true;
+          break;
+        }
+      }
+      if (!found)
+        owned_hardware_planes_.erase(controller->drm().get());
+
+      return controller.Pass();
+    }
+  }
+
+  return nullptr;
+}
+
+bool HardwareDisplayController::HasCrtc(const scoped_refptr<DrmDevice>& drm,
+                                        uint32_t crtc) const {
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    if (crtc_controllers_[i]->drm() == drm &&
+        crtc_controllers_[i]->crtc() == crtc)
+      return true;
+
+  return false;
+}
+
+bool HardwareDisplayController::IsMirrored() const {
+  return crtc_controllers_.size() > 1;
+}
+
+bool HardwareDisplayController::IsDisabled() const {
+  return is_disabled_;
+}
+
+gfx::Size HardwareDisplayController::GetModeSize() const {
+  return gfx::Size(mode_.hdisplay, mode_.vdisplay);
+}
+
+uint64_t HardwareDisplayController::GetTimeOfLastFlip() const {
+  uint64_t time = 0;
+  for (size_t i = 0; i < crtc_controllers_.size(); ++i)
+    if (time < crtc_controllers_[i]->time_of_last_flip())
+      time = crtc_controllers_[i]->time_of_last_flip();
+
+  return time;
+}
+
+scoped_refptr<DrmDevice> HardwareDisplayController::GetAllocationDrmDevice()
+    const {
+  DCHECK(!crtc_controllers_.empty());
+  // TODO(dnicoara) When we support mirroring across DRM devices, figure out
+  // which device should be used for allocations.
+  return crtc_controllers_[0]->drm();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
new file mode 100644
index 0000000..3772c35
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
@@ -0,0 +1,181 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_CONTROLLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+#include <deque>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class CrtcController;
+class ScanoutBuffer;
+class DrmDevice;
+
+// The HDCOz will handle modesettings and scannout operations for hardware
+// devices.
+//
+// In the DRM world there are 3 components that need to be paired up to be able
+// to display an image to the monitor: CRTC (cathode ray tube controller),
+// encoder and connector. The CRTC determines which framebuffer to read, when
+// to scanout and where to scanout. Encoders converts the stream from the CRTC
+// to the appropriate format for the connector. The connector is the physical
+// connection that monitors connect to.
+//
+// There is no 1:1:1 pairing for these components. It is possible for an encoder
+// to be compatible to multiple CRTCs and each connector can be used with
+// multiple encoders. In addition, it is possible to use one CRTC with multiple
+// connectors such that we can display the same image on multiple monitors.
+//
+// For example, the following configuration shows 2 different screens being
+// initialized separately.
+// -------------      -------------
+// | Connector |      | Connector |
+// |   HDMI    |      |    VGA    |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |  Encoder1  |     |  Encoder2 |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |   CRTC1   |      |   CRTC2   |
+// -------------      -------------
+//
+// In the following configuration 2 different screens are associated with the
+// same CRTC, so on scanout the same framebuffer will be displayed on both
+// monitors.
+// -------------      -------------
+// | Connector |      | Connector |
+// |   HDMI    |      |    VGA    |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |  Encoder1  |     |  Encoder2 |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+//      ----------------------
+//      |        CRTC1       |
+//      ----------------------
+//
+// Note that it is possible to have more connectors than CRTCs which means that
+// only a subset of connectors can be active independently, showing different
+// framebuffers. Though, in this case, it would be possible to have all
+// connectors active if some use the same CRTC to mirror the display.
+class OZONE_EXPORT HardwareDisplayController {
+  typedef base::Callback<void(gfx::SwapResult)> PageFlipCallback;
+
+ public:
+  HardwareDisplayController(scoped_ptr<CrtcController> controller,
+                            const gfx::Point& origin);
+  ~HardwareDisplayController();
+
+  // Performs the initial CRTC configuration. If successful, it will display the
+  // framebuffer for |primary| with |mode|.
+  bool Modeset(const OverlayPlane& primary, drmModeModeInfo mode);
+
+  // Disables the CRTC.
+  void Disable();
+
+  // Schedules the |overlays|' framebuffers to be displayed on the next vsync
+  // event. The event will be posted on the graphics card file descriptor |fd_|
+  // and it can be read and processed by |drmHandleEvent|. That function can
+  // define the callback for the page flip event. A generic data argument will
+  // be presented to the callback. We use that argument to pass in the HDCO
+  // object the event belongs to.
+  //
+  // Between this call and the callback, the framebuffers used in this call
+  // should not be modified in any way as it would cause screen tearing if the
+  // hardware performed the flip. Note that the frontbuffer should also not
+  // be modified as it could still be displayed.
+  //
+  // Note that this function does not block. Also, this function should not be
+  // called again before the page flip occurrs.
+  //
+  // Returns true if the page flip was successfully registered, false otherwise.
+  //
+  // When called with |test_only| true, this performs the page flip without
+  // changing any state, reporting if this page flip would be allowed to occur.
+  // This is always a synchronous operation, so |is_sync| is ignored and the
+  // callback is called immediately but should also be ignored; only the return
+  // value matters.
+  bool SchedulePageFlip(const OverlayPlaneList& plane_list,
+                        bool is_sync,
+                        bool test_only,
+                        const PageFlipCallback& callback);
+
+  // Set the hardware cursor to show the contents of |surface|.
+  bool SetCursor(const scoped_refptr<ScanoutBuffer>& buffer);
+
+  bool UnsetCursor();
+
+  // Moves the hardware cursor to |location|.
+  bool MoveCursor(const gfx::Point& location);
+
+  void AddCrtc(scoped_ptr<CrtcController> controller);
+  scoped_ptr<CrtcController> RemoveCrtc(const scoped_refptr<DrmDevice>& drm,
+                                        uint32_t crtc);
+  bool HasCrtc(const scoped_refptr<DrmDevice>& drm, uint32_t crtc) const;
+  bool IsMirrored() const;
+  bool IsDisabled() const;
+  gfx::Size GetModeSize() const;
+
+  gfx::Point origin() const { return origin_; }
+  void set_origin(const gfx::Point& origin) { origin_ = origin; }
+
+  const drmModeModeInfo& get_mode() const { return mode_; };
+
+  uint64_t GetTimeOfLastFlip() const;
+
+  const std::vector<CrtcController*>& crtc_controllers() const {
+    return crtc_controllers_.get();
+  }
+
+  scoped_refptr<DrmDevice> GetAllocationDrmDevice() const;
+
+ private:
+  base::ScopedPtrHashMap<DrmDevice*, scoped_ptr<HardwareDisplayPlaneList>>
+      owned_hardware_planes_;
+
+  // Stores the CRTC configuration. This is used to identify monitors and
+  // configure them.
+  ScopedVector<CrtcController> crtc_controllers_;
+
+  // Location of the controller on the screen.
+  gfx::Point origin_;
+
+  // The mode used by the last modesetting operation.
+  drmModeModeInfo mode_;
+
+  bool is_disabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayController);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_CONTROLLER_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
new file mode 100644
index 0000000..eaa3cdc
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
@@ -0,0 +1,331 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace {
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kPrimaryCrtc = 1;
+const uint32_t kPrimaryConnector = 2;
+const uint32_t kSecondaryCrtc = 3;
+const uint32_t kSecondaryConnector = 4;
+const size_t kPlanesPerCrtc = 2;
+
+const gfx::Size kDefaultModeSize(kDefaultMode.hdisplay, kDefaultMode.vdisplay);
+const gfx::SizeF kDefaultModeSizeF(1.0, 1.0);
+
+class MockScanoutBuffer : public ui::ScanoutBuffer {
+ public:
+  MockScanoutBuffer(const gfx::Size& size) : size_(size) {}
+
+  // ScanoutBuffer:
+  uint32_t GetFramebufferId() const override { return 0; }
+  uint32_t GetHandle() const override { return 0; }
+  gfx::Size GetSize() const override { return size_; }
+
+ private:
+  ~MockScanoutBuffer() override {}
+
+  gfx::Size size_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockScanoutBuffer);
+};
+
+}  // namespace
+
+class HardwareDisplayControllerTest : public testing::Test {
+ public:
+  HardwareDisplayControllerTest() : page_flips_(0) {}
+  ~HardwareDisplayControllerTest() override {}
+
+  void SetUp() override;
+  void TearDown() override;
+
+  void PageFlipCallback(gfx::SwapResult);
+
+ protected:
+  scoped_ptr<ui::HardwareDisplayController> controller_;
+  scoped_refptr<ui::MockDrmDevice> drm_;
+
+  int page_flips_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerTest);
+};
+
+void HardwareDisplayControllerTest::SetUp() {
+  std::vector<uint32_t> crtcs;
+  crtcs.push_back(kPrimaryCrtc);
+  crtcs.push_back(kSecondaryCrtc);
+  drm_ = new ui::MockDrmDevice(false, crtcs, kPlanesPerCrtc);
+  controller_.reset(new ui::HardwareDisplayController(
+      scoped_ptr<ui::CrtcController>(
+          new ui::CrtcController(drm_.get(), kPrimaryCrtc, kPrimaryConnector)),
+      gfx::Point()));
+}
+
+void HardwareDisplayControllerTest::TearDown() {
+  controller_.reset();
+  drm_ = nullptr;
+}
+
+void HardwareDisplayControllerTest::PageFlipCallback(gfx::SwapResult) {
+  page_flips_++;
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckModesettingResult) {
+  ui::OverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane, kDefaultMode));
+  EXPECT_FALSE(plane.buffer->HasOneRef());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_TRUE(plane1.buffer->HasOneRef());
+  EXPECT_FALSE(plane2.buffer->HasOneRef());
+
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(0, drm_->get_overlay_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) {
+  drm_->set_set_crtc_expectation(false);
+
+  ui::OverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_FALSE(controller_->Modeset(plane, kDefaultMode));
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) {
+  drm_->set_page_flip_expectation(false);
+
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane2);
+  EXPECT_FALSE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  planes.clear();
+
+  EXPECT_FALSE(plane1.buffer->HasOneRef());
+  EXPECT_TRUE(plane2.buffer->HasOneRef());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  ui::OverlayPlane plane2(
+      scoped_refptr<ui::ScanoutBuffer>(new MockScanoutBuffer(kDefaultModeSize)),
+      1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize),
+      gfx::RectF(kDefaultModeSizeF));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  std::vector<ui::OverlayPlane> planes;
+  planes.push_back(plane1);
+  planes.push_back(plane2);
+
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(1, drm_->get_overlay_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  ui::OverlayPlane plane2(
+      scoped_refptr<ui::ScanoutBuffer>(new MockScanoutBuffer(kDefaultModeSize)),
+      1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize),
+      gfx::RectF(kDefaultModeSizeF));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  std::vector<ui::OverlayPlane> planes;
+  planes.push_back(plane1);
+  planes.push_back(plane2);
+
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(1, drm_->get_overlay_flip_call_count());
+
+  // A test call shouldn't cause new flips, but should succeed.
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, true,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(1, drm_->get_overlay_flip_call_count());
+
+  // Regular flips should continue on normally.
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_EQ(2, drm_->get_page_flip_call_count());
+  EXPECT_EQ(2, drm_->get_overlay_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, RejectUnderlays) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  ui::OverlayPlane plane2(
+      scoped_refptr<ui::ScanoutBuffer>(new MockScanoutBuffer(kDefaultModeSize)),
+      -1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize),
+      gfx::RectF(kDefaultModeSizeF));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  std::vector<ui::OverlayPlane> planes;
+  planes.push_back(plane1);
+  planes.push_back(plane2);
+
+  EXPECT_FALSE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+}
+
+TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) {
+  controller_->AddCrtc(scoped_ptr<ui::CrtcController>(
+      new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
+
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  EXPECT_EQ(2, drm_->get_set_crtc_call_count());
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+  EXPECT_EQ(2, drm_->get_page_flip_call_count());
+  EXPECT_EQ(1, page_flips_);
+}
+
+TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane1);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+  drm_->RunCallbacks();
+
+  const ui::HardwareDisplayPlane* owned_plane = nullptr;
+  for (const auto& plane : drm_->plane_manager()->planes())
+    if (plane->in_use())
+      owned_plane = plane;
+  ASSERT_TRUE(owned_plane != nullptr);
+  EXPECT_EQ(kPrimaryCrtc, owned_plane->owning_crtc());
+  // Removing the crtc should free the plane.
+  scoped_ptr<ui::CrtcController> crtc =
+      controller_->RemoveCrtc(drm_, kPrimaryCrtc);
+  EXPECT_FALSE(owned_plane->in_use());
+}
+
+TEST_F(HardwareDisplayControllerTest, ModesetWhilePageFlipping) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane1);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, page_flips_);
+}
+
+TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane1);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+
+  controller_->AddCrtc(scoped_ptr<ui::CrtcController>(
+      new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
+
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, page_flips_);
+}
+
+TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  std::vector<ui::OverlayPlane> planes =
+      std::vector<ui::OverlayPlane>(1, plane1);
+  EXPECT_TRUE(controller_->SchedulePageFlip(
+      planes, false, false,
+      base::Bind(&HardwareDisplayControllerTest::PageFlipCallback,
+                 base::Unretained(this))));
+
+  controller_->RemoveCrtc(drm_, kPrimaryCrtc);
+
+  EXPECT_EQ(1, page_flips_);
+  drm_->RunCallbacks();
+  EXPECT_EQ(1, page_flips_);
+}
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane.cc
new file mode 100644
index 0000000..b8b3bd1
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
+
+#include <drm.h>
+#include <xf86drm.h>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+HardwareDisplayPlane::HardwareDisplayPlane(uint32_t plane_id,
+                                           uint32_t possible_crtcs)
+    : plane_id_(plane_id), possible_crtcs_(possible_crtcs) {
+}
+
+HardwareDisplayPlane::~HardwareDisplayPlane() {
+}
+
+bool HardwareDisplayPlane::CanUseForCrtc(uint32_t crtc_index) {
+  return possible_crtcs_ & (1 << crtc_index);
+}
+
+bool HardwareDisplayPlane::Initialize(DrmDevice* drm) {
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane.h b/ui/ozone/platform/drm/gpu/hardware_display_plane.h
new file mode 100644
index 0000000..cef1ada
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane.h
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/basictypes.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+
+class DrmDevice;
+
+class OZONE_EXPORT HardwareDisplayPlane {
+ public:
+  HardwareDisplayPlane(uint32_t plane_id, uint32_t possible_crtcs);
+
+  virtual ~HardwareDisplayPlane();
+
+  virtual bool Initialize(DrmDevice* drm);
+
+  bool CanUseForCrtc(uint32_t crtc_index);
+
+  bool in_use() const { return in_use_; }
+  void set_in_use(bool in_use) { in_use_ = in_use; }
+
+  bool is_dummy() const { return is_dummy_; }
+  void set_is_dummy(bool is_dummy) { is_dummy_ = is_dummy; }
+
+  uint32_t plane_id() const { return plane_id_; }
+
+  void set_owning_crtc(uint32_t crtc) { owning_crtc_ = crtc; }
+  uint32_t owning_crtc() const { return owning_crtc_; }
+
+ protected:
+  uint32_t plane_id_ = 0;
+  uint32_t possible_crtcs_ = 0;
+  uint32_t owning_crtc_ = 0;
+  bool in_use_ = false;
+  bool is_dummy_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlane);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
new file mode 100644
index 0000000..3f0952e
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
@@ -0,0 +1,117 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h"
+
+#include <drm.h>
+#include <errno.h>
+#include <xf86drm.h>
+
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+namespace {
+
+const char* kCrtcPropName = "CRTC_ID";
+const char* kFbPropName = "FB_ID";
+const char* kCrtcXPropName = "CRTC_X";
+const char* kCrtcYPropName = "CRTC_Y";
+const char* kCrtcWPropName = "CRTC_W";
+const char* kCrtcHPropName = "CRTC_H";
+const char* kSrcXPropName = "SRC_X";
+const char* kSrcYPropName = "SRC_Y";
+const char* kSrcWPropName = "SRC_W";
+const char* kSrcHPropName = "SRC_H";
+
+}  // namespace
+
+HardwareDisplayPlaneAtomic::Property::Property() {
+}
+
+bool HardwareDisplayPlaneAtomic::Property::Initialize(
+    DrmDevice* drm,
+    const char* name,
+    const ScopedDrmObjectPropertyPtr& plane_props) {
+  for (uint32_t i = 0; i < plane_props->count_props; i++) {
+    ScopedDrmPropertyPtr property(
+        drmModeGetProperty(drm->get_fd(), plane_props->props[i]));
+    if (property && !strcmp(property->name, name)) {
+      id = property->prop_id;
+      break;
+    }
+  }
+  if (!id) {
+    LOG(ERROR) << "Could not find property " << name;
+    return false;
+  }
+  return true;
+}
+
+HardwareDisplayPlaneAtomic::HardwareDisplayPlaneAtomic(uint32_t plane_id,
+                                                       uint32_t possible_crtcs)
+    : HardwareDisplayPlane(plane_id, possible_crtcs) {
+}
+HardwareDisplayPlaneAtomic::~HardwareDisplayPlaneAtomic() {
+}
+
+bool HardwareDisplayPlaneAtomic::SetPlaneData(drmModePropertySet* property_set,
+                                              uint32_t crtc_id,
+                                              uint32_t framebuffer,
+                                              const gfx::Rect& crtc_rect,
+                                              const gfx::Rect& src_rect) {
+  int plane_set_error =
+      drmModePropertySetAdd(property_set, plane_id_, crtc_prop_.id, crtc_id) ||
+      drmModePropertySetAdd(property_set, plane_id_, fb_prop_.id,
+                            framebuffer) ||
+      drmModePropertySetAdd(property_set, plane_id_, crtc_x_prop_.id,
+                            crtc_rect.x()) ||
+      drmModePropertySetAdd(property_set, plane_id_, crtc_y_prop_.id,
+                            crtc_rect.y()) ||
+      drmModePropertySetAdd(property_set, plane_id_, crtc_w_prop_.id,
+                            crtc_rect.width()) ||
+      drmModePropertySetAdd(property_set, plane_id_, crtc_h_prop_.id,
+                            crtc_rect.height()) ||
+      drmModePropertySetAdd(property_set, plane_id_, src_x_prop_.id,
+                            src_rect.x()) ||
+      drmModePropertySetAdd(property_set, plane_id_, src_y_prop_.id,
+                            src_rect.x()) ||
+      drmModePropertySetAdd(property_set, plane_id_, src_w_prop_.id,
+                            src_rect.width()) ||
+      drmModePropertySetAdd(property_set, plane_id_, src_h_prop_.id,
+                            src_rect.height());
+  if (plane_set_error) {
+    PLOG(ERROR) << "Failed to set plane data";
+    return false;
+  }
+  return true;
+}
+
+bool HardwareDisplayPlaneAtomic::Initialize(DrmDevice* drm) {
+  ScopedDrmObjectPropertyPtr plane_props(drmModeObjectGetProperties(
+      drm->get_fd(), plane_id_, DRM_MODE_OBJECT_PLANE));
+
+  if (!plane_props) {
+    PLOG(ERROR) << "Unable to get plane properties.";
+    return false;
+  }
+
+  bool props_init = crtc_prop_.Initialize(drm, kCrtcPropName, plane_props) &&
+                    fb_prop_.Initialize(drm, kFbPropName, plane_props) &&
+                    crtc_x_prop_.Initialize(drm, kCrtcXPropName, plane_props) &&
+                    crtc_y_prop_.Initialize(drm, kCrtcYPropName, plane_props) &&
+                    crtc_w_prop_.Initialize(drm, kCrtcWPropName, plane_props) &&
+                    crtc_h_prop_.Initialize(drm, kCrtcHPropName, plane_props) &&
+                    src_x_prop_.Initialize(drm, kSrcXPropName, plane_props) &&
+                    src_y_prop_.Initialize(drm, kSrcYPropName, plane_props) &&
+                    src_w_prop_.Initialize(drm, kSrcWPropName, plane_props) &&
+                    src_h_prop_.Initialize(drm, kSrcHPropName, plane_props);
+
+  if (!props_init) {
+    LOG(ERROR) << "Unable to get plane properties.";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
new file mode 100644
index 0000000..f89c119
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_ATOMIC_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_ATOMIC_H_
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
+
+#include <xf86drmMode.h>
+
+namespace ui {
+
+class CrtcController;
+class DrmDevice;
+
+class HardwareDisplayPlaneAtomic : public HardwareDisplayPlane {
+ public:
+  HardwareDisplayPlaneAtomic(uint32_t plane_id, uint32_t possible_crtcs);
+  ~HardwareDisplayPlaneAtomic() override;
+
+  bool SetPlaneData(drmModePropertySet* property_set,
+                    uint32_t crtc_id,
+                    uint32_t framebuffer,
+                    const gfx::Rect& crtc_rect,
+                    const gfx::Rect& src_rect);
+
+  // HardwareDisplayPlane:
+  bool Initialize(DrmDevice* drm) override;
+
+  void set_crtc(CrtcController* crtc) { crtc_ = crtc; }
+  CrtcController* crtc() const { return crtc_; }
+
+ private:
+  struct Property {
+    Property();
+    bool Initialize(DrmDevice* drm,
+                    const char* name,
+                    const ScopedDrmObjectPropertyPtr& plane_properties);
+    uint32_t id = 0;
+  };
+
+  Property crtc_prop_;
+  Property fb_prop_;
+  Property crtc_x_prop_;
+  Property crtc_y_prop_;
+  Property crtc_w_prop_;
+  Property crtc_h_prop_;
+  Property src_x_prop_;
+  Property src_y_prop_;
+  Property src_w_prop_;
+  Property src_h_prop_;
+  CrtcController* crtc_ = nullptr;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_ATOMIC_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
new file mode 100644
index 0000000..a790d62
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -0,0 +1,235 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+
+#include <drm.h>
+#include <xf86drm.h>
+
+#include <set>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+namespace {
+
+const float kFixedPointScaleValue = 65536.0f;
+
+}  // namespace
+
+HardwareDisplayPlaneList::HardwareDisplayPlaneList() {
+#if defined(USE_DRM_ATOMIC)
+  atomic_property_set.reset(drmModePropertySetAlloc());
+#endif  // defined(USE_DRM_ATOMIC)
+}
+
+HardwareDisplayPlaneList::~HardwareDisplayPlaneList() {
+  for (auto* plane : plane_list) {
+    plane->set_in_use(false);
+    plane->set_owning_crtc(0);
+  }
+  for (auto* plane : old_plane_list) {
+    plane->set_in_use(false);
+    plane->set_owning_crtc(0);
+  }
+}
+
+HardwareDisplayPlaneList::PageFlipInfo::PageFlipInfo(uint32_t crtc_id,
+                                                     uint32_t framebuffer,
+                                                     CrtcController* crtc)
+    : crtc_id(crtc_id), framebuffer(framebuffer), crtc(crtc) {
+}
+
+HardwareDisplayPlaneList::PageFlipInfo::~PageFlipInfo() {
+}
+
+HardwareDisplayPlaneList::PageFlipInfo::Plane::Plane(int plane,
+                                                     int framebuffer,
+                                                     const gfx::Rect& bounds,
+                                                     const gfx::Rect& src_rect)
+    : plane(plane),
+      framebuffer(framebuffer),
+      bounds(bounds),
+      src_rect(src_rect) {
+}
+
+HardwareDisplayPlaneList::PageFlipInfo::Plane::~Plane() {
+}
+
+HardwareDisplayPlaneManager::HardwareDisplayPlaneManager() : drm_(nullptr) {
+}
+
+HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() {
+}
+
+bool HardwareDisplayPlaneManager::Initialize(DrmDevice* drm) {
+  drm_ = drm;
+
+  // Try to get all of the planes if possible, so we don't have to try to
+  // discover hidden primary planes.
+  bool has_universal_planes = false;
+#if defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES)
+  has_universal_planes = drm->SetCapability(DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+#endif  // defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES)
+
+  ScopedDrmResourcesPtr resources(drmModeGetResources(drm->get_fd()));
+  if (!resources) {
+    PLOG(ERROR) << "Failed to get resources";
+    return false;
+  }
+
+  ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(drm->get_fd()));
+  if (!plane_resources) {
+    PLOG(ERROR) << "Failed to get plane resources";
+    return false;
+  }
+
+  crtcs_.clear();
+  for (int i = 0; i < resources->count_crtcs; ++i) {
+    crtcs_.push_back(resources->crtcs[i]);
+  }
+
+  uint32_t num_planes = plane_resources->count_planes;
+  std::set<uint32_t> plane_ids;
+  for (uint32_t i = 0; i < num_planes; ++i) {
+    ScopedDrmPlanePtr drm_plane(
+        drmModeGetPlane(drm->get_fd(), plane_resources->planes[i]));
+    if (!drm_plane) {
+      PLOG(ERROR) << "Failed to get plane " << i;
+      return false;
+    }
+    plane_ids.insert(drm_plane->plane_id);
+    scoped_ptr<HardwareDisplayPlane> plane(
+        CreatePlane(drm_plane->plane_id, drm_plane->possible_crtcs));
+    if (plane->Initialize(drm))
+      planes_.push_back(plane.Pass());
+  }
+
+  // crbug.com/464085: if driver reports no primary planes for a crtc, create a
+  // dummy plane for which we can assign exactly one overlay.
+  // TODO(dnicoara): refactor this to simplify AssignOverlayPlanes and move
+  // this workaround into HardwareDisplayPlaneLegacy.
+  if (!has_universal_planes) {
+    for (int i = 0; i < resources->count_crtcs; ++i) {
+      if (plane_ids.find(resources->crtcs[i] - 1) == plane_ids.end()) {
+        scoped_ptr<HardwareDisplayPlane> dummy_plane(
+            CreatePlane(resources->crtcs[i] - 1, (1 << i)));
+        dummy_plane->set_is_dummy(true);
+        if (dummy_plane->Initialize(drm))
+          planes_.push_back(dummy_plane.Pass());
+      }
+    }
+  }
+
+  std::sort(planes_.begin(), planes_.end(),
+            [](HardwareDisplayPlane* l, HardwareDisplayPlane* r) {
+              return l->plane_id() < r->plane_id();
+            });
+  return true;
+}
+
+scoped_ptr<HardwareDisplayPlane> HardwareDisplayPlaneManager::CreatePlane(
+    uint32_t plane_id,
+    uint32_t possible_crtcs) {
+  return scoped_ptr<HardwareDisplayPlane>(
+      new HardwareDisplayPlane(plane_id, possible_crtcs));
+}
+
+HardwareDisplayPlane* HardwareDisplayPlaneManager::FindNextUnusedPlane(
+    size_t* index,
+    uint32_t crtc_index) {
+  for (size_t i = *index; i < planes_.size(); ++i) {
+    auto plane = planes_[i];
+    if (!plane->in_use() && plane->CanUseForCrtc(crtc_index)) {
+      *index = i + 1;
+      return plane;
+    }
+  }
+  return nullptr;
+}
+
+int HardwareDisplayPlaneManager::LookupCrtcIndex(uint32_t crtc_id) {
+  for (size_t i = 0; i < crtcs_.size(); ++i)
+    if (crtcs_[i] == crtc_id)
+      return i;
+  return -1;
+}
+
+void HardwareDisplayPlaneManager::BeginFrame(
+    HardwareDisplayPlaneList* plane_list) {
+  for (auto* plane : plane_list->old_plane_list) {
+    plane->set_in_use(false);
+  }
+}
+
+bool HardwareDisplayPlaneManager::AssignOverlayPlanes(
+    HardwareDisplayPlaneList* plane_list,
+    const OverlayPlaneList& overlay_list,
+    uint32_t crtc_id,
+    CrtcController* crtc) {
+  int crtc_index = LookupCrtcIndex(crtc_id);
+  if (crtc_index < 0) {
+    LOG(ERROR) << "Cannot find crtc " << crtc_id;
+    return false;
+  }
+
+  size_t plane_idx = 0;
+  for (const auto& plane : overlay_list) {
+    HardwareDisplayPlane* hw_plane =
+        FindNextUnusedPlane(&plane_idx, crtc_index);
+    if (!hw_plane) {
+      LOG(ERROR) << "Failed to find a free plane for crtc " << crtc_id;
+      return false;
+    }
+
+    gfx::Rect fixed_point_rect;
+    if (!hw_plane->is_dummy()) {
+      const gfx::Size& size = plane.buffer->GetSize();
+      gfx::RectF crop_rect = plane.crop_rect;
+      crop_rect.Scale(size.width(), size.height());
+
+      // This returns a number in 16.16 fixed point, required by the DRM overlay
+      // APIs.
+      auto to_fixed_point =
+          [](double v) -> uint32_t { return v * kFixedPointScaleValue; };
+      fixed_point_rect = gfx::Rect(to_fixed_point(crop_rect.x()),
+                                   to_fixed_point(crop_rect.y()),
+                                   to_fixed_point(crop_rect.width()),
+                                   to_fixed_point(crop_rect.height()));
+    }
+
+    plane_list->plane_list.push_back(hw_plane);
+    hw_plane->set_owning_crtc(crtc_id);
+    if (SetPlaneData(plane_list, hw_plane, plane, crtc_id, fixed_point_rect,
+                     crtc)) {
+      hw_plane->set_in_use(true);
+    } else {
+      return false;
+    }
+  }
+  return true;
+}
+
+void HardwareDisplayPlaneManager::ResetPlanes(
+    HardwareDisplayPlaneList* plane_list,
+    uint32_t crtc_id) {
+  std::vector<HardwareDisplayPlane*> planes;
+  planes.swap(plane_list->old_plane_list);
+  for (auto* plane : planes) {
+    if (plane->owning_crtc() == crtc_id) {
+      plane->set_owning_crtc(0);
+      plane->set_in_use(false);
+    } else {
+      plane_list->old_plane_list.push_back(plane);
+    }
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
new file mode 100644
index 0000000..55afa9e
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+class CrtcController;
+class DrmDevice;
+
+// This contains the list of planes controlled by one HDC on a given DRM fd.
+// It is owned by the HDC and filled by the CrtcController.
+struct OZONE_EXPORT HardwareDisplayPlaneList {
+  HardwareDisplayPlaneList();
+  ~HardwareDisplayPlaneList();
+
+  // This is the list of planes to be committed this time.
+  std::vector<HardwareDisplayPlane*> plane_list;
+  // This is the list of planes that was committed last time.
+  std::vector<HardwareDisplayPlane*> old_plane_list;
+
+  struct PageFlipInfo {
+    PageFlipInfo(uint32_t crtc_id, uint32_t framebuffer, CrtcController* crtc);
+    ~PageFlipInfo();
+
+    uint32_t crtc_id;
+    uint32_t framebuffer;
+    CrtcController* crtc;
+
+    struct Plane {
+      Plane(int plane,
+            int framebuffer,
+            const gfx::Rect& bounds,
+            const gfx::Rect& src_rect);
+      ~Plane();
+      int plane;
+      int framebuffer;
+      gfx::Rect bounds;
+      gfx::Rect src_rect;
+    };
+    std::vector<Plane> planes;
+  };
+  // In the case of non-atomic operation, this info will be used for
+  // pageflipping.
+  std::vector<PageFlipInfo> legacy_page_flips;
+
+#if defined(USE_DRM_ATOMIC)
+  ScopedDrmPropertySetPtr atomic_property_set;
+#endif  // defined(USE_DRM_ATOMIC)
+};
+
+class OZONE_EXPORT HardwareDisplayPlaneManager {
+ public:
+  HardwareDisplayPlaneManager();
+  virtual ~HardwareDisplayPlaneManager();
+
+  // This parses information from the drm driver, adding any new planes
+  // or crtcs found.
+  bool Initialize(DrmDevice* drm);
+
+  // Clears old frame state out. Must be called before any AssignOverlayPlanes
+  // calls.
+  void BeginFrame(HardwareDisplayPlaneList* plane_list);
+
+  // Assign hardware planes from the |planes_| list to |overlay_list| entries,
+  // recording the plane IDs in the |plane_list|. Only planes compatible with
+  // |crtc_id| will be used. |overlay_list| must be sorted bottom-to-top.
+  virtual bool AssignOverlayPlanes(HardwareDisplayPlaneList* plane_list,
+                                   const OverlayPlaneList& overlay_list,
+                                   uint32_t crtc_id,
+                                   CrtcController* crtc);
+
+  // Commit the plane states in |plane_list|.
+  virtual bool Commit(HardwareDisplayPlaneList* plane_list,
+                      bool is_sync,
+                      bool test_only) = 0;
+
+  // Set all planes in |plane_list| owned by |crtc_id| to free.
+  static void ResetPlanes(HardwareDisplayPlaneList* plane_list,
+                          uint32_t crtc_id);
+
+  const ScopedVector<HardwareDisplayPlane>& planes() { return planes_; }
+
+ protected:
+  virtual bool SetPlaneData(HardwareDisplayPlaneList* plane_list,
+                            HardwareDisplayPlane* hw_plane,
+                            const OverlayPlane& overlay,
+                            uint32_t crtc_id,
+                            const gfx::Rect& src_rect,
+                            CrtcController* crtc) = 0;
+
+  virtual scoped_ptr<HardwareDisplayPlane> CreatePlane(uint32_t plane_id,
+                                                       uint32_t possible_crtcs);
+
+  // Finds the plane located at or after |*index| that is not in use and can
+  // be used with |crtc_index|.
+  HardwareDisplayPlane* FindNextUnusedPlane(size_t* index, uint32_t crtc_index);
+
+  // Convert |crtc_id| into an index, returning -1 if the ID couldn't be found.
+  int LookupCrtcIndex(uint32_t crtc_id);
+
+  // Object containing the connection to the graphics device and wraps the API
+  // calls to control it. Not owned.
+  DrmDevice* drm_;
+
+  ScopedVector<HardwareDisplayPlane> planes_;
+  std::vector<uint32_t> crtcs_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
new file mode 100644
index 0000000..9ee6948
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
+
+#include "base/bind.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+static void AtomicPageFlipCallback(
+    std::vector<base::WeakPtr<CrtcController>> crtcs,
+    unsigned int frame,
+    unsigned int seconds,
+    unsigned int useconds) {
+  for (auto& crtc : crtcs) {
+    auto* crtc_ptr = crtc.get();
+    if (crtc_ptr)
+      crtc_ptr->OnPageFlipEvent(frame, seconds, useconds);
+  }
+}
+
+HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic() {
+}
+
+HardwareDisplayPlaneManagerAtomic::~HardwareDisplayPlaneManagerAtomic() {
+}
+
+bool HardwareDisplayPlaneManagerAtomic::Commit(
+    HardwareDisplayPlaneList* plane_list,
+    bool is_sync,
+    bool test_only) {
+  for (HardwareDisplayPlane* plane : plane_list->old_plane_list) {
+    bool found =
+        std::find(plane_list->plane_list.begin(), plane_list->plane_list.end(),
+                  plane) != plane_list->plane_list.end();
+    if (!found) {
+      // This plane is being released, so we need to zero it.
+      plane->set_in_use(false);
+      HardwareDisplayPlaneAtomic* atomic_plane =
+          static_cast<HardwareDisplayPlaneAtomic*>(plane);
+      atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(), 0, 0,
+                                 gfx::Rect(), gfx::Rect());
+    }
+  }
+
+  std::vector<base::WeakPtr<CrtcController>> crtcs;
+  for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+    HardwareDisplayPlaneAtomic* atomic_plane =
+        static_cast<HardwareDisplayPlaneAtomic*>(plane);
+    if (crtcs.empty() || crtcs.back().get() != atomic_plane->crtc())
+      crtcs.push_back(atomic_plane->crtc()->AsWeakPtr());
+  }
+
+  if (test_only) {
+    for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+      plane->set_in_use(false);
+    }
+  } else {
+    plane_list->plane_list.swap(plane_list->old_plane_list);
+  }
+  plane_list->plane_list.clear();
+  if (!drm_->CommitProperties(plane_list->atomic_property_set.get(), 0, is_sync,
+                              test_only,
+                              base::Bind(&AtomicPageFlipCallback, crtcs))) {
+    PLOG(ERROR) << "Failed to commit properties";
+    return false;
+  }
+  plane_list->atomic_property_set.reset(drmModePropertySetAlloc());
+  return true;
+}
+
+bool HardwareDisplayPlaneManagerAtomic::SetPlaneData(
+    HardwareDisplayPlaneList* plane_list,
+    HardwareDisplayPlane* hw_plane,
+    const OverlayPlane& overlay,
+    uint32_t crtc_id,
+    const gfx::Rect& src_rect,
+    CrtcController* crtc) {
+  HardwareDisplayPlaneAtomic* atomic_plane =
+      static_cast<HardwareDisplayPlaneAtomic*>(hw_plane);
+  if (!atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(),
+                                  crtc_id, overlay.buffer->GetFramebufferId(),
+                                  overlay.display_bounds, src_rect)) {
+    LOG(ERROR) << "Failed to set plane properties";
+    return false;
+  }
+  atomic_plane->set_crtc(crtc);
+  return true;
+}
+
+scoped_ptr<HardwareDisplayPlane> HardwareDisplayPlaneManagerAtomic::CreatePlane(
+    uint32_t plane_id,
+    uint32_t possible_crtcs) {
+  return scoped_ptr<HardwareDisplayPlane>(
+      new HardwareDisplayPlaneAtomic(plane_id, possible_crtcs));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
new file mode 100644
index 0000000..de883ee
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_ATOMIC_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_ATOMIC_H_
+
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+
+namespace ui {
+
+class OZONE_EXPORT HardwareDisplayPlaneManagerAtomic
+    : public HardwareDisplayPlaneManager {
+ public:
+  HardwareDisplayPlaneManagerAtomic();
+  ~HardwareDisplayPlaneManagerAtomic() override;
+
+  // HardwareDisplayPlaneManager:
+  bool Commit(HardwareDisplayPlaneList* plane_list,
+              bool is_sync,
+              bool test_only) override;
+
+ private:
+  bool SetPlaneData(HardwareDisplayPlaneList* plane_list,
+                    HardwareDisplayPlane* hw_plane,
+                    const OverlayPlane& overlay,
+                    uint32_t crtc_id,
+                    const gfx::Rect& src_rect,
+                    CrtcController* crtc) override;
+
+  scoped_ptr<HardwareDisplayPlane> CreatePlane(
+      uint32_t plane_id,
+      uint32_t possible_crtcs) override;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManagerAtomic);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_ATOMIC_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
new file mode 100644
index 0000000..dff9e57
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
+
+#include "base/bind.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy() {
+}
+
+HardwareDisplayPlaneManagerLegacy::~HardwareDisplayPlaneManagerLegacy() {
+}
+
+bool HardwareDisplayPlaneManagerLegacy::Commit(
+    HardwareDisplayPlaneList* plane_list,
+    bool is_sync,
+    bool test_only) {
+  if (test_only) {
+    for (HardwareDisplayPlane* plane : plane_list->plane_list) {
+      plane->set_in_use(false);
+    }
+    plane_list->plane_list.clear();
+    plane_list->legacy_page_flips.clear();
+    return true;
+  }
+  if (plane_list->plane_list.empty())  // No assigned planes, nothing to do.
+    return true;
+  bool ret = true;
+  // The order of operations here (set new planes, pageflip, clear old planes)
+  // is designed to minimze the chance of a significant artifact occurring.
+  // The planes must be updated first because the main plane no longer contains
+  // their content. The old planes are removed last because the previous primary
+  // plane used them as overlays and thus didn't contain their content, so we
+  // must first flip to the new primary plane, which does. The error here will
+  // be the delta of (new contents, old contents), but it should be barely
+  // noticeable.
+  for (const auto& flip : plane_list->legacy_page_flips) {
+    // Permission Denied is a legitimate error
+    for (const auto& plane : flip.planes) {
+      if (!drm_->PageFlipOverlay(flip.crtc_id, plane.framebuffer, plane.bounds,
+                                 plane.src_rect, plane.plane)) {
+        PLOG(ERROR) << "Cannot display plane on overlay: crtc=" << flip.crtc
+                    << " plane=" << plane.plane;
+        ret = false;
+        flip.crtc->PageFlipFailed();
+        break;
+      }
+    }
+    if (!drm_->PageFlip(flip.crtc_id, flip.framebuffer, is_sync,
+                        base::Bind(&CrtcController::OnPageFlipEvent,
+                                   flip.crtc->AsWeakPtr()))) {
+      if (errno != EACCES) {
+        PLOG(ERROR) << "Cannot page flip: crtc=" << flip.crtc_id
+                    << " framebuffer=" << flip.framebuffer
+                    << " is_sync=" << is_sync;
+        ret = false;
+      }
+      flip.crtc->PageFlipFailed();
+    }
+  }
+  // For each element in |old_plane_list|, if it hasn't been reclaimed (by
+  // this or any other HDPL), clear the overlay contents.
+  for (HardwareDisplayPlane* plane : plane_list->old_plane_list) {
+    if (!plane->in_use() && !plane->is_dummy()) {
+      // This plane is being released, so we need to zero it.
+      if (!drm_->PageFlipOverlay(plane->owning_crtc(), 0, gfx::Rect(),
+                                 gfx::Rect(), plane->plane_id())) {
+        PLOG(ERROR) << "Cannot free overlay: crtc=" << plane->owning_crtc()
+                    << " plane=" << plane->plane_id();
+        ret = false;
+        break;
+      }
+    }
+  }
+  plane_list->plane_list.swap(plane_list->old_plane_list);
+  plane_list->plane_list.clear();
+  plane_list->legacy_page_flips.clear();
+  return ret;
+}
+
+bool HardwareDisplayPlaneManagerLegacy::SetPlaneData(
+    HardwareDisplayPlaneList* plane_list,
+    HardwareDisplayPlane* hw_plane,
+    const OverlayPlane& overlay,
+    uint32_t crtc_id,
+    const gfx::Rect& src_rect,
+    CrtcController* crtc) {
+  if (hw_plane->is_dummy() || plane_list->legacy_page_flips.empty() ||
+      plane_list->legacy_page_flips.back().crtc_id != crtc_id) {
+    plane_list->legacy_page_flips.push_back(
+        HardwareDisplayPlaneList::PageFlipInfo(
+            crtc_id, overlay.buffer->GetFramebufferId(), crtc));
+  } else {
+    plane_list->legacy_page_flips.back().planes.push_back(
+        HardwareDisplayPlaneList::PageFlipInfo::Plane(
+            hw_plane->plane_id(), overlay.buffer->GetFramebufferId(),
+            overlay.display_bounds, src_rect));
+  }
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
new file mode 100644
index 0000000..6ab43dd
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_LEGACY_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_LEGACY_H_
+
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+
+namespace ui {
+
+class OZONE_EXPORT HardwareDisplayPlaneManagerLegacy
+    : public HardwareDisplayPlaneManager {
+ public:
+  HardwareDisplayPlaneManagerLegacy();
+  ~HardwareDisplayPlaneManagerLegacy() override;
+
+  // HardwareDisplayPlaneManager:
+  bool Commit(HardwareDisplayPlaneList* plane_list,
+              bool is_sync,
+              bool test_only) override;
+
+ private:
+  bool SetPlaneData(HardwareDisplayPlaneList* plane_list,
+                    HardwareDisplayPlane* hw_plane,
+                    const OverlayPlane& overlay,
+                    uint32_t crtc_id,
+                    const gfx::Rect& src_rect,
+                    CrtcController* crtc) override;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManagerLegacy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_HARDWARE_DISPLAY_PLANE_MANAGER_LEGACY_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
new file mode 100644
index 0000000..f74497f
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+
+namespace {
+
+struct FakePlaneInfo {
+  uint32_t id;
+  uint32_t allowed_crtc_mask;
+};
+
+const FakePlaneInfo kOnePlanePerCrtc[] = {{10, 1}, {20, 2}};
+const FakePlaneInfo kTwoPlanesPerCrtc[] = {{10, 1}, {11, 1}, {20, 2}, {21, 2}};
+const FakePlaneInfo kOnePlanePerCrtcWithShared[] = {{10, 1}, {20, 2}, {50, 3}};
+
+class FakeScanoutBuffer : public ui::ScanoutBuffer {
+ public:
+  FakeScanoutBuffer() {}
+
+  // ui::ScanoutBuffer:
+  uint32_t GetFramebufferId() const override { return 1; }
+  uint32_t GetHandle() const override { return 0; }
+  gfx::Size GetSize() const override { return gfx::Size(1, 1); }
+
+ protected:
+  ~FakeScanoutBuffer() override {}
+};
+
+class FakePlaneManager : public ui::HardwareDisplayPlaneManager {
+ public:
+  FakePlaneManager() : plane_count_(0) {}
+  ~FakePlaneManager() override {}
+
+  // Normally we'd use DRM to figure out the controller configuration. But we
+  // can't use DRM in unit tests, so we just create a fake configuration.
+  void InitForTest(const FakePlaneInfo* planes,
+                   size_t count,
+                   const std::vector<uint32_t>& crtcs) {
+    crtcs_ = crtcs;
+    for (size_t i = 0; i < count; i++) {
+      planes_.push_back(new ui::HardwareDisplayPlane(
+          planes[i].id, planes[i].allowed_crtc_mask));
+    }
+    // The real HDPM uses sorted planes, so sort them for consistency.
+    std::sort(planes_.begin(), planes_.end(),
+              [](ui::HardwareDisplayPlane* l, ui::HardwareDisplayPlane* r) {
+                return l->plane_id() < r->plane_id();
+              });
+  }
+
+  bool Commit(ui::HardwareDisplayPlaneList* plane_list,
+              bool is_sync,
+              bool test_only) override {
+    return false;
+  }
+
+  bool SetPlaneData(ui::HardwareDisplayPlaneList* plane_list,
+                    ui::HardwareDisplayPlane* hw_plane,
+                    const ui::OverlayPlane& overlay,
+                    uint32_t crtc_id,
+                    const gfx::Rect& src_rect,
+                    ui::CrtcController* crtc) override {
+    // Check that the chosen plane is a legal choice for the crtc.
+    EXPECT_NE(-1, LookupCrtcIndex(crtc_id));
+    EXPECT_TRUE(hw_plane->CanUseForCrtc(LookupCrtcIndex(crtc_id)));
+    EXPECT_FALSE(hw_plane->in_use());
+    plane_count_++;
+    return true;
+  }
+
+  int plane_count() const { return plane_count_; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakePlaneManager);
+
+  int plane_count_;
+};
+
+class HardwareDisplayPlaneManagerTest : public testing::Test {
+ public:
+  HardwareDisplayPlaneManagerTest() {}
+
+  void SetUp() override;
+
+ protected:
+  scoped_ptr<FakePlaneManager> plane_manager_;
+  ui::HardwareDisplayPlaneList state_;
+  std::vector<uint32_t> default_crtcs_;
+  scoped_refptr<ui::ScanoutBuffer> fake_buffer_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManagerTest);
+};
+
+void HardwareDisplayPlaneManagerTest::SetUp() {
+  fake_buffer_ = new FakeScanoutBuffer();
+  plane_manager_.reset(new FakePlaneManager());
+  default_crtcs_.push_back(100);
+  default_crtcs_.push_back(200);
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, SinglePlaneAssignment) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kOnePlanePerCrtc, arraysize(kOnePlanePerCrtc),
+                              default_crtcs_);
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(1, plane_manager_->plane_count());
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, BadCrtc) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kOnePlanePerCrtc, arraysize(kOnePlanePerCrtc),
+                              default_crtcs_);
+  EXPECT_FALSE(
+      plane_manager_->AssignOverlayPlanes(&state_, assigns, 1, nullptr));
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, MultiplePlaneAssignment) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kTwoPlanesPerCrtc, arraysize(kTwoPlanesPerCrtc),
+                              default_crtcs_);
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(2, plane_manager_->plane_count());
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, NotEnoughPlanes) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kOnePlanePerCrtc, arraysize(kOnePlanePerCrtc),
+                              default_crtcs_);
+
+  EXPECT_FALSE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                   default_crtcs_[0], nullptr));
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, MultipleCrtcs) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kOnePlanePerCrtc, arraysize(kOnePlanePerCrtc),
+                              default_crtcs_);
+
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[1], nullptr));
+  EXPECT_EQ(2, plane_manager_->plane_count());
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, MultiplePlanesAndCrtcs) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kTwoPlanesPerCrtc, arraysize(kTwoPlanesPerCrtc),
+                              default_crtcs_);
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[1], nullptr));
+  EXPECT_EQ(4, plane_manager_->plane_count());
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, MultipleFrames) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kTwoPlanesPerCrtc, arraysize(kTwoPlanesPerCrtc),
+                              default_crtcs_);
+
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(1, plane_manager_->plane_count());
+  // Pretend we committed the frame.
+  state_.plane_list.swap(state_.old_plane_list);
+  plane_manager_->BeginFrame(&state_);
+  ui::HardwareDisplayPlane* old_plane = state_.old_plane_list[0];
+  // The same plane should be used.
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(2, plane_manager_->plane_count());
+  EXPECT_EQ(state_.plane_list[0], old_plane);
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, MultipleFramesDifferentPlanes) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kTwoPlanesPerCrtc, arraysize(kTwoPlanesPerCrtc),
+                              default_crtcs_);
+
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(1, plane_manager_->plane_count());
+  // The other plane should be used.
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[0], nullptr));
+  EXPECT_EQ(2, plane_manager_->plane_count());
+  EXPECT_NE(state_.plane_list[0], state_.plane_list[1]);
+}
+
+TEST_F(HardwareDisplayPlaneManagerTest, SharedPlanes) {
+  ui::OverlayPlaneList assigns;
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  assigns.push_back(ui::OverlayPlane(fake_buffer_));
+  plane_manager_->InitForTest(kOnePlanePerCrtcWithShared,
+                              arraysize(kOnePlanePerCrtcWithShared),
+                              default_crtcs_);
+
+  EXPECT_TRUE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                  default_crtcs_[1], nullptr));
+  EXPECT_EQ(2, plane_manager_->plane_count());
+  // The shared plane is now unavailable for use by the other CRTC.
+  EXPECT_FALSE(plane_manager_->AssignOverlayPlanes(&state_, assigns,
+                                                   default_crtcs_[0], nullptr));
+}
+
+TEST(HardwareDisplayPlaneManagerLegacyTest, UnusedPlanesAreReleased) {
+  std::vector<uint32_t> crtcs;
+  crtcs.push_back(100);
+  scoped_refptr<ui::MockDrmDevice> drm = new ui::MockDrmDevice(false, crtcs, 2);
+  ui::OverlayPlaneList assigns;
+  scoped_refptr<FakeScanoutBuffer> fake_buffer = new FakeScanoutBuffer();
+  assigns.push_back(ui::OverlayPlane(fake_buffer));
+  assigns.push_back(ui::OverlayPlane(fake_buffer));
+  ui::HardwareDisplayPlaneList hdpl;
+  ui::CrtcController crtc(drm, crtcs[0], 0);
+  drm->plane_manager()->BeginFrame(&hdpl);
+  EXPECT_TRUE(drm->plane_manager()->AssignOverlayPlanes(&hdpl, assigns,
+                                                        crtcs[0], &crtc));
+  EXPECT_TRUE(drm->plane_manager()->Commit(&hdpl, false, false));
+  assigns.clear();
+  assigns.push_back(ui::OverlayPlane(fake_buffer));
+  drm->plane_manager()->BeginFrame(&hdpl);
+  EXPECT_TRUE(drm->plane_manager()->AssignOverlayPlanes(&hdpl, assigns,
+                                                        crtcs[0], &crtc));
+  EXPECT_EQ(0, drm->get_overlay_clear_call_count());
+  EXPECT_TRUE(drm->plane_manager()->Commit(&hdpl, false, false));
+  EXPECT_EQ(1, drm->get_overlay_clear_call_count());
+}
+
+}  // namespace
diff --git a/ui/ozone/platform/drm/gpu/overlay_plane.cc b/ui/ozone/platform/drm/gpu/overlay_plane.cc
new file mode 100644
index 0000000..1312969
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/overlay_plane.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/overlay_plane.h"
+
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer)
+    : buffer(buffer),
+      display_bounds(gfx::Point(), buffer->GetSize()),
+      crop_rect(0, 0, 1, 1) {
+}
+
+OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer,
+                           int z_order,
+                           gfx::OverlayTransform plane_transform,
+                           const gfx::Rect& display_bounds,
+                           const gfx::RectF& crop_rect)
+    : buffer(buffer),
+      z_order(z_order),
+      plane_transform(plane_transform),
+      display_bounds(display_bounds),
+      crop_rect(crop_rect) {
+}
+
+OverlayPlane::~OverlayPlane() {
+}
+
+// static
+const OverlayPlane* OverlayPlane::GetPrimaryPlane(
+    const OverlayPlaneList& overlays) {
+  for (size_t i = 0; i < overlays.size(); ++i) {
+    if (overlays[i].z_order == 0)
+      return &overlays[i];
+  }
+
+  return nullptr;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/overlay_plane.h b/ui/ozone/platform/drm/gpu/overlay_plane.h
new file mode 100644
index 0000000..4be105e
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/overlay_plane.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_OVERLAY_PLANE_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_OVERLAY_PLANE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace ui {
+
+class ScanoutBuffer;
+
+struct OverlayPlane;
+typedef std::vector<OverlayPlane> OverlayPlaneList;
+
+struct OZONE_EXPORT OverlayPlane {
+  // Simpler constructor for the primary plane.
+  explicit OverlayPlane(scoped_refptr<ScanoutBuffer> buffer);
+
+  OverlayPlane(scoped_refptr<ScanoutBuffer> buffer,
+               int z_order,
+               gfx::OverlayTransform plane_transform,
+               const gfx::Rect& display_bounds,
+               const gfx::RectF& crop_rect);
+
+  ~OverlayPlane();
+
+  // Returns the primary plane in |overlays|.
+  static const OverlayPlane* GetPrimaryPlane(const OverlayPlaneList& overlays);
+
+  scoped_refptr<ScanoutBuffer> buffer;
+  int z_order = 0;
+  gfx::OverlayTransform plane_transform;
+  gfx::Rect display_bounds;
+  gfx::RectF crop_rect;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_OVERLAY_PLANE_H_
diff --git a/ui/ozone/platform/drm/gpu/page_flip_request.cc b/ui/ozone/platform/drm/gpu/page_flip_request.cc
new file mode 100644
index 0000000..5b69d75
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/page_flip_request.cc
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/page_flip_request.h"
+
+namespace ui {
+
+PageFlipRequest::PageFlipRequest(int crtc_count,
+                                 const SwapCompletionCallback& callback)
+    : callback_(callback), crtc_count_(crtc_count) {
+}
+
+PageFlipRequest::~PageFlipRequest() {
+}
+
+void PageFlipRequest::Signal(gfx::SwapResult result) {
+  if (result == gfx::SwapResult::SWAP_FAILED)
+    result_ = gfx::SwapResult::SWAP_FAILED;
+  else if (result != gfx::SwapResult::SWAP_ACK)
+    result_ = result;
+
+  if (!--crtc_count_) {
+    callback_.Run(result_);
+    callback_.Reset();
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/page_flip_request.h b/ui/ozone/platform/drm/gpu/page_flip_request.h
new file mode 100644
index 0000000..01cc30c
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/page_flip_request.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_REQUEST_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_REQUEST_H_
+
+#include "base/atomic_ref_count.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+
+class PageFlipRequest : public base::RefCounted<PageFlipRequest> {
+ public:
+  PageFlipRequest(int crtc_count, const SwapCompletionCallback& callback);
+
+  void Signal(gfx::SwapResult result);
+
+ private:
+  friend class base::RefCounted<PageFlipRequest>;
+  ~PageFlipRequest();
+
+  SwapCompletionCallback callback_;
+  int crtc_count_;
+  gfx::SwapResult result_ = gfx::SwapResult::SWAP_ACK;
+
+  DISALLOW_COPY_AND_ASSIGN(PageFlipRequest);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_REQUEST_H_
diff --git a/ui/ozone/platform/drm/gpu/scanout_buffer.h b/ui/ozone/platform/drm/gpu/scanout_buffer.h
new file mode 100644
index 0000000..ca512aa
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/scanout_buffer.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_H_
+
+#include <stdint.h>
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+class DrmDevice;
+
+// Abstraction for a DRM buffer that can be scanned-out of.
+class ScanoutBuffer : public base::RefCountedThreadSafe<ScanoutBuffer> {
+ public:
+  // ID allocated by the KMS API when the buffer is registered (via the handle).
+  virtual uint32_t GetFramebufferId() const = 0;
+
+  // Handle for the buffer. This is received when allocating the buffer.
+  virtual uint32_t GetHandle() const = 0;
+
+  // Size of the buffer.
+  virtual gfx::Size GetSize() const = 0;
+
+ protected:
+  virtual ~ScanoutBuffer() {}
+
+  friend class base::RefCountedThreadSafe<ScanoutBuffer>;
+};
+
+class ScanoutBufferGenerator {
+ public:
+  virtual ~ScanoutBufferGenerator() {}
+
+  virtual scoped_refptr<ScanoutBuffer> Create(
+      const scoped_refptr<DrmDevice>& drm,
+      const gfx::Size& size) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_SCANOUT_BUFFER_H_
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.cc b/ui/ozone/platform/drm/gpu/screen_manager.cc
new file mode 100644
index 0000000..4b4d3f1
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/screen_manager.cc
@@ -0,0 +1,348 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+
+#include <xf86drmMode.h>
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_console_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+
+namespace ui {
+
+namespace {
+
+// Copies the contents of the saved framebuffer from the CRTCs in |controller|
+// to the new modeset buffer |buffer|.
+void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm,
+                       HardwareDisplayController* controller,
+                       ScanoutBuffer* buffer) {
+  DrmConsoleBuffer modeset_buffer(drm, buffer->GetFramebufferId());
+  if (!modeset_buffer.Initialize()) {
+    VLOG(2) << "Failed to grab framebuffer " << buffer->GetFramebufferId();
+    return;
+  }
+
+  auto crtcs = controller->crtc_controllers();
+  DCHECK(!crtcs.empty());
+
+  ScopedDrmCrtcPtr saved_crtc(drm->GetCrtc(crtcs[0]->crtc()));
+  if (!saved_crtc || !saved_crtc->buffer_id) {
+    VLOG(2) << "Crtc has no saved state or wasn't modeset";
+    return;
+  }
+
+  // If the display controller is in mirror mode, the CRTCs should be sharing
+  // the same framebuffer.
+  DrmConsoleBuffer saved_buffer(drm, saved_crtc->buffer_id);
+  if (!saved_buffer.Initialize()) {
+    VLOG(2) << "Failed to grab saved framebuffer " << saved_crtc->buffer_id;
+    return;
+  }
+
+  // Don't copy anything if the sizes mismatch. This can happen when the user
+  // changes modes.
+  if (saved_buffer.canvas()->getBaseLayerSize() !=
+      modeset_buffer.canvas()->getBaseLayerSize()) {
+    VLOG(2) << "Previous buffer has a different size than modeset buffer";
+    return;
+  }
+
+  skia::RefPtr<SkImage> image = saved_buffer.image();
+  SkPaint paint;
+  // Copy the source buffer. Do not perform any blending.
+  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+  modeset_buffer.canvas()->drawImage(image.get(), 0, 0, &paint);
+}
+
+}  // namespace
+
+ScreenManager::ScreenManager(ScanoutBufferGenerator* buffer_generator)
+    : buffer_generator_(buffer_generator) {
+}
+
+ScreenManager::~ScreenManager() {
+  DCHECK(window_map_.empty());
+}
+
+void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                         uint32_t crtc,
+                                         uint32_t connector) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
+  // TODO(dnicoara): Turn this into a DCHECK when async display configuration is
+  // properly supported. (When there can't be a race between forcing initial
+  // display configuration in ScreenManager and NativeDisplayDelegate creating
+  // the display controllers.)
+  if (it != controllers_.end()) {
+    LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
+    return;
+  }
+
+  controllers_.push_back(new HardwareDisplayController(
+      scoped_ptr<CrtcController>(new CrtcController(drm, crtc, connector)),
+      gfx::Point()));
+}
+
+void ScreenManager::RemoveDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                            uint32_t crtc) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
+  if (it != controllers_.end()) {
+    bool is_mirrored = (*it)->IsMirrored();
+    (*it)->RemoveCrtc(drm, crtc);
+    if (!is_mirrored) {
+      controllers_.erase(it);
+      UpdateControllerToWindowMapping();
+    }
+  }
+}
+
+bool ScreenManager::ConfigureDisplayController(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t crtc,
+    uint32_t connector,
+    const gfx::Point& origin,
+    const drmModeModeInfo& mode) {
+  bool status =
+      ActualConfigureDisplayController(drm, crtc, connector, origin, mode);
+  if (status)
+    UpdateControllerToWindowMapping();
+
+  return status;
+}
+
+bool ScreenManager::ActualConfigureDisplayController(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t crtc,
+    uint32_t connector,
+    const gfx::Point& origin,
+    const drmModeModeInfo& mode) {
+  gfx::Rect modeset_bounds(origin.x(), origin.y(), mode.hdisplay,
+                           mode.vdisplay);
+  HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
+  DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc
+                                   << ") doesn't exist.";
+
+  HardwareDisplayController* controller = *it;
+  // If nothing changed just enable the controller. Note, we perform an exact
+  // comparison on the mode since the refresh rate may have changed.
+  if (SameMode(mode, controller->get_mode()) &&
+      origin == controller->origin()) {
+    if (controller->IsDisabled()) {
+      HardwareDisplayControllers::iterator mirror =
+          FindActiveDisplayControllerByLocation(modeset_bounds);
+      // If there is an active controller at the same location then start mirror
+      // mode.
+      if (mirror != controllers_.end())
+        return HandleMirrorMode(it, mirror, drm, crtc, connector);
+    }
+
+    // Just re-enable the controller to re-use the current state.
+    return EnableController(controller, controller->origin(),
+                            controller->get_mode());
+  }
+
+  // Either the mode or the location of the display changed, so exit mirror
+  // mode and configure the display independently. If the caller still wants
+  // mirror mode, subsequent calls configuring the other controllers will
+  // restore mirror mode.
+  if (controller->IsMirrored()) {
+    controller = new HardwareDisplayController(
+        controller->RemoveCrtc(drm, crtc), controller->origin());
+    controllers_.push_back(controller);
+    it = controllers_.end() - 1;
+  }
+
+  HardwareDisplayControllers::iterator mirror =
+      FindActiveDisplayControllerByLocation(modeset_bounds);
+  // Handle mirror mode.
+  if (mirror != controllers_.end() && it != mirror)
+    return HandleMirrorMode(it, mirror, drm, crtc, connector);
+
+  return EnableController(controller, origin, mode);
+}
+
+bool ScreenManager::DisableDisplayController(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t crtc) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
+  if (it != controllers_.end()) {
+    HardwareDisplayController* controller = *it;
+    if (controller->IsMirrored()) {
+      controller = new HardwareDisplayController(
+          controller->RemoveCrtc(drm, crtc), controller->origin());
+      controllers_.push_back(controller);
+    }
+
+    controller->Disable();
+    UpdateControllerToWindowMapping();
+    return true;
+  }
+
+  LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
+  return false;
+}
+
+HardwareDisplayController* ScreenManager::GetDisplayController(
+    const gfx::Rect& bounds) {
+  HardwareDisplayControllers::iterator it =
+      FindActiveDisplayControllerByLocation(bounds);
+  if (it != controllers_.end())
+    return *it;
+
+  return nullptr;
+}
+
+void ScreenManager::AddWindow(gfx::AcceleratedWidget widget,
+                              scoped_ptr<DrmWindow> window) {
+  std::pair<WidgetToWindowMap::iterator, bool> result =
+      window_map_.add(widget, window.Pass());
+  DCHECK(result.second) << "Window already added.";
+  UpdateControllerToWindowMapping();
+}
+
+scoped_ptr<DrmWindow> ScreenManager::RemoveWindow(
+    gfx::AcceleratedWidget widget) {
+  scoped_ptr<DrmWindow> window = window_map_.take_and_erase(widget);
+  DCHECK(window) << "Attempting to remove non-existing window for " << widget;
+  UpdateControllerToWindowMapping();
+  return window.Pass();
+}
+
+DrmWindow* ScreenManager::GetWindow(gfx::AcceleratedWidget widget) {
+  WidgetToWindowMap::iterator it = window_map_.find(widget);
+  if (it != window_map_.end())
+    return it->second;
+
+  NOTREACHED() << "Attempting to get non-existing window for " << widget;
+  return nullptr;
+}
+
+ScreenManager::HardwareDisplayControllers::iterator
+ScreenManager::FindDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                     uint32_t crtc) {
+  for (HardwareDisplayControllers::iterator it = controllers_.begin();
+       it != controllers_.end(); ++it) {
+    if ((*it)->HasCrtc(drm, crtc))
+      return it;
+  }
+
+  return controllers_.end();
+}
+
+ScreenManager::HardwareDisplayControllers::iterator
+ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
+  for (HardwareDisplayControllers::iterator it = controllers_.begin();
+       it != controllers_.end(); ++it) {
+    gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
+    if (controller_bounds == bounds && !(*it)->IsDisabled())
+      return it;
+  }
+
+  return controllers_.end();
+}
+
+bool ScreenManager::HandleMirrorMode(
+    HardwareDisplayControllers::iterator original,
+    HardwareDisplayControllers::iterator mirror,
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t crtc,
+    uint32_t connector) {
+  (*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc));
+  if (EnableController(*mirror, (*mirror)->origin(), (*mirror)->get_mode())) {
+    controllers_.erase(original);
+    return true;
+  }
+
+  LOG(ERROR) << "Failed to switch to mirror mode";
+
+  // When things go wrong revert back to the previous configuration since
+  // it is expected that the configuration would not have changed if
+  // things fail.
+  (*original)->AddCrtc((*mirror)->RemoveCrtc(drm, crtc));
+  EnableController(*original, (*original)->origin(), (*original)->get_mode());
+  return false;
+}
+
+void ScreenManager::UpdateControllerToWindowMapping() {
+  std::map<DrmWindow*, HardwareDisplayController*> window_to_controller_map;
+  // First create a unique mapping between a window and a controller. Note, a
+  // controller may be associated with at most 1 window.
+  for (HardwareDisplayController* controller : controllers_) {
+    if (controller->IsDisabled())
+      continue;
+
+    DrmWindow* window = FindWindowAt(
+        gfx::Rect(controller->origin(), controller->GetModeSize()));
+    if (!window)
+      continue;
+
+    window_to_controller_map[window] = controller;
+  }
+
+  // Apply the new mapping to all windows.
+  for (auto pair : window_map_) {
+    auto it = window_to_controller_map.find(pair.second);
+    if (it != window_to_controller_map.end())
+      pair.second->SetController(it->second);
+    else
+      pair.second->SetController(nullptr);
+  }
+}
+
+bool ScreenManager::EnableController(HardwareDisplayController* controller,
+                                     const gfx::Point& origin,
+                                     const drmModeModeInfo& mode) {
+  DCHECK(!controller->crtc_controllers().empty());
+  gfx::Rect rect(origin, gfx::Size(mode.hdisplay, mode.vdisplay));
+  controller->set_origin(origin);
+
+  DrmWindow* window = FindWindowAt(rect);
+  if (window) {
+    const OverlayPlane* primary = window->GetLastModesetBuffer();
+    if (primary) {
+      if (!controller->Modeset(*primary, mode)) {
+        LOG(ERROR) << "Failed to modeset controller";
+        return false;
+      }
+
+      return true;
+    }
+  }
+
+  scoped_refptr<DrmDevice> drm = controller->GetAllocationDrmDevice();
+  scoped_refptr<ScanoutBuffer> buffer =
+      buffer_generator_->Create(drm, rect.size());
+  if (!buffer) {
+    LOG(ERROR) << "Failed to create scanout buffer";
+    return false;
+  }
+
+  FillModesetBuffer(drm, controller, buffer.get());
+  if (!controller->Modeset(OverlayPlane(buffer), mode)) {
+    LOG(ERROR) << "Failed to modeset controller";
+    return false;
+  }
+
+  return true;
+}
+
+DrmWindow* ScreenManager::FindWindowAt(const gfx::Rect& bounds) const {
+  for (auto pair : window_map_) {
+    if (pair.second->bounds() == bounds)
+      return pair.second;
+  }
+
+  return nullptr;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.h b/ui/ozone/platform/drm/gpu/screen_manager.h
new file mode 100644
index 0000000..f9c5bc0
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/screen_manager.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_SCREEN_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_SCREEN_MANAGER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+}  // namespace gfx
+
+namespace ui {
+
+class DrmDevice;
+class DrmWindow;
+class ScanoutBufferGenerator;
+
+// Responsible for keeping track of active displays and configuring them.
+class OZONE_EXPORT ScreenManager {
+ public:
+  ScreenManager(ScanoutBufferGenerator* surface_generator);
+  virtual ~ScreenManager();
+
+  // Register a display controller. This must be called before trying to
+  // configure it.
+  void AddDisplayController(const scoped_refptr<DrmDevice>& drm,
+                            uint32_t crtc,
+                            uint32_t connector);
+
+  // Remove a display controller from the list of active controllers. The
+  // controller is removed since it was disconnected.
+  void RemoveDisplayController(const scoped_refptr<DrmDevice>& drm,
+                               uint32_t crtc);
+
+  // Configure a display controller. The display controller is identified by
+  // (|crtc|, |connector|) and the controller is modeset using |mode|.
+  bool ConfigureDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                  uint32_t crtc,
+                                  uint32_t connector,
+                                  const gfx::Point& origin,
+                                  const drmModeModeInfo& mode);
+
+  // Disable the display controller identified by |crtc|. Note, the controller
+  // may still be connected, so this does not remove the controller.
+  bool DisableDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                uint32_t crtc);
+
+  // Returns a reference to the display controller configured to display within
+  // |bounds|. If the caller caches the controller it must also register as an
+  // observer to be notified when the controller goes out of scope.
+  HardwareDisplayController* GetDisplayController(const gfx::Rect& bounds);
+
+  // Adds a window for |widget|. Note: |widget| should not be associated with a
+  // window when calling this function.
+  void AddWindow(gfx::AcceleratedWidget widget, scoped_ptr<DrmWindow> window);
+
+  // Removes the window for |widget|. Note: |widget| must have a window
+  // associated with it when calling this function.
+  scoped_ptr<DrmWindow> RemoveWindow(gfx::AcceleratedWidget widget);
+
+  // Returns the window associated with |widget|. Note: This function should be
+  // called only if a valid window has been associated with |widget|.
+  DrmWindow* GetWindow(gfx::AcceleratedWidget widget);
+
+  // Updates the mapping between display controllers and windows such that a
+  // controller will be associated with at most one window.
+  void UpdateControllerToWindowMapping();
+
+ private:
+  typedef ScopedVector<HardwareDisplayController> HardwareDisplayControllers;
+
+  typedef base::ScopedPtrHashMap<gfx::AcceleratedWidget, scoped_ptr<DrmWindow>>
+      WidgetToWindowMap;
+
+  // Returns an iterator into |controllers_| for the controller identified by
+  // (|crtc|, |connector|).
+  HardwareDisplayControllers::iterator FindDisplayController(
+      const scoped_refptr<DrmDevice>& drm,
+      uint32_t crtc);
+
+  bool ActualConfigureDisplayController(const scoped_refptr<DrmDevice>& drm,
+                                        uint32_t crtc,
+                                        uint32_t connector,
+                                        const gfx::Point& origin,
+                                        const drmModeModeInfo& mode);
+
+  // Returns an iterator into |controllers_| for the controller located at
+  // |origin|.
+  HardwareDisplayControllers::iterator FindActiveDisplayControllerByLocation(
+      const gfx::Rect& bounds);
+
+  // Tries to set the controller identified by (|crtc|, |connector|) to mirror
+  // those in |mirror|. |original| is an iterator to the HDC where the
+  // controller is currently present.
+  bool HandleMirrorMode(HardwareDisplayControllers::iterator original,
+                        HardwareDisplayControllers::iterator mirror,
+                        const scoped_refptr<DrmDevice>& drm,
+                        uint32_t crtc,
+                        uint32_t connector);
+
+  // Modeset the |controller| using |origin| and |mode|. If there is a window at
+  // the controller location, then we'll re-use the current buffer.
+  bool EnableController(HardwareDisplayController* controller,
+                        const gfx::Point& origin,
+                        const drmModeModeInfo& mode);
+
+  DrmWindow* FindWindowAt(const gfx::Rect& bounds) const;
+
+  ScanoutBufferGenerator* buffer_generator_;  // Not owned.
+  // List of display controllers (active and disabled).
+  HardwareDisplayControllers controllers_;
+
+  WidgetToWindowMap window_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_SCREEN_MANAGER_H_
diff --git a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
new file mode 100644
index 0000000..f8b59a6
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
@@ -0,0 +1,477 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+
+namespace {
+
+void EmptySwapCallback(gfx::SwapResult) {
+}
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kPrimaryCrtc = 1;
+const uint32_t kPrimaryConnector = 2;
+const uint32_t kSecondaryCrtc = 3;
+const uint32_t kSecondaryConnector = 4;
+
+}  // namespace
+
+class ScreenManagerTest : public testing::Test {
+ public:
+  ScreenManagerTest() {}
+  ~ScreenManagerTest() override {}
+
+  gfx::Rect GetPrimaryBounds() const {
+    return gfx::Rect(0, 0, kDefaultMode.hdisplay, kDefaultMode.vdisplay);
+  }
+
+  // Secondary is in extended mode, right-of primary.
+  gfx::Rect GetSecondaryBounds() const {
+    return gfx::Rect(kDefaultMode.hdisplay, 0, kDefaultMode.hdisplay,
+                     kDefaultMode.vdisplay);
+  }
+
+  void SetUp() override {
+    drm_ = new ui::MockDrmDevice();
+    device_manager_.reset(new ui::DrmDeviceManager(nullptr));
+    buffer_generator_.reset(new ui::DrmBufferGenerator());
+    screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
+  }
+  void TearDown() override {
+    screen_manager_.reset();
+    drm_ = nullptr;
+  }
+
+ protected:
+  scoped_refptr<ui::MockDrmDevice> drm_;
+  scoped_ptr<ui::DrmDeviceManager> device_manager_;
+  scoped_ptr<ui::DrmBufferGenerator> buffer_generator_;
+  scoped_ptr<ui::ScreenManager> screen_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScreenManagerTest);
+};
+
+TEST_F(ScreenManagerTest, CheckWithNoControllers) {
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckWithValidController) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  ui::HardwareDisplayController* controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+
+  EXPECT_TRUE(controller);
+  EXPECT_TRUE(controller->HasCrtc(drm_, kPrimaryCrtc));
+}
+
+TEST_F(ScreenManagerTest, CheckWithInvalidBounds) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckForSecondValidController) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+
+  screen_manager_->RemoveDisplayController(drm_, kPrimaryCrtc);
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  uint32_t framebuffer = drm_->current_framebuffer();
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  // Should not hold onto buffers.
+  EXPECT_NE(framebuffer, drm_->current_framebuffer());
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckChangingMode) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  drmModeModeInfo new_mode = kDefaultMode;
+  new_mode.vdisplay = 10;
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      new_mode);
+
+  gfx::Rect new_bounds(0, 0, new_mode.hdisplay, new_mode.vdisplay);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(new_bounds));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+  drmModeModeInfo mode =
+      screen_manager_->GetDisplayController(new_bounds)->get_mode();
+  EXPECT_EQ(new_mode.vdisplay, mode.vdisplay);
+  EXPECT_EQ(new_mode.hdisplay, mode.hdisplay);
+}
+
+TEST_F(ScreenManagerTest, CheckForControllersInMirroredMode) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetSecondaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  screen_manager_->RemoveDisplayController(drm_, kSecondaryCrtc);
+
+  ui::HardwareDisplayController* controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+  EXPECT_TRUE(controller);
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  EXPECT_TRUE(controller->HasCrtc(drm_, kPrimaryCrtc));
+  EXPECT_FALSE(controller->HasCrtc(drm_, kSecondaryCrtc));
+}
+
+TEST_F(ScreenManagerTest, MonitorDisabledInMirrorMode) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  screen_manager_->DisableDisplayController(drm_, kSecondaryCrtc);
+
+  ui::HardwareDisplayController* controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+  EXPECT_TRUE(controller);
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  EXPECT_TRUE(controller->HasCrtc(drm_, kPrimaryCrtc));
+  EXPECT_FALSE(controller->HasCrtc(drm_, kSecondaryCrtc));
+}
+
+TEST_F(ScreenManagerTest, DoNotEnterMirrorModeUnlessSameBounds) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+
+  // Configure displays in extended mode.
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetSecondaryBounds().origin(),
+      kDefaultMode);
+
+  drmModeModeInfo new_mode = kDefaultMode;
+  new_mode.vdisplay = 10;
+  // Shouldn't enter mirror mode unless the display bounds are the same.
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      new_mode);
+
+  EXPECT_FALSE(
+      screen_manager_->GetDisplayController(GetPrimaryBounds())->IsMirrored());
+}
+
+TEST_F(ScreenManagerTest, ReuseFramebufferIfDisabledThenReEnabled) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  uint32_t framebuffer = drm_->current_framebuffer();
+
+  screen_manager_->DisableDisplayController(drm_, kPrimaryCrtc);
+  EXPECT_EQ(0u, drm_->current_framebuffer());
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  // Buffers are released when disabled.
+  EXPECT_NE(framebuffer, drm_->current_framebuffer());
+}
+
+TEST_F(ScreenManagerTest, CheckMirrorModeAfterBeginReEnabled) {
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->DisableDisplayController(drm_, kPrimaryCrtc);
+
+  screen_manager_->AddDisplayController(drm_, kSecondaryCrtc,
+                                        kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kSecondaryCrtc, kSecondaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  ui::HardwareDisplayController* controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+  EXPECT_TRUE(controller);
+  EXPECT_FALSE(controller->IsMirrored());
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  EXPECT_TRUE(controller);
+  EXPECT_TRUE(controller->IsMirrored());
+}
+
+TEST_F(ScreenManagerTest,
+       CheckProperConfigurationWithDifferentDeviceAndSameCrtc) {
+  scoped_refptr<ui::MockDrmDevice> drm2 = new ui::MockDrmDevice();
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->AddDisplayController(drm2, kPrimaryCrtc, kPrimaryConnector);
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+  screen_manager_->ConfigureDisplayController(
+      drm2, kPrimaryCrtc, kPrimaryConnector, GetSecondaryBounds().origin(),
+      kDefaultMode);
+
+  ui::HardwareDisplayController* controller1 =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+  ui::HardwareDisplayController* controller2 =
+      screen_manager_->GetDisplayController(GetSecondaryBounds());
+
+  EXPECT_NE(controller1, controller2);
+  EXPECT_EQ(drm_, controller1->crtc_controllers()[0]->drm());
+  EXPECT_EQ(drm2, controller2->crtc_controllers()[0]->drm());
+}
+
+TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) {
+  scoped_ptr<ui::DrmWindow> window(
+      new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  window->OnBoundsChanged(GetPrimaryBounds());
+  screen_manager_->AddWindow(1, window.Pass());
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetWindow(1)->GetController());
+
+  window = screen_manager_->RemoveWindow(1);
+  window->Shutdown();
+}
+
+TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) {
+  scoped_ptr<ui::DrmWindow> window(
+      new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  gfx::Rect new_bounds = GetPrimaryBounds();
+  new_bounds.Inset(0, 0, 1, 1);
+  window->OnBoundsChanged(new_bounds);
+  screen_manager_->AddWindow(1, window.Pass());
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_FALSE(screen_manager_->GetWindow(1)->GetController());
+
+  window = screen_manager_->RemoveWindow(1);
+  window->Shutdown();
+}
+
+TEST_F(ScreenManagerTest,
+       CheckControllerToWindowMappingWithOverlappingWindows) {
+  const size_t kWindowCount = 2;
+  for (size_t i = 1; i < kWindowCount + 1; ++i) {
+    scoped_ptr<ui::DrmWindow> window(
+        new ui::DrmWindow(i, device_manager_.get(), screen_manager_.get()));
+    window->Initialize();
+    window->OnBoundsChanged(GetPrimaryBounds());
+    screen_manager_->AddWindow(i, window.Pass());
+  }
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  bool window1_has_controller = screen_manager_->GetWindow(1)->GetController();
+  bool window2_has_controller = screen_manager_->GetWindow(2)->GetController();
+  // Only one of the windows can have a controller.
+  EXPECT_TRUE(window1_has_controller ^ window2_has_controller);
+
+  for (size_t i = 1; i < kWindowCount + 1; ++i) {
+    scoped_ptr<ui::DrmWindow> window = screen_manager_->RemoveWindow(i);
+    window->Shutdown();
+  }
+}
+
+TEST_F(ScreenManagerTest, ShouldDissociateWindowOnControllerRemoval) {
+  gfx::AcceleratedWidget window_id = 1;
+  scoped_ptr<ui::DrmWindow> window(new ui::DrmWindow(
+      window_id, device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  window->OnBoundsChanged(GetPrimaryBounds());
+  screen_manager_->AddWindow(window_id, window.Pass());
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetWindow(window_id)->GetController());
+
+  screen_manager_->RemoveDisplayController(drm_, kPrimaryCrtc);
+
+  EXPECT_FALSE(screen_manager_->GetWindow(window_id)->GetController());
+
+  window = screen_manager_->RemoveWindow(1);
+  window->Shutdown();
+}
+
+TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasNoBuffer) {
+  scoped_ptr<ui::DrmWindow> window(
+      new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  window->OnBoundsChanged(GetPrimaryBounds());
+  screen_manager_->AddWindow(1, window.Pass());
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetWindow(1)->GetController());
+  // There is a buffer after initial config.
+  uint32_t framebuffer = drm_->current_framebuffer();
+  EXPECT_NE(0U, framebuffer);
+
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  // There is a new buffer after we configured with the same mode but no
+  // pending frames on the window.
+  EXPECT_NE(framebuffer, drm_->current_framebuffer());
+
+  window = screen_manager_->RemoveWindow(1);
+  window->Shutdown();
+}
+
+TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasBuffer) {
+  scoped_ptr<ui::DrmWindow> window(
+      new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
+  window->Initialize();
+  window->OnBoundsChanged(GetPrimaryBounds());
+  scoped_refptr<ui::ScanoutBuffer> buffer =
+      buffer_generator_->Create(drm_, GetPrimaryBounds().size());
+  window->QueueOverlayPlane(ui::OverlayPlane(buffer));
+  window->SchedulePageFlip(false /* is_sync */, base::Bind(&EmptySwapCallback));
+  screen_manager_->AddWindow(1, window.Pass());
+
+  screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(
+      drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(),
+      kDefaultMode);
+
+  EXPECT_EQ(buffer->GetFramebufferId(), drm_->current_framebuffer());
+
+  window = screen_manager_->RemoveWindow(1);
+  window->Shutdown();
+}
diff --git a/ui/ozone/platform/drm/host/channel_observer.h b/ui/ozone/platform/drm/host/channel_observer.h
new file mode 100644
index 0000000..ffbfa04
--- /dev/null
+++ b/ui/ozone/platform/drm/host/channel_observer.h
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_
+
+namespace ui {
+
+// Observes the channel state.
+class ChannelObserver {
+ public:
+  virtual ~ChannelObserver() {}
+
+  virtual void OnChannelEstablished() = 0;
+  virtual void OnChannelDestroyed() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_
diff --git a/ui/ozone/platform/drm/host/drm_cursor.cc b/ui/ozone/platform/drm/host/drm_cursor.cc
new file mode 100644
index 0000000..f1c2021
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_cursor.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_cursor.h"
+
+#include "base/thread_task_runner_handle.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/platform/drm/host/drm_window_host.h"
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+
+#if defined(OS_CHROMEOS)
+#include "ui/events/ozone/chromeos/cursor_controller.h"
+#endif
+
+namespace ui {
+
+DrmCursor::DrmCursor(DrmWindowHostManager* window_manager) {
+}
+
+DrmCursor::~DrmCursor() {
+}
+
+void DrmCursor::SetCursor(gfx::AcceleratedWidget window,
+                          PlatformCursor platform_cursor) {
+  //TODO
+}
+
+void DrmCursor::OnWindowAdded(gfx::AcceleratedWidget window,
+                              const gfx::Rect& bounds_in_screen,
+                              const gfx::Rect& cursor_confined_bounds) {
+  //TODO
+}
+
+void DrmCursor::OnWindowRemoved(gfx::AcceleratedWidget window) {
+  //TODO
+}
+
+void DrmCursor::CommitBoundsChange(
+    gfx::AcceleratedWidget window,
+    const gfx::Rect& new_display_bounds_in_screen,
+    const gfx::Rect& new_confined_bounds) {
+  //TODO
+}
+
+void DrmCursor::MoveCursorTo(gfx::AcceleratedWidget window,
+                             const gfx::PointF& location) {
+  //TODO
+}
+
+void DrmCursor::MoveCursorTo(const gfx::PointF& screen_location) {
+  //TODO
+}
+
+void DrmCursor::MoveCursor(const gfx::Vector2dF& delta) {
+  //TODO
+}
+
+bool DrmCursor::IsCursorVisible() {
+  //TODO
+  return false;
+}
+
+gfx::PointF DrmCursor::GetLocation() {
+  //TODO
+  return gfx::PointF();
+}
+
+gfx::Rect DrmCursor::GetCursorConfinedBounds() {
+  //TODO
+  return gfx::Rect();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_cursor.h b/ui/ozone/platform/drm/host/drm_cursor.h
new file mode 100644
index 0000000..b747fcb
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_cursor.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/platform_window/platform_window.h" // for PlatformCursor
+
+namespace gfx {
+class PointF;
+class Vector2dF;
+class Rect;
+}
+
+namespace ui {
+
+class BitmapCursorOzone;
+class BitmapCursorFactoryOzone;
+class DrmGpuPlatformSupportHost;
+class DrmWindowHostManager;
+
+class DrmCursor : public CursorDelegateEvdev {
+ public:
+  explicit DrmCursor(DrmWindowHostManager* window_manager);
+  ~DrmCursor() override;
+
+  // Change the cursor over the specifed window.
+  void SetCursor(gfx::AcceleratedWidget window, PlatformCursor platform_cursor);
+
+  // Handle window lifecycle.
+  void OnWindowAdded(gfx::AcceleratedWidget window,
+                     const gfx::Rect& bounds_in_screen,
+                     const gfx::Rect& cursor_confined_bounds);
+  void OnWindowRemoved(gfx::AcceleratedWidget window);
+
+  // Handle window bounds changes.
+  void CommitBoundsChange(gfx::AcceleratedWidget window,
+                          const gfx::Rect& new_display_bounds_in_screen,
+                          const gfx::Rect& new_confined_bounds);
+
+  // CursorDelegateEvdev:
+  void MoveCursorTo(gfx::AcceleratedWidget window,
+                    const gfx::PointF& location) override;
+  void MoveCursorTo(const gfx::PointF& screen_location) override;
+  void MoveCursor(const gfx::Vector2dF& delta) override;
+  bool IsCursorVisible() override;
+  gfx::PointF GetLocation() override;
+  gfx::Rect GetCursorConfinedBounds() override;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_
diff --git a/ui/ozone/platform/drm/host/drm_device_handle.cc b/ui/ozone/platform/drm/host/drm_device_handle.cc
new file mode 100644
index 0000000..8d9fc0c
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_device_handle.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_device_handle.h"
+
+#include <fcntl.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "base/files/file_path.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace ui {
+
+namespace {
+
+bool Authenticate(int fd) {
+  drm_magic_t magic;
+  memset(&magic, 0, sizeof(magic));
+  // We need to make sure the DRM device has enough privilege. Use the DRM
+  // authentication logic to figure out if the device has enough permissions.
+  return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
+}
+
+}  // namespace
+
+DrmDeviceHandle::DrmDeviceHandle() {
+}
+
+DrmDeviceHandle::~DrmDeviceHandle() {
+  if (file_.is_valid())
+    base::ThreadRestrictions::AssertIOAllowed();
+}
+
+bool DrmDeviceHandle::Initialize(const base::FilePath& path) {
+  CHECK(path.DirName() == base::FilePath("/dev/dri"));
+  base::ThreadRestrictions::AssertIOAllowed();
+  bool print_warning = true;
+  while (true) {
+    file_.reset();
+    int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_CLOEXEC));
+    if (fd < 0) {
+      PLOG(ERROR) << "Failed to open " << path.value();
+      return false;
+    }
+
+    file_.reset(fd);
+
+    if (Authenticate(file_.get()))
+      break;
+
+    LOG_IF(WARNING, print_warning) << "Failed to authenticate " << path.value();
+    print_warning = false;
+    usleep(100000);
+  }
+
+  VLOG(1) << "Succeeded authenticating " << path.value();
+  return true;
+}
+
+bool DrmDeviceHandle::IsValid() const {
+  return file_.is_valid();
+}
+
+base::ScopedFD DrmDeviceHandle::PassFD() {
+  return file_.Pass();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_device_handle.h b/ui/ozone/platform/drm/host/drm_device_handle.h
new file mode 100644
index 0000000..b360837
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_device_handle.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
+
+#include "base/files/scoped_file.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace ui {
+
+class DrmDeviceHandle {
+ public:
+  DrmDeviceHandle();
+  ~DrmDeviceHandle();
+
+  int fd() const { return file_.get(); }
+
+  bool Initialize(const base::FilePath& path);
+
+  bool IsValid() const;
+  base::ScopedFD PassFD();
+
+ private:
+  base::ScopedFD file_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDeviceHandle);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
diff --git a/ui/ozone/platform/drm/host/drm_display_host.cc b/ui/ozone/platform/drm/host/drm_display_host.cc
new file mode 100644
index 0000000..c09b070
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_display_host.cc
@@ -0,0 +1,130 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_display_host.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/ozone/common/display_snapshot_proxy.h"
+#include "ui/ozone/common/display_util.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+
+namespace ui {
+
+DrmDisplayHost::DrmDisplayHost(DrmGpuPlatformSupportHost* sender,
+                               const DisplaySnapshot_Params& params,
+                               bool is_dummy)
+    : sender_(sender),
+      snapshot_(new DisplaySnapshotProxy(params)),
+      is_dummy_(is_dummy) {
+  sender_->AddChannelObserver(this);
+}
+
+DrmDisplayHost::~DrmDisplayHost() {
+  sender_->RemoveChannelObserver(this);
+  ClearCallbacks();
+}
+
+void DrmDisplayHost::UpdateDisplaySnapshot(
+    const DisplaySnapshot_Params& params) {
+  snapshot_ = make_scoped_ptr(new DisplaySnapshotProxy(params));
+}
+
+void DrmDisplayHost::Configure(const DisplayMode* mode,
+                               const gfx::Point& origin,
+                               const ConfigureCallback& callback) {
+  if (is_dummy_) {
+    callback.Run(true);
+    return;
+  }
+
+  configure_callback_ = callback;
+  bool status = false;
+  if (mode) {
+    status = sender_->ConfigureNativeDisplay(
+        snapshot_->display_id(), GetDisplayModeParams(*mode), origin);
+  } else {
+    status = sender_->DisableNativeDisplay(snapshot_->display_id());
+  }
+
+  if (!status)
+    OnDisplayConfigured(false);
+}
+
+void DrmDisplayHost::OnDisplayConfigured(bool status) {
+  if (!configure_callback_.is_null()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(configure_callback_, status));
+  } else {
+    LOG(ERROR) << "Got unexpected event for display "
+               << snapshot_->display_id();
+  }
+
+  configure_callback_.Reset();
+}
+
+void DrmDisplayHost::GetHDCPState(const GetHDCPStateCallback& callback) {
+  get_hdcp_callback_ = callback;
+  if (!sender_->GetHDCPState(snapshot_->display_id()))
+    OnHDCPStateReceived(false, HDCP_STATE_UNDESIRED);
+}
+
+void DrmDisplayHost::OnHDCPStateReceived(bool status, HDCPState state) {
+  if (!get_hdcp_callback_.is_null()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(get_hdcp_callback_, status, state));
+  } else {
+    LOG(ERROR) << "Got unexpected event for display "
+               << snapshot_->display_id();
+  }
+
+  get_hdcp_callback_.Reset();
+}
+
+void DrmDisplayHost::SetHDCPState(HDCPState state,
+                                  const SetHDCPStateCallback& callback) {
+  set_hdcp_callback_ = callback;
+  if (!sender_->SetHDCPState(snapshot_->display_id(), state))
+    OnHDCPStateUpdated(false);
+}
+
+void DrmDisplayHost::OnHDCPStateUpdated(bool status) {
+  if (!set_hdcp_callback_.is_null()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(set_hdcp_callback_, status));
+  } else {
+    LOG(ERROR) << "Got unexpected event for display "
+               << snapshot_->display_id();
+  }
+
+  set_hdcp_callback_.Reset();
+}
+
+void DrmDisplayHost::SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut) {
+  sender_->SetGammaRamp(snapshot_->display_id(), lut);
+}
+
+void DrmDisplayHost::OnChannelEstablished() {
+  is_dummy_ = false;
+
+  // Note: These responses are done here since the OnChannelDestroyed() is
+  // called after OnChannelEstablished().
+  ClearCallbacks();
+}
+
+void DrmDisplayHost::OnChannelDestroyed() {
+}
+
+void DrmDisplayHost::ClearCallbacks() {
+  if (!configure_callback_.is_null())
+    OnDisplayConfigured(false);
+  if (!get_hdcp_callback_.is_null())
+    OnHDCPStateReceived(false, HDCP_STATE_UNDESIRED);
+  if (!set_hdcp_callback_.is_null())
+    OnHDCPStateUpdated(false);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_display_host.h b/ui/ozone/platform/drm/host/drm_display_host.h
new file mode 100644
index 0000000..b6c09bf
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_display_host.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/ozone/platform/drm/host/channel_observer.h"
+
+namespace ui {
+
+struct DisplaySnapshot_Params;
+class DisplaySnapshot;
+class DrmGpuPlatformSupportHost;
+
+class DrmDisplayHost : public ChannelObserver {
+ public:
+  DrmDisplayHost(DrmGpuPlatformSupportHost* sender,
+                 const DisplaySnapshot_Params& params,
+                 bool is_dummy);
+  ~DrmDisplayHost() override;
+
+  DisplaySnapshot* snapshot() const { return snapshot_.get(); }
+
+  void UpdateDisplaySnapshot(const DisplaySnapshot_Params& params);
+  void Configure(const DisplayMode* mode,
+                 const gfx::Point& origin,
+                 const ConfigureCallback& callback);
+  void GetHDCPState(const GetHDCPStateCallback& callback);
+  void SetHDCPState(HDCPState state, const SetHDCPStateCallback& callback);
+  void SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut);
+
+  // Called when the IPC from the GPU process arrives to answer the above
+  // commands.
+  void OnDisplayConfigured(bool status);
+  void OnHDCPStateReceived(bool status, HDCPState state);
+  void OnHDCPStateUpdated(bool status);
+
+  // ChannelObserver:
+  void OnChannelEstablished() override;
+  void OnChannelDestroyed() override;
+
+ private:
+  // Calls all the callbacks with failure.
+  void ClearCallbacks();
+
+  DrmGpuPlatformSupportHost* sender_;  // Not owned.
+
+  scoped_ptr<DisplaySnapshot> snapshot_;
+
+  // Used during startup to signify that any display configuration should be
+  // synchronous and succeed.
+  bool is_dummy_;
+
+  ConfigureCallback configure_callback_;
+  GetHDCPStateCallback get_hdcp_callback_;
+  SetHDCPStateCallback set_hdcp_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDisplayHost);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_H_
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
new file mode 100644
index 0000000..3fa038d
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -0,0 +1,461 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+
+#include <fcntl.h>
+#include <xf86drm.h>
+
+#include "base/files/file_enumerator.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/worker_pool.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/events/ozone/device/device_event.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/ozone/common/display_util.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/host/drm_device_handle.h"
+#include "ui/ozone/platform/drm/host/drm_display_host.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
+
+namespace ui {
+
+namespace {
+
+typedef base::Callback<void(const base::FilePath&, scoped_ptr<DrmDeviceHandle>)>
+    OnOpenDeviceReplyCallback;
+
+const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d";
+const char kVgemDevDriCardPath[] = "/dev/dri/";
+const char kVgemSysCardPath[] = "/sys/bus/platform/devices/vgem/drm/";
+
+const char* kDisplayActionString[] = {
+    "ADD",
+    "REMOVE",
+    "CHANGE",
+};
+
+void OpenDeviceOnWorkerThread(
+    const base::FilePath& path,
+    const scoped_refptr<base::TaskRunner>& reply_runner,
+    const OnOpenDeviceReplyCallback& callback) {
+  scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle());
+  handle->Initialize(path);
+  reply_runner->PostTask(
+      FROM_HERE, base::Bind(callback, path, base::Passed(handle.Pass())));
+}
+
+base::FilePath GetPrimaryDisplayCardPath() {
+  struct drm_mode_card_res res;
+  for (int i = 0; /* end on first card# that does not exist */; i++) {
+    std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i);
+
+    if (access(card_path.c_str(), F_OK) != 0) {
+      LOG(WARNING) << "Can't access card: " << card_path;
+      break;
+    }
+
+
+    int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC);
+    if (fd < 0) {
+      VPLOG(1) << "Failed to open '" << card_path << "'";
+      continue;
+    }
+
+    memset(&res, 0, sizeof(struct drm_mode_card_res));
+    int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
+    close(fd);
+    if (ret == 0 && res.count_crtcs > 0) {
+      return base::FilePath(card_path);
+    }
+
+    VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'";
+  }
+
+  return base::FilePath();
+}
+
+base::FilePath GetVgemCardPath() {
+  base::FileEnumerator file_iter(base::FilePath(kVgemSysCardPath), false,
+                                 base::FileEnumerator::DIRECTORIES,
+                                 FILE_PATH_LITERAL("card*"));
+
+  while (!file_iter.Next().empty()) {
+    // Inspect the card%d directories in the directory and extract the filename.
+    std::string vgem_card_path =
+        kVgemDevDriCardPath + file_iter.GetInfo().GetName().BaseName().value();
+    DVLOG(1) << "VGEM card path is " << vgem_card_path;
+    return base::FilePath(vgem_card_path);
+  }
+  DVLOG(1) << "Don't support VGEM";
+  return base::FilePath();
+}
+
+class FindDrmDisplayHostById {
+ public:
+  explicit FindDrmDisplayHostById(int64_t display_id)
+      : display_id_(display_id) {}
+
+  bool operator()(const DrmDisplayHost* display) const {
+    return display->snapshot()->display_id() == display_id_;
+  }
+
+ private:
+  int64_t display_id_;
+};
+
+}  // namespace
+
+DrmDisplayHostManager::DrmDisplayHostManager(
+    DrmGpuPlatformSupportHost* proxy,
+    DeviceManager* device_manager,
+    InputControllerEvdev* input_controller)
+    : proxy_(proxy),
+      device_manager_(device_manager),
+      input_controller_(input_controller),
+      primary_graphics_card_path_(GetPrimaryDisplayCardPath()),
+      weak_ptr_factory_(this) {
+  {
+    // First device needs to be treated specially. We need to open this
+    // synchronously since the GPU process will need it to initialize the
+    // graphics state.
+    primary_drm_device_handle_.reset(new DrmDeviceHandle());
+    if (!primary_drm_device_handle_->Initialize(primary_graphics_card_path_)) {
+      LOG(FATAL) << "Failed to open primary graphics card";
+      return;
+    }
+    drm_devices_.insert(primary_graphics_card_path_);
+
+    vgem_card_path_ = GetVgemCardPath();
+    if (!vgem_card_path_.empty()) {
+      int fd = HANDLE_EINTR(
+          open(vgem_card_path_.value().c_str(), O_RDWR | O_CLOEXEC));
+      if (fd < 0) {
+        PLOG(ERROR) << "Failed to open vgem: " << vgem_card_path_.value();
+      }
+      vgem_card_device_file_.reset(fd);
+    }
+  }
+
+  device_manager_->AddObserver(this);
+
+  ScopedVector<HardwareDisplayControllerInfo> display_infos =
+      GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd());
+  has_dummy_display_ = !display_infos.empty();
+  for (size_t i = 0; i < display_infos.size(); ++i) {
+    displays_.push_back(new DrmDisplayHost(
+        proxy_, CreateDisplaySnapshotParams(display_infos[i],
+                                            primary_drm_device_handle_->fd(), i,
+                                            gfx::Point()),
+        true /* is_dummy */));
+  }
+}
+
+DrmDisplayHostManager::~DrmDisplayHostManager() {
+  device_manager_->RemoveObserver(this);
+}
+
+DrmDisplayHost* DrmDisplayHostManager::GetDisplay(int64_t display_id) {
+  auto it = std::find_if(displays_.begin(), displays_.end(),
+                         FindDrmDisplayHostById(display_id));
+  if (it == displays_.end())
+    return nullptr;
+
+  return *it;
+}
+
+void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate* delegate) {
+  DCHECK(!delegate_);
+  delegate_ = delegate;
+}
+
+void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate* delegate) {
+  DCHECK_EQ(delegate_, delegate);
+  delegate_ = nullptr;
+}
+
+void DrmDisplayHostManager::TakeDisplayControl(
+    const DisplayControlCallback& callback) {
+  if (display_control_change_pending_) {
+    LOG(ERROR) << "TakeDisplayControl called while change already pending";
+    callback.Run(false);
+    return;
+  }
+
+  if (!display_externally_controlled_) {
+    LOG(ERROR) << "TakeDisplayControl called while display already owned";
+    callback.Run(true);
+    return;
+  }
+
+  take_display_control_callback_ = callback;
+  display_control_change_pending_ = true;
+
+  if (!proxy_->TakeDisplayControl())
+     OnTakeDisplayControl(false);
+}
+
+void DrmDisplayHostManager::RelinquishDisplayControl(
+    const DisplayControlCallback& callback) {
+  if (display_control_change_pending_) {
+    LOG(ERROR)
+        << "RelinquishDisplayControl called while change already pending";
+    callback.Run(false);
+    return;
+  }
+
+  if (display_externally_controlled_) {
+    LOG(ERROR) << "RelinquishDisplayControl called while display not owned";
+    callback.Run(true);
+    return;
+  }
+
+  relinquish_display_control_callback_ = callback;
+  display_control_change_pending_ = true;
+
+  if (!proxy_->RelinquishDisplayControl())
+    OnRelinquishDisplayControl(false);
+}
+
+void DrmDisplayHostManager::UpdateDisplays(
+    const GetDisplaysCallback& callback) {
+  get_displays_callback_ = callback;
+  if (!proxy_->RefreshNativeDisplays()) {
+    get_displays_callback_.Reset();
+    RunUpdateDisplaysCallback(callback);
+  }
+}
+
+void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) {
+  if (event.device_type() != DeviceEvent::DISPLAY)
+    return;
+
+  event_queue_.push(DisplayEvent(event.action_type(), event.path()));
+  ProcessEvent();
+}
+
+void DrmDisplayHostManager::ProcessEvent() {
+  while (!event_queue_.empty() && !task_pending_) {
+    DisplayEvent event = event_queue_.front();
+    event_queue_.pop();
+    VLOG(1) << "Got display event " << kDisplayActionString[event.action_type]
+            << " for " << event.path.value();
+    switch (event.action_type) {
+      case DeviceEvent::ADD:
+        if (event.path == vgem_card_path_)
+          continue;
+        if (drm_devices_.find(event.path) == drm_devices_.end()) {
+          task_pending_ = base::WorkerPool::PostTask(
+              FROM_HERE,
+              base::Bind(&OpenDeviceOnWorkerThread, event.path,
+                         base::ThreadTaskRunnerHandle::Get(),
+                         base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice,
+                                    weak_ptr_factory_.GetWeakPtr())),
+              false /* task_is_slow */);
+        }
+        break;
+      case DeviceEvent::CHANGE:
+        task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE,
+            base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice,
+                       weak_ptr_factory_.GetWeakPtr()));
+        break;
+      case DeviceEvent::REMOVE:
+        DCHECK(event.path != primary_graphics_card_path_)
+            << "Removing primary graphics card";
+        DCHECK(event.path != vgem_card_path_) << "Removing VGEM device";
+        auto it = drm_devices_.find(event.path);
+        if (it != drm_devices_.end()) {
+          task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
+              FROM_HERE,
+              base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice,
+                         weak_ptr_factory_.GetWeakPtr(), event.path));
+          drm_devices_.erase(it);
+        }
+        break;
+    }
+  }
+}
+
+void DrmDisplayHostManager::OnAddGraphicsDevice(
+    const base::FilePath& path,
+    scoped_ptr<DrmDeviceHandle> handle) {
+  if (handle->IsValid()) {
+    drm_devices_.insert(path);
+    proxy_->AddGraphicsDevice(path, base::FileDescriptor(handle->PassFD()));
+    NotifyDisplayDelegate();
+  }
+
+  task_pending_ = false;
+  ProcessEvent();
+}
+
+void DrmDisplayHostManager::OnUpdateGraphicsDevice() {
+  NotifyDisplayDelegate();
+  task_pending_ = false;
+  ProcessEvent();
+}
+
+void DrmDisplayHostManager::OnRemoveGraphicsDevice(const base::FilePath& path) {
+  proxy_->RemoveGraphicsDevice(path);
+  NotifyDisplayDelegate();
+  task_pending_ = false;
+  ProcessEvent();
+}
+
+void DrmDisplayHostManager::OnChannelEstablished(int host_id) {
+  // If in the middle of a configuration, just respond with the old list of
+  // displays. This is fine, since after the DRM resources are initialized and
+  // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display
+  // delegate know that the display configuration changed and it needs to
+  // update it again.
+  if (!get_displays_callback_.is_null()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
+                   weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
+    get_displays_callback_.Reset();
+  }
+
+  // Signal that we're taking DRM master since we're going through the
+  // initialization process again and we'll take all the available resources.
+  if (!take_display_control_callback_.is_null())
+    OnTakeDisplayControl(true);
+
+  if (!relinquish_display_control_callback_.is_null())
+    OnRelinquishDisplayControl(false);
+
+  drm_devices_.clear();
+  drm_devices_.insert(primary_graphics_card_path_);
+  scoped_ptr<DrmDeviceHandle> handle = primary_drm_device_handle_.Pass();
+  if (!handle) {
+    handle.reset(new DrmDeviceHandle());
+    if (!handle->Initialize(primary_graphics_card_path_))
+      LOG(FATAL) << "Failed to open primary graphics card";
+  }
+
+  // Send the primary device first since this is used to initialize graphics
+  // state.
+  proxy_->AddGraphicsDevice(primary_graphics_card_path_,
+                            base::FileDescriptor(handle->PassFD()));
+
+  device_manager_->ScanDevices(this);
+  NotifyDisplayDelegate();
+}
+
+void DrmDisplayHostManager::OnChannelDestroyed(int host_id) {
+  // Do nothing.
+}
+
+void DrmDisplayHostManager::OnUpdateNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& params) {
+  ScopedVector<DrmDisplayHost> old_displays(displays_.Pass());
+  for (size_t i = 0; i < params.size(); ++i) {
+    auto it = std::find_if(old_displays.begin(), old_displays.end(),
+                           FindDrmDisplayHostById(params[i].display_id));
+    if (it == old_displays.end()) {
+      displays_.push_back(
+          new DrmDisplayHost(proxy_, params[i], false /* is_dummy */));
+    } else {
+      (*it)->UpdateDisplaySnapshot(params[i]);
+      displays_.push_back(*it);
+      old_displays.weak_erase(it);
+    }
+  }
+
+  if (!get_displays_callback_.is_null()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
+                   weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
+    get_displays_callback_.Reset();
+  }
+}
+
+void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id,
+                                                bool status) {
+  DrmDisplayHost* display = GetDisplay(display_id);
+  if (display)
+    display->OnDisplayConfigured(status);
+  else
+    LOG(ERROR) << "Couldn't find display with id=" << display_id;
+}
+
+void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id,
+                                                bool status,
+                                                HDCPState state) {
+  DrmDisplayHost* display = GetDisplay(display_id);
+  if (display)
+    display->OnHDCPStateReceived(status, state);
+  else
+    LOG(ERROR) << "Couldn't find display with id=" << display_id;
+}
+
+void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id,
+                                               bool status) {
+  DrmDisplayHost* display = GetDisplay(display_id);
+  if (display)
+    display->OnHDCPStateUpdated(status);
+  else
+    LOG(ERROR) << "Couldn't find display with id=" << display_id;
+}
+
+void DrmDisplayHostManager::OnTakeDisplayControl(bool status) {
+  if (take_display_control_callback_.is_null()) {
+    LOG(ERROR) << "No callback for take display control";
+    return;
+  }
+
+  DCHECK(display_externally_controlled_);
+  DCHECK(display_control_change_pending_);
+
+  if (status) {
+    input_controller_->SetInputDevicesEnabled(true);
+    display_externally_controlled_ = false;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(take_display_control_callback_, status));
+  take_display_control_callback_.Reset();
+  display_control_change_pending_ = false;
+}
+
+void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status) {
+  if (relinquish_display_control_callback_.is_null()) {
+    LOG(ERROR) << "No callback for relinquish display control";
+    return;
+  }
+
+  DCHECK(!display_externally_controlled_);
+  DCHECK(display_control_change_pending_);
+
+  if (status) {
+    input_controller_->SetInputDevicesEnabled(false);
+    display_externally_controlled_ = true;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(relinquish_display_control_callback_, status));
+  relinquish_display_control_callback_.Reset();
+  display_control_change_pending_ = false;
+}
+
+void DrmDisplayHostManager::RunUpdateDisplaysCallback(
+    const GetDisplaysCallback& callback) const {
+  std::vector<DisplaySnapshot*> snapshots;
+  for (auto* display : displays_)
+    snapshots.push_back(display->snapshot());
+
+  callback.Run(snapshots);
+}
+
+void DrmDisplayHostManager::NotifyDisplayDelegate() const {
+  if (delegate_)
+    delegate_->OnConfigurationChanged();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.h b/ui/ozone/platform/drm/host/drm_display_host_manager.h
new file mode 100644
index 0000000..bacba0b
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.h
@@ -0,0 +1,140 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_H_
+
+#include <queue>
+#include <set>
+
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/events/ozone/device/device_event.h"
+#include "ui/events/ozone/device/device_event_observer.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+
+namespace ui {
+
+class DeviceManager;
+class DrmDeviceHandle;
+class DrmDisplayHost;
+class DrmGpuPlatformSupportHost;
+class DrmNativeDisplayDelegate;
+
+struct DisplaySnapshot_Params;
+
+class DrmDisplayHostManager : public DeviceEventObserver {
+ public:
+  // Note: IO is performed in this constructor.
+  DrmDisplayHostManager(DrmGpuPlatformSupportHost* proxy,
+                        DeviceManager* device_manager,
+                        InputControllerEvdev* input_controller);
+  ~DrmDisplayHostManager() override;
+
+  DrmDisplayHost* GetDisplay(int64_t display_id);
+
+  void AddDelegate(DrmNativeDisplayDelegate* delegate);
+  void RemoveDelegate(DrmNativeDisplayDelegate* delegate);
+
+  void TakeDisplayControl(const DisplayControlCallback& callback);
+  void RelinquishDisplayControl(const DisplayControlCallback& callback);
+  void UpdateDisplays(const GetDisplaysCallback& callback);
+
+  // DeviceEventObserver overrides:
+  void OnDeviceEvent(const DeviceEvent& event) override;
+
+  // Note: IO is performed in OnChannelEstablished.
+  void OnChannelEstablished(int host_id);
+  void OnChannelDestroyed(int host_id);
+
+  void OnUpdateNativeDisplays(
+      const std::vector<DisplaySnapshot_Params>& displays);
+  void OnDisplayConfigured(int64_t display_id, bool status);
+
+  // Called as a result of finishing to process the display hotplug event. These
+  // are responsible for dequing the event and scheduling the next event.
+  void OnAddGraphicsDevice(const base::FilePath& path,
+                           scoped_ptr<DrmDeviceHandle> handle);
+  void OnUpdateGraphicsDevice();
+  void OnRemoveGraphicsDevice(const base::FilePath& path);
+
+  void OnHDCPStateReceived(int64_t display_id, bool status, HDCPState state);
+  void OnHDCPStateUpdated(int64_t display_id, bool status);
+
+  void OnTakeDisplayControl(bool status);
+  void OnRelinquishDisplayControl(bool status);
+
+ private:
+  struct DisplayEvent {
+    DisplayEvent(DeviceEvent::ActionType action_type,
+                 const base::FilePath& path)
+        : action_type(action_type), path(path) {}
+
+    DeviceEvent::ActionType action_type;
+    base::FilePath path;
+  };
+
+
+  void ProcessEvent();
+
+  void RunUpdateDisplaysCallback(const GetDisplaysCallback& callback) const;
+
+  void NotifyDisplayDelegate() const;
+
+  DrmGpuPlatformSupportHost* proxy_;  // Not owned.
+  DeviceManager* device_manager_;     // Not owned.
+  InputControllerEvdev* input_controller_;  // Not owned.
+
+  DrmNativeDisplayDelegate* delegate_ = nullptr;  // Not owned.
+
+  // File path for the primary graphics card which is opened by default in the
+  // GPU process. We'll avoid opening this in hotplug events since it will race
+  // with the GPU process trying to open it and aquire DRM master.
+  base::FilePath primary_graphics_card_path_;
+
+  // File path for virtual gem (VGEM) device.
+  base::FilePath vgem_card_path_;
+
+  // Keeps track if there is a dummy display. This happens on initialization
+  // when there is no connection to the GPU to update the displays.
+  bool has_dummy_display_ = false;
+
+  ScopedVector<DrmDisplayHost> displays_;
+
+  GetDisplaysCallback get_displays_callback_;
+
+  bool display_externally_controlled_ = false;
+  bool display_control_change_pending_ = false;
+  DisplayControlCallback take_display_control_callback_;
+  DisplayControlCallback relinquish_display_control_callback_;
+
+  // Used to serialize display event processing. This is done since
+  // opening/closing DRM devices cannot be done on the UI thread and are handled
+  // on a worker thread. Thus, we need to queue events in order to process them
+  // in the correct order.
+  std::queue<DisplayEvent> event_queue_;
+
+  // True if a display event is currently being processed on a worker thread.
+  bool task_pending_ = false;
+
+  // Keeps track of all the active DRM devices.
+  std::set<base::FilePath> drm_devices_;
+
+  // This is used to cache the primary DRM device until the channel is
+  // established.
+  scoped_ptr<DrmDeviceHandle> primary_drm_device_handle_;
+
+  // Manages the VGEM device by itself and doesn't send it to GPU process.
+  base::ScopedFD vgem_card_device_file_;
+
+  base::WeakPtrFactory<DrmDisplayHostManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmDisplayHostManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_H_
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
new file mode 100644
index 0000000..47f9f4f
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+
+#include "base/trace_event/trace_event.h"
+#include "ui/ozone/platform/drm/host/channel_observer.h"
+
+namespace ui {
+
+DrmGpuPlatformSupportHost::DrmGpuPlatformSupportHost(DrmCursor* cursor)
+  : display_manager_(nullptr),
+    window_manager_(nullptr),
+    cursor_(cursor),
+    delegate_(nullptr) {
+}
+
+DrmGpuPlatformSupportHost::~DrmGpuPlatformSupportHost() {
+}
+
+void DrmGpuPlatformSupportHost::SetDisplayManager(
+    DrmDisplayHostManager* display_manager) {
+  DCHECK(!display_manager_);
+  display_manager_ = display_manager;
+  if (IsConnected()) {
+    display_manager_->OnChannelEstablished(host_id_);
+  }
+}
+
+void DrmGpuPlatformSupportHost::SetWindowManager(
+    DrmWindowHostManager* window_manager) {
+  DCHECK(!window_manager_);
+  window_manager_ = window_manager;
+}
+
+void DrmGpuPlatformSupportHost::AddChannelObserver(ChannelObserver* observer) {
+  channel_observers_.AddObserver(observer);
+
+  if (IsConnected())
+    observer->OnChannelEstablished();
+}
+
+void DrmGpuPlatformSupportHost::RemoveChannelObserver(
+    ChannelObserver* observer) {
+  channel_observers_.RemoveObserver(observer);
+}
+
+bool DrmGpuPlatformSupportHost::IsConnected() {
+  return host_id_ >= 0;
+}
+
+void DrmGpuPlatformSupportHost::OnChannelEstablished(int host_id) {
+  TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnChannelEstablished",
+               "host_id", host_id);
+
+  host_id_ = host_id;
+
+  if (display_manager_) {
+    display_manager_->OnChannelEstablished(host_id_);
+  }
+
+  FOR_EACH_OBSERVER(ChannelObserver, channel_observers_,
+                    OnChannelEstablished());
+}
+
+void DrmGpuPlatformSupportHost::OnChannelDestroyed(int host_id) {
+   TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnChannelDestroyed",
+                "host_id", host_id);
+
+   if (host_id_ == host_id) {
+     host_id_ = -1;
+     FOR_EACH_OBSERVER(ChannelObserver, channel_observers_,
+                       OnChannelDestroyed());
+   }
+}
+
+void DrmGpuPlatformSupportHost::CreateWindow(
+    const gfx::AcceleratedWidget& widget) {
+  DCHECK(delegate_);
+  delegate_->CreateWindow(widget);
+}
+
+void DrmGpuPlatformSupportHost::DestroyWindow(
+    const gfx::AcceleratedWidget& widget) {
+  NOTIMPLEMENTED();
+}
+
+void DrmGpuPlatformSupportHost::WindowBoundsChanged(
+    const gfx::AcceleratedWidget& widget,
+    const gfx::Rect& bounds) {
+  DCHECK(delegate_);
+  delegate_->WindowBoundsChanged(widget, bounds);
+}
+
+void DrmGpuPlatformSupportHost::AddGraphicsDevice(
+    const base::FilePath& path,
+    const base::FileDescriptor& fd) {
+  DCHECK(delegate_);
+  delegate_->AddGraphicsDevice(path, fd);
+}
+
+void DrmGpuPlatformSupportHost::RemoveGraphicsDevice(
+    const base::FilePath& path) {
+  NOTIMPLEMENTED();
+}
+
+bool DrmGpuPlatformSupportHost::RefreshNativeDisplays() {
+  DCHECK(delegate_);
+  return delegate_->RefreshNativeDisplays();
+}
+
+bool DrmGpuPlatformSupportHost::ConfigureNativeDisplay(
+    int64_t id, const DisplayMode_Params& mode, const gfx::Point& originhost) {
+  DCHECK(delegate_);
+  return delegate_->ConfigureNativeDisplay(id, mode, originhost);
+}
+
+bool DrmGpuPlatformSupportHost::DisableNativeDisplay(int64_t id) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+void DrmGpuPlatformSupportHost::CheckOverlayCapabilities(
+  const gfx::AcceleratedWidget& widget,
+  const std::vector<OverlayCheck_Params>& list) {
+  NOTIMPLEMENTED();
+}
+
+bool DrmGpuPlatformSupportHost::GetHDCPState(int64_t id) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool DrmGpuPlatformSupportHost::SetHDCPState(int64_t id,
+                                             const HDCPState& state) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool DrmGpuPlatformSupportHost::TakeDisplayControl() {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool DrmGpuPlatformSupportHost::RelinquishDisplayControl() {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool DrmGpuPlatformSupportHost::SetGammaRamp(
+    int64_t id, const std::vector<GammaRampRGBEntry>& lut) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
new file mode 100644
index 0000000..b9745bf
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_PLATFORM_SUPPORT_HOST_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_PLATFORM_SUPPORT_HOST_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/file_descriptor_posix.h"
+#include "base/observer_list.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class ChannelObserver;
+class DrmCursor;
+
+class DrmGpuPlatformSupportHostDelegate {
+public:
+  DrmGpuPlatformSupportHostDelegate() {}
+  virtual ~DrmGpuPlatformSupportHostDelegate() {}
+
+  virtual void CreateWindow(const gfx::AcceleratedWidget& widget) = 0;
+  virtual void WindowBoundsChanged(const gfx::AcceleratedWidget& widget,
+                                   const gfx::Rect& bounds) = 0;
+  virtual void AddGraphicsDevice(const base::FilePath& path,
+                                 const base::FileDescriptor& fd) = 0;
+  virtual bool RefreshNativeDisplays() = 0;
+  virtual bool ConfigureNativeDisplay(int64_t id,
+                                      const DisplayMode_Params& mode,
+                                      const gfx::Point& originhost) = 0;
+};
+
+
+class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost {
+ public:
+  DrmGpuPlatformSupportHost(DrmCursor* cursor);
+  ~DrmGpuPlatformSupportHost() override;
+
+  void SetDisplayManager(DrmDisplayHostManager* display_manager);
+  void SetWindowManager(DrmWindowHostManager* window_manager);
+
+  DrmDisplayHostManager* get_display_manager() {
+    return display_manager_;
+  }
+  DrmWindowHostManager* get_window_manager() {
+    return window_manager_;
+  }
+
+  void SetDelegate(DrmGpuPlatformSupportHostDelegate* delegate) {
+    DCHECK(!delegate_);
+    delegate_ = delegate;
+  }
+
+  DrmGpuPlatformSupportHostDelegate* get_delegate() {
+    return delegate_;
+  }
+
+  void AddChannelObserver(ChannelObserver* observer);
+  void RemoveChannelObserver(ChannelObserver* observer);
+
+  bool IsConnected();
+
+  // GpuPlatformSupportHost:
+  void OnChannelEstablished(int host_id) override;
+  void OnChannelDestroyed(int host_id) override;
+
+  // host to gpu interfaces
+  void CreateWindow(const gfx::AcceleratedWidget& widget);
+  void DestroyWindow(const gfx::AcceleratedWidget& widget);
+  void WindowBoundsChanged(const gfx::AcceleratedWidget& widget,
+                           const gfx::Rect& bounds);
+  void AddGraphicsDevice(const base::FilePath& path,
+                         const base::FileDescriptor& fd);
+  void RemoveGraphicsDevice(const base::FilePath& path);
+  bool RefreshNativeDisplays();
+  bool ConfigureNativeDisplay(int64_t id,
+                              const DisplayMode_Params& mode,
+                              const gfx::Point& originhost);
+  bool DisableNativeDisplay(int64_t id);
+
+  void CheckOverlayCapabilities(const gfx::AcceleratedWidget& widget,
+                                const std::vector<OverlayCheck_Params>& list);
+
+  bool GetHDCPState(int64_t id);
+  bool SetHDCPState(int64_t id, const HDCPState& state);
+
+  bool TakeDisplayControl();
+  bool RelinquishDisplayControl();
+
+  bool SetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut);
+
+ private:
+  int host_id_ = -1;
+
+  DrmDisplayHostManager* display_manager_;           // Not owned
+  DrmWindowHostManager* window_manager_;           // Not owned
+  DrmCursor* cursor_;                              // Not owned.
+  base::ObserverList<ChannelObserver> channel_observers_;
+  DrmGpuPlatformSupportHostDelegate* delegate_;
+
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_GPU_DRM_GPU_PLATFORM_SUPPORT_HOST_H_
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.cc
new file mode 100644
index 0000000..248427b
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+DrmGpuPlatformSupportHostInprocess::DrmGpuPlatformSupportHostInprocess()
+  : platform_support_(static_cast<DrmGpuPlatformSupportHost*>(
+      ui::OzonePlatform::GetInstance()->GetGpuPlatformSupportHost())) {
+  platform_support_->SetDelegate(this);
+}
+
+DrmGpuPlatformSupportHostInprocess::~DrmGpuPlatformSupportHostInprocess() {
+}
+
+void DrmGpuPlatformSupportHostInprocess::OnChannelEstablished(
+    int host_id,
+    scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+    const base::Callback<void(Message*)>& send_callback) {
+
+  send_runner_ = send_runner;
+  send_callback_ = send_callback;
+
+  platform_support_->OnChannelEstablished(host_id);
+}
+
+void DrmGpuPlatformSupportHostInprocess::OnChannelDestroyed(int host_id) {
+  send_runner_ = nullptr;
+  send_callback_.Reset();
+
+  platform_support_->OnChannelDestroyed(host_id);
+}
+
+bool DrmGpuPlatformSupportHostInprocess::OnMessageReceived(
+    const Message& message) {
+  DrmDisplayHostManager* display_manager =
+    platform_support_->get_display_manager();
+  DCHECK(display_manager);
+
+  bool handled = true;
+
+  switch (message.id) {
+    case OZONE_HOST_MSG__UPDATE_NATIVE_DISPLAYS: {
+      auto message_params = static_cast<
+        const OzoneHostMsg_UpdateNativeDisplays*>(&message);
+      display_manager->OnUpdateNativeDisplays(message_params->displays);
+      break;
+    }
+
+    case OZONE_HOST_MSG__DISPLAY_CONFIGURED: {
+      auto message_params = static_cast<
+        const OzoneHostMsg_DisplayConfigured*>(&message);
+      display_manager->OnDisplayConfigured(message_params->id,
+                                           message_params->result);
+      break;
+    }
+
+    default:
+      handled = false;
+  }
+  return handled;
+}
+
+bool DrmGpuPlatformSupportHostInprocess::Send(Message* message) {
+  if (platform_support_->IsConnected() &&
+      send_runner_->PostTask(FROM_HERE, base::Bind(send_callback_, message)))
+    return true;
+
+  delete message;
+  return false;
+}
+
+void DrmGpuPlatformSupportHostInprocess::CreateWindow(
+    const gfx::AcceleratedWidget& widget) {
+  Send(new OzoneGpuMsg_CreateWindow(widget));
+}
+
+void DrmGpuPlatformSupportHostInprocess::WindowBoundsChanged(
+    const gfx::AcceleratedWidget& widget, const gfx::Rect& bounds) {
+  Send(new OzoneGpuMsg_WindowBoundsChanged(widget, bounds));
+}
+
+void DrmGpuPlatformSupportHostInprocess::AddGraphicsDevice(
+  const base::FilePath& path,
+  const base::FileDescriptor& fd) {
+  Send(new OzoneGpuMsg_AddGraphicsDevice(path, fd));
+}
+
+bool DrmGpuPlatformSupportHostInprocess::RefreshNativeDisplays() {
+  return Send(new OzoneGpuMsg_RefreshNativeDisplays());
+}
+
+bool DrmGpuPlatformSupportHostInprocess::ConfigureNativeDisplay(
+  int64_t id,
+  const DisplayMode_Params& mode,
+  const gfx::Point& originhost) {
+  return Send(new OzoneGpuMsg_ConfigureNativeDisplay(id, mode, originhost));
+}
+
+} // namespace
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.h b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.h
new file mode 100644
index 0000000..e910ca6
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host_inprocess.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_PLATFORM_SUPPORT_HOST_INPROCESS_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_GPU_PLATFORM_SUPPORT_HOST_INPROCESS_H_
+
+#include "base/single_thread_task_runner.h"
+#include "ui/ozone/platform/drm/common/inprocess_messages.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+
+namespace ui {
+
+class DrmGpuPlatformSupportHostInprocess :
+    public DrmGpuPlatformSupportHostDelegate {
+ public:
+  DrmGpuPlatformSupportHostInprocess();
+  ~DrmGpuPlatformSupportHostInprocess() override;
+
+  void OnChannelEstablished(
+      int host_id,
+      scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+      const base::Callback<void(Message*)>& send_callback);
+  void OnChannelDestroyed(int host_id);
+
+  bool OnMessageReceived(const Message& message);
+  bool Send(Message* message);
+
+  void CreateWindow(const gfx::AcceleratedWidget& widget) override;
+  void WindowBoundsChanged(const gfx::AcceleratedWidget& widget,
+                           const gfx::Rect& bounds) override;
+  void AddGraphicsDevice(const base::FilePath& primary_graphics_card_path,
+                         const base::FileDescriptor& fd) override;
+  bool RefreshNativeDisplays() override;
+  bool ConfigureNativeDisplay(int64_t id,
+                              const DisplayMode_Params& mode,
+                              const gfx::Point& originhost) override;
+
+ private:
+  DrmGpuPlatformSupportHost* platform_support_;
+  scoped_refptr<base::SingleThreadTaskRunner> send_runner_;
+  base::Callback<void(Message*)> send_callback_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_GPU_DRM_GPU_PLATFORM_SUPPORT_HOST_INPROCESS_H_
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
new file mode 100644
index 0000000..0d6389b
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -0,0 +1,121 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
+
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/ozone/platform/drm/host/drm_display_host.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+
+namespace ui {
+
+DrmNativeDisplayDelegate::DrmNativeDisplayDelegate(
+    DrmDisplayHostManager* display_manager)
+    : display_manager_(display_manager) {
+}
+
+DrmNativeDisplayDelegate::~DrmNativeDisplayDelegate() {
+  display_manager_->RemoveDelegate(this);
+}
+
+void DrmNativeDisplayDelegate::OnConfigurationChanged() {
+  FOR_EACH_OBSERVER(NativeDisplayObserver, observers_,
+                    OnConfigurationChanged());
+}
+
+void DrmNativeDisplayDelegate::Initialize() {
+  display_manager_->AddDelegate(this);
+}
+
+void DrmNativeDisplayDelegate::GrabServer() {
+}
+
+void DrmNativeDisplayDelegate::UngrabServer() {
+}
+
+void DrmNativeDisplayDelegate::TakeDisplayControl(
+    const DisplayControlCallback& callback) {
+  display_manager_->TakeDisplayControl(callback);
+}
+
+void DrmNativeDisplayDelegate::RelinquishDisplayControl(
+    const DisplayControlCallback& callback) {
+  display_manager_->RelinquishDisplayControl(callback);
+}
+
+void DrmNativeDisplayDelegate::SyncWithServer() {
+}
+
+void DrmNativeDisplayDelegate::SetBackgroundColor(uint32_t color_argb) {
+}
+
+void DrmNativeDisplayDelegate::ForceDPMSOn() {
+}
+
+void DrmNativeDisplayDelegate::GetDisplays(
+    const GetDisplaysCallback& callback) {
+  display_manager_->UpdateDisplays(callback);
+}
+
+void DrmNativeDisplayDelegate::AddMode(const ui::DisplaySnapshot& output,
+                                       const ui::DisplayMode* mode) {
+}
+
+void DrmNativeDisplayDelegate::Configure(const ui::DisplaySnapshot& output,
+                                         const ui::DisplayMode* mode,
+                                         const gfx::Point& origin,
+                                         const ConfigureCallback& callback) {
+  DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id());
+  display->Configure(mode, origin, callback);
+}
+
+void DrmNativeDisplayDelegate::CreateFrameBuffer(const gfx::Size& size) {
+}
+
+void DrmNativeDisplayDelegate::GetHDCPState(
+    const ui::DisplaySnapshot& output,
+    const GetHDCPStateCallback& callback) {
+  DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id());
+  display->GetHDCPState(callback);
+}
+
+void DrmNativeDisplayDelegate::SetHDCPState(
+    const ui::DisplaySnapshot& output,
+    ui::HDCPState state,
+    const SetHDCPStateCallback& callback) {
+  DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id());
+  display->SetHDCPState(state, callback);
+}
+
+std::vector<ui::ColorCalibrationProfile>
+DrmNativeDisplayDelegate::GetAvailableColorCalibrationProfiles(
+    const ui::DisplaySnapshot& output) {
+  return std::vector<ui::ColorCalibrationProfile>();
+}
+
+bool DrmNativeDisplayDelegate::SetColorCalibrationProfile(
+    const ui::DisplaySnapshot& output,
+    ui::ColorCalibrationProfile new_profile) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool DrmNativeDisplayDelegate::SetGammaRamp(
+    const ui::DisplaySnapshot& output,
+    const std::vector<GammaRampRGBEntry>& lut) {
+  DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id());
+  display->SetGammaRamp(lut);
+  return true;
+}
+
+void DrmNativeDisplayDelegate::AddObserver(NativeDisplayObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DrmNativeDisplayDelegate::RemoveObserver(NativeDisplayObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
new file mode 100644
index 0000000..6b8e690
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_NATIVE_DISPLAY_DELEGATE_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_NATIVE_DISPLAY_DELEGATE_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "ui/display/types/native_display_delegate.h"
+
+namespace ui {
+
+class DrmDisplayHostManager;
+
+class DrmNativeDisplayDelegate : public NativeDisplayDelegate {
+ public:
+  DrmNativeDisplayDelegate(DrmDisplayHostManager* display_manager);
+  ~DrmNativeDisplayDelegate() override;
+
+  void OnConfigurationChanged();
+
+  // NativeDisplayDelegate overrides:
+  void Initialize() override;
+  void GrabServer() override;
+  void UngrabServer() override;
+  void TakeDisplayControl(const DisplayControlCallback& callback) override;
+  void RelinquishDisplayControl(
+      const DisplayControlCallback& callback) override;
+  void SyncWithServer() override;
+  void SetBackgroundColor(uint32_t color_argb) override;
+  void ForceDPMSOn() override;
+  void GetDisplays(const GetDisplaysCallback& callback) override;
+  void AddMode(const ui::DisplaySnapshot& output,
+               const ui::DisplayMode* mode) override;
+  void Configure(const ui::DisplaySnapshot& output,
+                 const ui::DisplayMode* mode,
+                 const gfx::Point& origin,
+                 const ConfigureCallback& callback) override;
+  void CreateFrameBuffer(const gfx::Size& size) override;
+  void GetHDCPState(const ui::DisplaySnapshot& output,
+                    const GetHDCPStateCallback& callback) override;
+  void SetHDCPState(const ui::DisplaySnapshot& output,
+                    ui::HDCPState state,
+                    const SetHDCPStateCallback& callback) override;
+  std::vector<ui::ColorCalibrationProfile> GetAvailableColorCalibrationProfiles(
+      const ui::DisplaySnapshot& output) override;
+  bool SetColorCalibrationProfile(
+      const ui::DisplaySnapshot& output,
+      ui::ColorCalibrationProfile new_profile) override;
+  bool SetGammaRamp(const ui::DisplaySnapshot& output,
+                    const std::vector<GammaRampRGBEntry>& lut) override;
+
+  void AddObserver(NativeDisplayObserver* observer) override;
+  void RemoveObserver(NativeDisplayObserver* observer) override;
+
+ private:
+  DrmDisplayHostManager* display_manager_;  // Not owned.
+
+  base::ObserverList<NativeDisplayObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmNativeDisplayDelegate);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_NATIVE_DISPLAY_DELEGATE_H_
diff --git a/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc b/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc
new file mode 100644
index 0000000..61fe49f
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc
@@ -0,0 +1,120 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h"
+
+#include <algorithm>
+
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+
+namespace ui {
+
+namespace {
+const size_t kMaxCacheSize = 100;
+}  // namespace
+
+bool DrmOverlayCandidatesHost::OverlayCompare::operator()(
+    const OverlayCheck_Params& l,
+    const OverlayCheck_Params& r) {
+  if (l.plane_z_order < r.plane_z_order)
+    return true;
+  if (l.plane_z_order > r.plane_z_order)
+    return false;
+  if (l.display_rect < r.display_rect)
+    return true;
+  if (l.display_rect != r.display_rect)
+    return false;
+  if (l.format < r.format)
+    return true;
+  if (l.format > r.format)
+    return false;
+  if (l.transform < r.transform)
+    return true;
+  if (l.transform > r.transform)
+    return false;
+  if (l.buffer_size.width() < r.buffer_size.width())
+    return true;
+  if (l.buffer_size.width() > r.buffer_size.width())
+    return false;
+  return l.buffer_size.height() < r.buffer_size.height();
+}
+
+DrmOverlayCandidatesHost::DrmOverlayCandidatesHost(
+    gfx::AcceleratedWidget widget,
+    DrmGpuPlatformSupportHost* platform_support)
+    : widget_(widget),
+      platform_support_(platform_support),
+      cache_(kMaxCacheSize) {
+}
+
+DrmOverlayCandidatesHost::~DrmOverlayCandidatesHost() {
+}
+
+void DrmOverlayCandidatesHost::CheckOverlaySupport(
+    OverlaySurfaceCandidateList* candidates) {
+  if (candidates->size() == 2)
+    CheckSingleOverlay(candidates);
+}
+
+void DrmOverlayCandidatesHost::CheckSingleOverlay(
+    OverlaySurfaceCandidateList* candidates) {
+  OverlayCandidatesOzone::OverlaySurfaceCandidate* first = &(*candidates)[0];
+  OverlayCandidatesOzone::OverlaySurfaceCandidate* second = &(*candidates)[1];
+  OverlayCandidatesOzone::OverlaySurfaceCandidate* overlay;
+  if (first->plane_z_order == 0) {
+    overlay = second;
+  } else if (second->plane_z_order == 0) {
+    overlay = first;
+  } else {
+    return;
+  }
+  // 0.01 constant chosen to match DCHECKs in gfx::ToNearestRect and avoid
+  // that code asserting on quads that we accept.
+  if (!gfx::IsNearestRectWithinDistance(overlay->display_rect, 0.01f))
+    return;
+  if (overlay->transform == gfx::OVERLAY_TRANSFORM_INVALID)
+    return;
+
+  OverlayCheck_Params lookup(*overlay);
+  auto iter = cache_.Get(lookup);
+  if (iter == cache_.end()) {
+    cache_.Put(lookup, false);
+    SendRequest(*candidates, lookup);
+  } else {
+    overlay->overlay_handled = iter->second;
+  }
+}
+
+void DrmOverlayCandidatesHost::SendRequest(
+    const OverlaySurfaceCandidateList& candidates,
+    const OverlayCheck_Params& check) {
+  if (!platform_support_->IsConnected())
+    return;
+  pending_checks_.push_back(check);
+  std::vector<OverlayCheck_Params> list;
+  for (const auto& candidate : candidates)
+    list.push_back(OverlayCheck_Params(candidate));
+  platform_support_->CheckOverlayCapabilities(widget_, list);
+}
+
+void DrmOverlayCandidatesHost::OnOverlayResult(bool* handled,
+                                               gfx::AcceleratedWidget widget,
+                                               bool result) {
+  if (widget != widget_)
+    return;
+  *handled = true;
+  DCHECK(!pending_checks_.empty());
+  ProcessResult(pending_checks_.front(), result);
+  pending_checks_.pop_front();
+}
+
+void DrmOverlayCandidatesHost::ProcessResult(const OverlayCheck_Params& check,
+                                             bool result) {
+  if (result)
+    cache_.Put(check, true);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h b/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h
new file mode 100644
index 0000000..f0e935f
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_
+
+#include <deque>
+#include <map>
+#include <vector>
+
+#include "base/containers/mru_cache.h"
+#include "base/single_thread_task_runner.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+namespace ui {
+
+class DrmGpuPlatformSupportHost;
+
+// This is an implementation of OverlayCandidatesOzone where the driver is asked
+// about overlay capabilities via IPC. We have no way of querying abstract
+// capabilities, only if a particular configuration is supported or not.
+// Each time we we are asked if a particular configuration is supported, if we
+// have not seen that configuration before, it is IPCed to the GPU via
+// OzoneGpuMsg_CheckOverlayCapabilities; a test commit is then performed and
+// the result is returned in OzoneHostMsg_OverlayCapabilitiesReceived. Testing
+// is asynchronous, until the reply arrives that configuration will be failed.
+//
+// There is a many:1 relationship between this class and
+// DrmGpuPlatformSupportHost, each compositor will own one of these objects.
+// Each request has a unique request ID, which is assigned from a shared
+// sequence number so that replies can be routed to the correct object.
+class DrmOverlayCandidatesHost : public OverlayCandidatesOzone {
+  struct OverlayCompare {
+    bool operator()(const OverlayCheck_Params& l, const OverlayCheck_Params& r);
+  };
+
+ public:
+  DrmOverlayCandidatesHost(gfx::AcceleratedWidget widget,
+                           DrmGpuPlatformSupportHost* platform_support);
+  ~DrmOverlayCandidatesHost() override;
+
+  // OverlayCandidatesOzone:
+  void CheckOverlaySupport(OverlaySurfaceCandidateList* candidates) override;
+
+ private:
+  void SendRequest(const OverlaySurfaceCandidateList& candidates,
+                   const OverlayCheck_Params& check);
+  void OnOverlayResult(bool* handled,
+                       gfx::AcceleratedWidget widget,
+                       bool result);
+  void ProcessResult(const OverlayCheck_Params& check, bool result);
+
+  void CheckSingleOverlay(OverlaySurfaceCandidateList* candidates);
+
+  gfx::AcceleratedWidget widget_;
+  DrmGpuPlatformSupportHost* platform_support_;  // Not owned.
+
+  std::deque<OverlayCheck_Params> pending_checks_;
+
+  template <class KeyType, class ValueType>
+  struct OverlayMap {
+    typedef std::map<KeyType, ValueType, OverlayCompare> Type;
+  };
+  base::MRUCacheBase<OverlayCheck_Params,
+                     bool,
+                     base::MRUCacheNullDeletor<bool>,
+                     OverlayMap> cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmOverlayCandidatesHost);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.cc b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
new file mode 100644
index 0000000..4f101cc
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
+
+#include "base/command_line.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+DrmOverlayManager::DrmOverlayManager(
+    bool allow_surfaceless,
+    DrmGpuPlatformSupportHost* platform_support_host)
+    : platform_support_host_(platform_support_host),
+      allow_surfaceless_(allow_surfaceless) {
+  is_supported_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kOzoneTestSingleOverlaySupport);
+}
+
+DrmOverlayManager::~DrmOverlayManager() {
+}
+
+scoped_ptr<OverlayCandidatesOzone> DrmOverlayManager::CreateOverlayCandidates(
+    gfx::AcceleratedWidget w) {
+  if (!is_supported_)
+    return nullptr;
+  return make_scoped_ptr(
+      new DrmOverlayCandidatesHost(w, platform_support_host_));
+}
+
+bool DrmOverlayManager::CanShowPrimaryPlaneAsOverlay() {
+  return allow_surfaceless_;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.h b/ui/ozone/platform/drm/host/drm_overlay_manager.h
new file mode 100644
index 0000000..fcaf621
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_MANAGER_H_
+
+#include "ui/ozone/public/overlay_manager_ozone.h"
+
+namespace ui {
+
+class DrmGpuPlatformSupportHost;
+
+class DrmOverlayManager : public OverlayManagerOzone {
+ public:
+  DrmOverlayManager(bool allow_surfaceless,
+                    DrmGpuPlatformSupportHost* platform_support_host);
+  ~DrmOverlayManager() override;
+
+  // OverlayManagerOzone:
+  scoped_ptr<OverlayCandidatesOzone> CreateOverlayCandidates(
+      gfx::AcceleratedWidget w) override;
+  bool CanShowPrimaryPlaneAsOverlay() override;
+
+ private:
+  DrmGpuPlatformSupportHost* platform_support_host_;
+  bool allow_surfaceless_;
+  bool is_supported_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmOverlayManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_MANAGER_H_
diff --git a/ui/ozone/platform/drm/host/drm_window_host.cc b/ui/ozone/platform/drm/host/drm_window_host.cc
new file mode 100644
index 0000000..51c19ce
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_window_host.cc
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_window_host.h"
+
+#include "base/bind.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/event.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/events_ozone.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/display.h"
+#include "ui/ozone/platform/drm/host/drm_cursor.h"
+#include "ui/ozone/platform/drm/host/drm_display_host.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+DrmWindowHost::DrmWindowHost(PlatformWindowDelegate* delegate,
+                             const gfx::Rect& bounds,
+                             DrmGpuPlatformSupportHost* sender,
+                             EventFactoryEvdev* event_factory,
+                             DrmCursor* cursor,
+                             DrmWindowHostManager* window_manager,
+                             DrmDisplayHostManager* display_manager)
+    : delegate_(delegate),
+      sender_(sender),
+      event_factory_(event_factory),
+      cursor_(cursor),
+      window_manager_(window_manager),
+      display_manager_(display_manager),
+      bounds_(bounds),
+      widget_(window_manager->NextAcceleratedWidget()) {
+  window_manager_->AddWindow(widget_, this);
+}
+
+DrmWindowHost::~DrmWindowHost() {
+  PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+  window_manager_->RemoveWindow(widget_);
+  cursor_->OnWindowRemoved(widget_);
+
+  sender_->RemoveChannelObserver(this);
+  sender_->DestroyWindow(widget_);
+}
+
+void DrmWindowHost::Initialize() {
+  sender_->AddChannelObserver(this);
+  PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+  cursor_->OnWindowAdded(widget_, bounds_, GetCursorConfinedBounds());
+  delegate_->OnAcceleratedWidgetAvailable(widget_);
+}
+
+gfx::AcceleratedWidget DrmWindowHost::GetAcceleratedWidget() {
+  return widget_;
+}
+
+gfx::Rect DrmWindowHost::GetCursorConfinedBounds() const {
+  return cursor_confined_bounds_.IsEmpty() ? gfx::Rect(bounds_.size())
+                                           : cursor_confined_bounds_;
+}
+
+void DrmWindowHost::Show() {
+}
+
+void DrmWindowHost::Hide() {
+}
+
+void DrmWindowHost::Close() {
+}
+
+void DrmWindowHost::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+  SendBoundsChange();
+}
+
+gfx::Rect DrmWindowHost::GetBounds() {
+  return bounds_;
+}
+
+void DrmWindowHost::SetCapture() {
+  window_manager_->GrabEvents(widget_);
+}
+
+void DrmWindowHost::ReleaseCapture() {
+  window_manager_->UngrabEvents(widget_);
+}
+
+void DrmWindowHost::ToggleFullscreen() {
+}
+
+void DrmWindowHost::Maximize() {
+}
+
+void DrmWindowHost::Minimize() {
+}
+
+void DrmWindowHost::Restore() {
+}
+
+void DrmWindowHost::SetCursor(PlatformCursor cursor) {
+  cursor_->SetCursor(widget_, cursor);
+}
+
+void DrmWindowHost::MoveCursorTo(const gfx::Point& location) {
+  event_factory_->WarpCursorTo(widget_, location);
+}
+
+void DrmWindowHost::ConfineCursorToBounds(const gfx::Rect& bounds) {
+  if (cursor_confined_bounds_ == bounds)
+    return;
+
+  cursor_confined_bounds_ = bounds;
+  cursor_->CommitBoundsChange(widget_, bounds_, bounds);
+}
+
+bool DrmWindowHost::CanDispatchEvent(const PlatformEvent& ne) {
+  DCHECK(ne);
+  Event* event = static_cast<Event*>(ne);
+
+  // If there is a grab, capture events here.
+  gfx::AcceleratedWidget grabber = window_manager_->event_grabber();
+  if (grabber != gfx::kNullAcceleratedWidget)
+    return grabber == widget_;
+
+  if (event->IsTouchEvent()) {
+    // Dispatch the event if it is from the touchscreen associated with the
+    // DrmWindowHost. We cannot check the event's location because if the
+    // touchscreen has a bezel, touches in the bezel have a location outside of
+    // |bounds_|.
+    int64_t display_id =
+        DeviceDataManager::GetInstance()->GetTargetDisplayForTouchDevice(
+            event->source_device_id());
+
+    if (display_id == gfx::Display::kInvalidDisplayID)
+      return false;
+
+    DrmDisplayHost* display = display_manager_->GetDisplay(display_id);
+    if (!display)
+      return false;
+
+    DisplaySnapshot* snapshot = display->snapshot();
+    if (!snapshot->current_mode())
+      return false;
+
+    gfx::Rect display_bounds(snapshot->origin(),
+                             snapshot->current_mode()->size());
+    return display_bounds == bounds_;
+  } else if (event->IsLocatedEvent()) {
+    LocatedEvent* located_event = static_cast<LocatedEvent*>(event);
+    return bounds_.Contains(gfx::ToFlooredPoint(located_event->location()));
+  }
+
+  // TODO(spang): For non-ash builds we would need smarter keyboard focus.
+  return true;
+}
+
+uint32_t DrmWindowHost::DispatchEvent(const PlatformEvent& native_event) {
+  DCHECK(native_event);
+
+  Event* event = static_cast<Event*>(native_event);
+  if (event->IsLocatedEvent()) {
+    // Make the event location relative to this window's origin.
+    LocatedEvent* located_event = static_cast<LocatedEvent*>(event);
+    gfx::PointF location = located_event->location();
+    location -= bounds_.OffsetFromOrigin();
+    located_event->set_location(location);
+    located_event->set_root_location(location);
+  }
+  DispatchEventFromNativeUiEvent(
+      native_event, base::Bind(&PlatformWindowDelegate::DispatchEvent,
+                               base::Unretained(delegate_)));
+  return POST_DISPATCH_STOP_PROPAGATION;
+}
+
+void DrmWindowHost::OnChannelEstablished() {
+  sender_->CreateWindow(widget_);
+  SendBoundsChange();
+}
+
+void DrmWindowHost::OnChannelDestroyed() {
+}
+
+void DrmWindowHost::SendBoundsChange() {
+  // Update the cursor before the window so that the cursor stays within the
+  // window bounds when the window size shrinks.
+  cursor_->CommitBoundsChange(widget_, bounds_, GetCursorConfinedBounds());
+  sender_->WindowBoundsChanged(widget_, bounds_);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_window_host.h b/ui/ozone/platform/drm/host/drm_window_host.h
new file mode 100644
index 0000000..70f1393
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_window_host.h
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/drm/host/channel_observer.h"
+#include "ui/platform_window/platform_window.h"
+
+namespace ui {
+
+class DrmDisplayHostManager;
+class DrmCursor;
+class DrmGpuPlatformSupportHost;
+class DrmGpuWindow;
+class DrmWindowHostManager;
+class EventFactoryEvdev;
+
+// Implementation of the platform window. This object and its handle |widget_|
+// uniquely identify a window. Since the DRI/GBM platform is split into 2
+// pieces (Browser process and GPU process), internally we need to make sure the
+// state is synchronized between the 2 processes.
+//
+// |widget_| is used in both processes to uniquely identify the window. This
+// means that any state on the browser side needs to be propagated to the GPU.
+// State propagation needs to happen before the state change is acknowledged to
+// |delegate_| as |delegate_| is responsible for initializing the surface
+// associated with the window (the surface is created on the GPU process).
+class DrmWindowHost : public PlatformWindow,
+                      public PlatformEventDispatcher,
+                      public ChannelObserver {
+ public:
+  DrmWindowHost(PlatformWindowDelegate* delegate,
+                const gfx::Rect& bounds,
+                DrmGpuPlatformSupportHost* sender,
+                EventFactoryEvdev* event_factory,
+                DrmCursor* cursor,
+                DrmWindowHostManager* window_manager,
+                DrmDisplayHostManager* display_manager);
+  ~DrmWindowHost() override;
+
+  void Initialize();
+
+  gfx::AcceleratedWidget GetAcceleratedWidget();
+
+  gfx::Rect GetCursorConfinedBounds() const;
+
+  // PlatformWindow:
+  void Show() override;
+  void Hide() override;
+  void Close() override;
+  void SetBounds(const gfx::Rect& bounds) override;
+  gfx::Rect GetBounds() override;
+  void SetCapture() override;
+  void ReleaseCapture() override;
+  void ToggleFullscreen() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  void SetCursor(PlatformCursor cursor) override;
+  void MoveCursorTo(const gfx::Point& location) override;
+  void ConfineCursorToBounds(const gfx::Rect& bounds) override;
+
+  // PlatformEventDispatcher:
+  bool CanDispatchEvent(const PlatformEvent& event) override;
+  uint32_t DispatchEvent(const PlatformEvent& event) override;
+
+  // ChannelObserver:
+  void OnChannelEstablished() override;
+  void OnChannelDestroyed() override;
+
+ private:
+  void SendBoundsChange();
+
+  PlatformWindowDelegate* delegate_;        // Not owned.
+  DrmGpuPlatformSupportHost* sender_;       // Not owned.
+  EventFactoryEvdev* event_factory_;        // Not owned.
+  DrmCursor* cursor_;                       // Not owned.
+  DrmWindowHostManager* window_manager_;    // Not owned.
+  DrmDisplayHostManager* display_manager_;  // Not owned.
+
+  gfx::Rect bounds_;
+  gfx::AcceleratedWidget widget_;
+
+  gfx::Rect cursor_confined_bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmWindowHost);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_H_
diff --git a/ui/ozone/platform/drm/host/drm_window_host_manager.cc b/ui/ozone/platform/drm/host/drm_window_host_manager.cc
new file mode 100644
index 0000000..4aad916
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_window_host_manager.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/host/drm_window_host.h"
+
+namespace ui {
+
+DrmWindowHostManager::DrmWindowHostManager() {
+}
+
+DrmWindowHostManager::~DrmWindowHostManager() {
+}
+
+gfx::AcceleratedWidget DrmWindowHostManager::NextAcceleratedWidget() {
+  // We're not using 0 since other code assumes that a 0 AcceleratedWidget is an
+  // invalid widget.
+  return ++last_allocated_widget_;
+}
+
+void DrmWindowHostManager::AddWindow(gfx::AcceleratedWidget widget,
+                                     DrmWindowHost* window) {
+  std::pair<WidgetToWindowMap::iterator, bool> result = window_map_.insert(
+      std::pair<gfx::AcceleratedWidget, DrmWindowHost*>(widget, window));
+  DCHECK(result.second) << "Window for " << widget << " already added.";
+}
+
+void DrmWindowHostManager::RemoveWindow(gfx::AcceleratedWidget widget) {
+  WidgetToWindowMap::iterator it = window_map_.find(widget);
+  if (it != window_map_.end())
+    window_map_.erase(it);
+  else
+    NOTREACHED() << "Attempting to remove non-existing window " << widget;
+
+  if (event_grabber_ == widget)
+    event_grabber_ = gfx::kNullAcceleratedWidget;
+}
+
+DrmWindowHost* DrmWindowHostManager::GetWindow(gfx::AcceleratedWidget widget) {
+  WidgetToWindowMap::iterator it = window_map_.find(widget);
+  if (it != window_map_.end())
+    return it->second;
+
+  NOTREACHED() << "Attempting to get non-existing window " << widget;
+  return NULL;
+}
+
+DrmWindowHost* DrmWindowHostManager::GetWindowAt(const gfx::Point& location) {
+  for (auto it = window_map_.begin(); it != window_map_.end(); ++it)
+    if (it->second->GetBounds().Contains(location))
+      return it->second;
+
+  return NULL;
+}
+
+DrmWindowHost* DrmWindowHostManager::GetPrimaryWindow() {
+  auto it = window_map_.begin();
+  return it != window_map_.end() ? it->second : nullptr;
+}
+
+void DrmWindowHostManager::GrabEvents(gfx::AcceleratedWidget widget) {
+  if (event_grabber_ != gfx::kNullAcceleratedWidget)
+    return;
+  event_grabber_ = widget;
+}
+
+void DrmWindowHostManager::UngrabEvents(gfx::AcceleratedWidget widget) {
+  if (event_grabber_ != widget)
+    return;
+  event_grabber_ = gfx::kNullAcceleratedWidget;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_window_host_manager.h b/ui/ozone/platform/drm/host/drm_window_host_manager.h
new file mode 100644
index 0000000..7e9a368
--- /dev/null
+++ b/ui/ozone/platform/drm/host/drm_window_host_manager.h
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_MANAGER_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class DrmGpuPlatformSupportHost;
+class DrmWindowHost;
+
+// Responsible for keeping the mapping between the allocated widgets and
+// windows.
+class DrmWindowHostManager {
+ public:
+  DrmWindowHostManager();
+  ~DrmWindowHostManager();
+
+  gfx::AcceleratedWidget NextAcceleratedWidget();
+
+  // Adds a window for |widget|. Note: |widget| should not be associated when
+  // calling this function.
+  void AddWindow(gfx::AcceleratedWidget widget, DrmWindowHost* window);
+
+  // Removes the window association for |widget|. Note: |widget| must be
+  // associated with a window when calling this function.
+  void RemoveWindow(gfx::AcceleratedWidget widget);
+
+  // Returns the window associated with |widget|. Note: This function should
+  // only be called if a valid window has been associated.
+  DrmWindowHost* GetWindow(gfx::AcceleratedWidget widget);
+
+  // Returns the window containing the specified screen location, or NULL.
+  DrmWindowHost* GetWindowAt(const gfx::Point& location);
+
+  // Returns a window. Probably the first one created.
+  DrmWindowHost* GetPrimaryWindow();
+
+  // Tries to set a given widget as the recipient for events. It will
+  // fail if there is already another widget as recipient.
+  void GrabEvents(gfx::AcceleratedWidget widget);
+
+  // Unsets a given widget as the recipient for events.
+  void UngrabEvents(gfx::AcceleratedWidget widget);
+
+  // Gets the current widget recipient of mouse events.
+  gfx::AcceleratedWidget event_grabber() const { return event_grabber_; }
+
+ private:
+  typedef std::map<gfx::AcceleratedWidget, DrmWindowHost*> WidgetToWindowMap;
+
+  gfx::AcceleratedWidget last_allocated_widget_ = 0;
+  WidgetToWindowMap window_map_;
+
+  gfx::AcceleratedWidget event_grabber_ = gfx::kNullAcceleratedWidget;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmWindowHostManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_HOST_DRM_WINDOW_HOST_MANAGER_H_
diff --git a/ui/ozone/platform/drm/ozone_platform_drm.cc b/ui/ozone/platform/drm/ozone_platform_drm.cc
new file mode 100644
index 0000000..0424644
--- /dev/null
+++ b/ui/ozone/platform/drm/ozone_platform_drm.cc
@@ -0,0 +1,166 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/ozone_platform_drm.h"
+
+#include "base/at_exit.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h"
+#include "ui/ozone/platform/drm/gpu/drm_surface_factory.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/platform/drm/host/drm_cursor.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
+#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
+#include "ui/ozone/platform/drm/host/drm_window_host.h"
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+#if defined(USE_XKBCOMMON)
+#include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h"
+#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
+#else
+#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
+#endif
+
+namespace ui {
+
+namespace {
+
+// OzonePlatform for Linux DRM (Direct Rendering Manager)
+//
+// This platform is Linux without any display server (no X, wayland, or
+// anything). This means chrome alone owns the display and input devices.
+class OzonePlatformDrm : public OzonePlatform {
+ public:
+  OzonePlatformDrm()
+      : buffer_generator_(new DrmBufferGenerator()),
+        screen_manager_(new ScreenManager(buffer_generator_.get())),
+        device_manager_(CreateDeviceManager()) {}
+  ~OzonePlatformDrm() override {}
+
+  // OzonePlatform:
+  ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
+    return surface_factory_ozone_.get();
+  }
+  OverlayManagerOzone* GetOverlayManager() override {
+    return overlay_manager_.get();
+  }
+  CursorFactoryOzone* GetCursorFactoryOzone() override {
+    NOTIMPLEMENTED();
+    return nullptr;
+  }
+  InputController* GetInputController() override {
+    return event_factory_ozone_->input_controller();
+  }
+  GpuPlatformSupport* GetGpuPlatformSupport() override {
+    return gpu_platform_support_.get();
+  }
+  GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
+    return gpu_platform_support_host_.get();
+  }
+  scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override {
+    return event_factory_ozone_->CreateSystemInputInjector();
+  }
+  scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) override {
+    scoped_ptr<DrmWindowHost> platform_window(
+        new DrmWindowHost(delegate, bounds, gpu_platform_support_host_.get(),
+                          event_factory_ozone_.get(), cursor_.get(),
+                          window_manager_.get(), display_manager_.get()));
+    platform_window->Initialize();
+    return platform_window.Pass();
+  }
+  scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
+    return make_scoped_ptr(
+        new DrmNativeDisplayDelegate(display_manager_.get()));
+  }
+  void InitializeUI() override {
+    drm_device_manager_.reset(new DrmDeviceManager(
+        scoped_ptr<DrmDeviceGenerator>(new DrmDeviceGenerator())));
+    window_manager_.reset(new DrmWindowHostManager());
+    cursor_.reset(new DrmCursor(window_manager_.get()));
+#if defined(USE_XKBCOMMON)
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(make_scoped_ptr(
+        new XkbKeyboardLayoutEngine(xkb_evdev_code_converter_)));
+#else
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
+        make_scoped_ptr(new StubKeyboardLayoutEngine()));
+#endif
+    event_factory_ozone_.reset(new EventFactoryEvdev(
+        cursor_.get(), device_manager_.get(),
+        KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()));
+    overlay_manager_.reset(new DrmOverlayManager(false, nullptr));
+    surface_factory_ozone_.reset(new DrmSurfaceFactory(screen_manager_.get()));
+    scoped_ptr<DrmGpuDisplayManager> display_manager(new DrmGpuDisplayManager(
+        screen_manager_.get(), drm_device_manager_.get()));
+    gpu_platform_support_.reset(new DrmGpuPlatformSupport(
+        drm_device_manager_.get(), screen_manager_.get(),
+        buffer_generator_.get(), display_manager.Pass()));
+    gpu_platform_support_host_.reset(
+        new DrmGpuPlatformSupportHost(cursor_.get()));
+    display_manager_.reset(new DrmDisplayHostManager(
+        gpu_platform_support_host_.get(), device_manager_.get(),
+        event_factory_ozone_->input_controller()));
+
+    if (!gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
+                                base::ThreadTaskRunnerHandle::Get()))
+      LOG(FATAL) << "Failed to initialize dummy channel.";
+  }
+
+  void InitializeGPU() override {}
+
+ private:
+  // Objects in the "GPU" process.
+  scoped_ptr<DrmDeviceManager> drm_device_manager_;
+  scoped_ptr<DrmBufferGenerator> buffer_generator_;
+  scoped_ptr<ScreenManager> screen_manager_;
+  scoped_ptr<DrmGpuPlatformSupport> gpu_platform_support_;
+
+  // Objects in the "Browser" process.
+  scoped_ptr<OverlayManagerOzone> overlay_manager_;
+  scoped_ptr<DeviceManager> device_manager_;
+  scoped_ptr<DrmWindowHostManager> window_manager_;
+  scoped_ptr<DrmCursor> cursor_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+  scoped_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_;
+  scoped_ptr<DrmDisplayHostManager> display_manager_;
+
+#if defined(USE_XKBCOMMON)
+  XkbEvdevCodes xkb_evdev_code_converter_;
+#endif
+
+  // Objects on both processes.
+  scoped_ptr<DrmSurfaceFactory> surface_factory_ozone_;
+
+  OzoneGpuTestHelper gpu_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformDrm);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformDri() {
+  return new OzonePlatformDrm;
+}
+
+OzonePlatform* CreateOzonePlatformDrm() {
+  return new OzonePlatformDrm;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/ozone_platform_drm.h b/ui/ozone/platform/drm/ozone_platform_drm.h
new file mode 100644
index 0000000..5837870
--- /dev/null
+++ b/ui/ozone/platform/drm/ozone_platform_drm.h
@@ -0,0 +1,19 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_
+#define UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformDri();
+
+OzonePlatform* CreateOzonePlatformDrm();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_DRM_H_
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
new file mode 100644
index 0000000..a59908a
--- /dev/null
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -0,0 +1,236 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/ozone_platform_gbm.h"
+
+#include <dlfcn.h>
+#include <gbm.h>
+#include <stdlib.h>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/thread_task_runner_handle.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_gpu_platform_support.h"
+#include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
+#include "ui/ozone/platform/drm/gpu/gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/gbm_surface.h"
+#include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/platform/drm/host/drm_cursor.h"
+#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
+#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
+#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
+#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"
+#include "ui/ozone/platform/drm/host/drm_window_host.h"
+#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+#if defined(USE_XKBCOMMON)
+#include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h"
+#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
+#else
+#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
+#endif
+
+namespace ui {
+
+namespace {
+
+class GlApiLoader {
+ public:
+  GlApiLoader()
+      : glapi_lib_(dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL)) {}
+
+  ~GlApiLoader() {
+    if (glapi_lib_)
+      dlclose(glapi_lib_);
+  }
+
+ private:
+  // HACK: gbm drivers have broken linkage. The Mesa DRI driver references
+  // symbols in the libglapi library however it does not explicitly link against
+  // it. That caused linkage errors when running an application that does not
+  // explicitly link against libglapi.
+  void* glapi_lib_;
+
+  DISALLOW_COPY_AND_ASSIGN(GlApiLoader);
+};
+
+class GbmBufferGenerator : public ScanoutBufferGenerator {
+ public:
+  GbmBufferGenerator() {}
+  ~GbmBufferGenerator() override {}
+
+  // ScanoutBufferGenerator:
+  scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                      const gfx::Size& size) override {
+    scoped_refptr<GbmDevice> gbm(static_cast<GbmDevice*>(drm.get()));
+    return GbmBuffer::CreateBuffer(gbm, SurfaceFactoryOzone::BGRA_8888, size,
+                                   true);
+  }
+
+ protected:
+  DISALLOW_COPY_AND_ASSIGN(GbmBufferGenerator);
+};
+
+class GbmDeviceGenerator : public DrmDeviceGenerator {
+ public:
+  GbmDeviceGenerator(bool use_atomic) : use_atomic_(use_atomic) {}
+  ~GbmDeviceGenerator() override {}
+
+  // DrmDeviceGenerator:
+  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
+                                        base::File file) override {
+    scoped_refptr<DrmDevice> drm = new GbmDevice(path, file.Pass());
+    if (drm->Initialize(use_atomic_))
+      return drm;
+
+    return nullptr;
+  }
+
+ private:
+  bool use_atomic_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmDeviceGenerator);
+};
+
+class OzonePlatformGbm : public OzonePlatform {
+ public:
+  OzonePlatformGbm(bool use_surfaceless) : use_surfaceless_(use_surfaceless) {}
+  ~OzonePlatformGbm() override {}
+
+  // OzonePlatform:
+  ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
+    return surface_factory_ozone_.get();
+  }
+  OverlayManagerOzone* GetOverlayManager() override {
+    return overlay_manager_.get();
+  }
+  CursorFactoryOzone* GetCursorFactoryOzone() override {
+    NOTIMPLEMENTED();
+    return nullptr;
+  }
+  InputController* GetInputController() override {
+    return event_factory_ozone_->input_controller();
+  }
+  GpuPlatformSupport* GetGpuPlatformSupport() override {
+    return gpu_platform_support_.get();
+  }
+  GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
+    return gpu_platform_support_host_.get();
+  }
+  scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override {
+    return event_factory_ozone_->CreateSystemInputInjector();
+  }
+  scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) override {
+    scoped_ptr<DrmWindowHost> platform_window(
+        new DrmWindowHost(delegate, bounds, gpu_platform_support_host_.get(),
+                          event_factory_ozone_.get(), cursor_.get(),
+                          window_manager_.get(), display_manager_.get()));
+    platform_window->Initialize();
+    return platform_window.Pass();
+  }
+  scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
+    return make_scoped_ptr(
+        new DrmNativeDisplayDelegate(display_manager_.get()));
+  }
+  void InitializeUI() override {
+    device_manager_ = CreateDeviceManager();
+    window_manager_.reset(new DrmWindowHostManager());
+    cursor_.reset(new DrmCursor(window_manager_.get()));
+#if defined(USE_XKBCOMMON)
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(make_scoped_ptr(
+        new XkbKeyboardLayoutEngine(xkb_evdev_code_converter_)));
+#else
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
+        make_scoped_ptr(new StubKeyboardLayoutEngine()));
+#endif
+    event_factory_ozone_.reset(new EventFactoryEvdev(
+        cursor_.get(), device_manager_.get(),
+        KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()));
+    gpu_platform_support_host_.reset(
+        new DrmGpuPlatformSupportHost(cursor_.get()));
+    display_manager_.reset(new DrmDisplayHostManager(
+        gpu_platform_support_host_.get(), device_manager_.get(),
+        event_factory_ozone_->input_controller()));
+    overlay_manager_.reset(new DrmOverlayManager(
+        use_surfaceless_, gpu_platform_support_host_.get()));
+
+    gpu_platform_support_host_->SetDisplayManager(display_manager_.get());
+    gpu_platform_support_host_->SetWindowManager(window_manager_.get());
+  }
+
+  void InitializeGPU() override {
+    bool use_atomic = false;
+    gl_api_loader_.reset(new GlApiLoader());
+    drm_device_manager_.reset(new DrmDeviceManager(
+        scoped_ptr<DrmDeviceGenerator>(new GbmDeviceGenerator(use_atomic))));
+    buffer_generator_.reset(new GbmBufferGenerator());
+    screen_manager_.reset(new ScreenManager(buffer_generator_.get()));
+    surface_factory_ozone_.reset(new GbmSurfaceFactory(use_surfaceless_));
+    surface_factory_ozone_->InitializeGpu(drm_device_manager_.get(),
+                                          screen_manager_.get());
+    scoped_ptr<DrmGpuDisplayManager> display_manager(new DrmGpuDisplayManager(
+        screen_manager_.get(), drm_device_manager_.get()));
+    gpu_platform_support_.reset(new DrmGpuPlatformSupport(
+        drm_device_manager_.get(), screen_manager_.get(),
+        buffer_generator_.get(), display_manager.Pass()));
+  }
+
+ private:
+  // Objects in both processes.
+  bool use_surfaceless_;
+
+  // Objects in the GPU process.
+  scoped_ptr<GbmSurfaceFactory> surface_factory_ozone_;
+  scoped_ptr<GlApiLoader> gl_api_loader_;
+  scoped_ptr<DrmDeviceManager> drm_device_manager_;
+  scoped_ptr<GbmBufferGenerator> buffer_generator_;
+  scoped_ptr<ScreenManager> screen_manager_;
+  scoped_ptr<DrmGpuPlatformSupport> gpu_platform_support_;
+
+  // Objects in the Browser process.
+  scoped_ptr<DeviceManager> device_manager_;
+  scoped_ptr<DrmWindowHostManager> window_manager_;
+  scoped_ptr<DrmCursor> cursor_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+  scoped_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_;
+  scoped_ptr<DrmDisplayHostManager> display_manager_;
+  scoped_ptr<DrmOverlayManager> overlay_manager_;
+
+#if defined(USE_XKBCOMMON)
+  XkbEvdevCodes xkb_evdev_code_converter_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformGbm);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformGbm() {
+  base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+#if defined(USE_MESA_PLATFORM_NULL)
+  // Only works with surfaceless.
+  cmd->AppendSwitch(switches::kOzoneUseSurfaceless);
+#endif
+  return new OzonePlatformGbm(cmd->HasSwitch(switches::kOzoneUseSurfaceless));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.h b/ui/ozone/platform/drm/ozone_platform_gbm.h
new file mode 100644
index 0000000..324535d
--- /dev/null
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.h
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_
+#define UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformGbm();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_GBM_H_
diff --git a/ui/ozone/platform/drm/test/mock_drm_device.cc b/ui/ozone/platform/drm/test/mock_drm_device.cc
new file mode 100644
index 0000000..8999297
--- /dev/null
+++ b/ui/ozone/platform/drm/test/mock_drm_device.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/test/mock_drm_device.h"
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
+
+namespace ui {
+
+namespace {
+
+template <class Object>
+Object* DrmAllocator() {
+  return static_cast<Object*>(drmMalloc(sizeof(Object)));
+}
+
+class MockHardwareDisplayPlaneManager
+    : public HardwareDisplayPlaneManagerLegacy {
+ public:
+  MockHardwareDisplayPlaneManager(DrmDevice* drm,
+                                  std::vector<uint32_t> crtcs,
+                                  size_t planes_per_crtc) {
+    const int kPlaneBaseId = 50;
+    drm_ = drm;
+    crtcs_.swap(crtcs);
+    for (size_t crtc_idx = 0; crtc_idx < crtcs_.size(); crtc_idx++) {
+      for (size_t i = 0; i < planes_per_crtc; i++) {
+        planes_.push_back(
+            new HardwareDisplayPlane(kPlaneBaseId + i, 1 << crtc_idx));
+      }
+    }
+  }
+};
+
+}  // namespace
+
+MockDrmDevice::MockDrmDevice()
+    : DrmDevice(base::FilePath(), base::File()),
+      get_crtc_call_count_(0),
+      set_crtc_call_count_(0),
+      restore_crtc_call_count_(0),
+      add_framebuffer_call_count_(0),
+      remove_framebuffer_call_count_(0),
+      page_flip_call_count_(0),
+      overlay_flip_call_count_(0),
+      overlay_clear_call_count_(0),
+      allocate_buffer_count_(0),
+      set_crtc_expectation_(true),
+      add_framebuffer_expectation_(true),
+      page_flip_expectation_(true),
+      create_dumb_buffer_expectation_(true),
+      current_framebuffer_(0) {
+  plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy());
+}
+
+MockDrmDevice::MockDrmDevice(bool use_sync_flips,
+                             std::vector<uint32_t> crtcs,
+                             size_t planes_per_crtc)
+    : DrmDevice(base::FilePath(), base::File()),
+      get_crtc_call_count_(0),
+      set_crtc_call_count_(0),
+      restore_crtc_call_count_(0),
+      add_framebuffer_call_count_(0),
+      remove_framebuffer_call_count_(0),
+      page_flip_call_count_(0),
+      overlay_flip_call_count_(0),
+      overlay_clear_call_count_(0),
+      allocate_buffer_count_(0),
+      set_crtc_expectation_(true),
+      add_framebuffer_expectation_(true),
+      page_flip_expectation_(true),
+      create_dumb_buffer_expectation_(true),
+      use_sync_flips_(use_sync_flips),
+      current_framebuffer_(0) {
+  plane_manager_.reset(
+      new MockHardwareDisplayPlaneManager(this, crtcs, planes_per_crtc));
+}
+
+MockDrmDevice::~MockDrmDevice() {
+}
+
+ScopedDrmCrtcPtr MockDrmDevice::GetCrtc(uint32_t crtc_id) {
+  get_crtc_call_count_++;
+  return ScopedDrmCrtcPtr(DrmAllocator<drmModeCrtc>());
+}
+
+bool MockDrmDevice::SetCrtc(uint32_t crtc_id,
+                            uint32_t framebuffer,
+                            std::vector<uint32_t> connectors,
+                            drmModeModeInfo* mode) {
+  current_framebuffer_ = framebuffer;
+  set_crtc_call_count_++;
+  return set_crtc_expectation_;
+}
+
+bool MockDrmDevice::SetCrtc(drmModeCrtc* crtc,
+                            std::vector<uint32_t> connectors) {
+  restore_crtc_call_count_++;
+  return true;
+}
+
+bool MockDrmDevice::DisableCrtc(uint32_t crtc_id) {
+  current_framebuffer_ = 0;
+  return true;
+}
+
+ScopedDrmConnectorPtr MockDrmDevice::GetConnector(uint32_t connector_id) {
+  return ScopedDrmConnectorPtr(DrmAllocator<drmModeConnector>());
+}
+
+bool MockDrmDevice::AddFramebuffer(uint32_t width,
+                                   uint32_t height,
+                                   uint8_t depth,
+                                   uint8_t bpp,
+                                   uint32_t stride,
+                                   uint32_t handle,
+                                   uint32_t* framebuffer) {
+  add_framebuffer_call_count_++;
+  *framebuffer = add_framebuffer_call_count_;
+  return add_framebuffer_expectation_;
+}
+
+bool MockDrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
+  remove_framebuffer_call_count_++;
+  return true;
+}
+
+ScopedDrmFramebufferPtr MockDrmDevice::GetFramebuffer(uint32_t framebuffer) {
+  return ScopedDrmFramebufferPtr();
+}
+
+bool MockDrmDevice::PageFlip(uint32_t crtc_id,
+                             uint32_t framebuffer,
+                             bool is_sync,
+                             const PageFlipCallback& callback) {
+  page_flip_call_count_++;
+  current_framebuffer_ = framebuffer;
+  if (page_flip_expectation_) {
+    if (use_sync_flips_)
+      callback.Run(0, 0, 0);
+    else
+      callbacks_.push(callback);
+  }
+
+  return page_flip_expectation_;
+}
+
+bool MockDrmDevice::PageFlipOverlay(uint32_t crtc_id,
+                                    uint32_t framebuffer,
+                                    const gfx::Rect& location,
+                                    const gfx::Rect& source,
+                                    int overlay_plane) {
+  if (!framebuffer)
+    overlay_clear_call_count_++;
+  overlay_flip_call_count_++;
+  return true;
+}
+
+ScopedDrmPropertyPtr MockDrmDevice::GetProperty(drmModeConnector* connector,
+                                                const char* name) {
+  return ScopedDrmPropertyPtr(DrmAllocator<drmModePropertyRes>());
+}
+
+bool MockDrmDevice::SetProperty(uint32_t connector_id,
+                                uint32_t property_id,
+                                uint64_t value) {
+  return true;
+}
+
+bool MockDrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
+  return true;
+}
+
+ScopedDrmPropertyBlobPtr MockDrmDevice::GetPropertyBlob(
+    drmModeConnector* connector,
+    const char* name) {
+  return ScopedDrmPropertyBlobPtr(DrmAllocator<drmModePropertyBlobRes>());
+}
+
+bool MockDrmDevice::SetCursor(uint32_t crtc_id,
+                              uint32_t handle,
+                              const gfx::Size& size) {
+  crtc_cursor_map_[crtc_id] = handle;
+  return true;
+}
+
+bool MockDrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
+  return true;
+}
+
+bool MockDrmDevice::CreateDumbBuffer(const SkImageInfo& info,
+                                     uint32_t* handle,
+                                     uint32_t* stride) {
+  if (!create_dumb_buffer_expectation_)
+    return false;
+
+  *handle = allocate_buffer_count_++;
+  *stride = info.minRowBytes();
+  void* pixels = new char[info.getSafeSize(*stride)];
+  buffers_.push_back(
+      skia::AdoptRef(SkSurface::NewRasterDirect(info, pixels, *stride)));
+  buffers_[*handle]->getCanvas()->clear(SK_ColorBLACK);
+
+  return true;
+}
+
+bool MockDrmDevice::DestroyDumbBuffer(uint32_t handle) {
+  if (handle >= buffers_.size() || !buffers_[handle])
+    return false;
+
+  buffers_[handle].clear();
+  return true;
+}
+
+bool MockDrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
+  if (handle >= buffers_.size() || !buffers_[handle])
+    return false;
+
+  *pixels = const_cast<void*>(buffers_[handle]->peekPixels(nullptr, nullptr));
+  return true;
+}
+
+bool MockDrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
+  return true;
+}
+
+bool MockDrmDevice::CloseBufferHandle(uint32_t handle) {
+  return true;
+}
+
+bool MockDrmDevice::CommitProperties(drmModePropertySet* properties,
+                                     uint32_t flags,
+                                     bool is_sync,
+                                     bool test_only,
+                                     const PageFlipCallback& callback) {
+  return false;
+}
+
+bool MockDrmDevice::SetGammaRamp(uint32_t crtc_id,
+                                 const std::vector<GammaRampRGBEntry>& lut) {
+  return true;
+}
+
+bool MockDrmDevice::SetCapability(uint64_t capability, uint64_t value) {
+  return false;
+}
+
+void MockDrmDevice::RunCallbacks() {
+  while (!callbacks_.empty()) {
+    PageFlipCallback callback = callbacks_.front();
+    callbacks_.pop();
+    callback.Run(0, 0, 0);
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/test/mock_drm_device.h b/ui/ozone/platform/drm/test/mock_drm_device.h
new file mode 100644
index 0000000..a25bad9
--- /dev/null
+++ b/ui/ozone/platform/drm/test/mock_drm_device.h
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_TEST_MOCK_DRM_DEVICE_H_
+#define UI_OZONE_PLATFORM_DRM_TEST_MOCK_DRM_DEVICE_H_
+
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+
+namespace ui {
+
+class CrtcController;
+struct GammaRampRGBEntry;
+
+// The real DrmDevice makes actual DRM calls which we can't use in unit tests.
+class MockDrmDevice : public ui::DrmDevice {
+ public:
+  MockDrmDevice();
+  MockDrmDevice(bool use_sync_flips,
+                std::vector<uint32_t> crtcs,
+                size_t planes_per_crtc);
+
+  int get_get_crtc_call_count() const { return get_crtc_call_count_; }
+  int get_set_crtc_call_count() const { return set_crtc_call_count_; }
+  int get_restore_crtc_call_count() const { return restore_crtc_call_count_; }
+  int get_add_framebuffer_call_count() const {
+    return add_framebuffer_call_count_;
+  }
+  int get_remove_framebuffer_call_count() const {
+    return remove_framebuffer_call_count_;
+  }
+  int get_page_flip_call_count() const { return page_flip_call_count_; }
+  int get_overlay_flip_call_count() const { return overlay_flip_call_count_; }
+  int get_overlay_clear_call_count() const { return overlay_clear_call_count_; }
+  void set_set_crtc_expectation(bool state) { set_crtc_expectation_ = state; }
+  void set_page_flip_expectation(bool state) { page_flip_expectation_ = state; }
+  void set_add_framebuffer_expectation(bool state) {
+    add_framebuffer_expectation_ = state;
+  }
+  void set_create_dumb_buffer_expectation(bool state) {
+    create_dumb_buffer_expectation_ = state;
+  }
+
+  uint32_t current_framebuffer() const { return current_framebuffer_; }
+
+  const std::vector<skia::RefPtr<SkSurface>> buffers() const {
+    return buffers_;
+  }
+
+  uint32_t get_cursor_handle_for_crtc(uint32_t crtc) const {
+    const auto it = crtc_cursor_map_.find(crtc);
+    return it != crtc_cursor_map_.end() ? it->second : 0;
+  }
+
+  void RunCallbacks();
+
+  // DrmDevice:
+  ScopedDrmCrtcPtr GetCrtc(uint32_t crtc_id) override;
+  bool SetCrtc(uint32_t crtc_id,
+               uint32_t framebuffer,
+               std::vector<uint32_t> connectors,
+               drmModeModeInfo* mode) override;
+  bool SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) override;
+  bool DisableCrtc(uint32_t crtc_id) override;
+  ScopedDrmConnectorPtr GetConnector(uint32_t connector_id) override;
+  bool AddFramebuffer(uint32_t width,
+                      uint32_t height,
+                      uint8_t depth,
+                      uint8_t bpp,
+                      uint32_t stride,
+                      uint32_t handle,
+                      uint32_t* framebuffer) override;
+  bool RemoveFramebuffer(uint32_t framebuffer) override;
+  ScopedDrmFramebufferPtr GetFramebuffer(uint32_t framebuffer) override;
+  bool PageFlip(uint32_t crtc_id,
+                uint32_t framebuffer,
+                bool is_sync,
+                const PageFlipCallback& callback) override;
+  bool PageFlipOverlay(uint32_t crtc_id,
+                       uint32_t framebuffer,
+                       const gfx::Rect& location,
+                       const gfx::Rect& source,
+                       int overlay_plane) override;
+  ScopedDrmPropertyPtr GetProperty(drmModeConnector* connector,
+                                   const char* name) override;
+  bool SetProperty(uint32_t connector_id,
+                   uint32_t property_id,
+                   uint64_t value) override;
+  bool GetCapability(uint64_t capability, uint64_t* value) override;
+  ScopedDrmPropertyBlobPtr GetPropertyBlob(drmModeConnector* connector,
+                                           const char* name) override;
+  bool SetCursor(uint32_t crtc_id,
+                 uint32_t handle,
+                 const gfx::Size& size) override;
+  bool MoveCursor(uint32_t crtc_id, const gfx::Point& point) override;
+  bool CreateDumbBuffer(const SkImageInfo& info,
+                        uint32_t* handle,
+                        uint32_t* stride) override;
+  bool DestroyDumbBuffer(uint32_t handle) override;
+  bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels) override;
+  bool UnmapDumbBuffer(void* pixels, size_t size) override;
+  bool CloseBufferHandle(uint32_t handle) override;
+  bool CommitProperties(drmModePropertySet* properties,
+                        uint32_t flags,
+                        bool is_sync,
+                        bool test_only,
+                        const PageFlipCallback& callback) override;
+  bool SetGammaRamp(uint32_t crtc_id,
+                    const std::vector<GammaRampRGBEntry>& lut) override;
+  bool SetCapability(uint64_t capability, uint64_t value) override;
+
+ private:
+  ~MockDrmDevice() override;
+
+  int get_crtc_call_count_;
+  int set_crtc_call_count_;
+  int restore_crtc_call_count_;
+  int add_framebuffer_call_count_;
+  int remove_framebuffer_call_count_;
+  int page_flip_call_count_;
+  int overlay_flip_call_count_;
+  int overlay_clear_call_count_;
+  int allocate_buffer_count_;
+
+  bool set_crtc_expectation_;
+  bool add_framebuffer_expectation_;
+  bool page_flip_expectation_;
+  bool create_dumb_buffer_expectation_;
+
+  bool use_sync_flips_;
+
+  uint32_t current_framebuffer_;
+
+  std::vector<skia::RefPtr<SkSurface>> buffers_;
+
+  std::map<uint32_t, uint32_t> crtc_cursor_map_;
+
+  std::queue<PageFlipCallback> callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDrmDevice);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_TEST_MOCK_DRM_DEVICE_H_
diff --git a/ui/ozone/platform/egltest/BUILD.gn b/ui/ozone/platform/egltest/BUILD.gn
new file mode 100644
index 0000000..4ffd2f9
--- /dev/null
+++ b/ui/ozone/platform/egltest/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//tools/generate_library_loader/generate_library_loader.gni")
+
+source_set("egltest") {
+  sources = [
+    "ozone_platform_egltest.cc",
+    "ozone_platform_egltest.h",
+  ]
+
+  deps = [
+    ":eglplatform_shim",
+    "//base",
+    "//ui/events/devices",
+    "//ui/ozone:ozone_base",
+    "//ui/events",
+    "//ui/events/ozone:events_ozone",
+    "//ui/events/ozone:events_ozone_evdev",
+    "//ui/events/ozone:events_ozone_layout",
+    "//ui/events/platform",
+    "//ui/gfx",
+    "//ui/platform_window",
+  ]
+}
+
+generate_library_loader("eglplatform_shim") {
+  name = "LibeglplatformShimLoader"
+  output_h = "libeglplatform_shim.h"
+  output_cc = "libeglplatform_shim_loader.cc"
+  header = "\"ui/ozone/platform/egltest/eglplatform_shim.h\""
+
+  functions = [
+    "ShimQueryString",
+    "ShimInitialize",
+    "ShimTerminate",
+    "ShimCreateWindow",
+    "ShimQueryWindow",
+    "ShimDestroyWindow",
+    "ShimGetNativeDisplay",
+    "ShimGetNativeWindow",
+    "ShimReleaseNativeWindow",
+  ]
+}
+# TODO(spang): eglplatform_shim_x11 once support lands: http://crbug.com/380327
diff --git a/ui/ozone/platform/egltest/eglplatform_shim.h b/ui/ozone/platform/egltest/eglplatform_shim.h
new file mode 100644
index 0000000..ce7cce9
--- /dev/null
+++ b/ui/ozone/platform/egltest/eglplatform_shim.h
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __eglplatform_shim_h_
+#define __eglplatform_shim_h_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHIM_EXPORT __attribute__((visibility("default")))
+
+// Simple integral native window identifier.
+// NB: Unlike EGLNativeWindowType, this will be shipped between processes.
+typedef int ShimNativeWindowId;
+#define SHIM_NO_WINDOW_ID ((ShimNativeWindowId)0)
+
+// Opaque versions of EGL types (as used by ozone).
+typedef intptr_t ShimEGLNativeDisplayType;
+typedef intptr_t ShimEGLNativeWindowType;
+
+// QueryString targets
+#define SHIM_EGL_LIBRARY 0x1001
+#define SHIM_GLES_LIBRARY 0x1002
+
+// CreateWindow / QueryWindow attributes
+#define SHIM_WINDOW_WIDTH 0x0001
+#define SHIM_WINDOW_HEIGHT 0x0002
+
+// Query global implementation information.
+SHIM_EXPORT const char* ShimQueryString(int name);
+
+// Init/terminate library.
+SHIM_EXPORT bool ShimInitialize(void);
+SHIM_EXPORT bool ShimTerminate(void);
+
+// Create window handle & query window properties (called from browser process).
+SHIM_EXPORT ShimNativeWindowId ShimCreateWindow(void);
+SHIM_EXPORT bool ShimQueryWindow(ShimNativeWindowId window_id,
+                                 int attribute,
+                                 int* value);
+SHIM_EXPORT bool ShimDestroyWindow(ShimNativeWindowId window_id);
+
+// Manage actual EGL platform objects (called from GPU process).
+SHIM_EXPORT ShimEGLNativeDisplayType ShimGetNativeDisplay(void);
+SHIM_EXPORT ShimEGLNativeWindowType
+    ShimGetNativeWindow(ShimNativeWindowId native_window_id);
+SHIM_EXPORT bool ShimReleaseNativeWindow(ShimEGLNativeWindowType native_window);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __eglplatform_shim_h */
diff --git a/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc b/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc
new file mode 100644
index 0000000..c7e0080
--- /dev/null
+++ b/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc
@@ -0,0 +1,106 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/egltest/eglplatform_shim.h"
+
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Display* g_display;
+
+const int kDefaultX = 0;
+const int kDefaultY = 0;
+const int kDefaultWidth = 1280;
+const int kDefaultHeight = 720;
+const int kDefaultBorderWidth = 0;
+
+const char* ShimQueryString(int name) {
+  switch (name) {
+    case SHIM_EGL_LIBRARY:
+      return "libEGL.so.1";
+    case SHIM_GLES_LIBRARY:
+      return "libGLESv2.so.2";
+    default:
+      return NULL;
+  }
+}
+
+bool ShimInitialize(void) {
+  g_display = XOpenDisplay(NULL);
+  return g_display != NULL;
+}
+
+bool ShimTerminate(void) {
+  XCloseDisplay(g_display);
+  return true;
+}
+
+ShimNativeWindowId ShimCreateWindow(void) {
+  XSetWindowAttributes swa;
+  memset(&swa, 0, sizeof(swa));
+  swa.event_mask = 0;
+
+  Window window = XCreateWindow(g_display,
+                                DefaultRootWindow(g_display),
+                                kDefaultX,
+                                kDefaultY,
+                                kDefaultWidth,
+                                kDefaultHeight,
+                                kDefaultBorderWidth,
+                                CopyFromParent,
+                                InputOutput,
+                                CopyFromParent,
+                                CWEventMask,
+                                &swa);
+
+  XMapWindow(g_display, window);
+  XStoreName(g_display, window, "EGL test");
+  XFlush(g_display);
+
+  return window;
+}
+
+bool ShimQueryWindow(ShimNativeWindowId window_id, int attribute, int* value) {
+  XWindowAttributes window_attributes;
+  switch (attribute) {
+    case SHIM_WINDOW_WIDTH:
+      XGetWindowAttributes(g_display, window_id, &window_attributes);
+      *value = window_attributes.width;
+      return true;
+    case SHIM_WINDOW_HEIGHT:
+      XGetWindowAttributes(g_display, window_id, &window_attributes);
+      *value = window_attributes.height;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ShimDestroyWindow(ShimNativeWindowId window_id) {
+  XDestroyWindow(g_display, window_id);
+  return true;
+}
+
+ShimEGLNativeDisplayType ShimGetNativeDisplay(void) {
+  return reinterpret_cast<ShimEGLNativeDisplayType>(g_display);
+}
+
+ShimEGLNativeWindowType ShimGetNativeWindow(
+    ShimNativeWindowId native_window_id) {
+  return native_window_id;
+}
+
+bool ShimReleaseNativeWindow(ShimEGLNativeWindowType native_window) {
+  return true;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.cc b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
new file mode 100644
index 0000000..31f35a6
--- /dev/null
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
@@ -0,0 +1,412 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/egltest/ozone_platform_egltest.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/threading/thread_checker.h"
+#include "library_loaders/libeglplatform_shim.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/event.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/events_ozone.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/common/egl_util.h"
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+#include "ui/ozone/common/stub_overlay_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+#include "ui/ozone/public/system_input_injector.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+namespace {
+
+const char kEglplatformShim[] = "EGLPLATFORM_SHIM";
+const char kEglplatformShimDefault[] = "libeglplatform_shim.so.1";
+const char kDefaultEglSoname[] = "libEGL.so.1";
+const char kDefaultGlesSoname[] = "libGLESv2.so.2";
+
+// Get the library soname to load.
+std::string GetShimLibraryName() {
+  std::string library;
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  if (env->GetVar(kEglplatformShim, &library))
+    return library;
+  return kEglplatformShimDefault;
+}
+
+// Touch events are reported in device coordinates. This scales the event to the
+// window's coordinate space.
+void ScaleTouchEvent(TouchEvent* event, const gfx::SizeF& size) {
+  for (const auto& device :
+       DeviceDataManager::GetInstance()->touchscreen_devices()) {
+    if (device.id == event->source_device_id()) {
+      gfx::SizeF touchscreen_size = device.size;
+      gfx::PointF location = event->location_f();
+
+      location.Scale(size.width() / touchscreen_size.width(),
+                     size.height() / touchscreen_size.height());
+      double ratio = std::sqrt(size.GetArea() / touchscreen_size.GetArea());
+
+      event->set_location(location);
+      event->set_radius_x(event->radius_x() * ratio);
+      event->set_radius_y(event->radius_y() * ratio);
+      return;
+    }
+  }
+}
+
+class EgltestWindow : public PlatformWindow, public PlatformEventDispatcher {
+ public:
+  EgltestWindow(PlatformWindowDelegate* delegate,
+                LibeglplatformShimLoader* eglplatform_shim,
+                EventFactoryEvdev* event_factory,
+                const gfx::Rect& bounds);
+  ~EgltestWindow() override;
+
+  // PlatformWindow:
+  gfx::Rect GetBounds() override;
+  void SetBounds(const gfx::Rect& bounds) override;
+  void Show() override;
+  void Hide() override;
+  void Close() override;
+  void SetCapture() override;
+  void ReleaseCapture() override;
+  void ToggleFullscreen() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  void SetCursor(PlatformCursor cursor) override;
+  void MoveCursorTo(const gfx::Point& location) override;
+  void ConfineCursorToBounds(const gfx::Rect& bounds) override;
+
+  // PlatformEventDispatcher:
+  bool CanDispatchEvent(const PlatformEvent& event) override;
+  uint32_t DispatchEvent(const PlatformEvent& event) override;
+
+ private:
+  PlatformWindowDelegate* delegate_;
+  LibeglplatformShimLoader* eglplatform_shim_;
+  EventFactoryEvdev* event_factory_;
+  gfx::Rect bounds_;
+  ShimNativeWindowId window_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(EgltestWindow);
+};
+
+EgltestWindow::EgltestWindow(PlatformWindowDelegate* delegate,
+                             LibeglplatformShimLoader* eglplatform_shim,
+                             EventFactoryEvdev* event_factory,
+                             const gfx::Rect& bounds)
+    : delegate_(delegate),
+      eglplatform_shim_(eglplatform_shim),
+      event_factory_(event_factory),
+      bounds_(bounds),
+      window_id_(SHIM_NO_WINDOW_ID) {
+  window_id_ = eglplatform_shim_->ShimCreateWindow();
+  delegate_->OnAcceleratedWidgetAvailable(window_id_);
+  ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+}
+
+EgltestWindow::~EgltestWindow() {
+  ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+  if (window_id_ != SHIM_NO_WINDOW_ID)
+    eglplatform_shim_->ShimDestroyWindow(window_id_);
+}
+
+gfx::Rect EgltestWindow::GetBounds() {
+  return bounds_;
+}
+
+void EgltestWindow::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+}
+
+void EgltestWindow::Show() {
+}
+
+void EgltestWindow::Hide() {
+}
+
+void EgltestWindow::Close() {
+}
+
+void EgltestWindow::SetCapture() {
+}
+
+void EgltestWindow::ReleaseCapture() {
+}
+
+void EgltestWindow::ToggleFullscreen() {
+}
+
+void EgltestWindow::Maximize() {
+}
+
+void EgltestWindow::Minimize() {
+}
+
+void EgltestWindow::Restore() {
+}
+
+void EgltestWindow::SetCursor(PlatformCursor cursor) {
+}
+
+void EgltestWindow::MoveCursorTo(const gfx::Point& location) {
+  event_factory_->WarpCursorTo(window_id_, location);
+}
+
+void EgltestWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
+}
+
+bool EgltestWindow::CanDispatchEvent(const ui::PlatformEvent& ne) {
+  return true;
+}
+
+uint32_t EgltestWindow::DispatchEvent(const ui::PlatformEvent& native_event) {
+  DCHECK(native_event);
+  Event* event = static_cast<Event*>(native_event);
+  if (event->IsTouchEvent())
+    ScaleTouchEvent(static_cast<TouchEvent*>(event), bounds_.size());
+
+  DispatchEventFromNativeUiEvent(
+      native_event, base::Bind(&PlatformWindowDelegate::DispatchEvent,
+                               base::Unretained(delegate_)));
+
+  return ui::POST_DISPATCH_STOP_PROPAGATION;
+}
+
+// EGL surface wrapper for libeglplatform_shim.
+//
+// This just manages the native window lifetime using
+// ShimGetNativeWindow & ShimReleaseNativeWindow.
+class SurfaceOzoneEgltest : public SurfaceOzoneEGL {
+ public:
+  SurfaceOzoneEgltest(ShimNativeWindowId window_id,
+                      LibeglplatformShimLoader* eglplatform_shim)
+      : eglplatform_shim_(eglplatform_shim) {
+    native_window_ = eglplatform_shim_->ShimGetNativeWindow(window_id);
+  }
+  ~SurfaceOzoneEgltest() override {
+    bool ret = eglplatform_shim_->ShimReleaseNativeWindow(native_window_);
+    DCHECK(ret);
+  }
+
+  intptr_t GetNativeWindow() override { return native_window_; }
+
+  bool OnSwapBuffers() override { return true; }
+
+  bool OnSwapBuffersAsync(const SwapCompletionCallback& callback) override {
+    callback.Run(gfx::SwapResult::SWAP_ACK);
+    return true;
+  }
+
+  bool ResizeNativeWindow(const gfx::Size& viewport_size) override {
+    return true;
+  }
+
+  scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override {
+    return nullptr;
+  }
+
+ private:
+  LibeglplatformShimLoader* eglplatform_shim_;
+  intptr_t native_window_;
+};
+
+// EGL surface factory for libeglplatform_shim.
+//
+// This finds the right EGL/GLES2 libraries for loading, and creates
+// a single native window via ShimCreateWindow for drawing
+// into.
+class SurfaceFactoryEgltest : public ui::SurfaceFactoryOzone {
+ public:
+  SurfaceFactoryEgltest(LibeglplatformShimLoader* eglplatform_shim)
+      : eglplatform_shim_(eglplatform_shim) {}
+  ~SurfaceFactoryEgltest() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+  }
+
+  // SurfaceFactoryOzone:
+  intptr_t GetNativeDisplay() override;
+  scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget) override;
+  const int32* GetEGLSurfaceProperties(const int32* desired_list) override;
+  bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) override;
+
+ private:
+  LibeglplatformShimLoader* eglplatform_shim_;
+  base::ThreadChecker thread_checker_;
+};
+
+intptr_t SurfaceFactoryEgltest::GetNativeDisplay() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return eglplatform_shim_->ShimGetNativeDisplay();
+}
+
+scoped_ptr<SurfaceOzoneEGL> SurfaceFactoryEgltest::CreateEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return make_scoped_ptr<SurfaceOzoneEGL>(
+      new SurfaceOzoneEgltest(widget, eglplatform_shim_));
+}
+
+bool SurfaceFactoryEgltest::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  const char* egl_soname = eglplatform_shim_->ShimQueryString(SHIM_EGL_LIBRARY);
+  const char* gles_soname =
+      eglplatform_shim_->ShimQueryString(SHIM_GLES_LIBRARY);
+  if (!egl_soname)
+    egl_soname = kDefaultEglSoname;
+  if (!gles_soname)
+    gles_soname = kDefaultGlesSoname;
+
+  return ::ui::LoadEGLGLES2Bindings(add_gl_library, set_gl_get_proc_address,
+                                    egl_soname, gles_soname);
+}
+
+const int32* SurfaceFactoryEgltest::GetEGLSurfaceProperties(
+    const int32* desired_list) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  static const int32 broken_props[] = {
+      EGL_RENDERABLE_TYPE,
+      EGL_OPENGL_ES2_BIT,
+      EGL_SURFACE_TYPE,
+      EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+      EGL_NONE,
+  };
+  return broken_props;
+}
+
+// Test platform for EGL.
+//
+// This is a tiny EGL-based platform. Creation of the native window is
+// handled by a separate library called eglplatform_shim.so.1 because
+// this itself is platform specific and we want to test out multiple
+// hardware platforms.
+class OzonePlatformEgltest : public OzonePlatform {
+ public:
+  OzonePlatformEgltest() : shim_initialized_(false) {}
+  ~OzonePlatformEgltest() override {
+    if (shim_initialized_)
+      eglplatform_shim_.ShimTerminate();
+  }
+
+  void LoadShim() {
+    std::string library = GetShimLibraryName();
+
+    if (eglplatform_shim_.Load(library))
+      return;
+
+    base::FilePath module_path;
+    if (!PathService::Get(base::DIR_MODULE, &module_path))
+      LOG(ERROR) << "failed to get DIR_MODULE from PathService";
+    base::FilePath library_path = module_path.Append(library);
+
+    if (eglplatform_shim_.Load(library_path.value()))
+      return;
+
+    LOG(FATAL) << "failed to load " << library;
+  }
+
+  void Initialize() {
+    LoadShim();
+    shim_initialized_ = eglplatform_shim_.ShimInitialize();
+  }
+
+  // OzonePlatform:
+  ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
+    return surface_factory_ozone_.get();
+  }
+  OverlayManagerOzone* GetOverlayManager() override {
+    return overlay_manager_.get();
+  }
+  CursorFactoryOzone* GetCursorFactoryOzone() override {
+    return cursor_factory_ozone_.get();
+  }
+  InputController* GetInputController() override {
+    return event_factory_ozone_->input_controller();
+  }
+  GpuPlatformSupport* GetGpuPlatformSupport() override {
+    return gpu_platform_support_.get();
+  }
+  GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
+    return gpu_platform_support_host_.get();
+  }
+  scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override {
+    return nullptr;  // no input injection support.
+  }
+  scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) override {
+    return make_scoped_ptr<PlatformWindow>(new EgltestWindow(
+        delegate, &eglplatform_shim_, event_factory_ozone_.get(), bounds));
+  }
+  scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
+    return make_scoped_ptr(new NativeDisplayDelegateOzone());
+  }
+
+  void InitializeUI() override {
+    device_manager_ = CreateDeviceManager();
+    overlay_manager_.reset(new StubOverlayManager());
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
+        make_scoped_ptr(new StubKeyboardLayoutEngine()));
+    event_factory_ozone_.reset(new EventFactoryEvdev(
+        NULL, device_manager_.get(),
+        KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()));
+    cursor_factory_ozone_.reset(new CursorFactoryOzone());
+    gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
+  }
+
+  void InitializeGPU() override {
+    surface_factory_ozone_.reset(new SurfaceFactoryEgltest(&eglplatform_shim_));
+    gpu_platform_support_.reset(CreateStubGpuPlatformSupport());
+  }
+
+ private:
+  LibeglplatformShimLoader eglplatform_shim_;
+  scoped_ptr<DeviceManager> device_manager_;
+  scoped_ptr<SurfaceFactoryEgltest> surface_factory_ozone_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<GpuPlatformSupport> gpu_platform_support_;
+  scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
+  scoped_ptr<OverlayManagerOzone> overlay_manager_;
+
+  bool shim_initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformEgltest);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformEgltest() {
+  OzonePlatformEgltest* platform = new OzonePlatformEgltest;
+  platform->Initialize();
+  return platform;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.h b/ui/ozone/platform/egltest/ozone_platform_egltest.h
new file mode 100644
index 0000000..fb49be5
--- /dev/null
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.h
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
+#define UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformEgltest();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
diff --git a/ui/ozone/platform/test/BUILD.gn b/ui/ozone/platform/test/BUILD.gn
new file mode 100644
index 0000000..eaaa60c
--- /dev/null
+++ b/ui/ozone/platform/test/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("test") {
+  sources = [
+    "ozone_platform_test.cc",
+    "ozone_platform_test.h",
+    "test_window.cc",
+    "test_window.h",
+    "test_window_manager.cc",
+    "test_window_manager.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/base",
+    "//ui/gfx/geometry",
+    "//ui/events/ozone:events_ozone_layout",
+    "//ui/events/platform",
+  ]
+}
diff --git a/ui/ozone/platform/test/ozone_platform_test.cc b/ui/ozone/platform/test/ozone_platform_test.cc
new file mode 100644
index 0000000..c6da9e3
--- /dev/null
+++ b/ui/ozone/platform/test/ozone_platform_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/test/ozone_platform_test.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+#include "ui/ozone/common/stub_overlay_manager.h"
+#include "ui/ozone/platform/test/test_window.h"
+#include "ui/ozone/platform/test/test_window_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/input_controller.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+#include "ui/ozone/public/system_input_injector.h"
+
+namespace ui {
+
+namespace {
+
+// A test implementation of PlatformEventSource that we can instantiate to make
+// sure that the PlatformEventSource has an instance while in unit tests.
+class TestPlatformEventSource : public ui::PlatformEventSource {
+ public:
+  TestPlatformEventSource() {}
+  ~TestPlatformEventSource() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
+};
+
+// OzonePlatform for testing
+//
+// This platform dumps images to a file for testing purposes.
+class OzonePlatformTest : public OzonePlatform {
+ public:
+  OzonePlatformTest(const base::FilePath& dump_file) : file_path_(dump_file) {}
+  ~OzonePlatformTest() override {}
+
+  // OzonePlatform:
+  ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
+    return window_manager_.get();
+  }
+  OverlayManagerOzone* GetOverlayManager() override {
+    return overlay_manager_.get();
+  }
+  CursorFactoryOzone* GetCursorFactoryOzone() override {
+    return cursor_factory_ozone_.get();
+  }
+  InputController* GetInputController() override {
+    return input_controller_.get();
+  }
+  GpuPlatformSupport* GetGpuPlatformSupport() override {
+    return gpu_platform_support_.get();
+  }
+  GpuPlatformSupportHost* GetGpuPlatformSupportHost() override {
+    return gpu_platform_support_host_.get();
+  }
+  scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override {
+    return nullptr;  // no input injection support.
+  }
+  scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) override {
+    return make_scoped_ptr<PlatformWindow>(
+        new TestWindow(delegate, window_manager_.get(), bounds));
+  }
+  scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
+    return make_scoped_ptr(new NativeDisplayDelegateOzone());
+  }
+
+  void InitializeUI() override {
+    window_manager_.reset(new TestWindowManager(file_path_));
+    window_manager_->Initialize();
+    // This unbreaks tests that create their own.
+    if (!PlatformEventSource::GetInstance())
+      platform_event_source_.reset(new TestPlatformEventSource);
+    KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
+        make_scoped_ptr(new StubKeyboardLayoutEngine()));
+
+    overlay_manager_.reset(new StubOverlayManager());
+    input_controller_ = CreateStubInputController();
+    cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
+    gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
+  }
+
+  void InitializeGPU() override {
+    gpu_platform_support_.reset(CreateStubGpuPlatformSupport());
+  }
+
+ private:
+  scoped_ptr<TestWindowManager> window_manager_;
+  scoped_ptr<PlatformEventSource> platform_event_source_;
+  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<InputController> input_controller_;
+  scoped_ptr<GpuPlatformSupport> gpu_platform_support_;
+  scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
+  scoped_ptr<OverlayManagerOzone> overlay_manager_;
+  base::FilePath file_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformTest);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformTest() {
+  base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+  base::FilePath location;
+  if (cmd->HasSwitch(switches::kOzoneDumpFile))
+    location = cmd->GetSwitchValuePath(switches::kOzoneDumpFile);
+  return new OzonePlatformTest(location);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/ozone_platform_test.h b/ui/ozone/platform/test/ozone_platform_test.h
new file mode 100644
index 0000000..fb25fd6
--- /dev/null
+++ b/ui/ozone/platform/test/ozone_platform_test.h
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
+#define UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformTest();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
diff --git a/ui/ozone/platform/test/test_window.cc b/ui/ozone/platform/test/test_window.cc
new file mode 100644
index 0000000..2890ccf
--- /dev/null
+++ b/ui/ozone/platform/test/test_window.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/test/test_window.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/platform/test/test_window_manager.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+TestWindow::TestWindow(PlatformWindowDelegate* delegate,
+                       TestWindowManager* manager,
+                       const gfx::Rect& bounds)
+    : delegate_(delegate), manager_(manager), bounds_(bounds) {
+  widget_ = manager_->AddWindow(this);
+  delegate_->OnAcceleratedWidgetAvailable(widget_);
+}
+
+TestWindow::~TestWindow() {
+  manager_->RemoveWindow(widget_, this);
+}
+
+base::FilePath TestWindow::path() {
+  base::FilePath base_path = manager_->base_path();
+  if (base_path.empty() || base_path == base::FilePath("/dev/null"))
+    return base_path;
+
+  // Disambiguate multiple window output files with the window id.
+  return base_path.Append(base::IntToString(widget_));
+}
+
+gfx::Rect TestWindow::GetBounds() {
+  return bounds_;
+}
+
+void TestWindow::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+}
+
+void TestWindow::Show() {
+}
+
+void TestWindow::Hide() {
+}
+
+void TestWindow::Close() {
+}
+
+void TestWindow::SetCapture() {
+}
+
+void TestWindow::ReleaseCapture() {
+}
+
+void TestWindow::ToggleFullscreen() {
+}
+
+void TestWindow::Maximize() {
+}
+
+void TestWindow::Minimize() {
+}
+
+void TestWindow::Restore() {
+}
+
+void TestWindow::SetCursor(PlatformCursor cursor) {
+}
+
+void TestWindow::MoveCursorTo(const gfx::Point& location) {
+}
+
+void TestWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/test_window.h b/ui/ozone/platform/test/test_window.h
new file mode 100644
index 0000000..aaeb003
--- /dev/null
+++ b/ui/ozone/platform/test/test_window.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
+#define UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
+
+#include "base/files/file_path.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window.h"
+
+namespace ui {
+
+class PlatformWindowDelegate;
+class TestWindowManager;
+
+class TestWindow : public PlatformWindow {
+ public:
+  TestWindow(PlatformWindowDelegate* delegate,
+             TestWindowManager* manager,
+             const gfx::Rect& bounds);
+  ~TestWindow() override;
+
+  // Path for image file for this window.
+  base::FilePath path();
+
+  // PlatformWindow:
+  gfx::Rect GetBounds() override;
+  void SetBounds(const gfx::Rect& bounds) override;
+  void Show() override;
+  void Hide() override;
+  void Close() override;
+  void SetCapture() override;
+  void ReleaseCapture() override;
+  void ToggleFullscreen() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  void SetCursor(PlatformCursor cursor) override;
+  void MoveCursorTo(const gfx::Point& location) override;
+  void ConfineCursorToBounds(const gfx::Rect& bounds) override;
+
+ private:
+  PlatformWindowDelegate* delegate_;
+  TestWindowManager* manager_;
+  gfx::Rect bounds_;
+  gfx::AcceleratedWidget widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindow);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
diff --git a/ui/ozone/platform/test/test_window_manager.cc b/ui/ozone/platform/test/test_window_manager.cc
new file mode 100644
index 0000000..cd48bf3
--- /dev/null
+++ b/ui/ozone/platform/test/test_window_manager.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/test/test_window_manager.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/stl_util.h"
+#include "base/threading/worker_pool.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+
+namespace {
+
+void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) {
+  DCHECK(!location.empty());
+  std::vector<unsigned char> png_data;
+  gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, true, &png_data);
+  base::WriteFile(location,
+                  reinterpret_cast<const char*>(vector_as_array(&png_data)),
+                  png_data.size());
+}
+
+class FileSurface : public SurfaceOzoneCanvas {
+ public:
+  FileSurface(const base::FilePath& location) : location_(location) {}
+  ~FileSurface() override {}
+
+  // SurfaceOzoneCanvas overrides:
+  void ResizeCanvas(const gfx::Size& viewport_size) override {
+    surface_ = skia::AdoptRef(SkSurface::NewRaster(SkImageInfo::MakeN32Premul(
+        viewport_size.width(), viewport_size.height())));
+  }
+  skia::RefPtr<SkSurface> GetSurface() override { return surface_; }
+  void PresentCanvas(const gfx::Rect& damage) override {
+    if (location_.empty())
+      return;
+    SkBitmap bitmap;
+    bitmap.setInfo(surface_->getCanvas()->imageInfo());
+
+    // TODO(dnicoara) Use SkImage instead to potentially avoid a copy.
+    // See crbug.com/361605 for details.
+    if (surface_->getCanvas()->readPixels(&bitmap, 0, 0)) {
+      base::WorkerPool::PostTask(
+          FROM_HERE, base::Bind(&WriteDataToFile, location_, bitmap), true);
+    }
+  }
+  scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override {
+    return nullptr;
+  }
+
+ private:
+  base::FilePath location_;
+  skia::RefPtr<SkSurface> surface_;
+};
+
+}  // namespace
+
+TestWindowManager::TestWindowManager(const base::FilePath& dump_location)
+    : location_(dump_location) {
+}
+
+TestWindowManager::~TestWindowManager() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void TestWindowManager::Initialize() {
+  if (location_.empty())
+    return;
+  if (!DirectoryExists(location_) && !base::CreateDirectory(location_) &&
+      location_ != base::FilePath("/dev/null"))
+    PLOG(FATAL) << "unable to create output directory";
+  if (!base::PathIsWritable(location_))
+    PLOG(FATAL) << "unable to write to output location";
+}
+
+int32_t TestWindowManager::AddWindow(TestWindow* window) {
+  return windows_.Add(window);
+}
+
+void TestWindowManager::RemoveWindow(int32_t window_id, TestWindow* window) {
+  DCHECK_EQ(window, windows_.Lookup(window_id));
+  windows_.Remove(window_id);
+}
+
+base::FilePath TestWindowManager::base_path() const {
+  return location_;
+}
+
+scoped_ptr<SurfaceOzoneCanvas> TestWindowManager::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  TestWindow* window = windows_.Lookup(widget);
+  DCHECK(window);
+  return make_scoped_ptr<SurfaceOzoneCanvas>(new FileSurface(window->path()));
+}
+
+bool TestWindowManager::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/test_window_manager.h b/ui/ozone/platform/test/test_window_manager.h
new file mode 100644
index 0000000..b651207
--- /dev/null
+++ b/ui/ozone/platform/test/test_window_manager.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_
+
+#include "base/files/file_path.h"
+#include "base/id_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/test/test_window.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+class TestWindowManager : public SurfaceFactoryOzone {
+ public:
+  explicit TestWindowManager(const base::FilePath& dump_location);
+  ~TestWindowManager() override;
+
+  // Initialize (mainly check that we have a place to write output to).
+  void Initialize();
+
+  // Register a new window. Returns the window id.
+  int32_t AddWindow(TestWindow* window);
+
+  // Remove a window.
+  void RemoveWindow(int32_t window_id, TestWindow* window);
+
+  // User-supplied path for images.
+  base::FilePath base_path() const;
+
+  // SurfaceFactoryOzone:
+  scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget w) override;
+  bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) override;
+
+ private:
+  base::FilePath location_;
+
+  IDMap<TestWindow> windows_;
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindowManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_
diff --git a/ui/ozone/platform_constructor_list.h b/ui/ozone/platform_constructor_list.h
new file mode 100644
index 0000000..4f2fa98
--- /dev/null
+++ b/ui/ozone/platform_constructor_list.h
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_CONSTRUCTOR_LIST_H_
+#define UI_OZONE_PLATFORM_CONSTRUCTOR_LIST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/platform_list.h"
+
+namespace ui {
+
+template <class T>
+struct PlatformConstructorList {
+  typedef T* (*Constructor)();
+  static const Constructor kConstructors[kPlatformCount];
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CONSTRUCTOR_LIST_H_
diff --git a/ui/ozone/platform_object.h b/ui/ozone/platform_object.h
new file mode 100644
index 0000000..2518c7c
--- /dev/null
+++ b/ui/ozone/platform_object.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_OBJECT_H_
+#define UI_OZONE_PLATFORM_OBJECT_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace ui {
+
+// Create an instance of platform specific object.
+//
+// This calls a static constructor function based on the --ozone-platform flag.
+//
+// For the platform called "foo", PlatformObject<PlatformWidget> will ultimately
+// call the function with signature
+//
+//   PlatformWidget* CreatePlatformWidgetFoo();
+//
+// A definition of this function for each compiled platform must be provided, or
+// link errors will result.
+//
+// To find the right constructor function, this uses static data defined in the
+// source file generated by the generate_constructor_list.py.
+template <class T>
+class PlatformObject {
+ public:
+  static scoped_ptr<T> Create();
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_OBJECT_H_
diff --git a/ui/ozone/platform_object_internal.h b/ui/ozone/platform_object_internal.h
new file mode 100644
index 0000000..68a42b3
--- /dev/null
+++ b/ui/ozone/platform_object_internal.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_OBJECT_INTERNAL_H_
+#define UI_OZONE_PLATFORM_OBJECT_INTERNAL_H_
+
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform_constructor_list.h"
+#include "ui/ozone/platform_object.h"
+#include "ui/ozone/platform_selection.h"
+
+namespace ui {
+
+template <class T>
+scoped_ptr<T> PlatformObject<T>::Create() {
+  typedef typename PlatformConstructorList<T>::Constructor Constructor;
+
+  // Determine selected platform (from --ozone-platform flag, or default).
+  int platform = GetOzonePlatformId();
+
+  // Look up the constructor in the constructor list.
+  Constructor constructor = PlatformConstructorList<T>::kConstructors[platform];
+
+  // Call the constructor.
+  return make_scoped_ptr(constructor());
+}
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_OBJECT_INTERNAL_H_
diff --git a/ui/ozone/platform_selection.cc b/ui/ozone/platform_selection.cc
new file mode 100644
index 0000000..04dc04c
--- /dev/null
+++ b/ui/ozone/platform_selection.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform_selection.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "ui/ozone/platform_list.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+namespace {
+
+// Returns the name of the platform to use (value of --ozone-platform flag).
+std::string GetPlatformName() {
+  // The first platform is the default.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kOzonePlatform) &&
+      kPlatformCount > 0)
+    return "gbm";//kPlatformNames[0];
+  return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+      switches::kOzonePlatform);
+}
+
+int g_selected_platform = -1;
+
+}  // namespace
+
+int GetOzonePlatformId() {
+  if (g_selected_platform >= 0)
+    return g_selected_platform;
+
+  std::string platform_name = GetPlatformName();
+
+  // Search for a matching platform in the list.
+  for (int platform_id = 0; platform_id < kPlatformCount; ++platform_id) {
+    if (platform_name == kPlatformNames[platform_id]) {
+      g_selected_platform = platform_id;
+      return g_selected_platform;
+    }
+  }
+
+  LOG(FATAL) << "Invalid ozone platform: " << platform_name;
+  return -1;  // not reached
+}
+
+const char* GetOzonePlatformName() {
+  return kPlatformNames[GetOzonePlatformId()];
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform_selection.h b/ui/ozone/platform_selection.h
new file mode 100644
index 0000000..5a1229f
--- /dev/null
+++ b/ui/ozone/platform_selection.h
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_SELECTION_H_
+#define UI_OZONE_PLATFORM_SELECTION_H_
+
+#include "ui/ozone/ozone_export.h"
+#include "ui/ozone/platform_list.h"
+
+namespace ui {
+
+// Get active platform id (by parsing --ozone-platform flag).
+OZONE_EXPORT int GetOzonePlatformId();
+
+// Get active platform name.
+OZONE_EXPORT const char* GetOzonePlatformName();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_SELECTION_H_
diff --git a/ui/ozone/public/README b/ui/ozone/public/README
new file mode 100644
index 0000000..27e1b3b
--- /dev/null
+++ b/ui/ozone/public/README
@@ -0,0 +1,2 @@
+This directory contains the platform API exposed to higher level components such
+as content. These interfaces can be used outside of ui/ozone.
diff --git a/ui/ozone/public/cursor_factory_ozone.cc b/ui/ozone/public/cursor_factory_ozone.cc
new file mode 100644
index 0000000..c7f388d
--- /dev/null
+++ b/ui/ozone/public/cursor_factory_ozone.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/cursor_factory_ozone.h"
+
+#include "base/logging.h"
+
+namespace ui {
+
+// static
+CursorFactoryOzone* CursorFactoryOzone::impl_ = NULL;
+
+CursorFactoryOzone::CursorFactoryOzone() {
+  DCHECK(!impl_) << "There should only be a single CursorFactoryOzone.";
+  impl_ = this;
+}
+
+CursorFactoryOzone::~CursorFactoryOzone() {
+  DCHECK_EQ(impl_, this);
+  impl_ = NULL;
+}
+
+CursorFactoryOzone* CursorFactoryOzone::GetInstance() {
+  DCHECK(impl_) << "No CursorFactoryOzone implementation set.";
+  return impl_;
+}
+
+PlatformCursor CursorFactoryOzone::GetDefaultCursor(int type) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+PlatformCursor CursorFactoryOzone::CreateImageCursor(
+    const SkBitmap& bitmap,
+    const gfx::Point& hotspot) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+PlatformCursor CursorFactoryOzone::CreateAnimatedCursor(
+    const std::vector<SkBitmap>& bitmaps,
+    const gfx::Point& hotspot,
+    int frame_delay_ms) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+void CursorFactoryOzone::RefImageCursor(PlatformCursor cursor) {
+  NOTIMPLEMENTED();
+}
+
+void CursorFactoryOzone::UnrefImageCursor(PlatformCursor cursor) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/cursor_factory_ozone.h b/ui/ozone/public/cursor_factory_ozone.h
new file mode 100644
index 0000000..4950a31
--- /dev/null
+++ b/ui/ozone/public/cursor_factory_ozone.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_CURSOR_FACTORY_OZONE_H_
+#define UI_OZONE_PUBLIC_CURSOR_FACTORY_OZONE_H_
+
+#include <vector>
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+typedef void* PlatformCursor;
+
+class OZONE_BASE_EXPORT CursorFactoryOzone {
+ public:
+  CursorFactoryOzone();
+  virtual ~CursorFactoryOzone();
+
+  // Returns the singleton instance.
+  static CursorFactoryOzone* GetInstance();
+
+  // Return the default cursor of the specified type. The types are listed in
+  // ui/base/cursor/cursor.h. Default cursors are managed by the implementation
+  // and must live indefinitely; there's no way to know when to free them.
+  virtual PlatformCursor GetDefaultCursor(int type);
+
+  // Return a image cursor from the specified image & hotspot. Image cursors
+  // are referenced counted and have an initial refcount of 1. Therefore, each
+  // CreateImageCursor call must be matched with a call to UnrefImageCursor.
+  virtual PlatformCursor CreateImageCursor(const SkBitmap& bitmap,
+                                           const gfx::Point& hotspot);
+
+  // Return a animated cursor from the specified image & hotspot. Animated
+  // cursors are referenced counted and have an initial refcount of 1.
+  // Therefore, each CreateAnimatedCursor call must be matched with a call to
+  // UnrefImageCursor.
+  virtual PlatformCursor CreateAnimatedCursor(
+      const std::vector<SkBitmap>& bitmaps,
+      const gfx::Point& hotspot,
+      int frame_delay_ms);
+
+  // Increment platform image cursor refcount.
+  virtual void RefImageCursor(PlatformCursor cursor);
+
+  // Decrement platform image cursor refcount.
+  virtual void UnrefImageCursor(PlatformCursor cursor);
+
+ private:
+  static CursorFactoryOzone* impl_;  // not owned
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_CURSOR_FACTORY_OZONE_H_
diff --git a/ui/ozone/public/gpu_platform_support.cc b/ui/ozone/public/gpu_platform_support.cc
new file mode 100644
index 0000000..fdc5b54
--- /dev/null
+++ b/ui/ozone/public/gpu_platform_support.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/gpu_platform_support.h"
+
+#include "base/logging.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace ui {
+
+namespace {
+
+// No-op implementation of GpuPlatformSupport.
+class StubGpuPlatformSupport : public GpuPlatformSupport {
+ public:
+  // GpuPlatformSupport:
+  void OnChannelEstablished() override {}
+  // bool OnMessageReceived(const IPC::Message&) override { return false; }
+  //void RelinquishGpuResources(const base::Closure& callback) override;
+  //IPC::MessageFilter* GetMessageFilter() override { return nullptr; }
+};
+
+// void StubGpuPlatformSupport::RelinquishGpuResources(
+//     const base::Closure& callback) {
+//   callback.Run();
+// }
+
+}  // namespace
+
+GpuPlatformSupport::GpuPlatformSupport() {
+}
+
+GpuPlatformSupport::~GpuPlatformSupport() {
+}
+
+GpuPlatformSupport* CreateStubGpuPlatformSupport() {
+  return new StubGpuPlatformSupport;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/gpu_platform_support.h b/ui/ozone/public/gpu_platform_support.h
new file mode 100644
index 0000000..5e2750e
--- /dev/null
+++ b/ui/ozone/public/gpu_platform_support.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_H_
+#define UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_H_
+
+#include "base/callback.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace ui {
+
+// Platform-specific object to support a GPU process.
+//
+// See GpuPlatformSupportHost for more context.
+class OZONE_BASE_EXPORT GpuPlatformSupport {
+ public:
+  GpuPlatformSupport();
+  virtual ~GpuPlatformSupport();
+
+  // Called when the GPU process is spun up & channel established.
+  virtual void OnChannelEstablished() = 0;
+};
+
+// Create a stub implementation.
+OZONE_BASE_EXPORT GpuPlatformSupport* CreateStubGpuPlatformSupport();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_H_
diff --git a/ui/ozone/public/gpu_platform_support_host.cc b/ui/ozone/public/gpu_platform_support_host.cc
new file mode 100644
index 0000000..16b3561
--- /dev/null
+++ b/ui/ozone/public/gpu_platform_support_host.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/gpu_platform_support_host.h"
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace ui {
+
+namespace {
+
+// No-op implementations of GpuPlatformSupportHost.
+class StubGpuPlatformSupportHost : public GpuPlatformSupportHost {
+ public:
+  // GpuPlatformSupportHost:
+  void OnChannelEstablished(int host_id) override {}
+  void OnChannelDestroyed(int host_id) override {}
+};
+
+}  // namespace
+
+GpuPlatformSupportHost::GpuPlatformSupportHost() {
+}
+
+GpuPlatformSupportHost::~GpuPlatformSupportHost() {
+}
+
+GpuPlatformSupportHost* CreateStubGpuPlatformSupportHost() {
+  return new StubGpuPlatformSupportHost;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/gpu_platform_support_host.h b/ui/ozone/public/gpu_platform_support_host.h
new file mode 100644
index 0000000..d4c76af
--- /dev/null
+++ b/ui/ozone/public/gpu_platform_support_host.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_HOST_H_
+#define UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_HOST_H_
+
+#include "base/memory/ref_counted.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace ui {
+
+// Platform-specific object to support a GPU process host.
+//
+// ChromeOS on bare hardware will do display configuration and cursor
+// movement from the GPU process. This provides a conduit for the
+// messages needed to make this work.
+//
+// Under X11, we don't need any GPU messages for display configuration.
+// That's why there's no real functionality here: it's purely mechanism
+// to support additional messages needed by specific platforms.
+class OZONE_BASE_EXPORT GpuPlatformSupportHost {
+ public:
+  GpuPlatformSupportHost();
+  virtual ~GpuPlatformSupportHost();
+
+  // Called when the GPU process is spun up & channel established.
+  virtual void OnChannelEstablished(int host_id) = 0;
+
+  // Called when the GPU process is destroyed.
+  virtual void OnChannelDestroyed(int host_id) = 0;
+};
+
+// create a stub implementation.
+OZONE_BASE_EXPORT GpuPlatformSupportHost* CreateStubGpuPlatformSupportHost();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_GPU_PLATFORM_SUPPORT_HOST_H_
diff --git a/ui/ozone/public/input_controller.cc b/ui/ozone/public/input_controller.cc
new file mode 100644
index 0000000..dd508a3
--- /dev/null
+++ b/ui/ozone/public/input_controller.cc
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/input_controller.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+
+namespace ui {
+
+namespace {
+
+class StubInputController : public InputController {
+ public:
+  StubInputController();
+  ~StubInputController() override;
+
+  // InputController:
+  bool HasMouse() override;
+  bool HasTouchpad() override;
+  bool IsCapsLockEnabled() override;
+  void SetCapsLockEnabled(bool enabled) override;
+  void SetNumLockEnabled(bool enabled) override;
+  bool IsAutoRepeatEnabled() override;
+  void SetAutoRepeatEnabled(bool enabled) override;
+  void SetAutoRepeatRate(const base::TimeDelta& delay,
+                         const base::TimeDelta& interval) override;
+  void GetAutoRepeatRate(base::TimeDelta* delay,
+                         base::TimeDelta* interval) override;
+  void SetTouchpadSensitivity(int value) override;
+  void SetTapToClick(bool enabled) override;
+  void SetThreeFingerClick(bool enabled) override;
+  void SetTapDragging(bool enabled) override;
+  void SetNaturalScroll(bool enabled) override;
+  void SetMouseSensitivity(int value) override;
+  void SetPrimaryButtonRight(bool right) override;
+  void SetTapToClickPaused(bool state) override;
+  void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) override;
+  void GetTouchEventLog(const base::FilePath& out_dir,
+                        const GetTouchEventLogReply& reply) override;
+  void SetInternalTouchpadEnabled(bool enabled) override;
+  void SetInternalKeyboardFilter(bool enable_filter,
+                                 std::vector<DomCode> allowed_keys) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StubInputController);
+};
+
+StubInputController::StubInputController() {
+}
+
+StubInputController::~StubInputController() {
+}
+
+bool StubInputController::HasMouse() {
+  return false;
+}
+
+bool StubInputController::HasTouchpad() {
+  return false;
+}
+
+bool StubInputController::IsCapsLockEnabled() {
+  return false;
+}
+
+void StubInputController::SetCapsLockEnabled(bool enabled) {
+}
+
+void StubInputController::SetNumLockEnabled(bool enabled) {
+}
+
+bool StubInputController::IsAutoRepeatEnabled() {
+  return true;
+}
+
+void StubInputController::SetAutoRepeatEnabled(bool enabled) {
+}
+
+void StubInputController::SetAutoRepeatRate(const base::TimeDelta& delay,
+                                            const base::TimeDelta& interval) {
+}
+
+void StubInputController::GetAutoRepeatRate(base::TimeDelta* delay,
+                                            base::TimeDelta* interval) {
+}
+
+void StubInputController::SetTouchpadSensitivity(int value) {
+}
+
+void StubInputController::SetTapToClick(bool enabled) {
+}
+
+void StubInputController::SetThreeFingerClick(bool enabled) {
+}
+
+void StubInputController::SetTapDragging(bool enabled) {
+}
+
+void StubInputController::SetNaturalScroll(bool enabled) {
+}
+
+void StubInputController::SetMouseSensitivity(int value) {
+}
+
+void StubInputController::SetPrimaryButtonRight(bool right) {
+}
+
+void StubInputController::SetTapToClickPaused(bool state) {
+}
+
+void StubInputController::GetTouchDeviceStatus(
+    const GetTouchDeviceStatusReply& reply) {
+  reply.Run(scoped_ptr<std::string>(new std::string));
+}
+
+void StubInputController::GetTouchEventLog(const base::FilePath& out_dir,
+                                           const GetTouchEventLogReply& reply) {
+  reply.Run(make_scoped_ptr(new std::vector<base::FilePath>));
+}
+
+void StubInputController::SetInternalTouchpadEnabled(bool enabled) {
+}
+
+void StubInputController::SetInternalKeyboardFilter(
+    bool enable_filter,
+    std::vector<DomCode> allowed_keys) {
+}
+
+}  // namespace
+
+scoped_ptr<InputController> CreateStubInputController() {
+  return make_scoped_ptr(new StubInputController);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/input_controller.h b/ui/ozone/public/input_controller.h
new file mode 100644
index 0000000..b8dae61
--- /dev/null
+++ b/ui/ozone/public/input_controller.h
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_INPUT_CONTROLLER_H_
+#define UI_OZONE_PUBLIC_INPUT_CONTROLLER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace base {
+class TimeDelta;
+}
+
+namespace ui {
+
+enum class DomCode;
+
+// Platform-specific interface for controlling input devices.
+//
+// The object provides methods for the preference page to configure input
+// devices w.r.t. the user setting. On ChromeOS, this replaces the inputcontrol
+// script that is originally located at /opt/google/chrome/.
+class OZONE_EXPORT InputController {
+ public:
+  typedef base::Callback<void(scoped_ptr<std::string>)>
+      GetTouchDeviceStatusReply;
+  typedef base::Callback<void(scoped_ptr<std::vector<base::FilePath>>)>
+      GetTouchEventLogReply;
+
+  InputController() {}
+  virtual ~InputController() {}
+
+  // Functions for checking devices existence.
+  virtual bool HasMouse() = 0;
+  virtual bool HasTouchpad() = 0;
+
+  // Keyboard settings.
+  virtual bool IsCapsLockEnabled() = 0;
+  virtual void SetCapsLockEnabled(bool enabled) = 0;
+  virtual void SetNumLockEnabled(bool enabled) = 0;
+  virtual bool IsAutoRepeatEnabled() = 0;
+  virtual void SetAutoRepeatEnabled(bool enabled) = 0;
+  virtual void SetAutoRepeatRate(const base::TimeDelta& delay,
+                                 const base::TimeDelta& interval) = 0;
+  virtual void GetAutoRepeatRate(base::TimeDelta* delay,
+                                 base::TimeDelta* interval) = 0;
+
+  // Touchpad settings.
+  virtual void SetTouchpadSensitivity(int value) = 0;
+  virtual void SetTapToClick(bool enabled) = 0;
+  virtual void SetThreeFingerClick(bool enabled) = 0;
+  virtual void SetTapDragging(bool enabled) = 0;
+  virtual void SetNaturalScroll(bool enabled) = 0;
+
+  // Mouse settings.
+  virtual void SetMouseSensitivity(int value) = 0;
+  virtual void SetPrimaryButtonRight(bool right) = 0;
+
+  // Touch log collection.
+  virtual void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) = 0;
+  virtual void GetTouchEventLog(const base::FilePath& out_dir,
+                                const GetTouchEventLogReply& reply) = 0;
+
+  // Temporarily enable/disable Tap-to-click. Used to enhance the user
+  // experience in some use cases (e.g., typing, watching video).
+  virtual void SetTapToClickPaused(bool state) = 0;
+
+  virtual void SetInternalTouchpadEnabled(bool enabled) = 0;
+
+  // If |enable_filter| is true, all keys on the internal keyboard except
+  // |allowed_keys| are disabled.
+  virtual void SetInternalKeyboardFilter(bool enable_filter,
+                                         std::vector<DomCode> allowed_keys) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InputController);
+};
+
+// Create an input controller that does nothing.
+OZONE_EXPORT scoped_ptr<InputController> CreateStubInputController();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_INPUT_CONTROLLER_H_
diff --git a/ui/ozone/public/native_pixmap.h b/ui/ozone/public/native_pixmap.h
new file mode 100644
index 0000000..a1204f7
--- /dev/null
+++ b/ui/ozone/public/native_pixmap.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_NATIVE_PIXMAP_H_
+#define UI_OZONE_PUBLIC_NATIVE_PIXMAP_H_
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/overlay_transform.h"
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+namespace ui {
+
+// This represents a buffer that can be directly imported via GL for
+// rendering, or exported via dma-buf fds.
+class NativePixmap : public base::RefCountedThreadSafe<NativePixmap> {
+ public:
+  NativePixmap() {}
+
+  virtual void* /* EGLClientBuffer */ GetEGLClientBuffer() = 0;
+  virtual int GetDmaBufFd() = 0;
+  virtual int GetDmaBufPitch() = 0;
+
+  // Sets the overlay plane to switch to at the next page flip.
+  // |w| specifies the screen to display this overlay plane on.
+  // |plane_z_order| specifies the stacking order of the plane relative to the
+  // main framebuffer located at index 0.
+  // |plane_transform| specifies how the buffer is to be transformed during.
+  // composition.
+  // |buffer| to be presented by the overlay.
+  // |display_bounds| specify where it is supposed to be on the screen.
+  // |crop_rect| specifies the region within the buffer to be placed
+  // inside |display_bounds|. This is specified in texture coordinates, in the
+  // range of [0,1].
+  virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                                    int plane_z_order,
+                                    gfx::OverlayTransform plane_transform,
+                                    const gfx::Rect& display_bounds,
+                                    const gfx::RectF& crop_rect) = 0;
+
+ protected:
+  virtual ~NativePixmap() {}
+
+ private:
+  friend class base::RefCountedThreadSafe<NativePixmap>;
+
+  DISALLOW_COPY_AND_ASSIGN(NativePixmap);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_NATIVE_PIXMAP_H_
diff --git a/ui/ozone/public/overlay_candidates_ozone.cc b/ui/ozone/public/overlay_candidates_ozone.cc
new file mode 100644
index 0000000..9e01440
--- /dev/null
+++ b/ui/ozone/public/overlay_candidates_ozone.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+#include <stdlib.h>
+
+namespace ui {
+
+OverlayCandidatesOzone::OverlaySurfaceCandidate::OverlaySurfaceCandidate() {
+}
+
+OverlayCandidatesOzone::OverlaySurfaceCandidate::~OverlaySurfaceCandidate() {
+}
+
+void OverlayCandidatesOzone::CheckOverlaySupport(
+    OverlaySurfaceCandidateList* surfaces) {
+  NOTREACHED();
+}
+
+OverlayCandidatesOzone::~OverlayCandidatesOzone() {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/overlay_candidates_ozone.h b/ui/ozone/public/overlay_candidates_ozone.h
new file mode 100644
index 0000000..e71dc6f
--- /dev/null
+++ b/ui/ozone/public/overlay_candidates_ozone.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_OVERLAY_CANDIDATES_OZONE_H_
+#define UI_OZONE_PUBLIC_OVERLAY_CANDIDATES_OZONE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/ozone/ozone_base_export.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+// This class can be used to answer questions about possible overlay
+// configurations for a particular output device. We get an instance of this
+// class from SurfaceFactoryOzone given an AcceleratedWidget.
+class OZONE_BASE_EXPORT OverlayCandidatesOzone {
+ public:
+  struct OverlaySurfaceCandidate {
+    OverlaySurfaceCandidate();
+    ~OverlaySurfaceCandidate();
+
+    // Transformation to apply to layer during composition.
+    gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE;
+    // Format of the buffer to composite.
+    SurfaceFactoryOzone::BufferFormat format = SurfaceFactoryOzone::UNKNOWN;
+    // Size of the buffer, in pixels.
+    gfx::Size buffer_size;
+    // Rect on the display to position the overlay to. Input rectangle may
+    // not have integer coordinates, but when accepting for overlay, must
+    // be modified by CheckOverlaySupport to output integer values.
+    gfx::RectF display_rect;
+    // Crop within the buffer to be placed inside |display_rect|.
+    gfx::RectF crop_rect;
+    // Stacking order of the overlay plane relative to the main surface,
+    // which is 0. Signed to allow for "underlays".
+    int plane_z_order = 0;
+
+    // To be modified by the implementer if this candidate can go into
+    // an overlay.
+    bool overlay_handled = false;
+  };
+
+  typedef std::vector<OverlaySurfaceCandidate> OverlaySurfaceCandidateList;
+
+  // A list of possible overlay candidates is presented to this function.
+  // The expected result is that those candidates that can be in a separate
+  // plane are marked with |overlay_handled| set to true, otherwise they are
+  // to be traditionally composited. When setting |overlay_handled| to true,
+  // the implementation must also snap |display_rect| to integer coordinates
+  // if necessary.
+  virtual void CheckOverlaySupport(OverlaySurfaceCandidateList* surfaces);
+
+  virtual ~OverlayCandidatesOzone();
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OVERLAY_CANDIDATES_OZONE_H_
diff --git a/ui/ozone/public/overlay_manager_ozone.h b/ui/ozone/public/overlay_manager_ozone.h
new file mode 100644
index 0000000..5e8d94f
--- /dev/null
+++ b/ui/ozone/public/overlay_manager_ozone.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_OVERLAY_MANAGER_OZONE_H_
+#define UI_OZONE_PUBLIC_OVERLAY_MANAGER_OZONE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+
+class OverlayCandidatesOzone;
+
+// Responsible for providing the oracles used to decide when overlays can be
+// used.
+class OverlayManagerOzone {
+ public:
+  virtual ~OverlayManagerOzone() {}
+
+  // Get the hal struct to check for overlay support.
+  virtual scoped_ptr<OverlayCandidatesOzone> CreateOverlayCandidates(
+      gfx::AcceleratedWidget w) = 0;
+
+  // Returns true if overlays can be shown at z-index 0, replacing the main
+  // surface. Combined with surfaceless extensions, it allows for an
+  // overlay-only mode.
+  virtual bool CanShowPrimaryPlaneAsOverlay() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OVERLAY_MANAGER_OZONE_H_
diff --git a/ui/ozone/public/ozone_gpu_test_helper.h b/ui/ozone/public/ozone_gpu_test_helper.h
new file mode 100644
index 0000000..e342865
--- /dev/null
+++ b/ui/ozone/public/ozone_gpu_test_helper.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
+#define UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class Thread;
+}
+
+namespace ui {
+
+class FakeGpuProcess;
+class FakeGpuProcessHost;
+
+// Helper class for applications that do not have a dedicated GPU channel.
+//
+// This sets up message forwarding between the "gpu" and "ui" threads.
+class OZONE_EXPORT OzoneGpuTestHelper {
+ public:
+  OzoneGpuTestHelper();
+  virtual ~OzoneGpuTestHelper();
+
+  // Start processing gpu messages. The host process will be using the
+  // |gpu_task_runner| to post messages intended for the GPU thread. The "gpu"
+  // process will be using the |ui_task_runner| to post messages intended for
+  // the "ui" thread.
+  bool Initialize(
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+      const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner);
+
+ private:
+  scoped_ptr<FakeGpuProcess> fake_gpu_process_;
+  scoped_ptr<FakeGpuProcessHost> fake_gpu_process_host_;
+  scoped_ptr<base::Thread> io_helper_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzoneGpuTestHelper);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
new file mode 100644
index 0000000..b69bc28
--- /dev/null
+++ b/ui/ozone/public/ozone_platform.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/ozone/platform_object.h"
+#include "ui/ozone/platform_selection.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+namespace {
+
+bool g_platform_initialized_ui = false;
+bool g_platform_initialized_gpu = false;
+
+}
+
+OzonePlatform::OzonePlatform() {
+  DCHECK(!instance_) << "There should only be a single OzonePlatform.";
+  instance_ = this;
+  g_platform_initialized_ui = false;
+  g_platform_initialized_gpu = false;
+}
+
+OzonePlatform::~OzonePlatform() {
+  DCHECK_EQ(instance_, this);
+  instance_ = NULL;
+}
+
+// static
+void OzonePlatform::InitializeForUI() {
+  CreateInstance();
+  if (g_platform_initialized_ui)
+    return;
+  g_platform_initialized_ui = true;
+  instance_->InitializeUI();
+  // This is deliberately created after initializing so that the platform can
+  // create its own version of DDM.
+  DeviceDataManager::CreateInstance();
+}
+
+// static
+void OzonePlatform::InitializeForGPU() {
+  CreateInstance();
+  if (g_platform_initialized_gpu)
+    return;
+  g_platform_initialized_gpu = true;
+  instance_->InitializeGPU();
+}
+
+// static
+OzonePlatform* OzonePlatform::GetInstance() {
+  DCHECK(instance_) << "OzonePlatform is not initialized";
+  return instance_;
+}
+
+// static
+void OzonePlatform::CreateInstance() {
+  if (!instance_) {
+    TRACE_EVENT1("ozone",
+                 "OzonePlatform::Initialize",
+                 "platform",
+                 GetOzonePlatformName());
+    scoped_ptr<OzonePlatform> platform =
+        PlatformObject<OzonePlatform>::Create();
+
+    // TODO(spang): Currently need to leak this object.
+    OzonePlatform* pl = platform.release();
+    DCHECK_EQ(instance_, pl);
+  }
+}
+
+// static
+OzonePlatform* OzonePlatform::instance_;
+
+}  // namespace ui
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
new file mode 100644
index 0000000..590104b
--- /dev/null
+++ b/ui/ozone/public/ozone_platform.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_OZONE_PLATFORM_H_
+#define UI_OZONE_PUBLIC_OZONE_PLATFORM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+
+class CursorFactoryOzone;
+class InputController;
+class GpuPlatformSupport;
+class GpuPlatformSupportHost;
+class NativeDisplayDelegate;
+class OverlayManagerOzone;
+class PlatformWindow;
+class PlatformWindowDelegate;
+class SurfaceFactoryOzone;
+class SystemInputInjector;
+
+// Base class for Ozone platform implementations.
+//
+// Ozone platforms must override this class and implement the virtual
+// GetFooFactoryOzone() methods to provide implementations of the
+// various ozone interfaces.
+//
+// The OzonePlatform subclass can own any state needed by the
+// implementation that is shared between the various ozone interfaces,
+// such as a connection to the windowing system.
+//
+// A platform is free to use different implementations of each
+// interface depending on the context. You can, for example, create
+// different objects depending on the underlying hardware, command
+// line flags, or whatever is appropriate for the platform.
+class OZONE_EXPORT OzonePlatform {
+ public:
+  OzonePlatform();
+  virtual ~OzonePlatform();
+
+  // Initializes the subsystems/resources necessary for the UI process (e.g.
+  // events, surface, etc.)
+  static void InitializeForUI();
+
+  // Initializes the subsystems/resources necessary for the GPU process.
+  static void InitializeForGPU();
+
+  static OzonePlatform* GetInstance();
+
+  // Factory getters to override in subclasses. The returned objects will be
+  // injected into the appropriate layer at startup. Subclasses should not
+  // inject these objects themselves. Ownership is retained by OzonePlatform.
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() = 0;
+  virtual ui::OverlayManagerOzone* GetOverlayManager() = 0;
+  virtual ui::CursorFactoryOzone* GetCursorFactoryOzone() = 0;
+  virtual ui::InputController* GetInputController() = 0;
+  virtual ui::GpuPlatformSupport* GetGpuPlatformSupport() = 0;
+  virtual ui::GpuPlatformSupportHost* GetGpuPlatformSupportHost() = 0;
+  virtual scoped_ptr<SystemInputInjector> CreateSystemInputInjector() = 0;
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) = 0;
+  virtual scoped_ptr<ui::NativeDisplayDelegate>
+      CreateNativeDisplayDelegate() = 0;
+
+ private:
+  virtual void InitializeUI() = 0;
+  virtual void InitializeGPU() = 0;
+
+  static void CreateInstance();
+
+  static OzonePlatform* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatform);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OZONE_PLATFORM_H_
diff --git a/ui/ozone/public/ozone_switches.cc b/ui/ozone/public/ozone_switches.cc
new file mode 100644
index 0000000..47b6b57
--- /dev/null
+++ b/ui/ozone/public/ozone_switches.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace switches {
+
+// Specify ozone platform implementation to use.
+const char kOzonePlatform[] = "ozone-platform";
+
+// Specify location for image dumps.
+const char kOzoneDumpFile[] = "ozone-dump-file";
+
+// Specify if the accelerated path should use surfaceless rendering. In this
+// mode there is no EGL surface.
+const char kOzoneUseSurfaceless[] = "ozone-use-surfaceless";
+
+// Enable support for a single overlay plane.
+const char kOzoneTestSingleOverlaySupport[] =
+    "ozone-test-single-overlay-support";
+
+// Specifies the size of the primary display at initialization.
+const char kOzoneInitialDisplayBounds[] = "ozone-initial-display-bounds";
+
+// Specifies the physical display size in millimeters.
+const char kOzoneInitialDisplayPhysicalSizeMm[] =
+    "ozone-initial-display-physical-size-mm";
+
+}  // namespace switches
diff --git a/ui/ozone/public/ozone_switches.h b/ui/ozone/public/ozone_switches.h
new file mode 100644
index 0000000..4cc85a8
--- /dev/null
+++ b/ui/ozone/public/ozone_switches.h
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
+#define UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
+
+#include "base/compiler_specific.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace switches {
+
+OZONE_EXPORT extern const char kOzonePlatform[];
+
+OZONE_EXPORT extern const char kOzoneDumpFile[];
+
+OZONE_EXPORT extern const char kOzoneUseSurfaceless[];
+
+OZONE_EXPORT extern const char kOzoneTestSingleOverlaySupport[];
+
+OZONE_EXPORT extern const char kOzoneInitialDisplayBounds[];
+
+OZONE_EXPORT extern const char kOzoneInitialDisplayPhysicalSizeMm[];
+
+}  // namespace switches
+
+#endif  // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
diff --git a/ui/ozone/public/surface_factory_ozone.cc b/ui/ozone/public/surface_factory_ozone.cc
new file mode 100644
index 0000000..70a81ad
--- /dev/null
+++ b/ui/ozone/public/surface_factory_ozone.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+#include <stdlib.h>
+
+#include "base/command_line.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+
+SurfaceFactoryOzone::SurfaceFactoryOzone() {
+}
+
+SurfaceFactoryOzone::~SurfaceFactoryOzone() {
+}
+
+intptr_t SurfaceFactoryOzone::GetNativeDisplay() {
+  return 0;
+}
+
+scoped_ptr<SurfaceOzoneEGL> SurfaceFactoryOzone::CreateEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+scoped_ptr<SurfaceOzoneEGL>
+SurfaceFactoryOzone::CreateSurfacelessEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+scoped_ptr<SurfaceOzoneCanvas> SurfaceFactoryOzone::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+const int32* SurfaceFactoryOzone::GetEGLSurfaceProperties(
+    const int32* desired_attributes) {
+  return desired_attributes;
+}
+
+scoped_refptr<ui::NativePixmap> SurfaceFactoryOzone::CreateNativePixmap(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    BufferFormat format,
+    BufferUsage usage) {
+  return NULL;
+}
+
+bool SurfaceFactoryOzone::CanShowPrimaryPlaneAsOverlay() {
+  return false;
+}
+
+bool SurfaceFactoryOzone::CanCreateNativePixmap(BufferUsage usage) {
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/surface_factory_ozone.h b/ui/ozone/public/surface_factory_ozone.h
new file mode 100644
index 0000000..4dc5572
--- /dev/null
+++ b/ui/ozone/public/surface_factory_ozone.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_SURFACE_FACTORY_OZONE_H_
+#define UI_OZONE_PUBLIC_SURFACE_FACTORY_OZONE_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/native_library.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace ui {
+
+class NativePixmap;
+class SurfaceOzoneCanvas;
+class SurfaceOzoneEGL;
+
+// The Ozone interface allows external implementations to hook into Chromium to
+// provide a system specific implementation. The Ozone interface supports two
+// drawing modes: 1) accelerated drawing through EGL and 2) software drawing
+// through Skia.
+//
+// If you want to paint on a window with ozone, you need to create a
+// SurfaceOzoneEGL or SurfaceOzoneCanvas for that window. The platform can
+// support software, EGL, or both for painting on the window.
+// The following functionality is specific to the drawing mode and may not have
+// any meaningful implementation in the other mode. An implementation must
+// provide functionality for at least one mode.
+//
+// 1) Accelerated Drawing (EGL path):
+//
+// The following functions are specific to EGL:
+//  - GetNativeDisplay
+//  - LoadEGLGLES2Bindings
+//  - GetEGLSurfaceProperties (optional if the properties match the default
+//  Chromium ones).
+//  - CreateEGLSurfaceForWidget
+//
+// 2) Software Drawing (Skia):
+//
+// The following function is specific to the software path:
+//  - CreateCanvasForWidget
+//
+// The accelerated path can optionally provide support for the software drawing
+// path.
+//
+// The remaining functions are not covered since they are needed in both drawing
+// modes (See comments bellow for descriptions).
+class OZONE_BASE_EXPORT SurfaceFactoryOzone {
+ public:
+  // Describes overlay buffer format.
+  // TODO: this is a placeholder for now and will be populated with more
+  // formats once we know what sorts of content, video, etc. we can support.
+  enum BufferFormat {
+    UNKNOWN,
+    BGRA_8888,
+    RGBX_8888,
+    BUFFER_FORMAT_LAST = RGBX_8888
+  };
+
+  enum BufferUsage {
+    MAP,
+    PERSISTENT_MAP,
+    SCANOUT,
+  };
+
+  typedef void* (*GLGetProcAddressProc)(const char* name);
+  typedef base::Callback<void(base::NativeLibrary)> AddGLLibraryCallback;
+  typedef base::Callback<void(GLGetProcAddressProc)>
+      SetGLGetProcAddressProcCallback;
+
+  // Returns native platform display handle. This is used to obtain the EGL
+  // display connection for the native display.
+  virtual intptr_t GetNativeDisplay();
+
+  // Create SurfaceOzoneEGL for the specified gfx::AcceleratedWidget.
+  //
+  // Note: When used from content, this is called in the GPU process. The
+  // platform must support creation of SurfaceOzoneEGL from the GPU process
+  // using only the handle contained in gfx::AcceleratedWidget.
+  virtual scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget);
+
+  // Create an EGL surface that isn't backed by any buffers, and is used
+  // for overlay-only displays. This will return NULL if this mode is
+  // not supported.
+  virtual scoped_ptr<SurfaceOzoneEGL> CreateSurfacelessEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget);
+
+  // Create SurfaceOzoneCanvas for the specified gfx::AcceleratedWidget.
+  //
+  // Note: The platform must support creation of SurfaceOzoneCanvas from the
+  // Browser Process using only the handle contained in gfx::AcceleratedWidget.
+  virtual scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget widget);
+
+  // Sets up GL bindings for the native surface. Takes two callback parameters
+  // that allow Ozone to register the GL bindings.
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) = 0;
+
+  // Returns an array of EGL properties, which can be used in any EGL function
+  // used to select a display configuration. Note that all properties should be
+  // immediately followed by the corresponding desired value and array should be
+  // terminated with EGL_NONE. Ownership of the array is not transferred to
+  // caller. desired_list contains list of desired EGL properties and values.
+  virtual const int32* GetEGLSurfaceProperties(const int32* desired_list);
+
+  // Create a single native buffer to be used for overlay planes or zero copy
+  // for |widget| representing a particular display controller or default
+  // display controller for kNullAcceleratedWidget.
+  // It can be called on any thread.
+  virtual scoped_refptr<NativePixmap> CreateNativePixmap(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      BufferFormat format,
+      BufferUsage usage);
+
+  // Returns true if overlays can be shown at z-index 0, replacing the main
+  // surface. Combined with surfaceless extensions, it allows for an
+  // overlay-only mode.
+  virtual bool CanShowPrimaryPlaneAsOverlay();
+
+  // Returns true if the platform is able to create buffers for a specific usage
+  // such as MAP for zero copy or SCANOUT for display controller.
+  virtual bool CanCreateNativePixmap(BufferUsage usage);
+
+ protected:
+  SurfaceFactoryOzone();
+  virtual ~SurfaceFactoryOzone();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SurfaceFactoryOzone);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_SURFACE_FACTORY_OZONE_H_
diff --git a/ui/ozone/public/surface_ozone_canvas.h b/ui/ozone/public/surface_ozone_canvas.h
new file mode 100644
index 0000000..42347c3
--- /dev/null
+++ b/ui/ozone/public/surface_ozone_canvas.h
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_SURFACE_OZONE_CANVAS_H_
+#define UI_OZONE_PUBLIC_SURFACE_OZONE_CANVAS_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "skia/ext/refptr.h"
+#include "ui/ozone/ozone_base_export.h"
+
+class SkSurface;
+
+namespace gfx {
+class Size;
+class VSyncProvider;
+}
+
+namespace ui {
+
+// The platform-specific part of an software output. The class is intended
+// for use when no EGL/GLES2 acceleration is possible.
+// This class owns any bits that the ozone implementation needs freed when
+// the software output is destroyed.
+class OZONE_BASE_EXPORT SurfaceOzoneCanvas {
+ public:
+  virtual ~SurfaceOzoneCanvas() {}
+
+  // Returns an SkSurface for drawing on the window.
+  virtual skia::RefPtr<SkSurface> GetSurface() = 0;
+
+  // Attempts to resize the canvas to match the viewport size. After
+  // resizing, the compositor must call GetCanvas() to get the next
+  // canvas - this invalidates any previous canvas from GetCanvas().
+  virtual void ResizeCanvas(const gfx::Size& viewport_size) = 0;
+
+  // Present the current canvas. After presenting, the compositor must
+  // call GetCanvas() to get the next canvas - this invalidates any
+  // previous canvas from GetCanvas().
+  //
+  // The implementation may assume that any pixels outside the damage
+  // rectangle are unchanged since the previous call to PresentCanvas().
+  virtual void PresentCanvas(const gfx::Rect& damage) = 0;
+
+  // Returns a gfx::VsyncProvider for this surface. Note that this may be
+  // called after we have entered the sandbox so if there are operations (e.g.
+  // opening a file descriptor providing vsync events) that must be done
+  // outside of the sandbox, they must have been completed in
+  // InitializeHardware. Returns an empty scoped_ptr on error.
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_SURFACE_OZONE_CANVAS_H_
diff --git a/ui/ozone/public/surface_ozone_egl.cc b/ui/ozone/public/surface_ozone_egl.cc
new file mode 100644
index 0000000..92330f3
--- /dev/null
+++ b/ui/ozone/public/surface_ozone_egl.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+
+bool SurfaceOzoneEGL::IsUniversalDisplayLinkDevice() {
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/surface_ozone_egl.h b/ui/ozone/public/surface_ozone_egl.h
new file mode 100644
index 0000000..3056dec
--- /dev/null
+++ b/ui/ozone/public/surface_ozone_egl.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_SURFACE_OZONE_EGL_H_
+#define UI_OZONE_PUBLIC_SURFACE_OZONE_EGL_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/ozone/ozone_base_export.h"
+
+namespace gfx {
+class Size;
+class VSyncProvider;
+}
+
+namespace ui {
+class NativePixmap;
+
+typedef base::Callback<void(gfx::SwapResult)> SwapCompletionCallback;
+
+// The platform-specific part of an EGL surface.
+//
+// This class owns any bits that the ozone implementation needs freed when
+// the EGL surface is destroyed.
+class OZONE_BASE_EXPORT SurfaceOzoneEGL {
+ public:
+  virtual ~SurfaceOzoneEGL() {}
+
+  // Returns the EGL native window for rendering onto this surface.
+  // This can be used to to create a GLSurface.
+  virtual intptr_t /* EGLNativeWindowType */ GetNativeWindow() = 0;
+
+  // Attempts to resize the EGL native window to match the viewport
+  // size.
+  virtual bool ResizeNativeWindow(const gfx::Size& viewport_size) = 0;
+
+  // Called after we swap buffers. This is usually a no-op but can
+  // be used to present the new front buffer if the platform requires this.
+  virtual bool OnSwapBuffers() = 0;
+
+  // Called after we swap buffers. This is usually a no-op but can
+  // be used to present the new front buffer if the platform requires this.
+  // The callback should be run on the calling thread
+  // (i.e. same thread SwapBuffersAsync is called).
+  virtual bool OnSwapBuffersAsync(const SwapCompletionCallback& callback) = 0;
+
+  // Returns a gfx::VsyncProvider for this surface. Note that this may be
+  // called after we have entered the sandbox so if there are operations (e.g.
+  // opening a file descriptor providing vsync events) that must be done
+  // outside of the sandbox, they must have been completed in
+  // InitializeHardware. Returns an empty scoped_ptr on error.
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() = 0;
+
+  // Returns true if the surface is created on a UDL device.
+  virtual bool IsUniversalDisplayLinkDevice();
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_SURFACE_OZONE_EGL_H_
diff --git a/ui/ozone/public/system_input_injector.h b/ui/ozone/public/system_input_injector.h
new file mode 100644
index 0000000..0bfb278
--- /dev/null
+++ b/ui/ozone/public/system_input_injector.h
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_SYSTEM_INPUT_INJECTOR_H_
+#define UI_OZONE_PUBLIC_SYSTEM_INPUT_INJECTOR_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace gfx {
+class PointF;
+}  // namespace gfx
+
+namespace ui {
+class Event;
+
+// Interface for converting input into ui::Events and injecting them to the
+// Ozone platform.
+class OZONE_EXPORT SystemInputInjector {
+ public:
+  SystemInputInjector() {}
+  virtual ~SystemInputInjector() {}
+
+  // Moves the cursor on the screen and generates the corresponding MouseMove or
+  // MouseDragged event.  |location| is in physical screen co-ordinates,
+  // independent of the scale factor and the display rotation settings.
+  virtual void MoveCursorTo(const gfx::PointF& location) = 0;
+
+  // Simulates a mouse button click.  |button| must be one of
+  // EF_LEFT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON or EF_MIDDLE_MOUSE_BUTTON.
+  // SystemInputInjector will apply the correct modifiers (shift, ctrl, etc).
+  virtual void InjectMouseButton(EventFlags button, bool down) = 0;
+
+  // |delta_x| and |delta_y| are in physical pixels independent of the scale
+  // factor.
+  virtual void InjectMouseWheel(int delta_x, int delta_y) = 0;
+
+  // Simulates a key event.  SystemInputInjector maps |physical_key| to the
+  // correct logical key based on the current keyboard layout. |down| is true
+  // for presses. If |suppress_auto_repeat| is set, the platform must not
+  // auto-repeat the event.
+  virtual void InjectKeyEvent(DomCode physical_key,
+                              bool down,
+                              bool suppress_auto_repeat) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemInputInjector);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_SYSTEM_INPUT_INJECTOR_H_
diff --git a/ui/ozone/run_all_unittests.cc b/ui/ozone/run_all_unittests.cc
new file mode 100644
index 0000000..5f038b3
--- /dev/null
+++ b/ui/ozone/run_all_unittests.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+namespace {
+
+class OzoneTestSuite : public base::TestSuite {
+ public:
+  OzoneTestSuite(int argc, char** argv);
+
+ protected:
+  // base::TestSuite:
+  void Initialize() override;
+  void Shutdown() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OzoneTestSuite);
+};
+
+OzoneTestSuite::OzoneTestSuite(int argc, char** argv)
+    : base::TestSuite(argc, argv) {}
+
+void OzoneTestSuite::Initialize() {
+  base::TestSuite::Initialize();
+}
+
+void OzoneTestSuite::Shutdown() {
+  base::TestSuite::Shutdown();
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  OzoneTestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTests(argc,
+                               argv,
+                               base::Bind(&OzoneTestSuite::Run,
+                                          base::Unretained(&test_suite)));
+}