OLD | NEW |
| (Empty) |
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/logging.h" | |
14 #include "base/message_loop/message_loop.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/synchronization/waitable_event.h" | |
17 #include "base/task_runner.h" | |
18 #include "base/thread_task_runner_handle.h" | |
19 #include "base/trace_event/trace_event.h" | |
20 #include "third_party/skia/include/core/SkImageInfo.h" | |
21 #include "ui/ozone/platform/dri/dri_util.h" | |
22 #include "ui/ozone/platform/dri/hardware_display_plane_manager_legacy.h" | |
23 | |
24 namespace ui { | |
25 | |
26 namespace { | |
27 | |
28 struct PageFlipPayload { | |
29 PageFlipPayload(const scoped_refptr<base::TaskRunner>& task_runner, | |
30 const DriWrapper::PageFlipCallback& callback) | |
31 : task_runner(task_runner), callback(callback) {} | |
32 | |
33 // Task runner for the thread scheduling the page flip event. This is used to | |
34 // run the callback on the same thread the callback was created on. | |
35 scoped_refptr<base::TaskRunner> task_runner; | |
36 DriWrapper::PageFlipCallback callback; | |
37 }; | |
38 | |
39 bool DrmCreateDumbBuffer(int fd, | |
40 const SkImageInfo& info, | |
41 uint32_t* handle, | |
42 uint32_t* stride) { | |
43 struct drm_mode_create_dumb request; | |
44 memset(&request, 0, sizeof(request)); | |
45 request.width = info.width(); | |
46 request.height = info.height(); | |
47 request.bpp = info.bytesPerPixel() << 3; | |
48 request.flags = 0; | |
49 | |
50 if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) { | |
51 VLOG(2) << "Cannot create dumb buffer (" << errno << ") " | |
52 << strerror(errno); | |
53 return false; | |
54 } | |
55 | |
56 // The driver may choose to align the last row as well. We don't care about | |
57 // the last alignment bits since they aren't used for display purposes, so | |
58 // just check that the expected size is <= to what the driver allocated. | |
59 DCHECK_LE(info.getSafeSize(request.pitch), request.size); | |
60 | |
61 *handle = request.handle; | |
62 *stride = request.pitch; | |
63 return true; | |
64 } | |
65 | |
66 void DrmDestroyDumbBuffer(int fd, uint32_t handle) { | |
67 struct drm_mode_destroy_dumb destroy_request; | |
68 memset(&destroy_request, 0, sizeof(destroy_request)); | |
69 destroy_request.handle = handle; | |
70 drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); | |
71 } | |
72 | |
73 void HandlePageFlipEventOnIO(int fd, | |
74 unsigned int frame, | |
75 unsigned int seconds, | |
76 unsigned int useconds, | |
77 void* data) { | |
78 scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data)); | |
79 payload->task_runner->PostTask( | |
80 FROM_HERE, base::Bind(payload->callback, frame, seconds, useconds)); | |
81 } | |
82 | |
83 void HandlePageFlipEventOnUI(int fd, | |
84 unsigned int frame, | |
85 unsigned int seconds, | |
86 unsigned int useconds, | |
87 void* data) { | |
88 scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data)); | |
89 payload->callback.Run(frame, seconds, useconds); | |
90 } | |
91 | |
92 bool CanQueryForResources(int fd) { | |
93 drm_mode_card_res resources; | |
94 memset(&resources, 0, sizeof(resources)); | |
95 // If there is no error getting DRM resources then assume this is a | |
96 // modesetting device. | |
97 return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources); | |
98 } | |
99 | |
100 } // namespace | |
101 | |
102 class DriWrapper::IOWatcher | |
103 : public base::RefCountedThreadSafe<DriWrapper::IOWatcher>, | |
104 public base::MessagePumpLibevent::Watcher { | |
105 public: | |
106 IOWatcher(int fd, | |
107 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) | |
108 : io_task_runner_(io_task_runner), paused_(true), fd_(fd) {} | |
109 | |
110 void SetPaused(bool paused) { | |
111 if (paused_ == paused) | |
112 return; | |
113 | |
114 paused_ = paused; | |
115 base::WaitableEvent done(false, false); | |
116 io_task_runner_->PostTask( | |
117 FROM_HERE, base::Bind(&IOWatcher::SetPausedOnIO, this, &done)); | |
118 } | |
119 | |
120 void Shutdown() { | |
121 if (!paused_) | |
122 io_task_runner_->PostTask(FROM_HERE, | |
123 base::Bind(&IOWatcher::UnregisterOnIO, this)); | |
124 } | |
125 | |
126 private: | |
127 friend class base::RefCountedThreadSafe<IOWatcher>; | |
128 | |
129 ~IOWatcher() override { SetPaused(true); } | |
130 | |
131 void RegisterOnIO() { | |
132 DCHECK(base::MessageLoopForIO::IsCurrent()); | |
133 base::MessageLoopForIO::current()->WatchFileDescriptor( | |
134 fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this); | |
135 } | |
136 | |
137 void UnregisterOnIO() { | |
138 DCHECK(base::MessageLoopForIO::IsCurrent()); | |
139 controller_.StopWatchingFileDescriptor(); | |
140 } | |
141 | |
142 void SetPausedOnIO(base::WaitableEvent* done) { | |
143 DCHECK(base::MessageLoopForIO::IsCurrent()); | |
144 if (paused_) | |
145 UnregisterOnIO(); | |
146 else | |
147 RegisterOnIO(); | |
148 done->Signal(); | |
149 } | |
150 | |
151 // base::MessagePumpLibevent::Watcher overrides: | |
152 void OnFileCanReadWithoutBlocking(int fd) override { | |
153 DCHECK(base::MessageLoopForIO::IsCurrent()); | |
154 TRACE_EVENT1("dri", "OnDrmEvent", "socket", fd); | |
155 | |
156 drmEventContext event; | |
157 event.version = DRM_EVENT_CONTEXT_VERSION; | |
158 event.page_flip_handler = HandlePageFlipEventOnIO; | |
159 event.vblank_handler = nullptr; | |
160 | |
161 drmHandleEvent(fd, &event); | |
162 } | |
163 | |
164 void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); } | |
165 | |
166 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | |
167 | |
168 base::MessagePumpLibevent::FileDescriptorWatcher controller_; | |
169 | |
170 bool paused_; | |
171 int fd_; | |
172 | |
173 DISALLOW_COPY_AND_ASSIGN(IOWatcher); | |
174 }; | |
175 | |
176 DriWrapper::DriWrapper(const base::FilePath& device_path) | |
177 : device_path_(device_path), | |
178 file_(device_path, | |
179 base::File::FLAG_OPEN | base::File::FLAG_READ | | |
180 base::File::FLAG_WRITE) { | |
181 LOG_IF(FATAL, !file_.IsValid()) | |
182 << "Failed to open '" << device_path_.value() | |
183 << "': " << base::File::ErrorToString(file_.error_details()); | |
184 } | |
185 | |
186 DriWrapper::DriWrapper(const base::FilePath& device_path, base::File file) | |
187 : device_path_(device_path), file_(file.Pass()) { | |
188 } | |
189 | |
190 DriWrapper::~DriWrapper() { | |
191 if (watcher_) | |
192 watcher_->Shutdown(); | |
193 } | |
194 | |
195 bool DriWrapper::Initialize() { | |
196 // Ignore devices that cannot perform modesetting. | |
197 if (!CanQueryForResources(file_.GetPlatformFile())) { | |
198 VLOG(2) << "Cannot query for resources for '" << device_path_.value() | |
199 << "'"; | |
200 return false; | |
201 } | |
202 | |
203 plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy()); | |
204 if (!plane_manager_->Initialize(this)) { | |
205 LOG(ERROR) << "Failed to initialize the plane manager for " | |
206 << device_path_.value(); | |
207 plane_manager_.reset(); | |
208 return false; | |
209 } | |
210 | |
211 return true; | |
212 } | |
213 | |
214 void DriWrapper::InitializeTaskRunner( | |
215 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { | |
216 DCHECK(!task_runner_); | |
217 task_runner_ = task_runner; | |
218 watcher_ = new IOWatcher(file_.GetPlatformFile(), task_runner_); | |
219 } | |
220 | |
221 ScopedDrmCrtcPtr DriWrapper::GetCrtc(uint32_t crtc_id) { | |
222 DCHECK(file_.IsValid()); | |
223 return ScopedDrmCrtcPtr(drmModeGetCrtc(file_.GetPlatformFile(), crtc_id)); | |
224 } | |
225 | |
226 bool DriWrapper::SetCrtc(uint32_t crtc_id, | |
227 uint32_t framebuffer, | |
228 std::vector<uint32_t> connectors, | |
229 drmModeModeInfo* mode) { | |
230 DCHECK(file_.IsValid()); | |
231 DCHECK(!connectors.empty()); | |
232 DCHECK(mode); | |
233 | |
234 TRACE_EVENT2("dri", "DriWrapper::SetCrtc", "crtc", crtc_id, "size", | |
235 gfx::Size(mode->hdisplay, mode->vdisplay).ToString()); | |
236 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0, | |
237 vector_as_array(&connectors), connectors.size(), mode); | |
238 } | |
239 | |
240 bool DriWrapper::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) { | |
241 DCHECK(file_.IsValid()); | |
242 // If there's no buffer then the CRTC was disabled. | |
243 if (!crtc->buffer_id) | |
244 return DisableCrtc(crtc->crtc_id); | |
245 | |
246 DCHECK(!connectors.empty()); | |
247 | |
248 TRACE_EVENT1("dri", "DriWrapper::RestoreCrtc", | |
249 "crtc", crtc->crtc_id); | |
250 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc->crtc_id, | |
251 crtc->buffer_id, crtc->x, crtc->y, | |
252 vector_as_array(&connectors), connectors.size(), | |
253 &crtc->mode); | |
254 } | |
255 | |
256 bool DriWrapper::DisableCrtc(uint32_t crtc_id) { | |
257 DCHECK(file_.IsValid()); | |
258 TRACE_EVENT1("dri", "DriWrapper::DisableCrtc", | |
259 "crtc", crtc_id); | |
260 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, 0, 0, 0, NULL, 0, | |
261 NULL); | |
262 } | |
263 | |
264 ScopedDrmConnectorPtr DriWrapper::GetConnector(uint32_t connector_id) { | |
265 DCHECK(file_.IsValid()); | |
266 TRACE_EVENT1("dri", "DriWrapper::GetConnector", "connector", connector_id); | |
267 return ScopedDrmConnectorPtr( | |
268 drmModeGetConnector(file_.GetPlatformFile(), connector_id)); | |
269 } | |
270 | |
271 bool DriWrapper::AddFramebuffer(uint32_t width, | |
272 uint32_t height, | |
273 uint8_t depth, | |
274 uint8_t bpp, | |
275 uint32_t stride, | |
276 uint32_t handle, | |
277 uint32_t* framebuffer) { | |
278 DCHECK(file_.IsValid()); | |
279 TRACE_EVENT1("dri", "DriWrapper::AddFramebuffer", | |
280 "handle", handle); | |
281 return !drmModeAddFB(file_.GetPlatformFile(), width, height, depth, bpp, | |
282 stride, handle, framebuffer); | |
283 } | |
284 | |
285 bool DriWrapper::RemoveFramebuffer(uint32_t framebuffer) { | |
286 DCHECK(file_.IsValid()); | |
287 TRACE_EVENT1("dri", "DriWrapper::RemoveFramebuffer", | |
288 "framebuffer", framebuffer); | |
289 return !drmModeRmFB(file_.GetPlatformFile(), framebuffer); | |
290 } | |
291 | |
292 bool DriWrapper::PageFlip(uint32_t crtc_id, | |
293 uint32_t framebuffer, | |
294 bool is_sync, | |
295 const PageFlipCallback& callback) { | |
296 DCHECK(file_.IsValid()); | |
297 TRACE_EVENT2("dri", "DriWrapper::PageFlip", | |
298 "crtc", crtc_id, | |
299 "framebuffer", framebuffer); | |
300 | |
301 if (watcher_) | |
302 watcher_->SetPaused(is_sync); | |
303 | |
304 // NOTE: Calling drmModeSetCrtc will immediately update the state, though | |
305 // callbacks to already scheduled page flips will be honored by the kernel. | |
306 scoped_ptr<PageFlipPayload> payload( | |
307 new PageFlipPayload(base::ThreadTaskRunnerHandle::Get(), callback)); | |
308 if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer, | |
309 DRM_MODE_PAGE_FLIP_EVENT, payload.get())) { | |
310 // If successful the payload will be removed by a PageFlip event. | |
311 ignore_result(payload.release()); | |
312 | |
313 // If the flip was requested synchronous or if no watcher has been installed | |
314 // yet, then synchronously handle the page flip events. | |
315 if (is_sync || !watcher_) { | |
316 TRACE_EVENT1("dri", "OnDrmEvent", "socket", file_.GetPlatformFile()); | |
317 | |
318 drmEventContext event; | |
319 event.version = DRM_EVENT_CONTEXT_VERSION; | |
320 event.page_flip_handler = HandlePageFlipEventOnUI; | |
321 event.vblank_handler = nullptr; | |
322 | |
323 drmHandleEvent(file_.GetPlatformFile(), &event); | |
324 } | |
325 | |
326 return true; | |
327 } | |
328 | |
329 return false; | |
330 } | |
331 | |
332 bool DriWrapper::PageFlipOverlay(uint32_t crtc_id, | |
333 uint32_t framebuffer, | |
334 const gfx::Rect& location, | |
335 const gfx::Rect& source, | |
336 int overlay_plane) { | |
337 DCHECK(file_.IsValid()); | |
338 TRACE_EVENT2("dri", "DriWrapper::PageFlipOverlay", | |
339 "crtc", crtc_id, | |
340 "framebuffer", framebuffer); | |
341 return !drmModeSetPlane(file_.GetPlatformFile(), overlay_plane, crtc_id, | |
342 framebuffer, 0, location.x(), location.y(), | |
343 location.width(), location.height(), source.x(), | |
344 source.y(), source.width(), source.height()); | |
345 } | |
346 | |
347 ScopedDrmFramebufferPtr DriWrapper::GetFramebuffer(uint32_t framebuffer) { | |
348 DCHECK(file_.IsValid()); | |
349 TRACE_EVENT1("dri", "DriWrapper::GetFramebuffer", | |
350 "framebuffer", framebuffer); | |
351 return ScopedDrmFramebufferPtr( | |
352 drmModeGetFB(file_.GetPlatformFile(), framebuffer)); | |
353 } | |
354 | |
355 ScopedDrmPropertyPtr DriWrapper::GetProperty(drmModeConnector* connector, | |
356 const char* name) { | |
357 TRACE_EVENT2("dri", "DriWrapper::GetProperty", | |
358 "connector", connector->connector_id, | |
359 "name", name); | |
360 for (int i = 0; i < connector->count_props; ++i) { | |
361 ScopedDrmPropertyPtr property( | |
362 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i])); | |
363 if (!property) | |
364 continue; | |
365 | |
366 if (strcmp(property->name, name) == 0) | |
367 return property.Pass(); | |
368 } | |
369 | |
370 return ScopedDrmPropertyPtr(); | |
371 } | |
372 | |
373 bool DriWrapper::SetProperty(uint32_t connector_id, | |
374 uint32_t property_id, | |
375 uint64_t value) { | |
376 DCHECK(file_.IsValid()); | |
377 return !drmModeConnectorSetProperty(file_.GetPlatformFile(), connector_id, | |
378 property_id, value); | |
379 } | |
380 | |
381 bool DriWrapper::GetCapability(uint64_t capability, uint64_t* value) { | |
382 DCHECK(file_.IsValid()); | |
383 return !drmGetCap(file_.GetPlatformFile(), capability, value); | |
384 } | |
385 | |
386 ScopedDrmPropertyBlobPtr DriWrapper::GetPropertyBlob( | |
387 drmModeConnector* connector, const char* name) { | |
388 DCHECK(file_.IsValid()); | |
389 TRACE_EVENT2("dri", "DriWrapper::GetPropertyBlob", | |
390 "connector", connector->connector_id, | |
391 "name", name); | |
392 for (int i = 0; i < connector->count_props; ++i) { | |
393 ScopedDrmPropertyPtr property( | |
394 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i])); | |
395 if (!property) | |
396 continue; | |
397 | |
398 if (strcmp(property->name, name) == 0 && | |
399 property->flags & DRM_MODE_PROP_BLOB) | |
400 return ScopedDrmPropertyBlobPtr(drmModeGetPropertyBlob( | |
401 file_.GetPlatformFile(), connector->prop_values[i])); | |
402 } | |
403 | |
404 return ScopedDrmPropertyBlobPtr(); | |
405 } | |
406 | |
407 bool DriWrapper::SetCursor(uint32_t crtc_id, | |
408 uint32_t handle, | |
409 const gfx::Size& size) { | |
410 DCHECK(file_.IsValid()); | |
411 TRACE_EVENT1("dri", "DriWrapper::SetCursor", "handle", handle); | |
412 return !drmModeSetCursor(file_.GetPlatformFile(), crtc_id, handle, | |
413 size.width(), size.height()); | |
414 } | |
415 | |
416 bool DriWrapper::MoveCursor(uint32_t crtc_id, const gfx::Point& point) { | |
417 DCHECK(file_.IsValid()); | |
418 return !drmModeMoveCursor(file_.GetPlatformFile(), crtc_id, point.x(), | |
419 point.y()); | |
420 } | |
421 | |
422 bool DriWrapper::CreateDumbBuffer(const SkImageInfo& info, | |
423 uint32_t* handle, | |
424 uint32_t* stride, | |
425 void** pixels) { | |
426 DCHECK(file_.IsValid()); | |
427 | |
428 TRACE_EVENT0("dri", "DriWrapper::CreateDumbBuffer"); | |
429 if (!DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride)) | |
430 return false; | |
431 | |
432 if (!MapDumbBuffer(file_.GetPlatformFile(), *handle, | |
433 info.getSafeSize(*stride), pixels)) { | |
434 DrmDestroyDumbBuffer(file_.GetPlatformFile(), *handle); | |
435 return false; | |
436 } | |
437 | |
438 return true; | |
439 } | |
440 | |
441 void DriWrapper::DestroyDumbBuffer(const SkImageInfo& info, | |
442 uint32_t handle, | |
443 uint32_t stride, | |
444 void* pixels) { | |
445 DCHECK(file_.IsValid()); | |
446 TRACE_EVENT1("dri", "DriWrapper::DestroyDumbBuffer", "handle", handle); | |
447 munmap(pixels, info.getSafeSize(stride)); | |
448 DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle); | |
449 } | |
450 | |
451 bool DriWrapper::SetMaster() { | |
452 DCHECK(file_.IsValid()); | |
453 return (drmSetMaster(file_.GetPlatformFile()) == 0); | |
454 } | |
455 | |
456 bool DriWrapper::DropMaster() { | |
457 DCHECK(file_.IsValid()); | |
458 return (drmDropMaster(file_.GetPlatformFile()) == 0); | |
459 } | |
460 | |
461 } // namespace ui | |
OLD | NEW |