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