| // 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 <utility> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/free_deleter.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/posix/safe_strerror.h" |
| #include "base/task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/trace_event_argument.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_atomic.h" |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h" |
| |
| |
| 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)); |
| std::unique_ptr<base::trace_event::TracedValue> drm_data( |
| new base::trace_event::TracedValue()); |
| drm_data->SetInteger("frame_count", 1); |
| drm_data->SetInteger("vblank.tv_sec", vblank.tv_sec); |
| drm_data->SetInteger("vblank.tv_usec", vblank.tv_usec); |
| TRACE_EVENT_INSTANT1("benchmark,drm", "DrmEventFlipComplete", |
| TRACE_EVENT_SCOPE_THREAD, "data", |
| std::move(drm_data)); |
| 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); |
| } |
| |
| // TODO(robert.bradford): Replace with libdrm structures after libdrm roll. |
| // https://crbug.com/586475 |
| struct DrmColorLut { |
| uint16_t red; |
| uint16_t green; |
| uint16_t blue; |
| uint16_t reserved; |
| }; |
| |
| struct DrmColorCtm { |
| int64_t ctm_coeff[9]; |
| }; |
| |
| struct DrmModeCreateBlob { |
| uint64_t data; |
| uint32_t length; |
| uint32_t blob_id; |
| }; |
| |
| struct DrmModeDestroyBlob { |
| uint32_t blob_id; |
| }; |
| |
| #ifndef DRM_IOCTL_MODE_CREATEPROPBLOB |
| #define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct DrmModeCreateBlob) |
| #endif |
| |
| #ifndef DRM_IOCTL_MODE_DESTROYPROPBLOB |
| #define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct DrmModeDestroyBlob) |
| #endif |
| |
| int CreatePropertyBlob(int fd, const void* data, size_t length, uint32_t* id) { |
| DrmModeCreateBlob create; |
| int ret; |
| |
| if (length >= 0xffffffff) |
| return -ERANGE; |
| |
| memset(&create, 0, sizeof(create)); |
| |
| create.length = length; |
| create.data = (uintptr_t)data; |
| create.blob_id = 0; |
| *id = 0; |
| |
| ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create); |
| ret = ret < 0 ? -errno : ret; |
| if (ret != 0) |
| return ret; |
| |
| *id = create.blob_id; |
| return 0; |
| } |
| |
| int DestroyPropertyBlob(int fd, uint32_t id) { |
| DrmModeDestroyBlob destroy; |
| int ret; |
| |
| memset(&destroy, 0, sizeof(destroy)); |
| destroy.blob_id = id; |
| ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy); |
| return ret < 0 ? -errno : ret; |
| } |
| |
| using ScopedDrmColorLutPtr = std::unique_ptr<DrmColorLut, base::FreeDeleter>; |
| using ScopedDrmColorCtmPtr = std::unique_ptr<DrmColorCtm, base::FreeDeleter>; |
| |
| ScopedDrmColorLutPtr CreateLutBlob( |
| const std::vector<display::GammaRampRGBEntry>& source) { |
| TRACE_EVENT0("drm", "CreateLutBlob"); |
| if (source.empty()) |
| return nullptr; |
| |
| ScopedDrmColorLutPtr lut( |
| static_cast<DrmColorLut*>(malloc(sizeof(DrmColorLut) * source.size()))); |
| DrmColorLut* p = lut.get(); |
| for (size_t i = 0; i < source.size(); ++i) { |
| p[i].red = source[i].r; |
| p[i].green = source[i].g; |
| p[i].blue = source[i].b; |
| } |
| return lut; |
| } |
| |
| ScopedDrmColorCtmPtr CreateCTMBlob( |
| const std::vector<float>& correction_matrix) { |
| if (correction_matrix.empty()) |
| return nullptr; |
| |
| ScopedDrmColorCtmPtr ctm( |
| static_cast<DrmColorCtm*>(malloc(sizeof(DrmColorCtm)))); |
| for (size_t i = 0; i < arraysize(ctm->ctm_coeff); ++i) { |
| if (correction_matrix[i] < 0) { |
| ctm->ctm_coeff[i] = static_cast<uint64_t>( |
| -correction_matrix[i] * (static_cast<uint64_t>(1) << 32)); |
| ctm->ctm_coeff[i] |= static_cast<uint64_t>(1) << 63; |
| } else { |
| ctm->ctm_coeff[i] = static_cast<uint64_t>( |
| correction_matrix[i] * (static_cast<uint64_t>(1) << 32)); |
| } |
| } |
| return ctm; |
| } |
| |
| bool SetBlobProperty(int fd, |
| uint32_t object_id, |
| uint32_t object_type, |
| uint32_t prop_id, |
| const char* property_name, |
| unsigned char* data, |
| size_t length) { |
| uint32_t blob_id = 0; |
| int res; |
| |
| if (data) { |
| res = CreatePropertyBlob(fd, data, length, &blob_id); |
| if (res != 0) { |
| LOG(ERROR) << "Error creating property blob: " << base::safe_strerror(res) |
| << " for property " << property_name; |
| return false; |
| } |
| } |
| |
| bool success = false; |
| res = drmModeObjectSetProperty(fd, object_id, object_type, prop_id, blob_id); |
| if (res != 0) { |
| LOG(ERROR) << "Error updating property: " << base::safe_strerror(res) |
| << " for property " << property_name; |
| } else { |
| success = true; |
| } |
| if (blob_id != 0) |
| DestroyPropertyBlob(fd, blob_id); |
| return success; |
| } |
| |
| std::vector<display::GammaRampRGBEntry> ResampleLut( |
| const std::vector<display::GammaRampRGBEntry>& lut_in, |
| size_t desired_size) { |
| TRACE_EVENT1("drm", "ResampleLut", "desired_size", desired_size); |
| if (lut_in.empty()) |
| return std::vector<display::GammaRampRGBEntry>(); |
| |
| if (lut_in.size() == desired_size) |
| return lut_in; |
| |
| std::vector<display::GammaRampRGBEntry> result; |
| result.resize(desired_size); |
| |
| for (size_t i = 0; i < desired_size; ++i) { |
| size_t base_index = lut_in.size() * i / desired_size; |
| size_t remaining = lut_in.size() * i % desired_size; |
| if (base_index < lut_in.size() - 1) { |
| result[i].r = lut_in[base_index].r + |
| (lut_in[base_index + 1].r - lut_in[base_index].r) * |
| remaining / desired_size; |
| result[i].g = lut_in[base_index].g + |
| (lut_in[base_index + 1].g - lut_in[base_index].g) * |
| remaining / desired_size; |
| result[i].b = lut_in[base_index].b + |
| (lut_in[base_index + 1].b - lut_in[base_index].b) * |
| remaining / desired_size; |
| } else { |
| result[i] = lut_in.back(); |
| } |
| } |
| |
| return result; |
| } |
| |
| } // namespace |
| |
| class DrmDevice::PageFlipManager { |
| public: |
| PageFlipManager() : next_id_(0) {} |
| ~PageFlipManager() {} |
| |
| 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; |
| it->pending_calls--; |
| if (it->pending_calls) |
| return; |
| |
| callbacks_.erase(it); |
| callback.Run(frame, seconds, useconds); |
| } |
| |
| uint64_t GetNextId() { return next_id_++; } |
| |
| void RegisterCallback(uint64_t id, |
| uint64_t pending_calls, |
| const DrmDevice::PageFlipCallback& callback) { |
| callbacks_.push_back({id, pending_calls, callback}); |
| } |
| |
| private: |
| struct PageFlip { |
| uint64_t id; |
| uint32_t pending_calls; |
| DrmDevice::PageFlipCallback callback; |
| }; |
| |
| struct FindCallback { |
| explicit FindCallback(uint64_t id) : id(id) {} |
| |
| bool operator()(const PageFlip& flip) const { return flip.id == id; } |
| |
| const uint64_t id; |
| }; |
| |
| uint64_t next_id_; |
| |
| std::vector<PageFlip> callbacks_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PageFlipManager); |
| }; |
| |
| class DrmDevice::IOWatcher : public base::MessagePumpLibevent::Watcher { |
| public: |
| IOWatcher(int fd, DrmDevice::PageFlipManager* page_flip_manager) |
| : page_flip_manager_(page_flip_manager), controller_(FROM_HERE), fd_(fd) { |
| Register(); |
| } |
| |
| ~IOWatcher() override { Unregister(); } |
| |
| private: |
| void Register() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| base::MessageLoopForIO::current()->WatchFileDescriptor( |
| fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this); |
| } |
| |
| void Unregister() { |
| DCHECK(base::MessageLoopForIO::IsCurrent()); |
| controller_.StopWatchingFileDescriptor(); |
| } |
| |
| // 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::PageFlipManager::OnPageFlip, |
| base::Unretained(page_flip_manager_)))) |
| Unregister(); |
| } |
| |
| void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); } |
| |
| DrmDevice::PageFlipManager* page_flip_manager_; |
| |
| base::MessagePumpLibevent::FileDescriptorWatcher controller_; |
| |
| int fd_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IOWatcher); |
| }; |
| |
| DrmDevice::DrmDevice(const base::FilePath& device_path, |
| base::File file, |
| bool is_primary_device) |
| : device_path_(device_path), |
| file_(std::move(file)), |
| page_flip_manager_(new PageFlipManager()), |
| is_primary_device_(is_primary_device) {} |
| |
| DrmDevice::~DrmDevice() {} |
| |
| 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; |
| } |
| |
| // 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()); |
| |
| LOG_IF(WARNING, use_atomic && !plane_manager_) |
| << "Drm atomic requested but capabilities don't allow it. Falling back " |
| "to legacy page flip."; |
| 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; |
| } |
| |
| watcher_.reset( |
| new IOWatcher(file_.GetPlatformFile(), page_flip_manager_.get())); |
| |
| return true; |
| } |
| |
| 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, |
| connectors.data(), 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, connectors.data(), |
| 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::AddFramebuffer2(uint32_t width, |
| uint32_t height, |
| uint32_t format, |
| uint32_t handles[4], |
| uint32_t strides[4], |
| uint32_t offsets[4], |
| uint64_t modifiers[4], |
| uint32_t* framebuffer, |
| uint32_t flags) { |
| DCHECK(file_.IsValid()); |
| TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handles[0]); |
| return !drmModeAddFB2WithModifiers(file_.GetPlatformFile(), width, height, |
| format, handles, strides, offsets, |
| modifiers, framebuffer, flags); |
| } |
| |
| 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, |
| const PageFlipCallback& callback) { |
| DCHECK(file_.IsValid()); |
| TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id, "framebuffer", |
| framebuffer); |
| |
| // 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, 1, callback); |
| 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; |
| } |
| |
| 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_EVENT2("drm", "DrmDevice::SetCursor", "crtc_id", crtc_id, "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()); |
| TRACE_EVENT1("drm", "DrmDevice::MoveCursor", "crtc_id", crtc_id); |
| 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(drmModeAtomicReq* properties, |
| uint32_t flags, |
| uint32_t crtc_count, |
| const PageFlipCallback& callback) { |
| uint64_t id = 0; |
| bool page_flip_event_requested = flags & DRM_MODE_PAGE_FLIP_EVENT; |
| |
| if (page_flip_event_requested) |
| id = page_flip_manager_->GetNextId(); |
| |
| if (!drmModeAtomicCommit(file_.GetPlatformFile(), properties, flags, |
| reinterpret_cast<void*>(id))) { |
| if (page_flip_event_requested) |
| page_flip_manager_->RegisterCallback(id, crtc_count, callback); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| bool DrmDevice::SetCapability(uint64_t capability, uint64_t value) { |
| DCHECK(file_.IsValid()); |
| |
| #ifndef DRM_IOCTL_SET_CLIENT_CAP |
| // drmSetClientCap was introduced in a later version of libdrm than the wheezy |
| // sysroot supplies. |
| // TODO(thomasanderson): Remove this when support for the wheezy sysroot is |
| // dropped in favor of jessie. |
| #define DRM_IOCTL_SET_CLIENT_CAP DRM_IOW(0x0d, struct drm_set_client_cap) |
| struct drm_set_client_cap { |
| __u64 capability; |
| __u64 value; |
| }; |
| #endif |
| |
| struct drm_set_client_cap cap = {capability, value}; |
| return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_SET_CLIENT_CAP, &cap); |
| } |
| |
| 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<display::GammaRampRGBEntry>& lut) { |
| ScopedDrmCrtcPtr crtc = GetCrtc(crtc_id); |
| size_t gamma_size = static_cast<size_t>(crtc->gamma_size); |
| |
| if (gamma_size == 0 && lut.empty()) |
| return true; |
| |
| if (gamma_size == 0) { |
| LOG(ERROR) << "Gamma table not supported"; |
| return false; |
| } |
| |
| // TODO(robert.bradford) resample the incoming ramp to match what the kernel |
| // expects. |
| if (!lut.empty() && gamma_size != lut.size()) { |
| LOG(ERROR) << "Gamma table size mismatch: supplied " << lut.size() |
| << " expected " << gamma_size; |
| return false; |
| } |
| |
| std::vector<uint16_t> r, g, b; |
| r.reserve(gamma_size); |
| g.reserve(gamma_size); |
| b.reserve(gamma_size); |
| |
| if (lut.empty()) { |
| // Create a linear gamma ramp table to deactivate the feature. |
| for (size_t i = 0; i < gamma_size; ++i) { |
| uint16_t value = (i * ((1 << 16) - 1)) / (gamma_size - 1); |
| r.push_back(value); |
| g.push_back(value); |
| b.push_back(value); |
| } |
| } else { |
| for (size_t i = 0; i < gamma_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); |
| } |
| |
| bool DrmDevice::SetColorCorrection( |
| uint32_t crtc_id, |
| const std::vector<display::GammaRampRGBEntry>& degamma_lut, |
| const std::vector<display::GammaRampRGBEntry>& gamma_lut, |
| const std::vector<float>& correction_matrix) { |
| ScopedDrmObjectPropertyPtr crtc_props(drmModeObjectGetProperties( |
| file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC)); |
| uint64_t degamma_lut_size = 0; |
| uint64_t gamma_lut_size = 0; |
| |
| for (uint32_t i = 0; i < crtc_props->count_props; ++i) { |
| ScopedDrmPropertyPtr property( |
| drmModeGetProperty(file_.GetPlatformFile(), crtc_props->props[i])); |
| if (property && !strcmp(property->name, "DEGAMMA_LUT_SIZE")) { |
| degamma_lut_size = crtc_props->prop_values[i]; |
| } |
| if (property && !strcmp(property->name, "GAMMA_LUT_SIZE")) { |
| gamma_lut_size = crtc_props->prop_values[i]; |
| } |
| |
| if (degamma_lut_size && gamma_lut_size) |
| break; |
| } |
| |
| // If we can't find the degamma & gamma lut size, it means the properties |
| // aren't available. We should then use the legacy gamma ramp ioctl. |
| if (degamma_lut_size == 0 || gamma_lut_size == 0) { |
| return SetGammaRamp(crtc_id, gamma_lut); |
| } |
| |
| ScopedDrmColorLutPtr degamma_blob_data = |
| CreateLutBlob(ResampleLut(degamma_lut, degamma_lut_size)); |
| ScopedDrmColorLutPtr gamma_blob_data = |
| CreateLutBlob(ResampleLut(gamma_lut, gamma_lut_size)); |
| ScopedDrmColorCtmPtr ctm_blob_data = CreateCTMBlob(correction_matrix); |
| |
| for (uint32_t i = 0; i < crtc_props->count_props; ++i) { |
| ScopedDrmPropertyPtr property( |
| drmModeGetProperty(file_.GetPlatformFile(), crtc_props->props[i])); |
| if (!property) |
| continue; |
| |
| if (!strcmp(property->name, "DEGAMMA_LUT")) { |
| if (!SetBlobProperty( |
| file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, |
| crtc_props->props[i], property->name, |
| reinterpret_cast<unsigned char*>(degamma_blob_data.get()), |
| sizeof(DrmColorLut) * degamma_lut_size)) |
| return false; |
| } |
| if (!strcmp(property->name, "GAMMA_LUT")) { |
| if (!SetBlobProperty( |
| file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, |
| crtc_props->props[i], property->name, |
| reinterpret_cast<unsigned char*>(gamma_blob_data.get()), |
| sizeof(DrmColorLut) * gamma_lut_size)) |
| return false; |
| } |
| if (!strcmp(property->name, "CTM")) { |
| if (!SetBlobProperty( |
| file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, |
| crtc_props->props[i], property->name, |
| reinterpret_cast<unsigned char*>(ctm_blob_data.get()), |
| sizeof(DrmColorCtm))) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace ui |