xref: /OK3568_Linux_fs/external/drm-cursor/drm_cursor.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  *  Copyright (c) 2021, Jeffy Chen <jeffy.chen@rock-chips.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  */
14 
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <inttypes.h>
18 #include <pthread.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 
25 #include <xf86drm.h>
26 #include <xf86drmMode.h>
27 
28 #include <gbm.h>
29 
30 #include "drm_common.h"
31 #include "drm_egl.h"
32 
33 #define DRM_CURSOR_CONFIG_FILE "/etc/drm-cursor.conf"
34 #define OPT_DEBUG "debug="
35 #define OPT_LOG_FILE "log-file="
36 #define OPT_HIDE "hide="
37 #define OPT_ALLOW_OVERLAY "allow-overlay="
38 #define OPT_PREFER_AFBC "prefer-afbc="
39 #define OPT_PREFER_PLANE "prefer-plane="
40 #define OPT_PREFER_PLANES "prefer-planes="
41 #define OPT_CRTC_BLOCKLIST "crtc-blocklist="
42 #define OPT_NUM_SURFACES "num-surfaces="
43 #define OPT_MAX_FPS "max-fps="
44 #define OPT_ATOMIC "atomic="
45 #define OPT_SCALE "scale="
46 #define OPT_SCALE_FROM "scale-from="
47 
48 #define DRM_MAX_CRTCS 8
49 
50 typedef enum {
51   PLANE_PROP_type = 0,
52   PLANE_PROP_IN_FORMATS,
53   PLANE_PROP_zpos,
54   PLANE_PROP_ZPOS,
55   PLANE_PROP_ASYNC_COMMIT,
56   PLANE_PROP_CRTC_ID,
57   PLANE_PROP_FB_ID,
58   PLANE_PROP_SRC_X,
59   PLANE_PROP_SRC_Y,
60   PLANE_PROP_SRC_W,
61   PLANE_PROP_SRC_H,
62   PLANE_PROP_CRTC_X,
63   PLANE_PROP_CRTC_Y,
64   PLANE_PROP_CRTC_W,
65   PLANE_PROP_CRTC_H,
66   PLANE_PROP_MAX,
67 } drm_plane_prop;
68 
69 static const char *drm_plane_prop_names[] = {
70   [PLANE_PROP_type] = "type",
71   [PLANE_PROP_IN_FORMATS] = "IN_FORMATS",
72   [PLANE_PROP_zpos] = "zpos",
73   [PLANE_PROP_ZPOS] = "ZPOS",
74   [PLANE_PROP_ASYNC_COMMIT] = "ASYNC_COMMIT",
75   [PLANE_PROP_CRTC_ID] = "CRTC_ID",
76   [PLANE_PROP_FB_ID] = "FB_ID",
77   [PLANE_PROP_SRC_X] = "SRC_X",
78   [PLANE_PROP_SRC_Y] = "SRC_Y",
79   [PLANE_PROP_SRC_W] = "SRC_W",
80   [PLANE_PROP_SRC_H] = "SRC_H",
81   [PLANE_PROP_CRTC_X] = "CRTC_X",
82   [PLANE_PROP_CRTC_Y] = "CRTC_Y",
83   [PLANE_PROP_CRTC_W] = "CRTC_W",
84   [PLANE_PROP_CRTC_H] = "CRTC_H",
85 };
86 
87 typedef struct {
88   uint32_t plane_id;
89   int cursor_plane;
90   int can_afbc;
91   int can_linear;
92   drmModePlane *plane;
93   drmModeObjectProperties *props;
94   int prop_ids[PLANE_PROP_MAX];
95 } drm_plane;
96 
97 #define REQ_SET_CURSOR  (1 << 0)
98 #define REQ_MOVE_CURSOR (1 << 1)
99 
100 typedef struct {
101   uint32_t handle;
102   uint32_t fb;
103 
104   int width;
105   int height;
106 
107   int scaled_w;
108   int scaled_h;
109 
110   int x;
111   int y;
112 
113   int scaled_x;
114   int scaled_y;
115 
116   int off_x;
117   int off_y;
118 
119   int hot_x;
120   int hot_y;
121 
122   int request;
123 } drm_cursor_state;
124 
125 typedef enum {
126   IDLE = 0,
127   FATAL_ERROR,
128   PENDING,
129 } drm_thread_state;
130 
131 typedef struct {
132   uint32_t crtc_id;
133   uint32_t crtc_pipe;
134 
135   int width;
136   int height;
137 
138   drm_plane *plane;
139   uint32_t prefer_plane_id;
140 
141   drm_cursor_state cursor_next;
142   drm_cursor_state cursor_curr;
143 
144   pthread_t thread;
145   pthread_cond_t cond;
146   pthread_mutex_t mutex;
147   drm_thread_state state;
148 
149   void *egl_ctx;
150 
151   int verified;
152 
153   int use_afbc_modifier;
154   int blocked;
155   int async_commit;
156 
157   uint64_t last_update_time;
158 } drm_crtc;
159 
160 typedef struct {
161   int fd;
162 
163   drm_crtc crtcs[DRM_MAX_CRTCS];
164   int num_crtcs;
165 
166   drmModePlaneResPtr pres;
167   drmModeRes *res;
168 
169   int prefer_afbc_modifier;
170   int allow_overlay;
171   int num_surfaces;
172   int inited;
173   int atomic;
174   int hide;
175   uint64_t min_interval;
176 
177   float scale_x, scale_y;
178   float scale_from;
179 
180   char *configs;
181 } drm_ctx;
182 
183 static drm_ctx g_drm_ctx = { 0, };
184 drm_private int g_drm_debug = 0;
185 drm_private FILE *g_log_fp = NULL;
186 
drm_curr_time(void)187 static inline uint64_t drm_curr_time(void)
188 {
189   struct timeval tv;
190 
191   gettimeofday(&tv, NULL);
192   return tv.tv_sec + tv.tv_usec / 1000;
193 }
194 
drm_plane_get_prop(drm_ctx * ctx,drm_plane * plane,drm_plane_prop p)195 static int drm_plane_get_prop(drm_ctx *ctx, drm_plane *plane, drm_plane_prop p)
196 {
197   drmModePropertyPtr prop;
198   uint32_t i;
199 
200   if (plane->prop_ids[p])
201     return plane->prop_ids[p];
202 
203   for (i = 0; i < plane->props->count_props; i++) {
204     prop = drmModeGetProperty(ctx->fd, plane->props->props[i]);
205     if (prop && !strcmp(prop->name, drm_plane_prop_names[p])) {
206       drmModeFreeProperty(prop);
207       plane->prop_ids[p] = i;
208       return i;
209     }
210     drmModeFreeProperty(prop);
211   }
212 
213   return -1;
214 }
215 
drm_atomic_add_plane_prop(drm_ctx * ctx,drmModeAtomicReq * request,drm_plane * plane,drm_plane_prop p,uint64_t value)216 static int drm_atomic_add_plane_prop(drm_ctx *ctx, drmModeAtomicReq *request,
217                                      drm_plane *plane, drm_plane_prop p,
218                                      uint64_t value)
219 {
220   int prop_idx = drm_plane_get_prop(ctx, plane, p);
221   if (prop_idx < 0)
222     return -1;
223 
224   return drmModeAtomicAddProperty(request, plane->plane_id,
225                                   plane->props->props[prop_idx], value);
226 }
227 
drm_set_plane(drm_ctx * ctx,drm_crtc * crtc,drm_plane * plane,uint32_t fb,int x,int y,int w,int h)228 static int drm_set_plane(drm_ctx *ctx, drm_crtc *crtc, drm_plane *plane,
229                          uint32_t fb, int x, int y, int w, int h)
230 {
231   drmModeAtomicReq *req;
232   int ret = 0;
233 
234   if (plane->cursor_plane || crtc->async_commit || !ctx->atomic)
235     goto legacy;
236 
237   req = drmModeAtomicAlloc();
238   if (!req)
239     goto legacy;
240 
241   if (!fb) {
242     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_CRTC_ID, 0);
243     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_FB_ID, 0);
244   } else {
245     ret |= drm_atomic_add_plane_prop(ctx, req, plane,
246                                      PLANE_PROP_CRTC_ID, crtc->crtc_id);
247     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_FB_ID, fb);
248     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_SRC_X, 0);
249     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_SRC_Y, 0);
250     ret |= drm_atomic_add_plane_prop(ctx, req, plane,
251                                      PLANE_PROP_SRC_W, w << 16);
252     ret |= drm_atomic_add_plane_prop(ctx, req,
253                                      plane, PLANE_PROP_SRC_H, h << 16);
254     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_CRTC_X, x);
255     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_CRTC_Y, y);
256     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_CRTC_W, w);
257     ret |= drm_atomic_add_plane_prop(ctx, req, plane, PLANE_PROP_CRTC_H, h);
258   }
259 
260   ret |= drmModeAtomicCommit(ctx->fd, req, DRM_MODE_ATOMIC_NONBLOCK, NULL);
261   drmModeAtomicFree(req);
262 
263   if (ret >= 0)
264     return 0;
265 
266 legacy:
267   if (ret < 0 && ctx->atomic) {
268     DRM_ERROR("CRTC[%d]: failed to do atomic commit (%d)\n",
269               crtc->crtc_id, errno);
270     ctx->atomic = 0;
271   }
272   return drmModeSetPlane(ctx->fd, plane->plane_id, crtc->crtc_id, fb, 0,
273                          x, y, w, h, 0, 0, w << 16, h << 16);
274 }
275 
drm_plane_get_prop_value(drm_ctx * ctx,drm_plane * plane,drm_plane_prop p,uint64_t * value)276 static int drm_plane_get_prop_value(drm_ctx *ctx, drm_plane *plane,
277                                     drm_plane_prop p, uint64_t *value)
278 {
279   int prop_idx = drm_plane_get_prop(ctx, plane, p);
280   if (prop_idx < 0)
281     return -1;
282 
283   *value = plane->props->prop_values[prop_idx];
284   return 0;
285 }
286 
drm_plane_set_prop_max(drm_ctx * ctx,drm_plane * plane,drm_plane_prop p)287 static int drm_plane_set_prop_max(drm_ctx *ctx, drm_plane *plane,
288                                   drm_plane_prop p)
289 {
290   drmModePropertyPtr prop;
291   int prop_idx = drm_plane_get_prop(ctx, plane, p);
292   if (prop_idx < 0)
293     return -1;
294 
295   prop = drmModeGetProperty(ctx->fd, plane->props->props[prop_idx]);
296   drmModeObjectSetProperty (ctx->fd, plane->plane_id,
297                             DRM_MODE_OBJECT_PLANE,
298                             plane->props->props[prop_idx],
299                             prop->values[prop->count_values - 1]);
300   DRM_DEBUG("set plane %d prop: %s to max: %"PRIu64"\n",
301             plane->plane_id, drm_plane_prop_names[p],
302             prop->values[prop->count_values - 1]);
303   drmModeFreeProperty(prop);
304   return 0;
305 }
306 
drm_free_plane(drm_plane * plane)307 static void drm_free_plane(drm_plane *plane)
308 {
309   drmModeFreeObjectProperties(plane->props);
310   drmModeFreePlane(plane->plane);
311   free(plane);
312 }
313 
drm_plane_update_format(drm_ctx * ctx,drm_plane * plane)314 static void drm_plane_update_format(drm_ctx *ctx, drm_plane *plane)
315 {
316   drmModePropertyBlobPtr blob;
317   struct drm_format_modifier_blob *header;
318   struct drm_format_modifier *modifiers;
319   uint32_t *formats;
320   uint64_t value;
321   uint32_t i, j;
322 
323   plane->can_afbc = plane->can_linear = 0;
324 
325   /* Check formats */
326   for (i = 0; i < plane->plane->count_formats; i++) {
327     if (plane->plane->formats[i] == DRM_FORMAT_ARGB8888)
328       break;
329   }
330   if (i == plane->plane->count_formats)
331     return;
332 
333   if (drm_plane_get_prop_value(ctx, plane, PLANE_PROP_IN_FORMATS, &value) < 0) {
334     /* No in_formats */
335     plane->can_linear = 1;
336     return;
337   }
338 
339   blob = drmModeGetPropertyBlob(ctx->fd, value);
340   if (!blob)
341     return;
342 
343   header = blob->data;
344   formats = (uint32_t *) ((char *) header + header->formats_offset);
345   modifiers = (struct drm_format_modifier *)
346     ((char *) header + header->modifiers_offset);
347 
348   /* Check in_formats */
349   for (i = 0; i < header->count_formats; i++) {
350     if (formats[i] == DRM_FORMAT_ARGB8888)
351       break;
352   }
353   if (i == header->count_formats)
354     goto out;
355 
356   if (!header->count_modifiers) {
357     plane->can_linear = 1;
358     goto out;
359   }
360 
361   /* Check modifiers */
362   for (j = 0; j < header->count_modifiers; j++) {
363     struct drm_format_modifier *mod = &modifiers[j];
364 
365     if ((i < mod->offset) || (i > mod->offset + 63))
366       continue;
367     if (!(mod->formats & (1 << (i - mod->offset))))
368       continue;
369 
370     if (mod->modifier == DRM_AFBC_MODIFIER)
371       plane->can_afbc = 1;
372 
373     if (mod->modifier == DRM_FORMAT_MOD_LINEAR)
374       plane->can_linear = 1;
375   }
376 
377 out:
378   drmModeFreePropertyBlob(blob);
379 }
380 
drm_get_plane(drm_ctx * ctx,uint32_t plane_id)381 static drm_plane *drm_get_plane(drm_ctx *ctx, uint32_t plane_id)
382 {
383   drm_plane *plane = calloc(1, sizeof(*plane));
384   if (!plane)
385     return NULL;
386 
387   plane->plane_id = plane_id;
388   plane->plane = drmModeGetPlane(ctx->fd, plane_id);
389   if (!plane->plane)
390     goto err;
391 
392   plane->props = drmModeObjectGetProperties(ctx->fd, plane_id,
393                                             DRM_MODE_OBJECT_PLANE);
394   if (!plane->props)
395     goto err;
396 
397   drm_plane_update_format(ctx, plane);
398   return plane;
399 err:
400   drm_free_plane(plane);
401   return NULL;
402 }
403 
drm_load_configs(drm_ctx * ctx)404 static void drm_load_configs(drm_ctx *ctx)
405 {
406   struct stat st;
407   const char *file = DRM_CURSOR_CONFIG_FILE;
408   char *ptr, *tmp;
409   int fd;
410 
411   if (stat(file, &st) < 0)
412     return;
413 
414   fd = open(file, O_RDONLY);
415   if (fd < 0)
416     return;
417 
418   ptr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
419   if (ptr == MAP_FAILED)
420     goto out_close_fd;
421 
422   ctx->configs = malloc(st.st_size + 1);
423   if (!ctx->configs)
424     goto out_unmap;
425 
426   memcpy(ctx->configs, ptr, st.st_size);
427   ctx->configs[st.st_size] = '\0';
428 
429   tmp = ctx->configs;
430   while ((tmp = strchr(tmp, '#'))) {
431     while (*tmp != '\n' && *tmp != '\0')
432       *tmp++ = '\n';
433   }
434 
435 out_unmap:
436   munmap(ptr, st.st_size);
437 out_close_fd:
438   close(fd);
439 }
440 
drm_get_config(drm_ctx * ctx,const char * name)441 static const char *drm_get_config(drm_ctx *ctx, const char *name)
442 {
443   static char buf[4096];
444   const char *config;
445 
446   if (!ctx->configs)
447     return NULL;
448 
449   config = strstr(ctx->configs, name);
450   if (!config)
451     return NULL;
452 
453   if (config[strlen(name)] == '\n' || config[strlen(name)] == '\r')
454     return NULL;
455 
456   sscanf(config + strlen(name), "%4095s", buf);
457   return buf;
458 }
459 
drm_get_config_int(drm_ctx * ctx,const char * name,int def)460 static int drm_get_config_int(drm_ctx *ctx, const char *name, int def)
461 {
462   const char *config = drm_get_config(ctx, name);
463 
464   if (config)
465     return atoi(config);
466 
467   return def;
468 }
469 
drm_get_ctx(int fd)470 static drm_ctx *drm_get_ctx(int fd)
471 {
472   drm_ctx *ctx = &g_drm_ctx;
473   uint32_t prefer_planes[DRM_MAX_CRTCS] = { 0, };
474   uint32_t prefer_plane = 0;
475   uint32_t i, max_fps, count_crtcs;
476   const char *config;
477 
478   if (fd < 0)
479     return ctx;
480 
481   if (ctx->inited) {
482     /* Make sure the ctx's fd is the same as the input fd */
483     int flags = fcntl(ctx->fd, F_GETFL, 0);
484     if (fcntl(fd, F_GETFL, 0) == flags) {
485       fcntl(ctx->fd, F_SETFL, flags ^ O_NONBLOCK);
486       if (fcntl(fd, F_GETFL, 0) != flags) {
487         fcntl(ctx->fd, F_SETFL, flags);
488         return ctx;
489       }
490     }
491 
492     close(ctx->fd);
493     ctx->fd = dup(fd);
494     return ctx;
495   }
496 
497   /* Failed already */
498   if (ctx->fd < 0)
499     return NULL;
500 
501   ctx->fd = dup(fd);
502   if (ctx->fd < 0)
503     return NULL;
504 
505   drm_load_configs(ctx);
506 
507   g_drm_debug = drm_get_config_int(ctx, OPT_DEBUG, 0);
508 
509   if (getenv("DRM_DEBUG") || !access("/tmp/.drm_cursor_debug", F_OK))
510     g_drm_debug = 1;
511 
512   if (!(config = getenv("DRM_CURSOR_LOG_FILE")))
513     config = drm_get_config(ctx, OPT_LOG_FILE);
514 
515   g_log_fp = fopen(config ? config : "/var/log/drm-cursor.log", "wb+");
516 
517   ctx->atomic = drm_get_config_int(ctx, OPT_ATOMIC, 1);
518   DRM_INFO("atomic drm API %s\n", ctx->atomic ? "enabled" : "disabled");
519 
520   ctx->hide = drm_get_config_int(ctx, OPT_HIDE, 0);
521   if (ctx->hide)
522     DRM_INFO("invisible cursors\n");
523 
524 #ifdef PREFER_AFBC_MODIFIER
525   ctx->prefer_afbc_modifier = 1;
526 #endif
527 
528   ctx->prefer_afbc_modifier =
529     drm_get_config_int(ctx, OPT_PREFER_AFBC, ctx->prefer_afbc_modifier);
530 
531   if (ctx->prefer_afbc_modifier)
532     DRM_DEBUG("prefer ARM AFBC modifier\n");
533 
534   ctx->allow_overlay = drm_get_config_int(ctx, OPT_ALLOW_OVERLAY, 0);
535 
536   if (ctx->allow_overlay)
537     DRM_DEBUG("allow overlay planes\n");
538 
539   drmSetClientCap(ctx->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
540 
541   ctx->num_surfaces = drm_get_config_int(ctx, OPT_NUM_SURFACES, 8);
542 
543   max_fps = drm_get_config_int(ctx, OPT_MAX_FPS, 0);
544   if (max_fps <= 0)
545     max_fps = 60;
546 
547   ctx->min_interval = 1000 / max_fps;
548   if (ctx->min_interval)
549     ctx->min_interval--;
550 
551   DRM_INFO("max fps: %d\n", max_fps);
552 
553   config = drm_get_config(ctx, OPT_SCALE_FROM);
554   if (config) {
555     int w, h, screen_w, screen_h;
556     if (config &&
557         sscanf(config, "%dx%d/%dx%d", &w, &h, &screen_w, &screen_h) == 4) {
558       ctx->scale_from = 1.0 * w * h / screen_w / screen_h;
559       DRM_INFO("scale from: %s\n", config);
560     }
561   } else {
562     config = drm_get_config(ctx, OPT_SCALE);
563     if (config && sscanf(config, "%fx%f", &ctx->scale_x, &ctx->scale_y) == 2)
564       DRM_INFO("scale: %s\n", config);
565   }
566 
567   ctx->res = drmModeGetResources(ctx->fd);
568   if (!ctx->res)
569     goto err_free_configs;
570 
571   ctx->pres = drmModeGetPlaneResources(ctx->fd);
572   if (!ctx->pres)
573     goto err_free_res;
574 
575   count_crtcs = ctx->res->count_crtcs;
576 
577   /* Allow specifying prefer plane */
578   if ((config = getenv("DRM_CURSOR_PREFER_PLANE")))
579     prefer_plane = atoi(config);
580   else
581     prefer_plane = drm_get_config_int(ctx, OPT_PREFER_PLANE, 0);
582 
583   /* Allow specifying prefer planes */
584   if (!(config = getenv("DRM_CURSOR_PREFER_PLANES")))
585     config = drm_get_config(ctx, OPT_PREFER_PLANES);
586   for (i = 0; config && i < count_crtcs; i++) {
587     prefer_planes[i] = atoi(config);
588 
589     config = strchr(config, ',');
590     if (config)
591       config++;
592   }
593 
594   /* Fetch all CRTCs */
595   for (i = 0; i < count_crtcs; i++) {
596     drmModeCrtcPtr c = drmModeGetCrtc(ctx->fd, ctx->res->crtcs[i]);
597     drm_crtc *crtc = &ctx->crtcs[ctx->num_crtcs];
598 
599     if (!c)
600       continue;
601 
602     crtc->crtc_id = c->crtc_id;
603     crtc->crtc_pipe = i;
604     crtc->prefer_plane_id = prefer_planes[i] ? prefer_planes[i] : prefer_plane;
605 
606     DRM_DEBUG("found %d CRTC: %d(%d) (%dx%d) prefer plane: %d\n",
607               ctx->num_crtcs, c->crtc_id, i, c->width, c->height,
608               crtc->prefer_plane_id);
609 
610     ctx->num_crtcs++;
611     drmModeFreeCrtc(c);
612   }
613 
614   DRM_DEBUG("found %d CRTCs\n", ctx->num_crtcs);
615 
616   if (!ctx->num_crtcs)
617     goto err_free_pres;
618 
619   config = drm_get_config(ctx, OPT_CRTC_BLOCKLIST);
620   for (i = 0; config && i < count_crtcs; i++) {
621     uint32_t crtc_id = atoi(config);
622 
623     for (int j = 0; j < ctx->num_crtcs; j++) {
624       drm_crtc *crtc = &ctx->crtcs[j];
625       if (crtc->crtc_id != crtc_id)
626         continue;
627 
628       DRM_DEBUG("CRTC: %d blocked\n", crtc_id);
629       crtc->blocked = 1;
630     }
631 
632     config = strchr(config, ',');
633     if (config)
634       config++;
635   }
636 
637   if (g_drm_debug) {
638     /* Dump planes for debugging */
639     for (i = 0; i < ctx->pres->count_planes; i++) {
640       drm_plane *plane = drm_get_plane(ctx, ctx->pres->planes[i]);
641       char *type;
642       uint64_t value = 0;
643 
644       if (!plane)
645         continue;
646 
647       drm_plane_get_prop_value(ctx, plane, PLANE_PROP_type, &value);
648       switch (value) {
649       case DRM_PLANE_TYPE_PRIMARY:
650         type = "primary";
651         break;
652       case DRM_PLANE_TYPE_OVERLAY:
653         type = "overlay";
654         break;
655       case DRM_PLANE_TYPE_CURSOR:
656         type = "cursor ";
657         break;
658       default:
659         type = "unknown";
660         break;
661       }
662 
663       DRM_DEBUG("found plane: %d[%s] crtcs: 0x%x %s%s\n",
664                 plane->plane_id, type, plane->plane->possible_crtcs,
665                 plane->can_linear ? "(ARGB)" : "",
666                 plane->can_afbc ? "(AFBC)" : "");
667 
668       drm_free_plane(plane);
669     }
670   }
671 
672   DRM_INFO("using libdrm-cursor (%s)\n", LIBDRM_CURSOR_VERSION);
673 
674   ctx->inited = 1;
675   return ctx;
676 
677 err_free_pres:
678   drmModeFreePlaneResources(ctx->pres);
679 err_free_res:
680   drmModeFreeResources(ctx->res);
681 err_free_configs:
682   free(ctx->configs);
683   close(ctx->fd);
684   ctx->fd = -1;
685   return NULL;
686 }
687 
688 #define drm_crtc_bind_plane_force(ctx, crtc, plane) \
689   drm_crtc_bind_plane(ctx, crtc, plane, 1)
690 
691 #define drm_crtc_bind_plane_cursor(ctx, crtc, plane) \
692   drm_crtc_bind_plane(ctx, crtc, plane, 0)
693 
drm_crtc_bind_plane(drm_ctx * ctx,drm_crtc * crtc,uint32_t plane_id,int allow_overlay)694 static int drm_crtc_bind_plane(drm_ctx *ctx, drm_crtc *crtc, uint32_t plane_id,
695                                int allow_overlay)
696 {
697   drm_plane *plane;
698   uint64_t value;
699   int i;
700 
701   /* CRTC already assigned */
702   if (crtc->plane)
703     return 1;
704 
705   /* Plane already assigned */
706   for (i = 0; i < ctx->num_crtcs; i++) {
707     if (ctx->crtcs[i].plane && ctx->crtcs[i].plane->plane_id == plane_id)
708       return -1;
709   }
710 
711   plane = drm_get_plane(ctx, plane_id);
712   if (!plane)
713     return -1;
714 
715   /* Unable to use */
716   if (!plane->can_afbc && !plane->can_linear)
717     goto err;
718 
719   /* Not for this CRTC */
720   if (!(plane->plane->possible_crtcs & (1 << crtc->crtc_pipe)))
721     goto err;
722 
723   /* Not using primary planes */
724   if (drm_plane_get_prop_value(ctx, plane, PLANE_PROP_type, &value) < 0)
725     goto err;
726 
727   if (value == DRM_PLANE_TYPE_PRIMARY)
728     goto err;
729 
730   /* Check for overlay plane */
731   if (!allow_overlay && value == DRM_PLANE_TYPE_OVERLAY)
732     goto err;
733 
734   plane->cursor_plane = value == DRM_PLANE_TYPE_CURSOR;
735   if (plane->cursor_plane)
736     DRM_INFO("CRTC[%d]: using cursor plane\n", crtc->crtc_id);
737 
738   if (ctx->prefer_afbc_modifier && plane->can_afbc)
739     crtc->use_afbc_modifier = 1;
740   else if (!plane->can_linear)
741     crtc->use_afbc_modifier = 1;
742 
743   DRM_DEBUG("CRTC[%d]: bind plane: %d%s\n", crtc->crtc_id, plane->plane_id,
744             crtc->use_afbc_modifier ? "(AFBC)" : "");
745 
746   crtc->plane = plane;
747 
748   return 0;
749 err:
750   drm_free_plane(plane);
751   return -1;
752 }
753 
drm_crtc_valid(drm_crtc * crtc)754 static int drm_crtc_valid(drm_crtc *crtc)
755 {
756   return (crtc->width > 0 && crtc->height > 0) ? 0 : -1;
757 }
758 
drm_update_crtc(drm_ctx * ctx,drm_crtc * crtc)759 static int drm_update_crtc(drm_ctx *ctx, drm_crtc *crtc)
760 {
761   drmModeCrtcPtr c;
762   int was_connected, connected;
763 
764   c = drmModeGetCrtc(ctx->fd, crtc->crtc_id);
765   if (!c)
766     return -1;
767 
768   was_connected = drm_crtc_valid(crtc) >= 0;
769   crtc->width = c->width;
770   crtc->height = c->height;
771   connected = drm_crtc_valid(crtc) >= 0;
772 
773   drmModeFreeCrtc(c);
774 
775   if (connected != was_connected)
776     DRM_DEBUG("CRTC[%d]: %s!\n", crtc->crtc_id, \
777               connected ? "connected" : "disconnected");
778 
779   return drm_crtc_valid(crtc);
780 }
781 
drm_crtc_update_offsets(drm_ctx * ctx,drm_crtc * crtc,drm_cursor_state * cursor_state)782 static int drm_crtc_update_offsets(drm_ctx *ctx, drm_crtc *crtc,
783                                    drm_cursor_state *cursor_state)
784 {
785   int x, y, off_x, off_y, width, height, area_w, area_h;
786   float scale_x, scale_y;
787 
788   if (drm_update_crtc(ctx, crtc) < 0)
789     return -1;
790 
791   width = cursor_state->width;
792   height = cursor_state->height;
793 
794   if (ctx->scale_from) {
795     scale_x = scale_y =
796       ctx->scale_from * crtc->width * crtc->height / width / height;
797   } else {
798     scale_x = ctx->scale_x ? ctx->scale_x : 1.0;
799     scale_y = ctx->scale_y ? ctx->scale_y : 1.0;
800   }
801 
802   width *= scale_x;
803   height *= scale_y;
804 
805   x = cursor_state->x + cursor_state->hot_x - cursor_state->hot_x * scale_x;
806   y = cursor_state->y + cursor_state->hot_y - cursor_state->hot_y * scale_y;
807   area_w = crtc->width - width;
808   area_h = crtc->height - height;
809 
810   off_x = off_y = 0;
811 
812   if (x < 0)
813     off_x = x;
814 
815   if (y < 0)
816     off_y = y;
817 
818   if (x > area_w)
819     off_x = x - area_w;
820 
821   if (y > area_h)
822     off_y = y - area_h;
823 
824   cursor_state->scaled_x = x;
825   cursor_state->scaled_y = y;
826   cursor_state->off_x = off_x;
827   cursor_state->off_y = off_y;
828   cursor_state->scaled_w = width;
829   cursor_state->scaled_h = height;
830 
831   return 0;
832 }
833 
834 #define drm_crtc_disable_cursor(ctx, crtc) \
835   drm_crtc_update_cursor(ctx, crtc, NULL)
836 
drm_crtc_update_cursor(drm_ctx * ctx,drm_crtc * crtc,drm_cursor_state * cursor_state)837 static int drm_crtc_update_cursor(drm_ctx *ctx, drm_crtc *crtc,
838                                   drm_cursor_state *cursor_state)
839 {
840   drm_plane *plane = crtc->plane;
841   uint32_t old_fb = crtc->cursor_curr.fb;
842   uint32_t fb;
843   int x, y, w, h, ret;
844 
845   /* Disable */
846   if (!cursor_state) {
847     if (old_fb) {
848       DRM_DEBUG("CRTC[%d]: disabling cursor\n", crtc->crtc_id);
849       drm_set_plane(ctx, crtc, plane, 0, 0, 0, 0, 0);
850       drmModeRmFB(ctx->fd, old_fb);
851     }
852 
853     memset(&crtc->cursor_curr, 0, sizeof(drm_cursor_state));
854     return 0;
855   }
856 
857   /* Unchanged */
858   if (crtc->cursor_curr.fb == cursor_state->fb &&
859       crtc->cursor_curr.scaled_x == cursor_state->scaled_x &&
860       crtc->cursor_curr.scaled_y == cursor_state->scaled_y &&
861       crtc->cursor_curr.off_x == cursor_state->off_x &&
862       crtc->cursor_curr.off_y == cursor_state->off_y) {
863     crtc->cursor_curr = *cursor_state;
864     return 0;
865   }
866 
867   fb = cursor_state->fb;
868   x = cursor_state->scaled_x - cursor_state->off_x;
869   y = cursor_state->scaled_y - cursor_state->off_y;
870   w = cursor_state->scaled_w;
871   h = cursor_state->scaled_h;
872 
873   DRM_DEBUG("CRTC[%d]: setting fb: %d (%dx%d) on plane: %d at (%d,%d)\n",
874             crtc->crtc_id, fb, w, h, plane->plane_id, x, y);
875 
876   ret = drm_set_plane(ctx, crtc, plane, fb, x, y, w, h);
877   if (ret)
878     DRM_ERROR("CRTC[%d]: failed to set plane (%d)\n", crtc->crtc_id, errno);
879 
880   if (old_fb && old_fb != fb) {
881     DRM_DEBUG("CRTC[%d]: remove FB: %d\n", crtc->crtc_id, old_fb);
882     drmModeRmFB(ctx->fd, old_fb);
883   }
884 
885   crtc->cursor_curr = *cursor_state;
886   return ret;
887 }
888 
drm_crtc_create_fb(drm_ctx * ctx,drm_crtc * crtc,drm_cursor_state * cursor_state)889 static int drm_crtc_create_fb(drm_ctx *ctx, drm_crtc *crtc,
890                               drm_cursor_state *cursor_state)
891 {
892   uint32_t handle = cursor_state->handle;
893   int width = cursor_state->width;
894   int height = cursor_state->height;
895   int scaled_w = cursor_state->scaled_w;
896   int scaled_h = cursor_state->scaled_h;
897   int off_x = cursor_state->off_x;
898   int off_y = cursor_state->off_y;
899 
900   DRM_DEBUG("CRTC[%d]: convert FB from %d (%dx%d) to (%dx%d) offset: (%d,%d)\n",
901             crtc->crtc_id, handle, width, height,
902             scaled_w, scaled_h, off_x, off_y);
903 
904   if (!crtc->egl_ctx) {
905     uint64_t modifier;
906     int format;
907 
908     if (crtc->use_afbc_modifier) {
909       /* Mali only support AFBC with BGR formats now */
910       format = GBM_FORMAT_ABGR8888;
911       modifier = DRM_AFBC_MODIFIER;
912     } else {
913       format = GBM_FORMAT_ARGB8888;
914       modifier = 0;
915     }
916 
917     crtc->egl_ctx = egl_init_ctx(ctx->fd, ctx->num_surfaces, format, modifier);
918     if (!crtc->egl_ctx) {
919       DRM_ERROR("CRTC[%d]: failed to init egl ctx\n", crtc->crtc_id);
920       return -1;
921     }
922   }
923 
924   cursor_state->fb =
925     egl_convert_fb(ctx->fd, crtc->egl_ctx, handle, width, height,
926                    scaled_w, scaled_h, off_x, off_y);
927   if (!cursor_state->fb) {
928     DRM_ERROR("CRTC[%d]: failed to create FB\n", crtc->crtc_id);
929     return -1;
930   }
931 
932   DRM_DEBUG("CRTC[%d]: created FB: %d\n", crtc->crtc_id, cursor_state->fb);
933   return 0;
934 }
935 
drm_crtc_thread_fn(void * data)936 static void *drm_crtc_thread_fn(void *data)
937 {
938   drm_ctx *ctx = drm_get_ctx(-1);
939   drm_crtc *crtc = data;
940   drm_plane *plane = crtc->plane;
941   drm_cursor_state cursor_state;
942   uint64_t duration;
943   char name[256];
944 
945   DRM_DEBUG("CRTC[%d]: thread started\n", crtc->crtc_id);
946 
947   /**
948    * The new DRM driver doesn't allow setting atomic cap for Xorg.
949    * Let's use a custom thread name to workaround that.
950    */
951   snprintf(name, sizeof(name), "drm-cursor[%d]", crtc->crtc_id);
952   pthread_setname_np(crtc->thread, name);
953 
954   if (!plane->cursor_plane) {
955     drmSetClientCap(ctx->fd, DRM_CLIENT_CAP_ATOMIC, 1);
956 
957     /* Reflush props with atomic cap enabled */
958     drmModeFreeObjectProperties(plane->props);
959     plane->props = drmModeObjectGetProperties(ctx->fd, plane->plane_id,
960                                               DRM_MODE_OBJECT_PLANE);
961     if (!plane->props)
962       goto error;
963 
964     /* Set maximum ZPOS */
965     drm_plane_set_prop_max(ctx, plane, PLANE_PROP_zpos);
966     drm_plane_set_prop_max(ctx, plane, PLANE_PROP_ZPOS);
967 
968     /* Set async commit for Rockchip BSP kernel */
969     crtc->async_commit =
970       !drm_plane_set_prop_max(ctx, plane, PLANE_PROP_ASYNC_COMMIT);
971     if (crtc->async_commit)
972       DRM_INFO("CRTC[%d]: using async commit\n", crtc->crtc_id);
973   }
974 
975   crtc->last_update_time = drm_curr_time();
976 
977   while (1) {
978     /* Wait for new cursor state */
979     pthread_mutex_lock(&crtc->mutex);
980     while (crtc->state != PENDING)
981       pthread_cond_wait(&crtc->cond, &crtc->mutex);
982 
983     cursor_state = crtc->cursor_next;
984     crtc->cursor_next.request = 0;
985     crtc->state = IDLE;
986     cursor_state.request |= crtc->cursor_curr.request; /* For retry */
987     pthread_mutex_unlock(&crtc->mutex);
988 
989     /* For edge moving */
990     if (drm_crtc_update_offsets(ctx, crtc, &cursor_state) < 0) {
991       DRM_DEBUG("CRTC[%d]: unavailable!\n", crtc->crtc_id);
992       drm_crtc_disable_cursor(ctx, crtc);
993       goto retry;
994     }
995 
996     if (cursor_state.request & REQ_SET_CURSOR) {
997       cursor_state.request = 0;
998 
999       /* Handle set-cursor */
1000       DRM_DEBUG("CRTC[%d]: set new cursor %d (%dx%d)\n",
1001                 crtc->crtc_id, cursor_state.handle,
1002                 cursor_state.width, cursor_state.height);
1003 
1004       if (!cursor_state.handle) {
1005         drm_crtc_disable_cursor(ctx, crtc);
1006         goto next;
1007       }
1008 
1009       if (drm_crtc_create_fb(ctx, crtc, &cursor_state) < 0)
1010         goto error;
1011 
1012       if (drm_crtc_update_cursor(ctx, crtc, &cursor_state) < 0) {
1013         DRM_ERROR("CRTC[%d]: failed to set cursor\n", crtc->crtc_id);
1014         goto error;
1015       }
1016     } else if (cursor_state.request & REQ_MOVE_CURSOR) {
1017       cursor_state.request = 0;
1018 
1019       /* Handle move-cursor */
1020       DRM_DEBUG("CRTC[%d]: move cursor to (%d[%d],%d[%d])\n",
1021                 crtc->crtc_id, cursor_state.scaled_x, -cursor_state.off_x,
1022                 cursor_state.scaled_y, -cursor_state.off_y);
1023 
1024       if (!crtc->cursor_curr.handle) {
1025         /* Pre-moving */
1026         crtc->cursor_curr = cursor_state;
1027         goto next;
1028       } else if (crtc->cursor_curr.off_x != cursor_state.off_x ||
1029                  crtc->cursor_curr.off_y != cursor_state.off_y) {
1030         /* Edge moving */
1031         if (drm_crtc_create_fb(ctx, crtc, &cursor_state) < 0)
1032           goto error;
1033       } else {
1034         /* Normal moving */
1035         cursor_state.fb = crtc->cursor_curr.fb;
1036       }
1037 
1038       if (drm_crtc_update_cursor(ctx, crtc, &cursor_state) < 0) {
1039         DRM_ERROR("CRTC[%d]: failed to move cursor\n", crtc->crtc_id);
1040         goto error;
1041       }
1042     }
1043 
1044     if (!crtc->verified && crtc->cursor_curr.fb) {
1045       pthread_mutex_lock(&crtc->mutex);
1046       DRM_INFO("CRTC[%d]: it works!\n", crtc->crtc_id);
1047       crtc->verified = 1;
1048       pthread_cond_signal(&crtc->cond);
1049       pthread_mutex_unlock(&crtc->mutex);
1050     }
1051 
1052 next:
1053     duration = drm_curr_time() - crtc->last_update_time;
1054     if (duration < ctx->min_interval)
1055       usleep((ctx->min_interval - duration) * 1000);
1056     crtc->last_update_time = drm_curr_time();;
1057     continue;
1058 retry:
1059     /* Force setting cursor in next request */
1060     pthread_mutex_lock(&crtc->mutex);
1061     crtc->cursor_curr.request = REQ_SET_CURSOR;
1062     pthread_cond_signal(&crtc->cond);
1063     pthread_mutex_unlock(&crtc->mutex);
1064     goto next;
1065   }
1066 
1067 error:
1068   if (crtc->egl_ctx)
1069     egl_free_ctx(crtc->egl_ctx);
1070 
1071   drm_crtc_disable_cursor(ctx, crtc);
1072 
1073   pthread_mutex_lock(&crtc->mutex);
1074   DRM_DEBUG("CRTC[%d]: thread error\n", crtc->crtc_id);
1075   crtc->state = FATAL_ERROR;
1076 
1077   if (crtc->plane) {
1078     drm_free_plane(crtc->plane);
1079     crtc->plane = NULL;
1080   }
1081 
1082   pthread_cond_signal(&crtc->cond);
1083   pthread_mutex_unlock(&crtc->mutex);
1084 
1085   return NULL;
1086 }
1087 
drm_crtc_prepare(drm_ctx * ctx,drm_crtc * crtc)1088 static int drm_crtc_prepare(drm_ctx *ctx, drm_crtc *crtc)
1089 {
1090   uint32_t i;
1091 
1092   /* Update CRTC if unavailable */
1093   if (drm_crtc_valid(crtc) < 0)
1094     drm_update_crtc(ctx, crtc);
1095 
1096   /* CRTC already assigned */
1097   if (crtc->plane)
1098     return 1;
1099 
1100   /* Try specific plane */
1101   if (crtc->prefer_plane_id)
1102     drm_crtc_bind_plane_force(ctx, crtc, crtc->prefer_plane_id);
1103 
1104   /* Try cursor plane */
1105   for (i = 0; !crtc->plane && i < ctx->pres->count_planes; i++)
1106     drm_crtc_bind_plane_cursor(ctx, crtc, ctx->pres->planes[i]);
1107 
1108   /* Fallback to any available overlay plane */
1109   if (ctx->allow_overlay) {
1110     for (i = ctx->pres->count_planes; !crtc->plane && i; i--)
1111       drm_crtc_bind_plane_force(ctx, crtc, ctx->pres->planes[i - 1]);
1112   }
1113 
1114   if (!crtc->plane) {
1115     DRM_ERROR("CRTC[%d]: failed to find any plane\n", crtc->crtc_id);
1116     return -1;
1117   }
1118 
1119   crtc->state = IDLE;
1120 
1121   pthread_cond_init(&crtc->cond, NULL);
1122   pthread_mutex_init(&crtc->mutex, NULL);
1123   pthread_create(&crtc->thread, NULL, drm_crtc_thread_fn, crtc);
1124 
1125   return 0;
1126 }
1127 
drm_get_crtc(drm_ctx * ctx,uint32_t crtc_id)1128 static drm_crtc *drm_get_crtc(drm_ctx *ctx, uint32_t crtc_id)
1129 {
1130   drm_crtc *crtc = NULL;
1131   int i;
1132 
1133   for (i = 0; i < ctx->num_crtcs; i++) {
1134     crtc = &ctx->crtcs[i];
1135     if (!crtc_id && drm_update_crtc(ctx, crtc) < 0)
1136       continue;
1137 
1138     if (crtc->blocked)
1139       continue;
1140 
1141     if (!crtc_id || crtc->crtc_id == crtc_id)
1142       break;
1143   }
1144 
1145   if (i == ctx->num_crtcs) {
1146     DRM_ERROR("CRTC[%d]: not available\n", crtc_id);
1147     return NULL;
1148   }
1149 
1150   return crtc;
1151 }
1152 
drm_set_cursor(int fd,uint32_t crtc_id,uint32_t handle,uint32_t width,uint32_t height,int hot_x,int hot_y)1153 static int drm_set_cursor(int fd, uint32_t crtc_id, uint32_t handle,
1154                           uint32_t width, uint32_t height,
1155                           int hot_x, int hot_y)
1156 {
1157   drm_crtc *crtc;
1158   drm_ctx *ctx;
1159   drm_cursor_state *cursor_next;
1160 
1161   ctx = drm_get_ctx(fd);
1162   if (!ctx)
1163     return -1;
1164 
1165   if (ctx->hide)
1166     return 0;
1167 
1168   crtc = drm_get_crtc(ctx, crtc_id);
1169   if (!crtc)
1170     return -1;
1171 
1172   if (drm_crtc_prepare(ctx, crtc) < 0)
1173     return -1;
1174 
1175   DRM_DEBUG("CRTC[%d]: request setting new cursor %d (%dx%d)\n",
1176             crtc->crtc_id, handle, width, height);
1177 
1178   pthread_mutex_lock(&crtc->mutex);
1179   if (crtc->state == FATAL_ERROR) {
1180     pthread_mutex_unlock(&crtc->mutex);
1181     DRM_ERROR("CRTC[%d]: failed to set cursor\n", crtc->crtc_id);
1182     return -1;
1183   }
1184 
1185   /* Update next cursor state and notify the thread */
1186   cursor_next = &crtc->cursor_next;
1187 
1188   crtc->cursor_curr.request = 0;
1189   cursor_next->request = REQ_SET_CURSOR;
1190 
1191   cursor_next->fb = 0;
1192   cursor_next->handle = handle;
1193   cursor_next->width = width;
1194   cursor_next->height = height;
1195   cursor_next->hot_x = hot_x;
1196   cursor_next->hot_y = hot_y;
1197   crtc->state = PENDING;
1198   pthread_cond_signal(&crtc->cond);
1199 
1200   if (handle) {
1201     /**
1202      * Wait for verified or fatal error or retry.
1203      * HACK: Fake retry as successed.
1204      */
1205     while (!crtc->verified && crtc->state != FATAL_ERROR && \
1206            !crtc->cursor_curr.request)
1207       pthread_cond_wait(&crtc->cond, &crtc->mutex);
1208   }
1209 
1210   pthread_mutex_unlock(&crtc->mutex);
1211 
1212   if (crtc->state == FATAL_ERROR) {
1213     DRM_ERROR("CRTC[%d]: failed to set cursor\n", crtc->crtc_id);
1214     return -1;
1215   }
1216 
1217   return 0;
1218 }
1219 
drm_move_cursor(int fd,uint32_t crtc_id,int x,int y)1220 static int drm_move_cursor(int fd, uint32_t crtc_id, int x, int y)
1221 {
1222   drm_ctx *ctx;
1223   drm_crtc *crtc;
1224   drm_cursor_state *cursor_next;
1225 
1226   ctx = drm_get_ctx(fd);
1227   if (!ctx)
1228     return -1;
1229 
1230   if (ctx->hide)
1231     return 0;
1232 
1233   crtc = drm_get_crtc(ctx, crtc_id);
1234   if (!crtc)
1235     return -1;
1236 
1237   if (crtc->state == FATAL_ERROR || drm_crtc_prepare(ctx, crtc) < 0)
1238     return -1;
1239 
1240   if (drm_crtc_valid(crtc) < 0)
1241     return -1;
1242 
1243   DRM_DEBUG("CRTC[%d]: request moving cursor to (%d,%d) in (%dx%d)\n",
1244             crtc->crtc_id, x, y, crtc->width, crtc->height);
1245 
1246   pthread_mutex_lock(&crtc->mutex);
1247   if (crtc->state == FATAL_ERROR) {
1248     pthread_mutex_unlock(&crtc->mutex);
1249     return -1;
1250   }
1251 
1252   /* Update next cursor state and notify the thread */
1253   cursor_next = &crtc->cursor_next;
1254 
1255   cursor_next->request |= REQ_MOVE_CURSOR;
1256   cursor_next->fb = 0;
1257   cursor_next->x = x;
1258   cursor_next->y = y;
1259   crtc->state = PENDING;
1260   pthread_cond_signal(&crtc->cond);
1261   pthread_mutex_unlock(&crtc->mutex);
1262 
1263   return 0;
1264 }
1265 
1266 /* Hook functions */
1267 
drmModeSetCursor2(int fd,uint32_t crtcId,uint32_t bo_handle,uint32_t width,uint32_t height,int32_t hot_x,int32_t hot_y)1268 int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle,
1269                       uint32_t width, uint32_t height,
1270                       int32_t hot_x, int32_t hot_y)
1271 {
1272   /* Init log file */
1273   drm_get_ctx(fd);
1274 
1275   DRM_DEBUG("fd: %d crtc: %d handle: %d size: %dx%d (%d, %d)\n",
1276             fd, crtcId, bo_handle, width, height, hot_x, hot_y);
1277   return drm_set_cursor(fd, crtcId, bo_handle, width, height, hot_x, hot_y);
1278 }
1279 
drmModeSetCursor(int fd,uint32_t crtcId,uint32_t bo_handle,uint32_t width,uint32_t height)1280 int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle,
1281                      uint32_t width, uint32_t height)
1282 {
1283   drm_ctx *ctx;
1284 
1285   ctx = drm_get_ctx(fd);
1286   if (!ctx)
1287     return -1;
1288 
1289   DRM_DEBUG("fd: %d crtc: %d handle: %d size: %dx%d\n",
1290             fd, crtcId, bo_handle, width, height);
1291 
1292   if (bo_handle && width && height &&
1293       (ctx->scale_from || ctx->scale_x || ctx->scale_y))
1294     DRM_INFO("CRTC[%d]: scaling without hotspots, use drmModeSetCursor2()!\n",
1295              crtcId);
1296 
1297   return drm_set_cursor(fd, crtcId, bo_handle, width, height, 0, 0);
1298 }
1299 
drmModeMoveCursor(int fd,uint32_t crtcId,int x,int y)1300 int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
1301 {
1302   DRM_DEBUG("fd: %d crtc: %d position: %d,%d\n", fd, crtcId, x, y);
1303   return drm_move_cursor(fd, crtcId, x, y);
1304 }
1305