• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "graphics_drm.h"
18 
19 #include <fcntl.h>
20 #include <poll.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/mman.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include <memory>
28 
29 #include <android-base/macros.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/unique_fd.h>
32 #include <drm_fourcc.h>
33 #include <xf86drm.h>
34 #include <xf86drmMode.h>
35 
36 #include "minui/minui.h"
37 
~GRSurfaceDrm()38 GRSurfaceDrm::~GRSurfaceDrm() {
39   if (mmapped_buffer_) {
40     munmap(mmapped_buffer_, row_bytes * height);
41   }
42 
43   if (fb_id) {
44     if (drmModeRmFB(drm_fd_, fb_id) != 0) {
45       perror("Failed to drmModeRmFB");
46       // Falling through to free other resources.
47     }
48   }
49 
50   if (handle) {
51     drm_gem_close gem_close = {};
52     gem_close.handle = handle;
53 
54     if (drmIoctl(drm_fd_, DRM_IOCTL_GEM_CLOSE, &gem_close) != 0) {
55       perror("Failed to DRM_IOCTL_GEM_CLOSE");
56     }
57   }
58 }
59 
drm_format_to_bpp(uint32_t format)60 static int drm_format_to_bpp(uint32_t format) {
61   switch (format) {
62     case DRM_FORMAT_ABGR8888:
63     case DRM_FORMAT_BGRA8888:
64     case DRM_FORMAT_RGBX8888:
65     case DRM_FORMAT_RGBA8888:
66     case DRM_FORMAT_ARGB8888:
67     case DRM_FORMAT_BGRX8888:
68     case DRM_FORMAT_XBGR8888:
69     case DRM_FORMAT_XRGB8888:
70       return 32;
71     case DRM_FORMAT_RGB565:
72       return 16;
73     default:
74       printf("Unknown format %d\n", format);
75       return 32;
76   }
77 }
78 
Create(int drm_fd,int width,int height)79 std::unique_ptr<GRSurfaceDrm> GRSurfaceDrm::Create(int drm_fd, int width, int height) {
80   uint32_t format;
81   PixelFormat pixel_format = gr_pixel_format();
82   // PixelFormat comes in byte order, whereas DRM_FORMAT_* uses little-endian
83   // (external/libdrm/include/drm/drm_fourcc.h). Note that although drm_fourcc.h also defines a
84   // macro of DRM_FORMAT_BIG_ENDIAN, it doesn't seem to be actually supported (see the discussion
85   // in https://lists.freedesktop.org/archives/amd-gfx/2017-May/008560.html).
86   if (pixel_format == PixelFormat::ABGR) {
87     format = DRM_FORMAT_RGBA8888;
88   } else if (pixel_format == PixelFormat::BGRA) {
89     format = DRM_FORMAT_ARGB8888;
90   } else if (pixel_format == PixelFormat::RGBX) {
91     format = DRM_FORMAT_XBGR8888;
92   } else if (pixel_format == PixelFormat::ARGB) {
93     format = DRM_FORMAT_BGRA8888;
94   } else {
95     format = DRM_FORMAT_RGB565;
96   }
97 
98   drm_mode_create_dumb create_dumb = {};
99   create_dumb.height = height;
100   create_dumb.width = width;
101   create_dumb.bpp = drm_format_to_bpp(format);
102   create_dumb.flags = 0;
103 
104   if (drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) != 0) {
105     perror("Failed to DRM_IOCTL_MODE_CREATE_DUMB");
106     return nullptr;
107   }
108   printf("Allocating buffer with resolution %d x %d pitch: %d bpp: %d, size: %llu\n", width, height,
109          create_dumb.pitch, create_dumb.bpp, create_dumb.size);
110 
111   // Cannot use std::make_unique to access non-public ctor.
112   auto surface = std::unique_ptr<GRSurfaceDrm>(new GRSurfaceDrm(
113       width, height, create_dumb.pitch, create_dumb.bpp / 8, drm_fd, create_dumb.handle));
114 
115   uint32_t handles[4], pitches[4], offsets[4];
116 
117   handles[0] = surface->handle;
118   pitches[0] = create_dumb.pitch;
119   offsets[0] = 0;
120   if (drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &surface->fb_id, 0) !=
121       0) {
122     perror("Failed to drmModeAddFB2");
123     return nullptr;
124   }
125 
126   drm_mode_map_dumb map_dumb = {};
127   map_dumb.handle = create_dumb.handle;
128   if (drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) != 0) {
129     perror("Failed to DRM_IOCTL_MODE_MAP_DUMB");
130     return nullptr;
131   }
132 
133   auto mmapped =
134       mmap(nullptr, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_dumb.offset);
135   if (mmapped == MAP_FAILED) {
136     perror("Failed to mmap()");
137     return nullptr;
138   }
139   surface->mmapped_buffer_ = static_cast<uint8_t*>(mmapped);
140   printf("Framebuffer of size %llu allocated @ %p\n", create_dumb.size, surface->mmapped_buffer_);
141   return surface;
142 }
143 
DrmDisableCrtc(int drm_fd,drmModeCrtc * crtc)144 void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) {
145   if (crtc) {
146     drmModeSetCrtc(drm_fd, crtc->crtc_id,
147                    0,         // fb_id
148                    0, 0,      // x,y
149                    nullptr,   // connectors
150                    0,         // connector_count
151                    nullptr);  // mode
152   }
153 }
154 
DrmEnableCrtc(int drm_fd,drmModeCrtc * crtc,const std::unique_ptr<GRSurfaceDrm> & surface,uint32_t * connector_id)155 bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc,
156                                     const std::unique_ptr<GRSurfaceDrm>& surface,
157                                     uint32_t* connector_id) {
158   if (drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0,  // x,y
159                      connector_id, 1,                              // connector_count
160                      &crtc->mode) != 0) {
161     fprintf(stderr, "Failed to drmModeSetCrtc(%d)\n", *connector_id);
162     return false;
163   }
164 
165   return true;
166 }
167 
Blank(bool blank)168 void MinuiBackendDrm::Blank(bool blank) {
169   Blank(blank, DRM_MAIN);
170 }
171 
Blank(bool blank,DrmConnector index)172 void MinuiBackendDrm::Blank(bool blank, DrmConnector index) {
173   const auto* drmInterface = &drm[DRM_MAIN];
174 
175   switch (index) {
176     case DRM_MAIN:
177       drmInterface = &drm[DRM_MAIN];
178       break;
179     case DRM_SEC:
180       drmInterface = &drm[DRM_SEC];
181       break;
182     default:
183       fprintf(stderr, "Invalid index: %d\n", index);
184       return;
185   }
186 
187   if (!drmInterface->monitor_connector) {
188     fprintf(stderr, "Unsupported. index = %d\n", index);
189     return;
190   }
191 
192   if (blank) {
193     DrmDisableCrtc(drm_fd, drmInterface->monitor_crtc);
194   } else {
195     DrmEnableCrtc(drm_fd, drmInterface->monitor_crtc,
196                   drmInterface->GRSurfaceDrms[drmInterface->current_buffer],
197                   &drmInterface->monitor_connector->connector_id);
198 
199     active_display = index;
200   }
201 }
202 
HasMultipleConnectors()203 bool MinuiBackendDrm::HasMultipleConnectors() {
204   return (drm[DRM_SEC].GRSurfaceDrms[0] && drm[DRM_SEC].GRSurfaceDrms[1]);
205 }
206 
find_crtc_for_connector(int fd,drmModeRes * resources,drmModeConnector * connector)207 static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
208                                             drmModeConnector* connector) {
209   // Find the encoder. If we already have one, just use it.
210   drmModeEncoder* encoder;
211   if (connector->encoder_id) {
212     encoder = drmModeGetEncoder(fd, connector->encoder_id);
213   } else {
214     encoder = nullptr;
215   }
216 
217   int32_t crtc;
218   if (encoder && encoder->crtc_id) {
219     crtc = encoder->crtc_id;
220     drmModeFreeEncoder(encoder);
221     return drmModeGetCrtc(fd, crtc);
222   }
223 
224   // Didn't find anything, try to find a crtc and encoder combo.
225   crtc = -1;
226   for (int i = 0; i < connector->count_encoders; i++) {
227     encoder = drmModeGetEncoder(fd, connector->encoders[i]);
228 
229     if (encoder) {
230       for (int j = 0; j < resources->count_crtcs; j++) {
231         if (!(encoder->possible_crtcs & (1 << j))) continue;
232         crtc = resources->crtcs[j];
233         break;
234       }
235       if (crtc >= 0) {
236         drmModeFreeEncoder(encoder);
237         return drmModeGetCrtc(fd, crtc);
238       }
239     }
240   }
241 
242   return nullptr;
243 }
244 
find_used_connector_by_type(int fd,drmModeRes * resources,unsigned type)245 std::vector<drmModeConnector*> find_used_connector_by_type(int fd, drmModeRes* resources,
246                                                            unsigned type) {
247   std::vector<drmModeConnector*> drmConnectors;
248   for (int i = 0; i < resources->count_connectors; i++) {
249     drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
250     if (connector) {
251       if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
252           (connector->count_modes > 0)) {
253         drmConnectors.push_back(connector);
254       } else {
255         drmModeFreeConnector(connector);
256       }
257     }
258   }
259   return drmConnectors;
260 }
261 
find_first_connected_connector(int fd,drmModeRes * resources)262 static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) {
263   for (int i = 0; i < resources->count_connectors; i++) {
264     drmModeConnector* connector;
265 
266     connector = drmModeGetConnector(fd, resources->connectors[i]);
267     if (connector) {
268       if ((connector->count_modes > 0) && (connector->connection == DRM_MODE_CONNECTED))
269         return connector;
270 
271       drmModeFreeConnector(connector);
272     }
273   }
274   return nullptr;
275 }
276 
FindAndSetMonitor(int fd,drmModeRes * resources)277 bool MinuiBackendDrm::FindAndSetMonitor(int fd, drmModeRes* resources) {
278   /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
279   static constexpr unsigned kConnectorPriority[] = {
280     DRM_MODE_CONNECTOR_LVDS,
281     DRM_MODE_CONNECTOR_eDP,
282     DRM_MODE_CONNECTOR_DSI,
283   };
284 
285   std::vector<drmModeConnector*> drmConnectors;
286   for (int i = 0; i < arraysize(kConnectorPriority) && drmConnectors.size() < DRM_MAX; i++) {
287     auto connectors = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
288     for (auto connector : connectors) {
289       drmConnectors.push_back(connector);
290       if (drmConnectors.size() >= DRM_MAX) break;
291     }
292   }
293 
294   /* If we didn't find a connector, grab the first one that is connected. */
295   if (drmConnectors.empty()) {
296     drmModeConnector* connector = find_first_connected_connector(fd, resources);
297     if (connector) {
298       drmConnectors.push_back(connector);
299     }
300   }
301 
302   for (int drm_index = 0; drm_index < drmConnectors.size(); drm_index++) {
303     drm[drm_index].monitor_connector = drmConnectors[drm_index];
304 
305     drm[drm_index].selected_mode = 0;
306     for (int modes = 0; modes < drmConnectors[drm_index]->count_modes; modes++) {
307       printf("Display Mode %d resolution: %d x %d @ %d FPS\n", modes,
308              drmConnectors[drm_index]->modes[modes].hdisplay,
309              drmConnectors[drm_index]->modes[modes].vdisplay,
310              drmConnectors[drm_index]->modes[modes].vrefresh);
311       if (drmConnectors[drm_index]->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
312         printf("Choosing display mode #%d\n", modes);
313         drm[drm_index].selected_mode = modes;
314         break;
315       }
316     }
317   }
318 
319   return drmConnectors.size() > 0;
320 }
321 
DisableNonMainCrtcs(int fd,drmModeRes * resources,drmModeCrtc * main_crtc)322 void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) {
323   for (int i = 0; i < resources->count_connectors; i++) {
324     drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
325     drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector);
326     if (crtc->crtc_id != main_crtc->crtc_id) {
327       DrmDisableCrtc(fd, crtc);
328     }
329     drmModeFreeCrtc(crtc);
330   }
331 }
332 
Init()333 GRSurface* MinuiBackendDrm::Init() {
334   drmModeRes* res = nullptr;
335   drm_fd = -1;
336 
337   /* Consider DRM devices in order. */
338   for (int i = 0; i < DRM_MAX_MINOR; i++) {
339     auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i);
340     android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR | O_CLOEXEC));
341     if (fd == -1) continue;
342 
343     /* We need dumb buffers. */
344     if (uint64_t cap = 0; drmGetCap(fd.get(), DRM_CAP_DUMB_BUFFER, &cap) != 0 || cap == 0) {
345       continue;
346     }
347 
348     res = drmModeGetResources(fd.get());
349     if (!res) {
350       continue;
351     }
352 
353     /* Use this device if it has at least one connected monitor. */
354     if (res->count_crtcs > 0 && res->count_connectors > 0) {
355       if (find_first_connected_connector(fd.get(), res)) {
356         drm_fd = fd.release();
357         break;
358       }
359     }
360 
361     drmModeFreeResources(res);
362     res = nullptr;
363   }
364 
365   if (drm_fd == -1 || res == nullptr) {
366     perror("Failed to find/open a drm device");
367     return nullptr;
368   }
369 
370   if (!FindAndSetMonitor(drm_fd, res)) {
371     fprintf(stderr, "Failed to find main monitor_connector\n");
372     drmModeFreeResources(res);
373     return nullptr;
374   }
375 
376   for (int i = 0; i < DRM_MAX; i++) {
377     if (drm[i].monitor_connector) {
378       drm[i].monitor_crtc = find_crtc_for_connector(drm_fd, res, drm[i].monitor_connector);
379       if (!drm[i].monitor_crtc) {
380         fprintf(stderr, "Failed to find monitor_crtc, drm index=%d\n", i);
381         drmModeFreeResources(res);
382         return nullptr;
383       }
384 
385       drm[i].monitor_crtc->mode = drm[i].monitor_connector->modes[drm[i].selected_mode];
386 
387       int width = drm[i].monitor_crtc->mode.hdisplay;
388       int height = drm[i].monitor_crtc->mode.vdisplay;
389 
390       drm[i].GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height);
391       drm[i].GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height);
392       if (!drm[i].GRSurfaceDrms[0] || !drm[i].GRSurfaceDrms[1]) {
393         fprintf(stderr, "Failed to create GRSurfaceDrm, drm index=%d\n", i);
394         drmModeFreeResources(res);
395         return nullptr;
396       }
397 
398       drm[i].current_buffer = 0;
399     }
400   }
401 
402   DisableNonMainCrtcs(drm_fd, res, drm[DRM_MAIN].monitor_crtc);
403 
404   drmModeFreeResources(res);
405 
406   // We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails.
407   if (!DrmEnableCrtc(drm_fd, drm[DRM_MAIN].monitor_crtc, drm[DRM_MAIN].GRSurfaceDrms[1],
408                      &drm[DRM_MAIN].monitor_connector->connector_id)) {
409     return nullptr;
410   }
411 
412   return drm[DRM_MAIN].GRSurfaceDrms[0].get();
413 }
414 
page_flip_complete(__unused int fd,__unused unsigned int sequence,__unused unsigned int tv_sec,__unused unsigned int tv_usec,void * user_data)415 static void page_flip_complete(__unused int fd,
416                                __unused unsigned int sequence,
417                                __unused unsigned int tv_sec,
418                                __unused unsigned int tv_usec,
419                                void *user_data) {
420   *static_cast<bool*>(user_data) = false;
421 }
422 
Flip()423 GRSurface* MinuiBackendDrm::Flip() {
424   GRSurface* surface = NULL;
425   DrmInterface* current_drm = &drm[active_display];
426   bool ongoing_flip = true;
427 
428   if (!current_drm->monitor_connector) {
429     fprintf(stderr, "Unsupported. active_display = %d\n", active_display);
430     return nullptr;
431   }
432 
433   if (drmModePageFlip(drm_fd, current_drm->monitor_crtc->crtc_id,
434                       current_drm->GRSurfaceDrms[current_drm->current_buffer]->fb_id,
435                       DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) {
436     fprintf(stderr, "Failed to drmModePageFlip, active_display=%d", active_display);
437     return nullptr;
438   }
439 
440   while (ongoing_flip) {
441     struct pollfd fds = {
442       .fd = drm_fd,
443       .events = POLLIN
444     };
445 
446     if (poll(&fds, 1, -1) == -1 || !(fds.revents & POLLIN)) {
447       perror("Failed to poll() on drm fd");
448       break;
449     }
450 
451     drmEventContext evctx = {
452       .version = DRM_EVENT_CONTEXT_VERSION,
453       .page_flip_handler = page_flip_complete
454     };
455 
456     if (drmHandleEvent(drm_fd, &evctx) != 0) {
457       perror("Failed to drmHandleEvent");
458       break;
459     }
460   }
461 
462   current_drm->current_buffer = 1 - current_drm->current_buffer;
463   surface = current_drm->GRSurfaceDrms[current_drm->current_buffer].get();
464   return surface;
465 }
466 
~MinuiBackendDrm()467 MinuiBackendDrm::~MinuiBackendDrm() {
468   for (int i = 0; i < DRM_MAX; i++) {
469     if (drm[i].monitor_connector) {
470       DrmDisableCrtc(drm_fd, drm[i].monitor_crtc);
471       drmModeFreeCrtc(drm[i].monitor_crtc);
472       drmModeFreeConnector(drm[i].monitor_connector);
473     }
474   }
475   close(drm_fd);
476   drm_fd = -1;
477 }
478