1 /**
2 * @file lv_linux_drm.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_linux_drm.h"
10 #if LV_USE_LINUX_DRM
11
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <poll.h>
15 #include <stdint.h>
16 #include <sys/mman.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <string.h>
20
21 #include <xf86drm.h>
22 #include <xf86drmMode.h>
23 #include <drm_fourcc.h>
24
25 #include "../../../stdlib/lv_sprintf.h"
26
27 /*********************
28 * DEFINES
29 *********************/
30 #if LV_COLOR_DEPTH == 32
31 #define DRM_FOURCC DRM_FORMAT_XRGB8888
32 #elif LV_COLOR_DEPTH == 16
33 #define DRM_FOURCC DRM_FORMAT_RGB565
34 #else
35 #error LV_COLOR_DEPTH not supported
36 #endif
37
38 /**********************
39 * TYPEDEFS
40 **********************/
41 typedef struct {
42 uint32_t handle;
43 uint32_t pitch;
44 uint32_t offset;
45 unsigned long int size;
46 uint8_t * map;
47 uint32_t fb_handle;
48 } drm_buffer_t;
49
50 typedef struct {
51 int fd;
52 uint32_t conn_id, enc_id, crtc_id, plane_id, crtc_idx;
53 uint32_t width, height;
54 uint32_t mmWidth, mmHeight;
55 uint32_t fourcc;
56 drmModeModeInfo mode;
57 uint32_t blob_id;
58 drmModeCrtc * saved_crtc;
59 drmModeAtomicReq * req;
60 drmEventContext drm_event_ctx;
61 drmModePlane * plane;
62 drmModeCrtc * crtc;
63 drmModeConnector * conn;
64 uint32_t count_plane_props;
65 uint32_t count_crtc_props;
66 uint32_t count_conn_props;
67 drmModePropertyPtr plane_props[128];
68 drmModePropertyPtr crtc_props[128];
69 drmModePropertyPtr conn_props[128];
70 drm_buffer_t drm_bufs[2]; /*DUMB buffers*/
71 } drm_dev_t;
72
73 /**********************
74 * STATIC PROTOTYPES
75 **********************/
76 static uint32_t get_plane_property_id(drm_dev_t * drm_dev, const char * name);
77 static uint32_t get_crtc_property_id(drm_dev_t * drm_dev, const char * name);
78 static uint32_t get_conn_property_id(drm_dev_t * drm_dev, const char * name);
79 static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec,
80 void * user_data);
81 static int drm_get_plane_props(drm_dev_t * drm_dev);
82 static int drm_get_crtc_props(drm_dev_t * drm_dev);
83 static int drm_get_conn_props(drm_dev_t * drm_dev);
84 static int drm_add_plane_property(drm_dev_t * drm_dev, const char * name, uint64_t value);
85 static int drm_add_crtc_property(drm_dev_t * drm_dev, const char * name, uint64_t value);
86 static int drm_add_conn_property(drm_dev_t * drm_dev, const char * name, uint64_t value);
87 static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf);
88 static int find_plane(drm_dev_t * drm_dev, unsigned int fourcc, uint32_t * plane_id, uint32_t crtc_id,
89 uint32_t crtc_idx);
90 static int drm_find_connector(drm_dev_t * drm_dev, int64_t connector_id);
91 static int drm_open(const char * path);
92 static int drm_setup(drm_dev_t * drm_dev, const char * device_path, int64_t connector_id, unsigned int fourcc);
93 static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf);
94 static int drm_setup_buffers(drm_dev_t * drm_dev);
95 static void drm_flush_wait(lv_display_t * drm_dev);
96 static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
97
98 static uint32_t tick_get_cb(void);
99
100 /**********************
101 * STATIC VARIABLES
102 **********************/
103
104 /**********************
105 * MACROS
106 **********************/
107 #ifndef DIV_ROUND_UP
108 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
109 #endif
110
111 /**********************
112 * GLOBAL FUNCTIONS
113 **********************/
114
lv_linux_drm_create(void)115 lv_display_t * lv_linux_drm_create(void)
116 {
117 lv_tick_set_cb(tick_get_cb);
118
119 drm_dev_t * drm_dev = lv_malloc_zeroed(sizeof(drm_dev_t));
120 LV_ASSERT_MALLOC(drm_dev);
121 if(drm_dev == NULL) return NULL;
122
123 lv_display_t * disp = lv_display_create(800, 480);
124 if(disp == NULL) {
125 lv_free(drm_dev);
126 return NULL;
127 }
128 drm_dev->fd = -1;
129 lv_display_set_driver_data(disp, drm_dev);
130 lv_display_set_flush_wait_cb(disp, drm_flush_wait);
131 lv_display_set_flush_cb(disp, drm_flush);
132
133 return disp;
134 }
135
lv_linux_drm_set_file(lv_display_t * disp,const char * file,int64_t connector_id)136 void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t connector_id)
137 {
138 drm_dev_t * drm_dev = lv_display_get_driver_data(disp);
139 int ret;
140
141 ret = drm_setup(drm_dev, file, connector_id, DRM_FOURCC);
142 if(ret) {
143 close(drm_dev->fd);
144 drm_dev->fd = -1;
145 return;
146 }
147
148 ret = drm_setup_buffers(drm_dev);
149 if(ret) {
150 LV_LOG_ERROR("DRM buffer allocation failed");
151 close(drm_dev->fd);
152 drm_dev->fd = -1;
153 return;
154 }
155
156 LV_LOG_INFO("DRM subsystem and buffer mapped successfully");
157
158 int32_t hor_res = drm_dev->width;
159 int32_t ver_res = drm_dev->height;
160 int32_t width = drm_dev->mmWidth;
161
162 size_t buf_size = LV_MIN(drm_dev->drm_bufs[1].size, drm_dev->drm_bufs[0].size);
163 /* Resolution must be set first because if the screen is smaller than the size passed
164 * to lv_display_create then the buffers aren't big enough for LV_DISPLAY_RENDER_MODE_DIRECT.
165 */
166 lv_display_set_resolution(disp, hor_res, ver_res);
167 lv_display_set_buffers(disp, drm_dev->drm_bufs[1].map, drm_dev->drm_bufs[0].map, buf_size,
168 LV_DISPLAY_RENDER_MODE_DIRECT);
169
170 if(width) {
171 lv_display_set_dpi(disp, DIV_ROUND_UP(hor_res * 25400, width * 1000));
172 }
173
174 LV_LOG_INFO("Resolution is set to %" LV_PRId32 "x%" LV_PRId32 " at %" LV_PRId32 "dpi",
175 hor_res, ver_res, lv_display_get_dpi(disp));
176 }
177
178 /**********************
179 * STATIC FUNCTIONS
180 **********************/
181
get_plane_property_id(drm_dev_t * drm_dev,const char * name)182 static uint32_t get_plane_property_id(drm_dev_t * drm_dev, const char * name)
183 {
184 uint32_t i;
185
186 LV_LOG_TRACE("Find plane property: %s", name);
187
188 for(i = 0; i < drm_dev->count_plane_props; ++i)
189 if(!lv_strcmp(drm_dev->plane_props[i]->name, name))
190 return drm_dev->plane_props[i]->prop_id;
191
192 LV_LOG_TRACE("Unknown plane property: %s", name);
193
194 return 0;
195 }
196
get_crtc_property_id(drm_dev_t * drm_dev,const char * name)197 static uint32_t get_crtc_property_id(drm_dev_t * drm_dev, const char * name)
198 {
199 uint32_t i;
200
201 LV_LOG_TRACE("Find crtc property: %s", name);
202
203 for(i = 0; i < drm_dev->count_crtc_props; ++i)
204 if(!lv_strcmp(drm_dev->crtc_props[i]->name, name))
205 return drm_dev->crtc_props[i]->prop_id;
206
207 LV_LOG_TRACE("Unknown crtc property: %s", name);
208
209 return 0;
210 }
211
get_conn_property_id(drm_dev_t * drm_dev,const char * name)212 static uint32_t get_conn_property_id(drm_dev_t * drm_dev, const char * name)
213 {
214 uint32_t i;
215
216 LV_LOG_TRACE("Find conn property: %s", name);
217
218 for(i = 0; i < drm_dev->count_conn_props; ++i)
219 if(!lv_strcmp(drm_dev->conn_props[i]->name, name))
220 return drm_dev->conn_props[i]->prop_id;
221
222 LV_LOG_TRACE("Unknown conn property: %s", name);
223
224 return 0;
225 }
226
page_flip_handler(int fd,unsigned int sequence,unsigned int tv_sec,unsigned int tv_usec,void * user_data)227 static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec,
228 void * user_data)
229 {
230 LV_UNUSED(fd);
231 LV_UNUSED(sequence);
232 LV_UNUSED(tv_sec);
233 LV_UNUSED(tv_usec);
234 LV_LOG_TRACE("flip");
235 drm_dev_t * drm_dev = user_data;
236 if(drm_dev->req) {
237 drmModeAtomicFree(drm_dev->req);
238 drm_dev->req = NULL;
239 }
240 }
241
drm_get_plane_props(drm_dev_t * drm_dev)242 static int drm_get_plane_props(drm_dev_t * drm_dev)
243 {
244 uint32_t i;
245
246 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev->fd, drm_dev->plane_id,
247 DRM_MODE_OBJECT_PLANE);
248 if(!props) {
249 LV_LOG_ERROR("drmModeObjectGetProperties failed");
250 return -1;
251 }
252 LV_LOG_TRACE("Found %u plane props", props->count_props);
253 drm_dev->count_plane_props = props->count_props;
254 for(i = 0; i < props->count_props; i++) {
255 drm_dev->plane_props[i] = drmModeGetProperty(drm_dev->fd, props->props[i]);
256 LV_LOG_TRACE("Added plane prop %u:%s", drm_dev->plane_props[i]->prop_id, drm_dev->plane_props[i]->name);
257 }
258 drmModeFreeObjectProperties(props);
259
260 return 0;
261 }
262
drm_get_crtc_props(drm_dev_t * drm_dev)263 static int drm_get_crtc_props(drm_dev_t * drm_dev)
264 {
265 uint32_t i;
266
267 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev->fd, drm_dev->crtc_id,
268 DRM_MODE_OBJECT_CRTC);
269 if(!props) {
270 LV_LOG_ERROR("drmModeObjectGetProperties failed");
271 return -1;
272 }
273 LV_LOG_TRACE("Found %u crtc props", props->count_props);
274 drm_dev->count_crtc_props = props->count_props;
275 for(i = 0; i < props->count_props; i++) {
276 drm_dev->crtc_props[i] = drmModeGetProperty(drm_dev->fd, props->props[i]);
277 LV_LOG_TRACE("Added crtc prop %u:%s", drm_dev->crtc_props[i]->prop_id, drm_dev->crtc_props[i]->name);
278 }
279 drmModeFreeObjectProperties(props);
280
281 return 0;
282 }
283
drm_get_conn_props(drm_dev_t * drm_dev)284 static int drm_get_conn_props(drm_dev_t * drm_dev)
285 {
286 uint32_t i;
287
288 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev->fd, drm_dev->conn_id,
289 DRM_MODE_OBJECT_CONNECTOR);
290 if(!props) {
291 LV_LOG_ERROR("drmModeObjectGetProperties failed");
292 return -1;
293 }
294 LV_LOG_TRACE("Found %u connector props", props->count_props);
295 drm_dev->count_conn_props = props->count_props;
296 for(i = 0; i < props->count_props; i++) {
297 drm_dev->conn_props[i] = drmModeGetProperty(drm_dev->fd, props->props[i]);
298 LV_LOG_TRACE("Added connector prop %u:%s", drm_dev->conn_props[i]->prop_id, drm_dev->conn_props[i]->name);
299 }
300 drmModeFreeObjectProperties(props);
301
302 return 0;
303 }
304
drm_add_plane_property(drm_dev_t * drm_dev,const char * name,uint64_t value)305 static int drm_add_plane_property(drm_dev_t * drm_dev, const char * name, uint64_t value)
306 {
307 int ret;
308 uint32_t prop_id = get_plane_property_id(drm_dev, name);
309
310 if(!prop_id) {
311 LV_LOG_ERROR("Couldn't find plane prop %s", name);
312 return -1;
313 }
314
315 ret = drmModeAtomicAddProperty(drm_dev->req, drm_dev->plane_id, get_plane_property_id(drm_dev, name), value);
316 if(ret < 0) {
317 LV_LOG_ERROR("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret);
318 return ret;
319 }
320
321 return 0;
322 }
323
drm_add_crtc_property(drm_dev_t * drm_dev,const char * name,uint64_t value)324 static int drm_add_crtc_property(drm_dev_t * drm_dev, const char * name, uint64_t value)
325 {
326 int ret;
327 uint32_t prop_id = get_crtc_property_id(drm_dev, name);
328
329 if(!prop_id) {
330 LV_LOG_ERROR("Couldn't find crtc prop %s", name);
331 return -1;
332 }
333
334 ret = drmModeAtomicAddProperty(drm_dev->req, drm_dev->crtc_id, get_crtc_property_id(drm_dev, name), value);
335 if(ret < 0) {
336 LV_LOG_ERROR("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret);
337 return ret;
338 }
339
340 return 0;
341 }
342
drm_add_conn_property(drm_dev_t * drm_dev,const char * name,uint64_t value)343 static int drm_add_conn_property(drm_dev_t * drm_dev, const char * name, uint64_t value)
344 {
345 int ret;
346 uint32_t prop_id = get_conn_property_id(drm_dev, name);
347
348 if(!prop_id) {
349 LV_LOG_ERROR("Couldn't find conn prop %s", name);
350 return -1;
351 }
352
353 ret = drmModeAtomicAddProperty(drm_dev->req, drm_dev->conn_id, get_conn_property_id(drm_dev, name), value);
354 if(ret < 0) {
355 LV_LOG_ERROR("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret);
356 return ret;
357 }
358
359 return 0;
360 }
361
drm_dmabuf_set_plane(drm_dev_t * drm_dev,drm_buffer_t * buf)362 static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf)
363 {
364 int ret;
365 static int first = 1;
366 uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
367
368 drm_dev->req = drmModeAtomicAlloc();
369
370 /* On first Atomic commit, do a modeset */
371 if(first) {
372 drm_add_conn_property(drm_dev, "CRTC_ID", drm_dev->crtc_id);
373
374 drm_add_crtc_property(drm_dev, "MODE_ID", drm_dev->blob_id);
375 drm_add_crtc_property(drm_dev, "ACTIVE", 1);
376
377 flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
378
379 first = 0;
380 }
381
382 drm_add_plane_property(drm_dev, "FB_ID", buf->fb_handle);
383 drm_add_plane_property(drm_dev, "CRTC_ID", drm_dev->crtc_id);
384 drm_add_plane_property(drm_dev, "SRC_X", 0);
385 drm_add_plane_property(drm_dev, "SRC_Y", 0);
386 drm_add_plane_property(drm_dev, "SRC_W", drm_dev->width << 16);
387 drm_add_plane_property(drm_dev, "SRC_H", drm_dev->height << 16);
388 drm_add_plane_property(drm_dev, "CRTC_X", 0);
389 drm_add_plane_property(drm_dev, "CRTC_Y", 0);
390 drm_add_plane_property(drm_dev, "CRTC_W", drm_dev->width);
391 drm_add_plane_property(drm_dev, "CRTC_H", drm_dev->height);
392
393 ret = drmModeAtomicCommit(drm_dev->fd, drm_dev->req, flags, drm_dev);
394 if(ret) {
395 LV_LOG_ERROR("drmModeAtomicCommit failed: %s (%d)", strerror(errno), errno);
396 drmModeAtomicFree(drm_dev->req);
397 return ret;
398 }
399
400 return 0;
401 }
402
find_plane(drm_dev_t * drm_dev,unsigned int fourcc,uint32_t * plane_id,uint32_t crtc_id,uint32_t crtc_idx)403 static int find_plane(drm_dev_t * drm_dev, unsigned int fourcc, uint32_t * plane_id, uint32_t crtc_id,
404 uint32_t crtc_idx)
405 {
406 LV_UNUSED(crtc_id);
407 drmModePlaneResPtr planes;
408 drmModePlanePtr plane;
409 unsigned int i;
410 unsigned int j;
411 int ret = 0;
412 unsigned int format = fourcc;
413
414 planes = drmModeGetPlaneResources(drm_dev->fd);
415 if(!planes) {
416 LV_LOG_ERROR("drmModeGetPlaneResources failed");
417 return -1;
418 }
419
420 LV_LOG_TRACE("drm: found planes %u", planes->count_planes);
421
422 for(i = 0; i < planes->count_planes; ++i) {
423 plane = drmModeGetPlane(drm_dev->fd, planes->planes[i]);
424 if(!plane) {
425 LV_LOG_ERROR("drmModeGetPlane failed: %s", strerror(errno));
426 break;
427 }
428
429 if(!(plane->possible_crtcs & (1 << crtc_idx))) {
430 drmModeFreePlane(plane);
431 continue;
432 }
433
434 for(j = 0; j < plane->count_formats; ++j) {
435 if(plane->formats[j] == format)
436 break;
437 }
438
439 if(j == plane->count_formats) {
440 drmModeFreePlane(plane);
441 continue;
442 }
443
444 *plane_id = plane->plane_id;
445 drmModeFreePlane(plane);
446
447 LV_LOG_TRACE("found plane %d", *plane_id);
448
449 break;
450 }
451
452 if(i == planes->count_planes)
453 ret = -1;
454
455 drmModeFreePlaneResources(planes);
456
457 return ret;
458 }
459
drm_find_connector(drm_dev_t * drm_dev,int64_t connector_id)460 static int drm_find_connector(drm_dev_t * drm_dev, int64_t connector_id)
461 {
462 drmModeConnector * conn = NULL;
463 drmModeEncoder * enc = NULL;
464 drmModeRes * res;
465 int i;
466
467 if((res = drmModeGetResources(drm_dev->fd)) == NULL) {
468 LV_LOG_ERROR("drmModeGetResources() failed");
469 return -1;
470 }
471
472 if(res->count_crtcs <= 0) {
473 LV_LOG_ERROR("no Crtcs");
474 goto free_res;
475 }
476
477 /* find all available connectors */
478 for(i = 0; i < res->count_connectors; i++) {
479 conn = drmModeGetConnector(drm_dev->fd, res->connectors[i]);
480 if(!conn)
481 continue;
482
483 if(connector_id >= 0 && conn->connector_id != connector_id) {
484 drmModeFreeConnector(conn);
485 continue;
486 }
487
488 if(conn->connection == DRM_MODE_CONNECTED) {
489 LV_LOG_TRACE("drm: connector %d: connected", conn->connector_id);
490 }
491 else if(conn->connection == DRM_MODE_DISCONNECTED) {
492 LV_LOG_TRACE("drm: connector %d: disconnected", conn->connector_id);
493 }
494 else if(conn->connection == DRM_MODE_UNKNOWNCONNECTION) {
495 LV_LOG_TRACE("drm: connector %d: unknownconnection", conn->connector_id);
496 }
497 else {
498 LV_LOG_TRACE("drm: connector %d: unknown", conn->connector_id);
499 }
500
501 if(conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0)
502 break;
503
504 drmModeFreeConnector(conn);
505 conn = NULL;
506 };
507
508 if(!conn) {
509 LV_LOG_ERROR("suitable connector not found");
510 goto free_res;
511 }
512
513 drm_dev->conn_id = conn->connector_id;
514 LV_LOG_TRACE("conn_id: %d", drm_dev->conn_id);
515 drm_dev->mmWidth = conn->mmWidth;
516 drm_dev->mmHeight = conn->mmHeight;
517
518 lv_memcpy(&drm_dev->mode, &conn->modes[0], sizeof(drmModeModeInfo));
519
520 if(drmModeCreatePropertyBlob(drm_dev->fd, &drm_dev->mode, sizeof(drm_dev->mode),
521 &drm_dev->blob_id)) {
522 LV_LOG_ERROR("error creating mode blob");
523 goto free_res;
524 }
525
526 drm_dev->width = conn->modes[0].hdisplay;
527 drm_dev->height = conn->modes[0].vdisplay;
528
529 for(i = 0 ; i < res->count_encoders; i++) {
530 enc = drmModeGetEncoder(drm_dev->fd, res->encoders[i]);
531 if(!enc)
532 continue;
533
534 LV_LOG_TRACE("enc%d enc_id %d conn enc_id %d", i, enc->encoder_id, conn->encoder_id);
535
536 if(enc->encoder_id == conn->encoder_id)
537 break;
538
539 drmModeFreeEncoder(enc);
540 enc = NULL;
541 }
542
543 if(enc) {
544 drm_dev->enc_id = enc->encoder_id;
545 LV_LOG_TRACE("enc_id: %d", drm_dev->enc_id);
546 drm_dev->crtc_id = enc->crtc_id;
547 LV_LOG_TRACE("crtc_id: %d", drm_dev->crtc_id);
548 drmModeFreeEncoder(enc);
549 }
550 else {
551 /* Encoder hasn't been associated yet, look it up */
552 for(i = 0; i < conn->count_encoders; i++) {
553 int crtc, crtc_id = -1;
554
555 enc = drmModeGetEncoder(drm_dev->fd, conn->encoders[i]);
556 if(!enc)
557 continue;
558
559 for(crtc = 0 ; crtc < res->count_crtcs; crtc++) {
560 uint32_t crtc_mask = 1 << crtc;
561
562 crtc_id = res->crtcs[crtc];
563
564 LV_LOG_TRACE("enc_id %d crtc%d id %d mask %x possible %x", enc->encoder_id, crtc, crtc_id, crtc_mask,
565 enc->possible_crtcs);
566
567 if(enc->possible_crtcs & crtc_mask)
568 break;
569 }
570
571 if(crtc_id > 0) {
572 drm_dev->enc_id = enc->encoder_id;
573 LV_LOG_TRACE("enc_id: %d", drm_dev->enc_id);
574 drm_dev->crtc_id = crtc_id;
575 LV_LOG_TRACE("crtc_id: %d", drm_dev->crtc_id);
576 break;
577 }
578
579 drmModeFreeEncoder(enc);
580 enc = NULL;
581 }
582
583 if(!enc) {
584 LV_LOG_ERROR("suitable encoder not found");
585 goto free_res;
586 }
587
588 drmModeFreeEncoder(enc);
589 }
590
591 drm_dev->crtc_idx = UINT32_MAX;
592
593 for(i = 0; i < res->count_crtcs; ++i) {
594 if(drm_dev->crtc_id == res->crtcs[i]) {
595 drm_dev->crtc_idx = i;
596 break;
597 }
598 }
599
600 if(drm_dev->crtc_idx == UINT32_MAX) {
601 LV_LOG_ERROR("drm: CRTC not found");
602 goto free_res;
603 }
604
605 LV_LOG_TRACE("crtc_idx: %d", drm_dev->crtc_idx);
606
607 return 0;
608
609 free_res:
610 drmModeFreeResources(res);
611
612 return -1;
613 }
614
drm_open(const char * path)615 static int drm_open(const char * path)
616 {
617 int fd, flags;
618 uint64_t has_dumb;
619 int ret;
620
621 fd = open(path, O_RDWR);
622 if(fd < 0) {
623 LV_LOG_ERROR("cannot open \"%s\"", path);
624 return -1;
625 }
626
627 /* set FD_CLOEXEC flag */
628 if((flags = fcntl(fd, F_GETFD)) < 0 ||
629 fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
630 LV_LOG_ERROR("fcntl FD_CLOEXEC failed");
631 goto err;
632 }
633
634 /* check capability */
635 ret = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb);
636 if(ret < 0 || has_dumb == 0) {
637 LV_LOG_ERROR("drmGetCap DRM_CAP_DUMB_BUFFER failed or \"%s\" doesn't have dumb "
638 "buffer", path);
639 goto err;
640 }
641
642 return fd;
643 err:
644 close(fd);
645 return -1;
646 }
647
drm_setup(drm_dev_t * drm_dev,const char * device_path,int64_t connector_id,unsigned int fourcc)648 static int drm_setup(drm_dev_t * drm_dev, const char * device_path, int64_t connector_id, unsigned int fourcc)
649 {
650 int ret;
651
652 drm_dev->fd = drm_open(device_path);
653 if(drm_dev->fd < 0)
654 return -1;
655
656 ret = drmSetClientCap(drm_dev->fd, DRM_CLIENT_CAP_ATOMIC, 1);
657 if(ret) {
658 LV_LOG_ERROR("No atomic modesetting support: %s", strerror(errno));
659 goto err;
660 }
661
662 ret = drm_find_connector(drm_dev, connector_id);
663 if(ret) {
664 LV_LOG_ERROR("available drm devices not found");
665 goto err;
666 }
667
668 ret = find_plane(drm_dev, fourcc, &drm_dev->plane_id, drm_dev->crtc_id, drm_dev->crtc_idx);
669 if(ret) {
670 LV_LOG_ERROR("Cannot find plane");
671 goto err;
672 }
673
674 drm_dev->plane = drmModeGetPlane(drm_dev->fd, drm_dev->plane_id);
675 if(!drm_dev->plane) {
676 LV_LOG_ERROR("Cannot get plane");
677 goto err;
678 }
679
680 drm_dev->crtc = drmModeGetCrtc(drm_dev->fd, drm_dev->crtc_id);
681 if(!drm_dev->crtc) {
682 LV_LOG_ERROR("Cannot get crtc");
683 goto err;
684 }
685
686 drm_dev->conn = drmModeGetConnector(drm_dev->fd, drm_dev->conn_id);
687 if(!drm_dev->conn) {
688 LV_LOG_ERROR("Cannot get connector");
689 goto err;
690 }
691
692 ret = drm_get_plane_props(drm_dev);
693 if(ret) {
694 LV_LOG_ERROR("Cannot get plane props");
695 goto err;
696 }
697
698 ret = drm_get_crtc_props(drm_dev);
699 if(ret) {
700 LV_LOG_ERROR("Cannot get crtc props");
701 goto err;
702 }
703
704 ret = drm_get_conn_props(drm_dev);
705 if(ret) {
706 LV_LOG_ERROR("Cannot get connector props");
707 goto err;
708 }
709
710 drm_dev->drm_event_ctx.version = DRM_EVENT_CONTEXT_VERSION;
711 drm_dev->drm_event_ctx.page_flip_handler = page_flip_handler;
712 drm_dev->fourcc = fourcc;
713
714 LV_LOG_INFO("drm: Found plane_id: %u connector_id: %d crtc_id: %d",
715 drm_dev->plane_id, drm_dev->conn_id, drm_dev->crtc_id);
716
717 LV_LOG_INFO("drm: %dx%d (%dmm X% dmm) pixel format %c%c%c%c",
718 drm_dev->width, drm_dev->height, drm_dev->mmWidth, drm_dev->mmHeight,
719 (fourcc >> 0) & 0xff, (fourcc >> 8) & 0xff, (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff);
720
721 return 0;
722
723 err:
724 close(drm_dev->fd);
725 return -1;
726 }
727
drm_allocate_dumb(drm_dev_t * drm_dev,drm_buffer_t * buf)728 static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf)
729 {
730 struct drm_mode_create_dumb creq;
731 struct drm_mode_map_dumb mreq;
732 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
733 int ret;
734
735 /* create dumb buffer */
736 lv_memzero(&creq, sizeof(creq));
737 creq.width = drm_dev->width;
738 creq.height = drm_dev->height;
739 creq.bpp = LV_COLOR_DEPTH;
740 ret = drmIoctl(drm_dev->fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
741 if(ret < 0) {
742 LV_LOG_ERROR("DRM_IOCTL_MODE_CREATE_DUMB fail");
743 return -1;
744 }
745
746 buf->handle = creq.handle;
747 buf->pitch = creq.pitch;
748 buf->size = creq.size;
749
750 /* prepare buffer for memory mapping */
751 lv_memzero(&mreq, sizeof(mreq));
752 mreq.handle = creq.handle;
753 ret = drmIoctl(drm_dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
754 if(ret) {
755 LV_LOG_ERROR("DRM_IOCTL_MODE_MAP_DUMB fail");
756 return -1;
757 }
758
759 buf->offset = mreq.offset;
760 LV_LOG_INFO("size %lu pitch %u offset %u", buf->size, buf->pitch, buf->offset);
761
762 /* perform actual memory mapping */
763 buf->map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_dev->fd, mreq.offset);
764 if(buf->map == MAP_FAILED) {
765 LV_LOG_ERROR("mmap fail");
766 return -1;
767 }
768
769 /* clear the framebuffer to 0 (= full transparency in ARGB8888) */
770 lv_memzero(buf->map, creq.size);
771
772 /* create framebuffer object for the dumb-buffer */
773 handles[0] = creq.handle;
774 pitches[0] = creq.pitch;
775 offsets[0] = 0;
776 ret = drmModeAddFB2(drm_dev->fd, drm_dev->width, drm_dev->height, drm_dev->fourcc,
777 handles, pitches, offsets, &buf->fb_handle, 0);
778 if(ret) {
779 LV_LOG_ERROR("drmModeAddFB fail");
780 return -1;
781 }
782
783 return 0;
784 }
785
drm_setup_buffers(drm_dev_t * drm_dev)786 static int drm_setup_buffers(drm_dev_t * drm_dev)
787 {
788 int ret;
789
790 /*Allocate DUMB buffers*/
791 ret = drm_allocate_dumb(drm_dev, &drm_dev->drm_bufs[0]);
792 if(ret)
793 return ret;
794
795 ret = drm_allocate_dumb(drm_dev, &drm_dev->drm_bufs[1]);
796 if(ret)
797 return ret;
798
799 return 0;
800 }
801
drm_flush_wait(lv_display_t * disp)802 static void drm_flush_wait(lv_display_t * disp)
803 {
804 drm_dev_t * drm_dev = lv_display_get_driver_data(disp);
805
806 struct pollfd pfd;
807 pfd.fd = drm_dev->fd;
808 pfd.events = POLLIN;
809
810 while(drm_dev->req) {
811 int ret;
812 do {
813 ret = poll(&pfd, 1, -1);
814 } while(ret == -1 && errno == EINTR);
815
816 if(ret > 0)
817 drmHandleEvent(drm_dev->fd, &drm_dev->drm_event_ctx);
818 else {
819 LV_LOG_ERROR("poll failed: %s", strerror(errno));
820 return;
821 }
822 }
823 }
824
drm_flush(lv_display_t * disp,const lv_area_t * area,uint8_t * px_map)825 static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
826 {
827 if(!lv_display_flush_is_last(disp)) return;
828
829 LV_UNUSED(area);
830 LV_UNUSED(px_map);
831 drm_dev_t * drm_dev = lv_display_get_driver_data(disp);
832
833 for(int idx = 0; idx < 2; idx++) {
834 if(drm_dev->drm_bufs[idx].map == px_map) {
835 /*Request buffer swap*/
836 if(drm_dmabuf_set_plane(drm_dev, &drm_dev->drm_bufs[idx])) {
837 LV_LOG_ERROR("Flush fail");
838 return;
839 }
840 else
841 LV_LOG_TRACE("Flush done");
842 }
843 }
844 }
845
tick_get_cb(void)846 static uint32_t tick_get_cb(void)
847 {
848 struct timespec t;
849 clock_gettime(CLOCK_MONOTONIC, &t);
850 uint64_t time_ms = t.tv_sec * 1000 + (t.tv_nsec / 1000000);
851 return time_ms;
852 }
853
854 #endif /*LV_USE_LINUX_DRM*/
855