1 #include "ecore_drm2_private.h"
2 
3 #define INSIDE(x, y, xx, yy, ww, hh) \
4    (((x) < ((xx) + (ww))) && ((y) < ((yy) + (hh))) && \
5        ((x) >= (xx)) && ((y) >= (yy)))
6 
7 #define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
8 #define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
9 #define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
10 #define EDID_OFFSET_DATA_BLOCKS 0x36
11 #define EDID_OFFSET_LAST_BLOCK 0x6c
12 #define EDID_OFFSET_PNPID 0x08
13 #define EDID_OFFSET_SERIAL 0x0c
14 
15 static const char *conn_types[] =
16 {
17    "None", "VGA", "DVI-I", "DVI-D", "DVI-A",
18    "Composite", "S-Video", "LVDS", "Component", "DIN",
19    "DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP", "Virtual", "DSI",
20 };
21 
22 static void
_output_debug(Ecore_Drm2_Output * output,const drmModeConnector * conn)23 _output_debug(Ecore_Drm2_Output *output, const drmModeConnector *conn)
24 {
25    Eina_List *l;
26    Ecore_Drm2_Output_Mode *omode;
27    Ecore_Drm2_Plane_State *pstate;
28 
29    DBG("Created New Output At %d,%d", output->x, output->y);
30    DBG("\tCrtc Pos: %d %d", output->ocrtc->x, output->ocrtc->y);
31    DBG("\tCrtc: %d", output->crtc_id);
32    DBG("\tConn: %d", output->conn_id);
33    DBG("\tName: %s", output->name);
34    DBG("\tMake: %s", output->make);
35    DBG("\tModel: %s", output->model);
36    DBG("\tSerial: %s", output->serial);
37    DBG("\tCloned: %d", output->cloned);
38    DBG("\tPrimary: %d", output->primary);
39    DBG("\tConnected: %d", output->connected);
40    DBG("\tEnabled: %d", output->enabled);
41 
42    if (output->backlight.path)
43      {
44         DBG("\tBacklight");
45         switch (output->backlight.type)
46           {
47            case ECORE_DRM2_BACKLIGHT_RAW:
48              DBG("\t\tType: Raw");
49              break;
50            case ECORE_DRM2_BACKLIGHT_PLATFORM:
51              DBG("\t\tType: Platform");
52              break;
53            case ECORE_DRM2_BACKLIGHT_FIRMWARE:
54              DBG("\t\tType: Firmware");
55              break;
56           }
57         DBG("\t\tPath: %s", output->backlight.path);
58      }
59 
60    EINA_LIST_FOREACH(output->plane_states, l, pstate)
61      DBG("\tPossible Plane: %d", pstate->obj_id);
62 
63    EINA_LIST_FOREACH(output->modes, l, omode)
64      {
65         DBG("\tAdded Mode: %dx%d@%d%s%s%s",
66             omode->width, omode->height, omode->refresh,
67             (omode->flags & DRM_MODE_TYPE_PREFERRED) ? ", preferred" : "",
68             (omode->flags & DRM_MODE_TYPE_DEFAULT) ? ", current" : "",
69             (conn->count_modes == 0) ? ", built-in" : "");
70      }
71 }
72 
73 static void
_cb_output_event_free(void * data EINA_UNUSED,void * event)74 _cb_output_event_free(void *data EINA_UNUSED, void *event)
75 {
76    Ecore_Drm2_Event_Output_Changed *ev;
77 
78    ev = event;
79    eina_stringshare_del(ev->make);
80    eina_stringshare_del(ev->model);
81    eina_stringshare_del(ev->name);
82    free(ev);
83 }
84 
85 static void
_output_event_send(Ecore_Drm2_Output * output)86 _output_event_send(Ecore_Drm2_Output *output)
87 {
88    Ecore_Drm2_Event_Output_Changed *ev;
89 
90    ev = calloc(1, sizeof(Ecore_Drm2_Event_Output_Changed));
91    if (!ev) return;
92 
93    ev->id = output->crtc_id;
94 
95    ev->x = output->x;
96    ev->y = output->y;
97    if (output->current_mode)
98      {
99         ev->w = output->current_mode->width;
100         ev->h = output->current_mode->height;
101         ev->refresh = output->current_mode->refresh;
102      }
103    else
104      {
105         ev->w = output->ocrtc->width;
106         ev->h = output->ocrtc->height;
107         ev->refresh = 0;
108      }
109 
110    ev->phys_width = output->pw;
111    ev->phys_height = output->ph;
112 
113    ev->scale = output->scale;
114    ev->subpixel = output->subpixel;
115    ev->transform = output->transform;
116    ev->connected = output->connected;
117    ev->enabled = output->enabled;
118 
119    ev->name = eina_stringshare_ref(output->name);
120    ev->make = eina_stringshare_ref(output->make);
121    ev->model = eina_stringshare_ref(output->model);
122 
123    ecore_event_add(ECORE_DRM2_EVENT_OUTPUT_CHANGED, ev,
124                    _cb_output_event_free, NULL);
125 }
126 
127 static void
_output_edid_parse_string(const uint8_t * data,char text[])128 _output_edid_parse_string(const uint8_t *data, char text[])
129 {
130    int i = 0, rep = 0;
131 
132    strncpy(text, (const char *)data, 12);
133 
134    for (; text[i] != '\0'; i++)
135      {
136         if ((text[i] == '\n') || (text[i] == '\r'))
137           {
138              text[i] = '\0';
139              break;
140           }
141      }
142 
143    for (i = 0; text[i] != '\0'; i++)
144      {
145         if (!isprint(text[i]))
146           {
147              text[i] = '-';
148              rep++;
149           }
150      }
151 
152    if (rep > 4) text[0] = '\0';
153 }
154 
155 static int
_output_edid_parse(Ecore_Drm2_Output * output,const uint8_t * data,size_t len)156 _output_edid_parse(Ecore_Drm2_Output *output, const uint8_t *data, size_t len)
157 {
158    int i = 0;
159    uint32_t serial;
160 
161    if (len < 128) return -1;
162    if ((data[0] != 0x00) || (data[1] != 0xff)) return -1;
163 
164    output->edid.pnp[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
165    output->edid.pnp[1] =
166      'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) +
167      ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
168    output->edid.pnp[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
169    output->edid.pnp[3] = '\0';
170 
171    serial = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
172    serial += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
173    serial += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
174    serial += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
175    if (serial > 0)
176      sprintf(output->edid.serial, "%lu", (unsigned long)serial);
177 
178    for (i = EDID_OFFSET_DATA_BLOCKS; i <= EDID_OFFSET_LAST_BLOCK; i += 18)
179      {
180         if (data[i] != 0) continue;
181         if (data[i + 2] != 0) continue;
182 
183         if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME)
184           _output_edid_parse_string(&data[i + 5], output->edid.monitor);
185         else if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER)
186           _output_edid_parse_string(&data[i + 5], output->edid.serial);
187         else if (data[i + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING)
188           _output_edid_parse_string(&data[i + 5], output->edid.eisa);
189      }
190 
191    return 0;
192 }
193 
194 static void
_output_edid_atomic_find(Ecore_Drm2_Output * output)195 _output_edid_atomic_find(Ecore_Drm2_Output *output)
196 {
197    Ecore_Drm2_Connector_State *cstate;
198    int ret = 0;
199 
200    cstate = output->conn_state;
201 
202    ret = _output_edid_parse(output, cstate->edid.data, cstate->edid.len);
203    if (!ret)
204      {
205         if (output->edid.pnp[0] != '\0')
206           eina_stringshare_replace(&output->make, output->edid.pnp);
207         if (output->edid.monitor[0] != '\0')
208           eina_stringshare_replace(&output->model, output->edid.monitor);
209         if (output->edid.serial[0] != '\0')
210           eina_stringshare_replace(&output->serial, output->edid.serial);
211      }
212 }
213 
214 static void
_output_edid_find(Ecore_Drm2_Output * output,const drmModeConnector * conn)215 _output_edid_find(Ecore_Drm2_Output *output, const drmModeConnector *conn)
216 {
217    drmModePropertyBlobPtr blob = NULL;
218    drmModePropertyPtr prop;
219    int i = 0, ret = 0;
220 
221    for (; i < conn->count_props && !blob; i++)
222      {
223         if (!(prop = sym_drmModeGetProperty(output->fd, conn->props[i])))
224           continue;
225         if ((prop->flags & DRM_MODE_PROP_BLOB) &&
226             (!strcmp(prop->name, "EDID")))
227           {
228              blob = sym_drmModeGetPropertyBlob(output->fd, conn->prop_values[i]);
229           }
230         sym_drmModeFreeProperty(prop);
231         if (blob) break;
232      }
233 
234    if (!blob) return;
235 
236    output->edid.blob = eina_memdup(blob->data, blob->length, 1);
237 
238    ret = _output_edid_parse(output, blob->data, blob->length);
239    if (!ret)
240      {
241         if (output->edid.pnp[0] != '\0')
242           eina_stringshare_replace(&output->make, output->edid.pnp);
243         if (output->edid.monitor[0] != '\0')
244           eina_stringshare_replace(&output->model, output->edid.monitor);
245         if (output->edid.serial[0] != '\0')
246           eina_stringshare_replace(&output->serial, output->edid.serial);
247      }
248 
249    sym_drmModeFreePropertyBlob(blob);
250 }
251 
252 static int
_output_crtc_find(const drmModeRes * res,const drmModeConnector * conn,int fd)253 _output_crtc_find(const drmModeRes *res, const drmModeConnector *conn, int fd)
254 {
255    drmModeEncoder *enc;
256    uint32_t crtc;
257    int i = 0, j = 0;
258 
259    /* Skip all disconnected connectors...
260     *
261     * When a connector is disconnected it still has an encoder id
262     * which messes up our output selection code later.  When we support
263     * multi-head properly and hotplug becomes a real thing we'll
264     * need to revisit this hack (and the crtc assignment code as well)
265     */
266    if (conn->connection != DRM_MODE_CONNECTED) return -1;
267 
268    for (j = 0; j < conn->count_encoders; j++)
269      {
270         enc = sym_drmModeGetEncoder(fd, conn->encoders[j]);
271         if (!enc) continue;
272 
273         crtc = enc->crtc_id;
274         sym_drmModeFreeEncoder(enc);
275 
276         for (i = 0; i < res->count_crtcs; i++)
277           if (crtc == res->crtcs[i])
278             return i;
279      }
280 
281    return -1;
282 }
283 
284 static char *
_output_name_get(const drmModeConnector * conn)285 _output_name_get(const drmModeConnector *conn)
286 {
287    char name[DRM_CONNECTOR_NAME_LEN];
288    const char *type = NULL;
289 
290    if (conn->connector_type < EINA_C_ARRAY_LENGTH(conn_types))
291      type = conn_types[conn->connector_type];
292    else
293      type = "UNKNOWN";
294 
295    snprintf(name, sizeof(name), "%s-%d", type, conn->connector_type_id);
296    return strdup(name);
297 }
298 
299 static Ecore_Drm2_Output_Mode *
_output_mode_add(Ecore_Drm2_Output * output,const drmModeModeInfo * info)300 _output_mode_add(Ecore_Drm2_Output *output, const drmModeModeInfo *info)
301 {
302    Ecore_Drm2_Output_Mode *mode;
303    uint64_t refresh;
304 
305    EINA_SAFETY_ON_NULL_RETURN_VAL(info, NULL);
306    EINA_SAFETY_ON_FALSE_RETURN_VAL((info->htotal > 0), NULL);
307    EINA_SAFETY_ON_FALSE_RETURN_VAL((info->vtotal > 0), NULL);
308 
309    mode = calloc(1, sizeof(Ecore_Drm2_Output_Mode));
310    if (!mode) return NULL;
311 
312    mode->flags = 0;
313    mode->width = info->hdisplay;
314    mode->height = info->vdisplay;
315 
316    refresh = (info->clock * 1000LL / info->htotal + info->vtotal / 2) /
317      info->vtotal;
318 
319    if (info->flags & DRM_MODE_FLAG_INTERLACE)
320      refresh *= 2;
321    if (info->flags & DRM_MODE_FLAG_DBLSCAN)
322      refresh /= 2;
323    if (info->vscan > 1)
324      refresh /= info->vscan;
325 
326    mode->refresh = refresh;
327    mode->info = *info;
328 
329    if (info->type & DRM_MODE_TYPE_PREFERRED)
330      mode->flags |= DRM_MODE_TYPE_PREFERRED;
331 
332    output->modes = eina_list_append(output->modes, mode);
333 
334    return mode;
335 }
336 
337 static void
_output_modes_create(Ecore_Drm2_Device * dev,Ecore_Drm2_Output * output,const drmModeConnector * conn)338 _output_modes_create(Ecore_Drm2_Device *dev, Ecore_Drm2_Output *output, const drmModeConnector *conn)
339 {
340    int i = 0;
341    drmModeCrtc *crtc;
342    drmModeEncoder *enc;
343    drmModeModeInfo crtc_mode;
344    Ecore_Drm2_Output_Mode *omode;
345    Ecore_Drm2_Output_Mode *current = NULL, *preferred = NULL, *best = NULL;
346    Eina_List *l = NULL;
347 
348    memset(&crtc_mode, 0, sizeof(crtc_mode));
349 
350    enc = sym_drmModeGetEncoder(dev->fd, conn->encoder_id);
351    if (enc)
352      {
353         crtc = sym_drmModeGetCrtc(dev->fd, enc->crtc_id);
354         sym_drmModeFreeEncoder(enc);
355         if (!crtc) return;
356         if (crtc->mode_valid) crtc_mode = crtc->mode;
357         sym_drmModeFreeCrtc(crtc);
358      }
359 
360    for (i = 0; i < conn->count_modes; i++)
361      {
362         omode = _output_mode_add(output, &conn->modes[i]);
363         if (!omode) continue;
364      }
365 
366    EINA_LIST_REVERSE_FOREACH(output->modes, l, omode)
367      {
368         if (!memcmp(&crtc_mode, &omode->info, sizeof(crtc_mode)))
369           current = omode;
370         if (omode->flags & DRM_MODE_TYPE_PREFERRED)
371           preferred = omode;
372         best = omode;
373      }
374 
375    if ((!current) && (crtc_mode.clock != 0))
376      {
377         current = _output_mode_add(output, &crtc_mode);
378         if (!current) goto err;
379      }
380 
381    if (current) output->current_mode = current;
382    else if (preferred) output->current_mode = preferred;
383    else if (best) output->current_mode = best;
384 
385    if (!output->current_mode) goto err;
386 
387    output->current_mode->flags |= DRM_MODE_TYPE_DEFAULT;
388 
389    return;
390 
391 err:
392    EINA_LIST_FREE(output->modes, omode)
393      free(omode);
394 }
395 
396 static drmModePropertyPtr
_output_dpms_property_get(int fd,const drmModeConnector * conn)397 _output_dpms_property_get(int fd, const drmModeConnector *conn)
398 {
399    drmModePropertyPtr prop;
400    int i = 0;
401 
402    for (; i < conn->count_props; i++)
403      {
404         prop = sym_drmModeGetProperty(fd, conn->props[i]);
405         if (!prop) continue;
406 
407         if (!strcmp(prop->name, "DPMS")) return prop;
408 
409         sym_drmModeFreeProperty(prop);
410      }
411 
412    return NULL;
413 }
414 
415 static double
_output_backlight_value_get(Ecore_Drm2_Output * output,const char * attr)416 _output_backlight_value_get(Ecore_Drm2_Output *output, const char *attr)
417 {
418    const char *b = NULL;
419    double ret = 0.0;
420 
421    if ((!output) || (!output->backlight.path)) return 0.0;
422 
423    b = eeze_udev_syspath_get_sysattr(output->backlight.path, attr);
424    if (!b) return 0.0;
425 
426    ret = strtod(b, NULL);
427    if (ret < 0) ret = 0.0;
428 
429    return ret;
430 }
431 
432 static void
_output_backlight_init(Ecore_Drm2_Output * output,unsigned int conn_type)433 _output_backlight_init(Ecore_Drm2_Output *output, unsigned int conn_type)
434 {
435    Eina_List *devs, *l;
436    const char *dev, *t;
437    Eina_Bool found = EINA_FALSE;
438    Ecore_Drm2_Backlight_Type type = 0;
439 
440    devs = eeze_udev_find_by_filter("backlight", NULL, NULL);
441 
442    EINA_LIST_FOREACH(devs, l, dev)
443      {
444         t = eeze_udev_syspath_get_sysattr(dev, "type");
445         if (!t) continue;
446 
447         if (!strcmp(t, "raw"))
448           type = ECORE_DRM2_BACKLIGHT_RAW;
449         else if (!strcmp(t, "platform"))
450           type = ECORE_DRM2_BACKLIGHT_PLATFORM;
451         else if (!strcmp(t, "firmware"))
452           type = ECORE_DRM2_BACKLIGHT_FIRMWARE;
453 
454         if ((conn_type == DRM_MODE_CONNECTOR_LVDS) ||
455             (conn_type == DRM_MODE_CONNECTOR_eDP) ||
456             (type == ECORE_DRM2_BACKLIGHT_RAW))
457           found = EINA_TRUE;
458 
459         eina_stringshare_del(t);
460         if (found) break;
461      }
462 
463    if (found)
464      {
465         output->backlight.type = type;
466         output->backlight.path = eina_stringshare_add(dev);
467         output->backlight.max =
468           _output_backlight_value_get(output, "max_brightness");
469         output->backlight.value =
470           _output_backlight_value_get(output, "brightness");
471      }
472 
473    EINA_LIST_FREE(devs, dev)
474      eina_stringshare_del(dev);
475 }
476 
477 static void
_output_scale_init(Ecore_Drm2_Output * output,Ecore_Drm2_Transform transform,unsigned int scale)478 _output_scale_init(Ecore_Drm2_Output *output, Ecore_Drm2_Transform transform, unsigned int scale)
479 {
480    output->transform = transform;
481 
482    if ((output->enabled) && (output->current_mode))
483      {
484         switch (transform)
485           {
486            case ECORE_DRM2_TRANSFORM_90:
487            case ECORE_DRM2_TRANSFORM_270:
488            case ECORE_DRM2_TRANSFORM_FLIPPED_90:
489            case ECORE_DRM2_TRANSFORM_FLIPPED_270:
490              output->w = output->current_mode->height;
491              output->h = output->current_mode->width;
492              break;
493            case ECORE_DRM2_TRANSFORM_NORMAL:
494            case ECORE_DRM2_TRANSFORM_180:
495            case ECORE_DRM2_TRANSFORM_FLIPPED:
496            case ECORE_DRM2_TRANSFORM_FLIPPED_180:
497              output->w = output->current_mode->width;
498              output->h = output->current_mode->height;
499              break;
500            default:
501              break;
502           }
503      }
504 
505    output->scale = scale;
506    output->w /= scale;
507    output->h /= scale;
508 }
509 
510 static void
_output_matrix_rotate_xy(Eina_Matrix3 * matrix,double x,double y)511 _output_matrix_rotate_xy(Eina_Matrix3 *matrix, double x, double y)
512 {
513    Eina_Matrix4 tmp, m;
514 
515    eina_matrix4_identity(&tmp);
516    eina_matrix4_values_set(&tmp, x, y, 0, 0, -y, x, 0, 0,
517                            0, 0, 1, 0, 0, 0, 0, 1);
518 
519    eina_matrix3_matrix4_to(&m, matrix);
520    eina_matrix4_multiply(&m, &m, &tmp);
521    eina_matrix4_matrix3_to(matrix, &m);
522 }
523 
524 static void
_output_matrix_update(Ecore_Drm2_Output * output)525 _output_matrix_update(Ecore_Drm2_Output *output)
526 {
527    Eina_Matrix3 m3;
528 
529    eina_matrix4_identity(&output->matrix);
530    eina_matrix4_matrix3_to(&m3, &output->matrix);
531    eina_matrix3_translate(&m3, -output->x, -output->y);
532 
533    switch (output->transform)
534      {
535       case ECORE_DRM2_TRANSFORM_FLIPPED:
536       case ECORE_DRM2_TRANSFORM_FLIPPED_90:
537       case ECORE_DRM2_TRANSFORM_FLIPPED_180:
538       case ECORE_DRM2_TRANSFORM_FLIPPED_270:
539         eina_matrix3_translate(&m3, -output->w, 0);
540         break;
541       default:
542         break;
543      }
544 
545    switch (output->transform)
546      {
547       case ECORE_DRM2_TRANSFORM_NORMAL:
548       case ECORE_DRM2_TRANSFORM_FLIPPED:
549       default:
550         break;
551       case ECORE_DRM2_TRANSFORM_90:
552       case ECORE_DRM2_TRANSFORM_FLIPPED_90:
553         eina_matrix3_translate(&m3, 0, -output->h);
554         _output_matrix_rotate_xy(&m3, 0, 1);
555         break;
556       case ECORE_DRM2_TRANSFORM_180:
557       case ECORE_DRM2_TRANSFORM_FLIPPED_180:
558         eina_matrix3_translate(&m3, -output->w, -output->h);
559         _output_matrix_rotate_xy(&m3, -1, 0);
560         break;
561       case ECORE_DRM2_TRANSFORM_270:
562       case ECORE_DRM2_TRANSFORM_FLIPPED_270:
563         eina_matrix3_translate(&m3, -output->w, 0);
564         _output_matrix_rotate_xy(&m3, 0, -1);
565         break;
566      }
567 
568    if (output->scale != 1)
569      eina_matrix3_scale(&m3, output->scale, output->scale);
570 
571    eina_matrix3_matrix4_to(&output->matrix, &m3);
572    eina_matrix4_inverse(&output->inverse, &output->matrix);
573 }
574 
575 static Ecore_Drm2_Crtc_State *
_atomic_state_crtc_duplicate(Ecore_Drm2_Crtc_State * state)576 _atomic_state_crtc_duplicate(Ecore_Drm2_Crtc_State *state)
577 {
578    Ecore_Drm2_Crtc_State *cstate;
579 
580    cstate = calloc(1, sizeof(Ecore_Drm2_Crtc_State));
581    if (!cstate) return NULL;
582 
583    memcpy(cstate, state, sizeof(Ecore_Drm2_Crtc_State));
584 
585    return cstate;
586 }
587 
588 static Ecore_Drm2_Crtc_State *
_output_crtc_state_get(Ecore_Drm2_Atomic_State * state,unsigned int id)589 _output_crtc_state_get(Ecore_Drm2_Atomic_State *state, unsigned int id)
590 {
591    Ecore_Drm2_Crtc_State *cstate;
592    int i = 0;
593 
594    for (; i < state->crtcs; i++)
595      {
596         cstate = &state->crtc_states[i];
597         if (cstate->obj_id != id) continue;
598         return _atomic_state_crtc_duplicate(cstate);
599      }
600 
601    return NULL;
602 }
603 
604 static Ecore_Drm2_Connector_State *
_atomic_state_conn_duplicate(Ecore_Drm2_Connector_State * state)605 _atomic_state_conn_duplicate(Ecore_Drm2_Connector_State *state)
606 {
607    Ecore_Drm2_Connector_State *cstate;
608 
609    cstate = calloc(1, sizeof(Ecore_Drm2_Connector_State));
610    if (!cstate) return NULL;
611 
612    memcpy(cstate, state, sizeof(Ecore_Drm2_Connector_State));
613 
614    return cstate;
615 }
616 
617 static Ecore_Drm2_Connector_State *
_output_conn_state_get(Ecore_Drm2_Atomic_State * state,unsigned int id)618 _output_conn_state_get(Ecore_Drm2_Atomic_State *state, unsigned int id)
619 {
620    Ecore_Drm2_Connector_State *cstate;
621    int i = 0;
622 
623    for (; i < state->conns; i++)
624      {
625         cstate = &state->conn_states[i];
626         if (cstate->obj_id != id) continue;
627         return _atomic_state_conn_duplicate(cstate);
628      }
629 
630    return NULL;
631 }
632 
633 static Ecore_Drm2_Plane_State *
_atomic_state_plane_duplicate(Ecore_Drm2_Plane_State * state)634 _atomic_state_plane_duplicate(Ecore_Drm2_Plane_State *state)
635 {
636    Ecore_Drm2_Plane_State *pstate;
637 
638    pstate = calloc(1, sizeof(Ecore_Drm2_Plane_State));
639    if (!pstate) return NULL;
640 
641    memcpy(pstate, state, sizeof(Ecore_Drm2_Plane_State));
642 
643    return pstate;
644 }
645 
646 static Eina_List *
_output_plane_states_get(Ecore_Drm2_Atomic_State * state,unsigned int crtc_id,int index)647 _output_plane_states_get(Ecore_Drm2_Atomic_State *state, unsigned int crtc_id, int index)
648 {
649    Eina_List *states = NULL;
650    Ecore_Drm2_Plane_State *pstate;
651 
652    int i = 0;
653 
654    for (; i < state->planes; i++)
655      {
656         pstate = &state->plane_states[i];
657         if (pstate->cid.value == crtc_id)
658           {
659              states =
660                eina_list_append(states, _atomic_state_plane_duplicate(pstate));
661           }
662         else if (pstate->mask & (1ULL << index))
663           {
664              states =
665                eina_list_append(states, _atomic_state_plane_duplicate(pstate));
666           }
667      }
668 
669    return states;
670 }
671 
672 static Eina_Bool
_output_create(Ecore_Drm2_Device * dev,const drmModeRes * res,const drmModeConnector * conn,int x,int y,int * w,Eina_Bool cloned)673 _output_create(Ecore_Drm2_Device *dev, const drmModeRes *res, const drmModeConnector *conn, int x, int y, int *w, Eina_Bool cloned)
674 {
675    Ecore_Drm2_Output *output;
676    int i = 0;
677    char *name = NULL;
678 
679    if (w) *w = 0;
680 
681    i = _output_crtc_find(res, conn, dev->fd);
682    if (i < 0) return EINA_FALSE;
683 
684    output = calloc(1, sizeof(Ecore_Drm2_Output));
685    if (!output) return EINA_FALSE;
686 
687    output->fd = dev->fd;
688    output->x = x;
689    output->y = y;
690    output->cloned = cloned;
691    output->pw = conn->mmWidth;
692    output->ph = conn->mmHeight;
693 
694    switch (conn->subpixel)
695      {
696       case DRM_MODE_SUBPIXEL_UNKNOWN:
697         output->subpixel = 0; // WL_OUTPUT_SUBPIXEL_UNKNOWN
698         break;
699       case DRM_MODE_SUBPIXEL_NONE:
700         output->subpixel = 1; // WL_OUTPUT_SUBPIXEL_NONE
701         break;
702       case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
703         output->subpixel = 2; // WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB
704         break;
705       case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
706         output->subpixel = 3; // WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR
707         break;
708       case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
709         output->subpixel = 4; // WL_OUTPUT_SUBPIXEL_VERTICAL_RGB
710         break;
711       case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
712         output->subpixel = 5; // WL_OUTPUT_SUBPIXEL_VERTICAL_BGR
713         break;
714       default:
715         output->subpixel = 0;
716         break;
717      }
718 
719    name = _output_name_get(conn);
720    output->name = eina_stringshare_add(name);
721    output->make = eina_stringshare_add("unknown");
722    output->model = eina_stringshare_add("unknown");
723    output->serial = eina_stringshare_add("unknown");
724    free(name);
725 
726    output->pipe = i;
727    output->crtc_id = res->crtcs[i];
728    output->conn_id = conn->connector_id;
729    output->conn_type = conn->connector_type;
730 
731    output->connected = (conn->connection == DRM_MODE_CONNECTED);
732 
733    output->ocrtc = sym_drmModeGetCrtc(dev->fd, output->crtc_id);
734 
735    if (_ecore_drm2_use_atomic)
736      {
737         output->crtc_state =
738           _output_crtc_state_get(dev->state, output->crtc_id);
739         output->conn_state =
740           _output_conn_state_get(dev->state, output->conn_id);
741         output->plane_states =
742           _output_plane_states_get(dev->state, output->crtc_id, output->pipe);
743      }
744 
745    output->dpms = _output_dpms_property_get(dev->fd, conn);
746 
747    _output_backlight_init(output, conn->connector_type);
748 
749    output->gamma = output->ocrtc->gamma_size;
750 
751    _output_modes_create(dev, output, conn);
752 
753    if (_ecore_drm2_use_atomic)
754      _output_edid_atomic_find(output);
755    else
756      _output_edid_find(output, conn);
757 
758    if (output->connected) output->enabled = EINA_TRUE;
759 
760    _output_scale_init(output, ECORE_DRM2_TRANSFORM_NORMAL, 1);
761    _output_matrix_update(output);
762 
763    if (!eina_list_count(dev->outputs))
764      output->primary = EINA_TRUE;
765    else
766      {
767         /* temporarily disable other outputs which are not primary */
768         output->connected = EINA_FALSE;
769         output->enabled = EINA_FALSE;
770      }
771 
772    dev->outputs = eina_list_append(dev->outputs, output);
773 
774    _output_debug(output, conn);
775 
776    if ((output->enabled) && (output->current_mode))
777      {
778         if (w) *w = output->current_mode->width;
779      }
780 
781    return EINA_TRUE;
782 }
783 
784 static Ecore_Drm2_Output *
_output_find_by_con(Ecore_Drm2_Device * dev,uint32_t id)785 _output_find_by_con(Ecore_Drm2_Device *dev, uint32_t id)
786 {
787    Ecore_Drm2_Output *output;
788    Eina_List *l;
789 
790    EINA_LIST_FOREACH(dev->outputs, l, output)
791      if (output->conn_id == id) return output;
792 
793    return NULL;
794 }
795 
796 static void
_outputs_update(Ecore_Drm2_Device * dev)797 _outputs_update(Ecore_Drm2_Device *dev)
798 {
799    Ecore_Drm2_Output *output;
800    Eina_List *l, *ll;
801    drmModeRes *res;
802    drmModeConnector *conn;
803    uint32_t *connected;
804    int i = 0, x = 0, y = 0;
805 
806    res = sym_drmModeGetResources(dev->fd);
807    if (!res) return;
808 
809    connected = calloc(res->count_connectors, sizeof(uint32_t));
810    if (!connected)
811      {
812         sym_drmModeFreeResources(res);
813         return;
814      }
815 
816    for (i = 0; i < res->count_connectors; i++)
817      {
818         conn = sym_drmModeGetConnector(dev->fd, res->connectors[i]);
819         if (!conn) continue;
820 
821         if (conn->connection != DRM_MODE_CONNECTED) goto next;
822 
823         connected[i] = res->connectors[i];
824         if (!_output_find_by_con(dev, res->connectors[i]))
825           {
826              if (dev->outputs)
827                {
828                   Ecore_Drm2_Output *last;
829 
830                   last = eina_list_last_data_get(dev->outputs);
831                   if (last) x = last->x + last->current_mode->width;
832                   else x = 0;
833                }
834              else
835                x = 0;
836 
837              if (!_output_create(dev, res, conn, x, y, NULL, EINA_TRUE))
838                goto next;
839           }
840 
841 next:
842         sym_drmModeFreeConnector(conn);
843      }
844 
845    sym_drmModeFreeResources(res);
846 
847    EINA_LIST_FOREACH_SAFE(dev->outputs, l, ll, output)
848      {
849         Eina_Bool disconnected = EINA_TRUE;
850 
851         for (i = 0; i < res->count_connectors; i++)
852           if (connected[i] == output->conn_id)
853             {
854                disconnected = EINA_FALSE;
855                break;
856             }
857 
858         if (disconnected)
859           {
860              output->connected = EINA_FALSE;
861              output->enabled = EINA_FALSE;
862           }
863         else
864           {
865              output->connected = EINA_TRUE;
866              output->enabled = EINA_TRUE;
867           }
868 
869         _output_event_send(output);
870      }
871    free(connected);
872 }
873 
874 static void
_cb_output_event(const char * device EINA_UNUSED,Eeze_Udev_Event event EINA_UNUSED,void * data,Eeze_Udev_Watch * watch EINA_UNUSED)875 _cb_output_event(const char *device EINA_UNUSED, Eeze_Udev_Event event EINA_UNUSED, void *data, Eeze_Udev_Watch *watch EINA_UNUSED)
876 {
877    Ecore_Drm2_Device *dev;
878 
879    dev = data;
880    _outputs_update(dev);
881 }
882 
883 static void
_output_destroy(Ecore_Drm2_Device * dev EINA_UNUSED,Ecore_Drm2_Output * output)884 _output_destroy(Ecore_Drm2_Device *dev EINA_UNUSED, Ecore_Drm2_Output *output)
885 {
886    Ecore_Drm2_Output_Mode *mode;
887    Ecore_Drm2_Plane *plane;
888    Ecore_Drm2_Plane_State *pstate;
889 
890    if (_ecore_drm2_use_atomic)
891      {
892         if (output->prep.atomic_req)
893           sym_drmModeAtomicFree(output->prep.atomic_req);
894 
895         EINA_LIST_FREE(output->plane_states, pstate)
896           free(pstate);
897 
898         EINA_LIST_FREE(output->planes, plane)
899           free(plane);
900 
901         free(output->conn_state);
902         free(output->crtc_state);
903      }
904 
905    EINA_LIST_FREE(output->modes, mode)
906      {
907         if (mode->id)
908           sym_drmModeDestroyPropertyBlob(output->fd, mode->id);
909         free(mode);
910      }
911 
912    eina_stringshare_del(output->backlight.path);
913    eina_stringshare_del(output->name);
914    eina_stringshare_del(output->make);
915    eina_stringshare_del(output->model);
916    eina_stringshare_del(output->serial);
917    eina_stringshare_del(output->relative.to);
918 
919    if (output->flip_timeout) ecore_timer_del(output->flip_timeout);
920 
921    sym_drmModeFreeProperty(output->dpms);
922    free(output->edid.blob);
923 
924    free(output);
925 }
926 
927 /* this function is used to indicate if we are in a multi-gpu situation
928  * and need to calculate vblank sync with high crtc mask */
929 static unsigned int
_output_vblank_pipe(Ecore_Drm2_Output * output)930 _output_vblank_pipe(Ecore_Drm2_Output *output)
931 {
932    if (output->pipe > 1)
933      return ((output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) &
934              DRM_VBLANK_HIGH_CRTC_MASK);
935    else if (output->pipe > 0)
936      return DRM_VBLANK_SECONDARY;
937    else
938      return 0;
939 }
940 
941 EAPI Eina_Bool
ecore_drm2_outputs_create(Ecore_Drm2_Device * device)942 ecore_drm2_outputs_create(Ecore_Drm2_Device *device)
943 {
944    drmModeConnector *conn;
945    drmModeRes *res;
946    int i = 0, x = 0, y = 0, w = 0;
947    int events = 0;
948 
949    EINA_SAFETY_ON_NULL_RETURN_VAL(device, EINA_FALSE);
950    EINA_SAFETY_ON_TRUE_RETURN_VAL((device->fd < 0), EINA_FALSE);
951 
952    res = sym_drmModeGetResources(device->fd);
953    if (!res) return EINA_FALSE;
954 
955    device->crtcs = calloc(res->count_crtcs, sizeof(uint32_t));
956    if (!device->crtcs) goto err;
957 
958    device->min.width = res->min_width;
959    device->min.height = res->min_height;
960    device->max.width = res->max_width;
961    device->max.height = res->max_height;
962 
963    device->num_crtcs = res->count_crtcs;
964    memcpy(device->crtcs, res->crtcs, sizeof(uint32_t) * res->count_crtcs);
965 
966    for (i = 0; i < res->count_connectors; i++)
967      {
968         conn = sym_drmModeGetConnector(device->fd, res->connectors[i]);
969         if (!conn) continue;
970 
971         if (!_output_create(device, res, conn, x, y, &w, EINA_FALSE))
972           goto next;
973 
974         x += w;
975 
976 next:
977         sym_drmModeFreeConnector(conn);
978      }
979 
980    if (eina_list_count(device->outputs) < 1) goto err;
981 
982    sym_drmModeFreeResources(res);
983 
984    events = (EEZE_UDEV_EVENT_ADD | EEZE_UDEV_EVENT_REMOVE |
985              EEZE_UDEV_EVENT_CHANGE);
986 
987    device->watch =
988      eeze_udev_watch_add(EEZE_UDEV_TYPE_DRM, events, _cb_output_event, device);
989 
990    return EINA_TRUE;
991 
992 err:
993    sym_drmModeFreeResources(res);
994    return EINA_FALSE;
995 }
996 
997 EAPI void
ecore_drm2_outputs_destroy(Ecore_Drm2_Device * device)998 ecore_drm2_outputs_destroy(Ecore_Drm2_Device *device)
999 {
1000    Ecore_Drm2_Output *output;
1001 
1002    EINA_SAFETY_ON_NULL_RETURN(device);
1003 
1004    EINA_LIST_FREE(device->outputs, output)
1005      _output_destroy(device, output);
1006 }
1007 
1008 EAPI const Eina_List *
ecore_drm2_outputs_get(Ecore_Drm2_Device * device)1009 ecore_drm2_outputs_get(Ecore_Drm2_Device *device)
1010 {
1011    EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL);
1012    return device->outputs;
1013 }
1014 
1015 EAPI int
ecore_drm2_output_dpms_get(Ecore_Drm2_Output * output)1016 ecore_drm2_output_dpms_get(Ecore_Drm2_Output *output)
1017 {
1018    drmModeObjectProperties *props;
1019    drmModePropertyRes *prop;
1020    int val = -1;
1021    unsigned int i;
1022 
1023    EINA_SAFETY_ON_NULL_RETURN_VAL(output, -1);
1024 
1025    props =
1026      sym_drmModeObjectGetProperties(output->fd, output->conn_id,
1027                                     DRM_MODE_OBJECT_CONNECTOR);
1028    if (!props) return -1;
1029 
1030    for (i = 0; i < props->count_props; i++)
1031      {
1032         prop = sym_drmModeGetProperty(output->fd, props->props[i]);
1033         if (!prop) continue;
1034 
1035         if (!strcmp(prop->name, "DPMS"))
1036           val = props->prop_values[i];
1037 
1038         sym_drmModeFreeProperty(prop);
1039      }
1040 
1041    sym_drmModeFreeObjectProperties(props);
1042 
1043    return val;
1044 }
1045 
1046 EAPI void
ecore_drm2_output_dpms_set(Ecore_Drm2_Output * output,int level)1047 ecore_drm2_output_dpms_set(Ecore_Drm2_Output *output, int level)
1048 {
1049    EINA_SAFETY_ON_NULL_RETURN(output);
1050    EINA_SAFETY_ON_TRUE_RETURN(!output->enabled);
1051 
1052    sym_drmModeConnectorSetProperty(output->fd, output->conn_id,
1053                                    output->dpms->prop_id, level);
1054 
1055    if (level == 0) /* DPMS on */
1056      ecore_drm2_fb_flip(NULL, output);
1057 }
1058 
1059 EAPI char *
ecore_drm2_output_edid_get(Ecore_Drm2_Output * output)1060 ecore_drm2_output_edid_get(Ecore_Drm2_Output *output)
1061 {
1062    char *edid_str = NULL;
1063    unsigned char *blob;
1064    unsigned char fallback_blob[128];
1065 
1066    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1067 
1068    if (_ecore_drm2_use_atomic)
1069      blob = output->conn_state->edid.data;
1070    else
1071      {
1072         EINA_SAFETY_ON_NULL_RETURN_VAL(output->edid.blob, NULL);
1073         blob = output->edid.blob;
1074      }
1075    if (!blob)
1076      {
1077         memset(fallback_blob, 0, sizeof(fallback_blob));
1078         blob = fallback_blob;
1079      }
1080 
1081    edid_str = malloc((128 * 2) + 1);
1082    if (edid_str)
1083      {
1084         unsigned int k, kk;
1085         const char *hexch = "0123456789abcdef";
1086 
1087         for (kk = 0, k = 0; k < 128; k++)
1088           {
1089              edid_str[kk] = hexch[(blob[k] >> 4) & 0xf];
1090              edid_str[kk + 1] = hexch[blob[k] & 0xf];
1091              kk += 2;
1092           }
1093         edid_str[kk] = 0;
1094      }
1095 
1096    return edid_str;
1097 }
1098 
1099 EAPI Eina_Bool
ecore_drm2_output_backlight_get(Ecore_Drm2_Output * output)1100 ecore_drm2_output_backlight_get(Ecore_Drm2_Output *output)
1101 {
1102    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1103    return (output->backlight.path != NULL);
1104 }
1105 
1106 EAPI Ecore_Drm2_Output *
ecore_drm2_output_find(Ecore_Drm2_Device * device,int x,int y)1107 ecore_drm2_output_find(Ecore_Drm2_Device *device, int x, int y)
1108 {
1109    Eina_List *l;
1110    Ecore_Drm2_Output *output;
1111 
1112    EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL);
1113 
1114    EINA_LIST_FOREACH(device->outputs, l, output)
1115      {
1116         int ox, oy, ow, oh;
1117 
1118         if (!output->enabled) continue;
1119 
1120         ox = output->x;
1121         oy = output->y;
1122         ow = output->current_mode->width;
1123         oh = output->current_mode->height;
1124 
1125         if (INSIDE(x, y, ox, oy, ow, oh))
1126           return output;
1127      }
1128 
1129    return NULL;
1130 }
1131 
1132 EAPI void
ecore_drm2_output_dpi_get(Ecore_Drm2_Output * output,int * xdpi,int * ydpi)1133 ecore_drm2_output_dpi_get(Ecore_Drm2_Output *output, int *xdpi, int *ydpi)
1134 {
1135    EINA_SAFETY_ON_NULL_RETURN(output);
1136    EINA_SAFETY_ON_TRUE_RETURN(!output->enabled);
1137 
1138    if (xdpi)
1139      *xdpi = ((25.4 * (output->current_mode->width)) / output->pw);
1140 
1141    if (ydpi)
1142      *ydpi = ((25.4 * (output->current_mode->height)) / output->ph);
1143 }
1144 
1145 EAPI unsigned int
ecore_drm2_output_crtc_get(Ecore_Drm2_Output * output)1146 ecore_drm2_output_crtc_get(Ecore_Drm2_Output *output)
1147 {
1148    EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0);
1149    return output->crtc_id;
1150 }
1151 
1152 EAPI Ecore_Drm2_Fb *
ecore_drm2_output_latest_fb_get(Ecore_Drm2_Output * output)1153 ecore_drm2_output_latest_fb_get(Ecore_Drm2_Output *output)
1154 {
1155    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1156    if (output->pending.fb) return output->pending.fb;
1157    if (output->current.fb) return output->current.fb;
1158    return output->next.fb;
1159 }
1160 
1161 EAPI Eina_Bool
ecore_drm2_output_primary_get(Ecore_Drm2_Output * output)1162 ecore_drm2_output_primary_get(Ecore_Drm2_Output *output)
1163 {
1164    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1165    return output->primary;
1166 }
1167 
1168 EAPI void
ecore_drm2_output_primary_set(Ecore_Drm2_Output * output,Eina_Bool primary)1169 ecore_drm2_output_primary_set(Ecore_Drm2_Output *output, Eina_Bool primary)
1170 {
1171    EINA_SAFETY_ON_NULL_RETURN(output);
1172    output->primary = primary;
1173 }
1174 
1175 EAPI Eina_Bool
ecore_drm2_output_enabled_get(Ecore_Drm2_Output * output)1176 ecore_drm2_output_enabled_get(Ecore_Drm2_Output *output)
1177 {
1178    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1179    return output->enabled;
1180 }
1181 
1182 EAPI void
ecore_drm2_output_enabled_set(Ecore_Drm2_Output * output,Eina_Bool enabled)1183 ecore_drm2_output_enabled_set(Ecore_Drm2_Output *output, Eina_Bool enabled)
1184 {
1185    EINA_SAFETY_ON_NULL_RETURN(output);
1186 
1187    if (!output->connected) return;
1188    if (output->enabled == enabled) return;
1189 
1190    if (enabled)
1191      {
1192         output->enabled = enabled;
1193         ecore_drm2_output_dpms_set(output, DRM_MODE_DPMS_ON);
1194      }
1195    else
1196      {
1197         if (_ecore_drm2_use_atomic)
1198           ecore_drm2_fb_flip(NULL, output);
1199 
1200         ecore_drm2_output_dpms_set(output, DRM_MODE_DPMS_OFF);
1201         output->enabled = enabled;
1202 
1203         if (output->current.fb)
1204           _ecore_drm2_fb_buffer_release(output, &output->current);
1205 
1206         if (output->next.fb)
1207           _ecore_drm2_fb_buffer_release(output, &output->next);
1208 
1209         if (output->pending.fb)
1210           _ecore_drm2_fb_buffer_release(output, &output->pending);
1211      }
1212 
1213    _output_event_send(output);
1214 }
1215 
1216 EAPI void
ecore_drm2_output_physical_size_get(Ecore_Drm2_Output * output,int * w,int * h)1217 ecore_drm2_output_physical_size_get(Ecore_Drm2_Output *output, int *w, int *h)
1218 {
1219    if (w) *w = 0;
1220    if (h) *h = 0;
1221 
1222    EINA_SAFETY_ON_NULL_RETURN(output);
1223 
1224    if (w) *w = output->pw;
1225    if (h) *h = output->ph;
1226 }
1227 
1228 EAPI const Eina_List *
ecore_drm2_output_modes_get(Ecore_Drm2_Output * output)1229 ecore_drm2_output_modes_get(Ecore_Drm2_Output *output)
1230 {
1231    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1232    return output->modes;
1233 }
1234 
1235 EAPI void
ecore_drm2_output_mode_info_get(Ecore_Drm2_Output_Mode * mode,int * w,int * h,unsigned int * refresh,unsigned int * flags)1236 ecore_drm2_output_mode_info_get(Ecore_Drm2_Output_Mode *mode, int *w, int *h, unsigned int *refresh, unsigned int *flags)
1237 {
1238    if (w) *w = 0;
1239    if (h) *h = 0;
1240    if (refresh) *refresh = 0;
1241    if (flags) *flags = 0;
1242 
1243    EINA_SAFETY_ON_NULL_RETURN(mode);
1244 
1245    if (w) *w = mode->width;
1246    if (h) *h = mode->height;
1247    if (refresh) *refresh = mode->refresh;
1248    if (flags) *flags = mode->flags;
1249 }
1250 
1251 static Eina_Bool
_output_mode_atomic_set(Ecore_Drm2_Output * output,Ecore_Drm2_Output_Mode * mode)1252 _output_mode_atomic_set(Ecore_Drm2_Output *output, Ecore_Drm2_Output_Mode *mode)
1253 {
1254    Ecore_Drm2_Crtc_State *cstate;
1255    drmModeAtomicReq *req = NULL;
1256    int ret = 0;
1257 
1258    cstate = output->crtc_state;
1259 
1260    if (mode)
1261      {
1262         if (mode->id)
1263           sym_drmModeDestroyPropertyBlob(output->fd, mode->id);
1264 
1265         ret =
1266           sym_drmModeCreatePropertyBlob(output->fd, &mode->info,
1267                                         sizeof(drmModeModeInfo), &mode->id);
1268         if (ret < 0)
1269           {
1270              ERR("Failed to create Mode Property Blob");
1271              return EINA_FALSE;
1272           }
1273      }
1274 
1275    req = sym_drmModeAtomicAlloc();
1276    if (!req) return EINA_FALSE;
1277 
1278    sym_drmModeAtomicSetCursor(req, 0);
1279 
1280    if (mode)
1281      {
1282         cstate->active.value = 1;
1283         cstate->mode.value = mode->id;
1284      }
1285    else
1286      cstate->active.value = 0;
1287 
1288    ret = sym_drmModeAtomicAddProperty(req, cstate->obj_id, cstate->mode.id,
1289                                       cstate->mode.value);
1290    if (ret < 0)
1291      {
1292         ERR("Could not add atomic property");
1293         ret = EINA_FALSE;
1294         goto err;
1295      }
1296 
1297    ret = sym_drmModeAtomicAddProperty(req, cstate->obj_id,
1298                                       cstate->active.id, cstate->active.value);
1299    if (ret < 0)
1300      {
1301         ERR("Could not add atomic property");
1302         ret = EINA_FALSE;
1303         goto err;
1304      }
1305 
1306    if (cstate->background.id)
1307      {
1308         ret =
1309           sym_drmModeAtomicAddProperty(req, cstate->obj_id,
1310                                        cstate->background.id,
1311                                        cstate->background.value);
1312         if (ret < 0)
1313           {
1314              ERR("Could not add atomic property");
1315              ret = EINA_FALSE;
1316              goto err;
1317           }
1318      }
1319 
1320    ret = sym_drmModeAtomicCommit(output->fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET,
1321                                  output);
1322    if (ret < 0)
1323      {
1324         ERR("Failed to commit atomic Mode: %m");
1325         ret = EINA_FALSE;
1326         goto err;
1327      }
1328    else
1329      ret = EINA_TRUE;
1330 
1331 err:
1332    sym_drmModeAtomicFree(req);
1333    return ret;
1334 }
1335 
1336 EAPI Eina_Bool
ecore_drm2_output_mode_set(Ecore_Drm2_Output * output,Ecore_Drm2_Output_Mode * mode,int x,int y)1337 ecore_drm2_output_mode_set(Ecore_Drm2_Output *output, Ecore_Drm2_Output_Mode *mode, int x, int y)
1338 {
1339    Eina_Bool ret = EINA_TRUE;
1340 
1341    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1342    EINA_SAFETY_ON_TRUE_RETURN_VAL((output->fd < 0), EINA_FALSE);
1343 
1344    output->x = x;
1345    output->y = y;
1346    output->current_mode = mode;
1347 
1348    if (_ecore_drm2_use_atomic)
1349      ret = _output_mode_atomic_set(output, mode);
1350    else
1351      {
1352         if (mode)
1353           {
1354              unsigned int buffer = 0;
1355 
1356              if (output->current.fb)
1357                buffer = output->current.fb->id;
1358              else if (output->next.fb)
1359                buffer = output->next.fb->id;
1360              else
1361                buffer = output->ocrtc->buffer_id;
1362 
1363              if (sym_drmModeSetCrtc(output->fd, output->crtc_id, buffer, 0, 0,
1364                                     &output->conn_id, 1, &mode->info) < 0)
1365                {
1366                   ERR("Failed to set Mode %dx%d for Output %s: %m",
1367                       mode->width, mode->height, output->name);
1368                   ret = EINA_FALSE;
1369                }
1370           }
1371         else
1372           {
1373              if (sym_drmModeSetCrtc(output->fd, output->crtc_id, 0,
1374                                     0, 0, 0, 0, NULL) < 0)
1375                {
1376                   ERR("Failed to turn off Output %s: %m", output->name);
1377                   ret = EINA_FALSE;
1378                }
1379           }
1380      }
1381 
1382    return ret;
1383 }
1384 
1385 EAPI char *
ecore_drm2_output_name_get(Ecore_Drm2_Output * output)1386 ecore_drm2_output_name_get(Ecore_Drm2_Output *output)
1387 {
1388    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1389    EINA_SAFETY_ON_NULL_RETURN_VAL(output->name, NULL);
1390    return strdup(output->name);
1391 }
1392 
1393 EAPI char *
ecore_drm2_output_model_get(Ecore_Drm2_Output * output)1394 ecore_drm2_output_model_get(Ecore_Drm2_Output *output)
1395 {
1396    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1397    EINA_SAFETY_ON_NULL_RETURN_VAL(output->model, NULL);
1398    return strdup(output->model);
1399 }
1400 
1401 EAPI Eina_Bool
ecore_drm2_output_connected_get(Ecore_Drm2_Output * output)1402 ecore_drm2_output_connected_get(Ecore_Drm2_Output *output)
1403 {
1404    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1405    return output->connected;
1406 }
1407 
1408 EAPI Eina_Bool
ecore_drm2_output_cloned_get(Ecore_Drm2_Output * output)1409 ecore_drm2_output_cloned_get(Ecore_Drm2_Output *output)
1410 {
1411    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1412    return (output->cloned ||
1413            output->relative.mode == ECORE_DRM2_RELATIVE_MODE_CLONE);
1414 }
1415 
1416 EAPI unsigned int
ecore_drm2_output_connector_type_get(Ecore_Drm2_Output * output)1417 ecore_drm2_output_connector_type_get(Ecore_Drm2_Output *output)
1418 {
1419    EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0);
1420    return output->conn_type;
1421 }
1422 
1423 EAPI Eina_Bool
ecore_drm2_output_possible_crtc_get(Ecore_Drm2_Output * output,unsigned int crtc)1424 ecore_drm2_output_possible_crtc_get(Ecore_Drm2_Output *output, unsigned int crtc)
1425 {
1426    drmModeRes *res;
1427    drmModeConnector *conn;
1428    drmModeEncoder *enc;
1429    int i = 0, j = 0, k = 0;
1430    unsigned int p = 0;
1431    Eina_Bool ret = EINA_FALSE;
1432 
1433    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1434    EINA_SAFETY_ON_TRUE_RETURN_VAL((output->fd < 0), EINA_FALSE);
1435 
1436    res = sym_drmModeGetResources(output->fd);
1437    if (!res) return EINA_FALSE;
1438 
1439    for (; i < res->count_connectors; i++)
1440      {
1441         conn = sym_drmModeGetConnector(output->fd, res->connectors[i]);
1442         if (!conn) continue;
1443 
1444         for (j = 0; j < conn->count_encoders; j++)
1445           {
1446              enc = sym_drmModeGetEncoder(output->fd, conn->encoders[j]);
1447              if (!enc) continue;
1448 
1449              if (enc->crtc_id != crtc) goto next;
1450 
1451              p = enc->possible_crtcs;
1452 
1453              for (k = 0; k < res->count_crtcs; k++)
1454                {
1455                   if (res->crtcs[k] != output->crtc_id) continue;
1456 
1457                   if (p & (1ULL << k))
1458                     {
1459                        ret = EINA_TRUE;
1460                        break;
1461                     }
1462                }
1463 
1464 next:
1465              sym_drmModeFreeEncoder(enc);
1466              if (ret) break;
1467           }
1468 
1469         sym_drmModeFreeConnector(conn);
1470         if (ret) break;
1471      }
1472 
1473    sym_drmModeFreeResources(res);
1474 
1475    return ret;
1476 }
1477 
1478 EAPI void
ecore_drm2_output_user_data_set(Ecore_Drm2_Output * o,void * data)1479 ecore_drm2_output_user_data_set(Ecore_Drm2_Output *o, void *data)
1480 {
1481    EINA_SAFETY_ON_NULL_RETURN(o);
1482 
1483    o->user_data = data;
1484 }
1485 
1486 EAPI void *
ecore_drm2_output_user_data_get(Ecore_Drm2_Output * output)1487 ecore_drm2_output_user_data_get(Ecore_Drm2_Output *output)
1488 {
1489    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1490    return output->user_data;
1491 }
1492 
1493 EAPI void
ecore_drm2_output_gamma_set(Ecore_Drm2_Output * output,uint16_t size,uint16_t * red,uint16_t * green,uint16_t * blue)1494 ecore_drm2_output_gamma_set(Ecore_Drm2_Output *output, uint16_t size, uint16_t *red, uint16_t *green, uint16_t *blue)
1495 {
1496    EINA_SAFETY_ON_NULL_RETURN(output);
1497    EINA_SAFETY_ON_TRUE_RETURN(output->fd < 0);
1498 
1499    if (output->gamma != size) return;
1500 
1501    if (sym_drmModeCrtcSetGamma(output->fd, output->crtc_id, size,
1502                                red, green, blue) < 0)
1503      ERR("Failed to set gamma for Output %s: %m", output->name);
1504 }
1505 
1506 EAPI int
ecore_drm2_output_supported_rotations_get(Ecore_Drm2_Output * output)1507 ecore_drm2_output_supported_rotations_get(Ecore_Drm2_Output *output)
1508 {
1509    int ret = -1;
1510 
1511    EINA_SAFETY_ON_NULL_RETURN_VAL(output, -1);
1512 
1513    if (_ecore_drm2_use_atomic)
1514      {
1515         Ecore_Drm2_Plane_State *pstate;
1516         Eina_List *l;
1517 
1518         EINA_LIST_FOREACH(output->plane_states, l, pstate)
1519           {
1520              if (pstate->type.value != DRM_PLANE_TYPE_PRIMARY) continue;
1521              ret = pstate->supported_rotations;
1522              break;
1523           }
1524      }
1525    else
1526      return (ECORE_DRM2_ROTATION_NORMAL | ECORE_DRM2_ROTATION_90 |
1527              ECORE_DRM2_ROTATION_180 | ECORE_DRM2_ROTATION_270);
1528 
1529    return ret;
1530 }
1531 
1532 EAPI Eina_Bool
ecore_drm2_output_rotation_set(Ecore_Drm2_Output * output,int rotation)1533 ecore_drm2_output_rotation_set(Ecore_Drm2_Output *output, int rotation)
1534 {
1535    Eina_Bool ret = EINA_TRUE;
1536 
1537    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1538 
1539    output->rotation = rotation;
1540 
1541 #if 0
1542    /* XXX: Disable hardware plane rotation for now as this has broken
1543     * recently. The break happens because of an invalid argument,
1544     * ie: the value being sent from pstate->rotation_map ends up being
1545     * incorrect for some reason. I suspect the breakage to be from
1546     * kernel drivers (linux 4.20.0) but have not confirmed that version */
1547    if (_ecore_drm2_use_atomic)
1548      {
1549         Eina_List *l;
1550         Ecore_Drm2_Plane_State *pstate = NULL;
1551         drmModeAtomicReq *req = NULL;
1552         int res = 0;
1553         uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK |
1554           DRM_MODE_ATOMIC_ALLOW_MODESET;
1555 
1556         EINA_LIST_FOREACH(output->plane_states, l, pstate)
1557           {
1558              if (pstate->type.value != DRM_PLANE_TYPE_PRIMARY) continue;
1559 
1560              if ((pstate->supported_rotations & rotation) == 0)
1561                {
1562                   WRN("Unsupported rotation");
1563                   return EINA_FALSE;
1564                }
1565 
1566              req = sym_drmModeAtomicAlloc();
1567              if (!req) return EINA_FALSE;
1568 
1569              sym_drmModeAtomicSetCursor(req, 0);
1570 
1571              res = sym_drmModeAtomicAddProperty(req, pstate->obj_id,
1572                                                 pstate->rotation.id, rotation);
1573              if (res < 0) goto err;
1574 
1575              res = sym_drmModeAtomicCommit(output->fd, req, flags, output);
1576              if (res < 0)
1577                goto err;
1578              else
1579                {
1580                   ret = EINA_TRUE;
1581                   pstate->rotation.value = rotation;
1582                }
1583           }
1584 
1585 err:
1586         sym_drmModeAtomicFree(req);
1587      }
1588 #endif
1589 
1590    return ret;
1591 }
1592 
1593 EAPI int
ecore_drm2_output_rotation_get(Ecore_Drm2_Output * output)1594 ecore_drm2_output_rotation_get(Ecore_Drm2_Output *output)
1595 {
1596    EINA_SAFETY_ON_NULL_RETURN_VAL(output, -1);
1597    return output->rotation;
1598 }
1599 
1600 EAPI unsigned int
ecore_drm2_output_subpixel_get(const Ecore_Drm2_Output * output)1601 ecore_drm2_output_subpixel_get(const Ecore_Drm2_Output *output)
1602 {
1603    EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0);
1604    return output->subpixel;
1605 }
1606 
1607 static void
_blank_fallback_handler(int fd EINA_UNUSED,unsigned int frame EINA_UNUSED,unsigned int sec,unsigned int usec,void * data EINA_UNUSED)1608 _blank_fallback_handler(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec, unsigned int usec, void *data EINA_UNUSED)
1609 {
1610    Ecore_Drm2_Output *output;
1611 
1612    output = data;
1613    output->fallback_usec = usec;
1614    output->fallback_sec = sec;
1615 }
1616 
1617 static int
_blanktime_fallback(Ecore_Drm2_Output * output,int sequence,long * sec,long * usec)1618 _blanktime_fallback(Ecore_Drm2_Output *output, int sequence, long *sec, long *usec)
1619 {
1620    drmEventContext ctx;
1621    int ret;
1622 
1623    /* Too lazy to loop for > 1, and don't want to block for < 1 */
1624    if (sequence != 1) return -1;
1625 
1626    /* If we got here with a flip waiting to complete we can do nothing. */
1627    if (output->pending.fb) return -1;
1628 
1629    if (!output->current.fb) return -1;
1630 
1631    memset(&ctx, 0, sizeof(ctx));
1632    ctx.version = 2;
1633    ctx.page_flip_handler = _blank_fallback_handler;
1634    ctx.vblank_handler = NULL;
1635 
1636    ret = sym_drmModePageFlip(output->current.fb->fd, output->crtc_id,
1637                              output->current.fb->id, DRM_MODE_PAGE_FLIP_EVENT,
1638                              output);
1639    if (ret < 0) return -1;
1640    do
1641      {
1642         ret = sym_drmHandleEvent(output->current.fb->fd, &ctx);
1643      } while (ret != 0 && errno == EAGAIN);
1644    if (ret < 0) return -1;
1645 
1646    *sec = output->fallback_sec;
1647    *usec = output->fallback_usec;
1648    return 0;
1649 }
1650 
1651 EAPI Eina_Bool
ecore_drm2_output_blanktime_get(Ecore_Drm2_Output * output,int sequence,long * sec,long * usec)1652 ecore_drm2_output_blanktime_get(Ecore_Drm2_Output *output, int sequence, long *sec, long *usec)
1653 {
1654    drmVBlank v;
1655    int ret;
1656    Eina_Bool success;
1657 
1658    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1659    EINA_SAFETY_ON_NULL_RETURN_VAL(sec, EINA_FALSE);
1660    EINA_SAFETY_ON_NULL_RETURN_VAL(usec, EINA_FALSE);
1661 
1662    memset(&v, 0, sizeof(v));
1663    v.request.type = DRM_VBLANK_RELATIVE;
1664    v.request.type |= _output_vblank_pipe(output);
1665    v.request.sequence = sequence;
1666    ret = sym_drmWaitVBlank(output->fd, &v);
1667    success = (ret == 0) && (v.reply.tval_sec > 0 || v.reply.tval_usec > 0);
1668    if (!success)
1669      {
1670         ret = _blanktime_fallback(output, sequence, sec, usec);
1671         if (ret) return EINA_FALSE;
1672         return EINA_TRUE;
1673      }
1674 
1675    *sec = v.reply.tval_sec;
1676    *usec = v.reply.tval_usec;
1677    return EINA_TRUE;
1678 }
1679 
1680 EAPI void
ecore_drm2_output_info_get(Ecore_Drm2_Output * output,int * x,int * y,int * w,int * h,unsigned int * refresh)1681 ecore_drm2_output_info_get(Ecore_Drm2_Output *output, int *x, int *y, int *w, int *h, unsigned int *refresh)
1682 {
1683    if (x) *x = 0;
1684    if (y) *y = 0;
1685    if (w) *w = 0;
1686    if (h) *h = 0;
1687    if (refresh) *refresh = 0;
1688 
1689    EINA_SAFETY_ON_NULL_RETURN(output);
1690    EINA_SAFETY_ON_TRUE_RETURN(!output->current_mode);
1691 
1692    switch (output->rotation)
1693      {
1694       case ECORE_DRM2_ROTATION_90:
1695       case ECORE_DRM2_ROTATION_270:
1696         if (w) *w = output->current_mode->height;
1697         if (h) *h = output->current_mode->width;
1698         break;
1699       case ECORE_DRM2_ROTATION_NORMAL:
1700       case ECORE_DRM2_ROTATION_180:
1701       default:
1702         if (w) *w = output->current_mode->width;
1703         if (h) *h = output->current_mode->height;
1704         break;
1705      }
1706 
1707    if (refresh) *refresh = output->current_mode->refresh;
1708    if (x) *x = output->x;
1709    if (y) *y = output->y;
1710 }
1711 
1712 EAPI Eina_Bool
ecore_drm2_output_pending_get(Ecore_Drm2_Output * output)1713 ecore_drm2_output_pending_get(Ecore_Drm2_Output *output)
1714 {
1715    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1716 
1717    if (output->pending.fb) return EINA_TRUE;
1718 
1719    return EINA_FALSE;
1720 }
1721 
1722 EAPI void
ecore_drm2_output_relative_mode_set(Ecore_Drm2_Output * output,Ecore_Drm2_Relative_Mode mode)1723 ecore_drm2_output_relative_mode_set(Ecore_Drm2_Output *output, Ecore_Drm2_Relative_Mode mode)
1724 {
1725    EINA_SAFETY_ON_NULL_RETURN(output);
1726    output->relative.mode = mode;
1727 }
1728 
1729 EAPI Ecore_Drm2_Relative_Mode
ecore_drm2_output_relative_mode_get(Ecore_Drm2_Output * output)1730 ecore_drm2_output_relative_mode_get(Ecore_Drm2_Output *output)
1731 {
1732    EINA_SAFETY_ON_NULL_RETURN_VAL(output, ECORE_DRM2_RELATIVE_MODE_UNKNOWN);
1733    return output->relative.mode;
1734 }
1735 
1736 EAPI void
ecore_drm2_output_relative_to_set(Ecore_Drm2_Output * output,const char * relative)1737 ecore_drm2_output_relative_to_set(Ecore_Drm2_Output *output, const char *relative)
1738 {
1739    EINA_SAFETY_ON_NULL_RETURN(output);
1740    eina_stringshare_replace(&output->relative.to, relative);
1741 }
1742 
1743 EAPI const char *
ecore_drm2_output_relative_to_get(Ecore_Drm2_Output * output)1744 ecore_drm2_output_relative_to_get(Ecore_Drm2_Output *output)
1745 {
1746    EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
1747    return output->relative.to;
1748 }
1749 
1750 EAPI Eina_Bool
ecore_drm2_output_background_color_set(Ecore_Drm2_Output * output,uint64_t r,uint64_t g,uint64_t b,uint64_t a)1751 ecore_drm2_output_background_color_set(Ecore_Drm2_Output *output, uint64_t r, uint64_t g, uint64_t b, uint64_t a)
1752 {
1753    Ecore_Drm2_Crtc_State *cstate;
1754 
1755    EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE);
1756    EINA_SAFETY_ON_NULL_RETURN_VAL(output->crtc_state, EINA_FALSE);
1757 
1758    cstate = output->crtc_state;
1759    if (cstate->background.id)
1760      {
1761         cstate->background.value = (a << 48 | b << 32 | g << 16 | r);
1762         return _fb_atomic_flip_test(output);
1763      }
1764 
1765    return EINA_FALSE;
1766 }
1767