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