diff options
author | dnicoara@chromium.org <dnicoara@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-12 07:28:12 +0000 |
---|---|---|
committer | dnicoara@chromium.org <dnicoara@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-12 07:28:12 +0000 |
commit | 9f5881b7eff7046cb060825e4a3189def1370c5b (patch) | |
tree | 666c709ca97a910f6a42c57d3854603e372ea36f | |
parent | 34fadad9cca3303423ae114c71ca421f889e3259 (diff) | |
download | chromium_src-9f5881b7eff7046cb060825e4a3189def1370c5b.zip chromium_src-9f5881b7eff7046cb060825e4a3189def1370c5b.tar.gz chromium_src-9f5881b7eff7046cb060825e4a3189def1370c5b.tar.bz2 |
[Ozone] Adding HardwareDisplayController implementation
BUG=
Review URL: https://codereview.chromium.org/26538005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228329 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ui/gfx/ozone/impl/drm_skbitmap_ozone.h | 1 | ||||
-rw-r--r-- | ui/gfx/ozone/impl/drm_wrapper_ozone.cc | 86 | ||||
-rw-r--r-- | ui/gfx/ozone/impl/drm_wrapper_ozone.h | 79 | ||||
-rw-r--r-- | ui/gfx/ozone/impl/hardware_display_controller_ozone.cc | 110 | ||||
-rw-r--r-- | ui/gfx/ozone/impl/hardware_display_controller_ozone.h | 170 |
5 files changed, 446 insertions, 0 deletions
diff --git a/ui/gfx/ozone/impl/drm_skbitmap_ozone.h b/ui/gfx/ozone/impl/drm_skbitmap_ozone.h index b694747..20af5aa 100644 --- a/ui/gfx/ozone/impl/drm_skbitmap_ozone.h +++ b/ui/gfx/ozone/impl/drm_skbitmap_ozone.h @@ -32,6 +32,7 @@ class DrmSkBitmapOzone : public SkBitmap { private: friend class DrmAllocator; + friend class HardwareDisplayControllerOzone; void set_handle(uint32_t handle) { handle_ = handle; }; void set_framebuffer(uint32_t framebuffer) { framebuffer_ = framebuffer; }; diff --git a/ui/gfx/ozone/impl/drm_wrapper_ozone.cc b/ui/gfx/ozone/impl/drm_wrapper_ozone.cc new file mode 100644 index 0000000..8536786 --- /dev/null +++ b/ui/gfx/ozone/impl/drm_wrapper_ozone.cc @@ -0,0 +1,86 @@ +// 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/gfx/ozone/impl/drm_wrapper_ozone.h" + +#include <fcntl.h> +#include <unistd.h> +#include <xf86drmMode.h> + +#include "base/logging.h" + +namespace gfx { + +DrmWrapperOzone::DrmWrapperOzone(const char* device_path) { + fd_ = open(device_path, O_RDWR | O_CLOEXEC); +} + +DrmWrapperOzone::~DrmWrapperOzone() { + if (fd_ >= 0) + close(fd_); +} + +drmModeCrtc* DrmWrapperOzone::GetCrtc(uint32_t crtc_id) { + CHECK(fd_ >= 0); + return drmModeGetCrtc(fd_, crtc_id); +} + +void DrmWrapperOzone::FreeCrtc(drmModeCrtc* crtc) { + drmModeFreeCrtc(crtc); +} + +bool DrmWrapperOzone::SetCrtc(uint32_t crtc_id, + uint32_t framebuffer, + uint32_t* connectors, + drmModeModeInfo* mode) { + CHECK(fd_ >= 0); + return !drmModeSetCrtc(fd_, crtc_id, framebuffer, 0, 0, connectors, 1, mode); +} + +bool DrmWrapperOzone::SetCrtc(drmModeCrtc* crtc, uint32_t* connectors) { + CHECK(fd_ >= 0); + return !drmModeSetCrtc(fd_, + crtc->crtc_id, + crtc->buffer_id, + crtc->x, + crtc->y, + connectors, + 1, + &crtc->mode); +} + +bool DrmWrapperOzone::AddFramebuffer(const drmModeModeInfo& mode, + uint8_t depth, + uint8_t bpp, + uint32_t stride, + uint32_t handle, + uint32_t* framebuffer) { + CHECK(fd_ >= 0); + return !drmModeAddFB(fd_, + mode.hdisplay, + mode.vdisplay, + depth, + bpp, + stride, + handle, + framebuffer); +} + +bool DrmWrapperOzone::RemoveFramebuffer(uint32_t framebuffer) { + CHECK(fd_ >= 0); + return !drmModeRmFB(fd_, framebuffer); +} + +bool DrmWrapperOzone::PageFlip(uint32_t crtc_id, + uint32_t framebuffer, + void* data) { + CHECK(fd_ >= 0); + return !drmModePageFlip(fd_, + crtc_id, + framebuffer, + DRM_MODE_PAGE_FLIP_EVENT, + data); +} + +} // namespace gfx diff --git a/ui/gfx/ozone/impl/drm_wrapper_ozone.h b/ui/gfx/ozone/impl/drm_wrapper_ozone.h new file mode 100644 index 0000000..dbd12bc --- /dev/null +++ b/ui/gfx/ozone/impl/drm_wrapper_ozone.h @@ -0,0 +1,79 @@ +// 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_GFX_OZONE_IMPL_DRM_WRAPPER_OZONE_H_ +#define UI_GFX_OZONE_IMPL_DRM_WRAPPER_OZONE_H_ + +#include <stdint.h> + +#include "base/basictypes.h" + +typedef struct _drmModeCrtc drmModeCrtc; +typedef struct _drmModeModeInfo drmModeModeInfo; + +namespace gfx { + +// 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 DrmWrapperOzone { + public: + DrmWrapperOzone(const char* device_path); + virtual ~DrmWrapperOzone(); + + // 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 drmModeCrtc* GetCrtc(uint32_t crtc_id); + + // Frees the CRTC mode object. + virtual void FreeCrtc(drmModeCrtc* crtc); + + // 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, + 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, uint32_t* connectors); + + // Register a buffer with the CRTC. On successful registration, the CRTC will + // assign a framebuffer ID to |framebuffer|. + virtual bool AddFramebuffer(const drmModeModeInfo& mode, + 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); + + // 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_|. |data| is a generic pointer to some information the user + // will receive when processing the pageflip event. + virtual bool PageFlip(uint32_t crtc_id, uint32_t framebuffer, void* data); + + int get_fd() const { return fd_; } + + protected: + // The file descriptor associated with this wrapper. All DRM operations will + // be performed using this FD. + int fd_; + + private: + DISALLOW_COPY_AND_ASSIGN(DrmWrapperOzone); +}; + +} // namespace gfx + +#endif // UI_GFX_OZONE_IMPL_DRM_WRAPPER_OZONE_H_ diff --git a/ui/gfx/ozone/impl/hardware_display_controller_ozone.cc b/ui/gfx/ozone/impl/hardware_display_controller_ozone.cc new file mode 100644 index 0000000..c8c6459 --- /dev/null +++ b/ui/gfx/ozone/impl/hardware_display_controller_ozone.cc @@ -0,0 +1,110 @@ +// 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/gfx/ozone/impl/hardware_display_controller_ozone.h" + +#include <errno.h> +#include <string.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "ui/gfx/ozone/impl/drm_skbitmap_ozone.h" +#include "ui/gfx/ozone/impl/drm_wrapper_ozone.h" +#include "ui/gfx/ozone/impl/software_surface_ozone.h" + +namespace gfx { + +HardwareDisplayControllerOzone::HardwareDisplayControllerOzone() + : drm_(NULL), + connector_id_(0), + crtc_id_(0), + mode_(), + saved_crtc_(NULL), + state_(UNASSOCIATED), + surface_() { +} + +void HardwareDisplayControllerOzone::SetControllerInfo( + DrmWrapperOzone* drm, + uint32_t connector_id, + uint32_t crtc_id, + drmModeModeInfo mode) { + drm_ = drm; + connector_id_ = connector_id; + crtc_id_ = crtc_id; + mode_ = mode; + saved_crtc_ = drm_->GetCrtc(crtc_id_); + state_ = UNINITIALIZED; +} + +HardwareDisplayControllerOzone::~HardwareDisplayControllerOzone() { + if (saved_crtc_) { + if (!drm_->SetCrtc(saved_crtc_, &connector_id_)) + DLOG(ERROR) << "Failed to restore CRTC state: " << strerror(errno); + drm_->FreeCrtc(saved_crtc_); + } + + if (surface_.get()) { + // Unregister the buffers. + for (int i = 0; i < 2; ++i) { + if (!drm_->RemoveFramebuffer(surface_->bitmaps_[i]->get_framebuffer())) + DLOG(ERROR) << "Failed to remove FB: " << strerror(errno); + } + } +} + +bool +HardwareDisplayControllerOzone::BindSurfaceToController( + SoftwareSurfaceOzone* surface) { + CHECK(state_ == UNINITIALIZED); + + // Register the buffers. + for (int i = 0; i < 2; ++i) { + uint32_t fb_id; + if (!drm_->AddFramebuffer(mode_, + surface->bitmaps_[i]->GetColorDepth(), + surface->bitmaps_[i]->bytesPerPixel() << 3, + surface->bitmaps_[i]->rowBytes(), + surface->bitmaps_[i]->get_handle(), + &fb_id)) { + DLOG(ERROR) << "Failed to register framebuffer: " << strerror(errno); + state_ = FAILED; + return false; + } + surface->bitmaps_[i]->set_framebuffer(fb_id); + } + + surface_.reset(surface); + state_ = SURFACE_INITIALIZED; + return true; +} + +bool HardwareDisplayControllerOzone::SchedulePageFlip() { + CHECK(state_ == SURFACE_INITIALIZED || state_ == INITIALIZED); + + if (state_ == SURFACE_INITIALIZED) { + // Perform the initial modeset. + if (!drm_->SetCrtc(crtc_id_, + surface_->GetFramebufferId(), + &connector_id_, + &mode_)) { + DLOG(ERROR) << "Cannot set CRTC: " << strerror(errno); + state_ = FAILED; + return false; + } else { + state_ = INITIALIZED; + } + } + + if (!drm_->PageFlip(crtc_id_, + surface_->GetFramebufferId(), + this)) { + state_ = FAILED; + LOG(ERROR) << "Cannot page flip: " << strerror(errno); + return false; + } + return true; +} + +} // namespace gfx diff --git a/ui/gfx/ozone/impl/hardware_display_controller_ozone.h b/ui/gfx/ozone/impl/hardware_display_controller_ozone.h new file mode 100644 index 0000000..ad0d1f4 --- /dev/null +++ b/ui/gfx/ozone/impl/hardware_display_controller_ozone.h @@ -0,0 +1,170 @@ +// 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_GFX_OZONE_IMPL_HARDWARE_DISPLAY_CONTROLLER_OZONE_H_ +#define UI_GFX_OZONE_IMPL_HARDWARE_DISPLAY_CONTROLLER_OZONE_H_ + +#include <stddef.h> +#include <stdint.h> +#include <xf86drmMode.h> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/ozone/impl/drm_wrapper_ozone.h" + +namespace gfx { + +class SoftwareSurfaceOzone; + +// 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. +// +// TODO(dnicoara) Need to have a way to detect events (such as monitor +// connected or disconnected). +class HardwareDisplayControllerOzone { + public: + // Controller states. The state transitions will happen from top to bottom. + enum State { + // When we allocate a HDCO as a stub. At this point there is no connector + // and CRTC associated with this device. + UNASSOCIATED, + + // When |SetControllerInfo| is called and the HDCO has the information of + // the hardware it will control. At this point it knows everything it needs + // to control the hardware but doesn't have a surface. + UNINITIALIZED, + + // A surface is associated with the HDCO. This means that the controller can + // potentially display the backing surface to the display. Though the + // surface framebuffer still needs to be registered with the CRTC. + SURFACE_INITIALIZED, + + // The CRTC now knows about the surface attributes. + INITIALIZED, + + // Error state if any of the initialization steps fail. + FAILED, + }; + + HardwareDisplayControllerOzone(); + + ~HardwareDisplayControllerOzone(); + + // Set the hardware configuration for this HDCO. Once this is set, the HDCO is + // responsible for keeping track of the connector and CRTC and cleaning up + // when it is destroyed. + void SetControllerInfo(DrmWrapperOzone* drm, + uint32_t connector_id, + uint32_t crtc_id, + drmModeModeInfo mode); + + // Associate the HDCO with a surface implementation and initialize it. + bool BindSurfaceToController(SoftwareSurfaceOzone* surface); + + // Schedules the |surface_|'s framebuffer 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 framebuffer 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. + bool SchedulePageFlip(); + + State get_state() const { return state_; }; + + int get_fd() const { return drm_->get_fd(); }; + + const drmModeModeInfo& get_mode() const { return mode_; }; + + SoftwareSurfaceOzone* get_surface() const { return surface_.get(); }; + + private: + // Object containing the connection to the graphics device and wraps the API + // calls to control it. + DrmWrapperOzone* drm_; + + // TODO(dnicoara) Need to allow a CRTC to have multiple connectors. + uint32_t connector_id_; + + uint32_t crtc_id_; + + // TODO(dnicoara) Need to store all the modes. + drmModeModeInfo mode_; + + // Saved CRTC state from before we used it. Need it to restore state once we + // are finished using this device. + drmModeCrtc* saved_crtc_; + + State state_; + + scoped_ptr<SoftwareSurfaceOzone> surface_; + + DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerOzone); +}; + +} // namespace gfx + +#endif // UI_GFX_OZONE_IMPL_HARDWARE_DISPLAY_CONTROLLER_OZONE_H_ |