1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/ozone/platform/dri/dri_wrapper.h"
6
7#include <fcntl.h>
8#include <sys/mman.h>
9#include <unistd.h>
10#include <xf86drm.h>
11#include <xf86drmMode.h>
12
13#include "base/debug/trace_event.h"
14#include "base/logging.h"
15#include "base/stl_util.h"
16#include "third_party/skia/include/core/SkImageInfo.h"
17#include "ui/ozone/platform/dri/dri_util.h"
18
19namespace ui {
20
21namespace {
22
23uint32_t ToFixedPoint(double v) {
24  // This returns a number in a 16-bit.16-bit fixed point.
25  return v * 65536.0;
26}
27
28bool DrmCreateDumbBuffer(int fd,
29                         const SkImageInfo& info,
30                         uint32_t* handle,
31                         uint32_t* stride) {
32  struct drm_mode_create_dumb request;
33  memset(&request, 0, sizeof(request));
34  request.width = info.width();
35  request.height = info.height();
36  request.bpp = info.bytesPerPixel() << 3;
37  request.flags = 0;
38
39  if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
40    VLOG(2) << "Cannot create dumb buffer (" << errno << ") "
41            << strerror(errno);
42    return false;
43  }
44
45  // The driver may choose to align the last row as well. We don't care about
46  // the last alignment bits since they aren't used for display purposes, so
47  // just check that the expected size is <= to what the driver allocated.
48  DCHECK_LE(info.getSafeSize(request.pitch), request.size);
49
50  *handle = request.handle;
51  *stride = request.pitch;
52  return true;
53}
54
55void DrmDestroyDumbBuffer(int fd, uint32_t handle) {
56  struct drm_mode_destroy_dumb destroy_request;
57  memset(&destroy_request, 0, sizeof(destroy_request));
58  destroy_request.handle = handle;
59  drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
60}
61
62}  // namespace
63
64DriWrapper::DriWrapper(const char* device_path)
65    : fd_(-1), device_path_(device_path) {
66}
67
68DriWrapper::~DriWrapper() {
69  if (fd_ >= 0)
70    close(fd_);
71}
72
73void DriWrapper::Initialize() {
74  fd_ = open(device_path_, O_RDWR | O_CLOEXEC);
75  if (fd_ < 0)
76    PLOG(FATAL) << "open: " << device_path_;
77}
78
79ScopedDrmCrtcPtr DriWrapper::GetCrtc(uint32_t crtc_id) {
80  DCHECK(fd_ >= 0);
81  return ScopedDrmCrtcPtr(drmModeGetCrtc(fd_, crtc_id));
82}
83
84bool DriWrapper::SetCrtc(uint32_t crtc_id,
85                         uint32_t framebuffer,
86                         std::vector<uint32_t> connectors,
87                         drmModeModeInfo* mode) {
88  DCHECK(fd_ >= 0);
89  DCHECK(!connectors.empty());
90  DCHECK(mode);
91
92  TRACE_EVENT2("dri",
93               "DriWrapper::SetCrtc",
94               "crtc",
95               crtc_id,
96               "size",
97               gfx::Size(mode->hdisplay, mode->vdisplay).ToString());
98  return !drmModeSetCrtc(fd_,
99                         crtc_id,
100                         framebuffer,
101                         0,
102                         0,
103                         vector_as_array(&connectors),
104                         connectors.size(), mode);
105}
106
107bool DriWrapper::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) {
108  DCHECK(fd_ >= 0);
109  // If there's no buffer then the CRTC was disabled.
110  if (!crtc->buffer_id)
111    return DisableCrtc(crtc->crtc_id);
112
113  DCHECK(!connectors.empty());
114
115  TRACE_EVENT1("dri", "DriWrapper::RestoreCrtc",
116               "crtc", crtc->crtc_id);
117  return !drmModeSetCrtc(fd_,
118                         crtc->crtc_id,
119                         crtc->buffer_id,
120                         crtc->x,
121                         crtc->y,
122                         vector_as_array(&connectors),
123                         connectors.size(),
124                         &crtc->mode);
125}
126
127bool DriWrapper::DisableCrtc(uint32_t crtc_id) {
128  DCHECK(fd_ >= 0);
129  TRACE_EVENT1("dri", "DriWrapper::DisableCrtc",
130               "crtc", crtc_id);
131  return !drmModeSetCrtc(fd_, crtc_id, 0, 0, 0, NULL, 0, NULL);
132}
133
134ScopedDrmConnectorPtr DriWrapper::GetConnector(uint32_t connector_id) {
135  DCHECK(fd_ >= 0);
136  TRACE_EVENT1("dri", "DriWrapper::GetConnector", "connector", connector_id);
137  return ScopedDrmConnectorPtr(drmModeGetConnector(fd_, connector_id));
138}
139
140bool DriWrapper::AddFramebuffer(uint32_t width,
141                                uint32_t height,
142                                uint8_t depth,
143                                uint8_t bpp,
144                                uint32_t stride,
145                                uint32_t handle,
146                                uint32_t* framebuffer) {
147  DCHECK(fd_ >= 0);
148  TRACE_EVENT1("dri", "DriWrapper::AddFramebuffer",
149               "handle", handle);
150  return !drmModeAddFB(fd_,
151                       width,
152                       height,
153                       depth,
154                       bpp,
155                       stride,
156                       handle,
157                       framebuffer);
158}
159
160bool DriWrapper::RemoveFramebuffer(uint32_t framebuffer) {
161  DCHECK(fd_ >= 0);
162  TRACE_EVENT1("dri", "DriWrapper::RemoveFramebuffer",
163               "framebuffer", framebuffer);
164  return !drmModeRmFB(fd_, framebuffer);
165}
166
167bool DriWrapper::PageFlip(uint32_t crtc_id,
168                          uint32_t framebuffer,
169                          void* data) {
170  DCHECK(fd_ >= 0);
171  TRACE_EVENT2("dri", "DriWrapper::PageFlip",
172               "crtc", crtc_id,
173               "framebuffer", framebuffer);
174  return !drmModePageFlip(fd_,
175                          crtc_id,
176                          framebuffer,
177                          DRM_MODE_PAGE_FLIP_EVENT,
178                          data);
179}
180
181bool DriWrapper::PageFlipOverlay(uint32_t crtc_id,
182                                 uint32_t framebuffer,
183                                 const gfx::Rect& location,
184                                 const gfx::RectF& source,
185                                 int overlay_plane) {
186  DCHECK(fd_ >= 0);
187  TRACE_EVENT2("dri", "DriWrapper::PageFlipOverlay",
188               "crtc", crtc_id,
189               "framebuffer", framebuffer);
190  return !drmModeSetPlane(fd_,
191                          overlay_plane,
192                          crtc_id,
193                          framebuffer,
194                          0,
195                          location.x(),
196                          location.y(),
197                          location.width(),
198                          location.height(),
199                          ToFixedPoint(source.x()),
200                          ToFixedPoint(source.y()),
201                          ToFixedPoint(source.width()),
202                          ToFixedPoint(source.height()));
203}
204
205ScopedDrmFramebufferPtr DriWrapper::GetFramebuffer(uint32_t framebuffer) {
206  DCHECK(fd_ >= 0);
207  TRACE_EVENT1("dri", "DriWrapper::GetFramebuffer",
208               "framebuffer", framebuffer);
209  return ScopedDrmFramebufferPtr(drmModeGetFB(fd_, framebuffer));
210}
211
212ScopedDrmPropertyPtr DriWrapper::GetProperty(drmModeConnector* connector,
213                                             const char* name) {
214  TRACE_EVENT2("dri", "DriWrapper::GetProperty",
215               "connector", connector->connector_id,
216               "name", name);
217  for (int i = 0; i < connector->count_props; ++i) {
218    ScopedDrmPropertyPtr property(drmModeGetProperty(fd_, connector->props[i]));
219    if (!property)
220      continue;
221
222    if (strcmp(property->name, name) == 0)
223      return property.Pass();
224  }
225
226  return ScopedDrmPropertyPtr();
227}
228
229bool DriWrapper::SetProperty(uint32_t connector_id,
230                             uint32_t property_id,
231                             uint64_t value) {
232  DCHECK(fd_ >= 0);
233  return !drmModeConnectorSetProperty(fd_, connector_id, property_id, value);
234}
235
236ScopedDrmPropertyBlobPtr DriWrapper::GetPropertyBlob(
237    drmModeConnector* connector, const char* name) {
238  DCHECK(fd_ >= 0);
239  TRACE_EVENT2("dri", "DriWrapper::GetPropertyBlob",
240               "connector", connector->connector_id,
241               "name", name);
242  for (int i = 0; i < connector->count_props; ++i) {
243    ScopedDrmPropertyPtr property(drmModeGetProperty(fd_, connector->props[i]));
244    if (!property)
245      continue;
246
247    if (strcmp(property->name, name) == 0 &&
248        property->flags & DRM_MODE_PROP_BLOB)
249      return ScopedDrmPropertyBlobPtr(
250          drmModeGetPropertyBlob(fd_, connector->prop_values[i]));
251  }
252
253  return ScopedDrmPropertyBlobPtr();
254}
255
256bool DriWrapper::SetCursor(uint32_t crtc_id,
257                           uint32_t handle,
258                           const gfx::Size& size) {
259  DCHECK(fd_ >= 0);
260  TRACE_EVENT1("dri", "DriWrapper::SetCursor", "handle", handle);
261  return !drmModeSetCursor(fd_, crtc_id, handle, size.width(), size.height());
262}
263
264bool DriWrapper::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
265  DCHECK(fd_ >= 0);
266  return !drmModeMoveCursor(fd_, crtc_id, point.x(), point.y());
267}
268
269void DriWrapper::HandleEvent(drmEventContext& event) {
270  DCHECK(fd_ >= 0);
271  TRACE_EVENT0("dri", "DriWrapper::HandleEvent");
272  drmHandleEvent(fd_, &event);
273}
274
275bool DriWrapper::CreateDumbBuffer(const SkImageInfo& info,
276                                  uint32_t* handle,
277                                  uint32_t* stride,
278                                  void** pixels) {
279  DCHECK(fd_ >= 0);
280
281  TRACE_EVENT0("dri", "DriWrapper::CreateDumbBuffer");
282  if (!DrmCreateDumbBuffer(fd_, info, handle, stride))
283    return false;
284
285  if (!MapDumbBuffer(fd_, *handle, info.getSafeSize(*stride), pixels)) {
286    DrmDestroyDumbBuffer(fd_, *handle);
287    return false;
288  }
289
290  return true;
291}
292
293void DriWrapper::DestroyDumbBuffer(const SkImageInfo& info,
294                                   uint32_t handle,
295                                   uint32_t stride,
296                                   void* pixels) {
297  DCHECK(fd_ >= 0);
298  TRACE_EVENT1("dri", "DriWrapper::DestroyDumbBuffer", "handle", handle);
299  munmap(pixels, info.getSafeSize(stride));
300  DrmDestroyDumbBuffer(fd_, handle);
301}
302
303
304}  // namespace ui
305