GCC Code Coverage Report


Directory: ./
File: plane.c
Date: 2025-02-26 18:18:17
Exec Total Coverage
Lines: 182 224 81.2%
Functions: 13 13 100.0%
Branches: 114 147 77.6%

Line Branch Exec Source
1 #include <errno.h>
2 #include <inttypes.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <xf86drm.h>
7 #include "private.h"
8
9 static int
10 146 guess_plane_zpos_from_type(struct liftoff_device *device, uint32_t plane_id,
11 uint32_t type)
12 {
13 struct liftoff_plane *primary;
14
15 /* From far to close to the eye: primary, overlay, cursor. Unless
16 * the overlay ID < primary ID. */
17
3/4
✓ Branch 0 (2→3) taken 53 times.
✓ Branch 1 (2→4) taken 41 times.
✓ Branch 2 (2→5) taken 52 times.
✗ Branch 3 (2→11) not taken.
146 switch (type) {
18 53 case DRM_PLANE_TYPE_PRIMARY:
19 53 return 0;
20 41 case DRM_PLANE_TYPE_CURSOR:
21 41 return 2;
22 52 case DRM_PLANE_TYPE_OVERLAY:
23
2/2
✓ Branch 0 (6→7) taken 3 times.
✓ Branch 1 (6→8) taken 49 times.
52 if (liftoff_list_empty(&device->planes)) {
24 3 return 0; /* No primary plane, shouldn't happen */
25 }
26 49 primary = liftoff_container_of(device->planes.next,
27 primary, link);
28
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→10) taken 49 times.
49 if (plane_id < primary->id) {
29 return -1;
30 } else {
31 49 return 1;
32 }
33 }
34 return 0;
35 }
36
37 struct liftoff_plane *
38 148 liftoff_plane_create(struct liftoff_device *device, uint32_t id)
39 {
40 struct liftoff_plane *plane, *cur;
41 drmModePlane *drm_plane;
42 drmModeObjectProperties *drm_props;
43 uint32_t i;
44 drmModePropertyRes *prop;
45 int ret;
46 uint64_t value;
47 148 bool has_type = false, has_zpos = false;
48 ssize_t core_prop_idx;
49
50
2/2
✓ Branch 0 (7→3) taken 160 times.
✓ Branch 1 (7→8) taken 148 times.
308 liftoff_list_for_each(plane, &device->planes, link) {
51
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 160 times.
160 if (plane->id == id) {
52 liftoff_log(LIFTOFF_ERROR, "tried to register plane "
53 "%"PRIu32" twice\n", id);
54 errno = EEXIST;
55 return NULL;
56 }
57 }
58
59 148 plane = calloc(1, sizeof(*plane));
60
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→11) taken 148 times.
148 if (plane == NULL) {
61 liftoff_log_errno(LIFTOFF_ERROR, "calloc");
62 return NULL;
63 }
64
65 148 drm_plane = drmModeGetPlane(device->drm_fd, id);
66
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→15) taken 148 times.
148 if (drm_plane == NULL) {
67 liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetPlane");
68 return NULL;
69 }
70 148 plane->id = drm_plane->plane_id;
71 148 plane->possible_crtcs = drm_plane->possible_crtcs;
72
73 /* If the plane is displayed on a CRTC at start-up time, make sure we
74 * don't try to use it on a different CRTC until it has been freed. */
75
1/2
✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→20) taken 148 times.
148 if (drm_plane->crtc_id != 0) {
76 plane->free_after_pageflip = drm_plane->crtc_id;
77 ret = drmCrtcGetSequence(device->drm_fd,
78 drm_plane->crtc_id, &plane->free_after_sequence, NULL);
79 if (ret != 0) {
80 liftoff_log_errno(LIFTOFF_ERROR, "drmCrtcGetSequence");
81 return NULL;
82 }
83 }
84
85 148 drmModeFreePlane(drm_plane);
86
87 148 drm_props = drmModeObjectGetProperties(device->drm_fd, id,
88 DRM_MODE_OBJECT_PLANE);
89
1/2
✗ Branch 0 (22→23) not taken.
✓ Branch 1 (22→25) taken 148 times.
148 if (drm_props == NULL) {
90 liftoff_log_errno(LIFTOFF_ERROR, "drmModeObjectGetProperties");
91 return NULL;
92 }
93 148 plane->props = calloc(drm_props->count_props, sizeof(plane->props[0]));
94
1/2
✗ Branch 0 (25→26) not taken.
✓ Branch 1 (25→29) taken 148 times.
148 if (plane->props == NULL) {
95 liftoff_log_errno(LIFTOFF_ERROR, "calloc");
96 drmModeFreeObjectProperties(drm_props);
97 return NULL;
98 }
99
2/2
✓ Branch 0 (48→30) taken 1690 times.
✓ Branch 1 (48→49) taken 148 times.
1838 for (i = 0; i < drm_props->count_props; i++) {
100 1690 prop = drmModeGetProperty(device->drm_fd, drm_props->props[i]);
101
1/2
✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→35) taken 1690 times.
1690 if (prop == NULL) {
102 liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetProperty");
103 drmModeFreeObjectProperties(drm_props);
104 return NULL;
105 }
106 1690 plane->props[i] = prop;
107 1690 plane->props_len++;
108
109 1690 value = drm_props->prop_values[i];
110
2/2
✓ Branch 0 (35→36) taken 148 times.
✓ Branch 1 (35→37) taken 1542 times.
1690 if (strcmp(prop->name, "type") == 0) {
111 148 plane->type = value;
112 148 has_type = true;
113
2/2
✓ Branch 0 (37→38) taken 2 times.
✓ Branch 1 (37→39) taken 1540 times.
1542 } else if (strcmp(prop->name, "zpos") == 0) {
114 2 plane->zpos = value;
115 2 has_zpos = true;
116
2/2
✓ Branch 0 (39→40) taken 1 times.
✓ Branch 1 (39→44) taken 1539 times.
1540 } else if (strcmp(prop->name, "IN_FORMATS") == 0) {
117 1 plane->in_formats_blob = drmModeGetPropertyBlob(device->drm_fd,
118 value);
119
1/2
✗ Branch 0 (41→42) not taken.
✓ Branch 1 (41→44) taken 1 times.
1 if (plane->in_formats_blob == NULL) {
120 liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetPropertyBlob");
121 return NULL;
122 }
123 }
124
125 1690 core_prop_idx = core_property_index(prop->name);
126
2/2
✓ Branch 0 (45→46) taken 1504 times.
✓ Branch 1 (45→47) taken 186 times.
1690 if (core_prop_idx >= 0) {
127 1504 plane->core_props[core_prop_idx] = prop;
128 }
129 }
130 148 drmModeFreeObjectProperties(drm_props);
131
132
1/2
✗ Branch 0 (50→51) not taken.
✓ Branch 1 (50→53) taken 148 times.
148 if (!has_type) {
133 liftoff_log(LIFTOFF_ERROR,
134 "plane %"PRIu32" is missing the 'type' property",
135 plane->id);
136 free(plane);
137 errno = EINVAL;
138 return NULL;
139
2/2
✓ Branch 0 (53→54) taken 146 times.
✓ Branch 1 (53→56) taken 2 times.
148 } else if (!has_zpos) {
140 146 plane->zpos = guess_plane_zpos_from_type(device, plane->id,
141 plane->type);
142 }
143
144 /* During plane allocation, we will use the plane list order to fill
145 * planes with FBs. Primary planes need to be filled first, then planes
146 * far from the primary planes, then planes closer and closer to the
147 * primary plane. */
148
2/2
✓ Branch 0 (56→57) taken 53 times.
✓ Branch 1 (56→58) taken 95 times.
148 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
149 53 liftoff_list_insert(&device->planes, &plane->link);
150 } else {
151
2/2
✓ Branch 0 (64→59) taken 160 times.
✓ Branch 1 (64→65) taken 68 times.
228 liftoff_list_for_each(cur, &device->planes, link) {
152
2/2
✓ Branch 0 (59→60) taken 73 times.
✓ Branch 1 (59→63) taken 87 times.
160 if (cur->type != DRM_PLANE_TYPE_PRIMARY &&
153
2/2
✓ Branch 0 (60→61) taken 27 times.
✓ Branch 1 (60→63) taken 46 times.
73 plane->zpos >= cur->zpos) {
154 27 liftoff_list_insert(cur->link.prev, &plane->link);
155 27 break;
156 }
157 }
158
159
2/2
✓ Branch 0 (65→66) taken 68 times.
✓ Branch 1 (65→67) taken 27 times.
95 if (plane->link.next == NULL) { /* not inserted */
160 68 liftoff_list_insert(device->planes.prev, &plane->link);
161 }
162 }
163
164 148 return plane;
165 }
166
167 void
168 148 liftoff_plane_destroy(struct liftoff_plane *plane)
169 {
170 size_t i;
171
172
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 148 times.
148 if (plane == NULL) {
173 return;
174 }
175
176
2/2
✓ Branch 0 (4→5) taken 69 times.
✓ Branch 1 (4→6) taken 79 times.
148 if (plane->layer != NULL) {
177 69 plane->layer->plane = NULL;
178 }
179
180
2/2
✓ Branch 0 (9→7) taken 1690 times.
✓ Branch 1 (9→10) taken 148 times.
1838 for (i = 0; i < plane->props_len; i++) {
181 1690 drmModeFreeProperty(plane->props[i]);
182 }
183
184 148 liftoff_list_remove(&plane->link);
185 148 free(plane->props);
186 148 drmModeFreePropertyBlob(plane->in_formats_blob);
187 148 free(plane);
188 }
189
190 uint32_t
191 44 liftoff_plane_get_id(struct liftoff_plane *plane)
192 {
193 44 return plane->id;
194 }
195
196 static const drmModePropertyRes *
197 14772 plane_get_property(struct liftoff_plane *plane,
198 const struct liftoff_layer_property *layer_prop)
199 {
200 size_t i;
201
202
2/2
✓ Branch 0 (2→3) taken 14753 times.
✓ Branch 1 (2→4) taken 19 times.
14772 if (layer_prop->core_index >= 0)
203 14753 return plane->core_props[layer_prop->core_index];
204
205
2/2
✓ Branch 0 (8→5) taken 232 times.
✓ Branch 1 (8→9) taken 5 times.
237 for (i = 0; i < plane->props_len; i++) {
206
2/2
✓ Branch 0 (5→6) taken 14 times.
✓ Branch 1 (5→7) taken 218 times.
232 if (strcmp(plane->props[i]->name, layer_prop->name) == 0) {
207 14 return plane->props[i];
208 }
209 }
210 5 return NULL;
211 }
212
213 static int
214 4 check_range_prop(const drmModePropertyRes *prop, uint64_t value)
215 {
216
3/4
✓ Branch 0 (2→3) taken 4 times.
✗ Branch 1 (2→4) not taken.
✓ Branch 2 (3→4) taken 2 times.
✓ Branch 3 (3→5) taken 2 times.
4 if (value < prop->values[0] || value > prop->values[1]) {
217 2 return -EINVAL;
218 }
219 2 return 0;
220 }
221
222 static int
223 3 check_enum_prop(const drmModePropertyRes *prop, uint64_t value)
224 {
225 int i;
226
227
2/2
✓ Branch 0 (6→3) taken 6 times.
✓ Branch 1 (6→7) taken 1 times.
7 for (i = 0; i < prop->count_enums; i++) {
228
2/2
✓ Branch 0 (3→4) taken 2 times.
✓ Branch 1 (3→5) taken 4 times.
6 if (prop->enums[i].value == value) {
229 2 return 0;
230 }
231 }
232 1 return -EINVAL;
233 }
234
235 static int
236 3 check_bitmask_prop(const drmModePropertyRes *prop, uint64_t value)
237 {
238 int i;
239 uint64_t mask;
240
241 3 mask = 0;
242
2/2
✓ Branch 0 (4→3) taken 9 times.
✓ Branch 1 (4→5) taken 3 times.
12 for (i = 0; i < prop->count_enums; i++) {
243 9 mask |= (uint64_t)1 << prop->enums[i].value;
244 }
245
246
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 2 times.
3 if ((value & ~mask) != 0) {
247 1 return -EINVAL;
248 }
249 2 return 0;
250 }
251
252 static int
253 4 check_signed_range_prop(const drmModePropertyRes *prop, uint64_t value)
254 {
255
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 2 times.
4 if ((int64_t) value < (int64_t) prop->values[0] ||
256
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 2 times.
2 (int64_t) value > (int64_t) prop->values[1]) {
257 2 return -EINVAL;
258 }
259 2 return 0;
260 }
261
262 static int
263 16446 plane_set_prop(struct liftoff_plane *plane, drmModeAtomicReq *req,
264 const drmModePropertyRes *prop, uint64_t value)
265 {
266 int ret;
267
268
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 16446 times.
16446 if (prop->flags & DRM_MODE_PROP_IMMUTABLE) {
269 return -EINVAL;
270 }
271
272 /* Manually check the property value if we can: this may avoid
273 * unnecessary test commits */
274 16446 ret = 0;
275
5/5
✓ Branch 0 (5→6) taken 4 times.
✓ Branch 1 (5→8) taken 3 times.
✓ Branch 2 (5→10) taken 3 times.
✓ Branch 3 (5→12) taken 4 times.
✓ Branch 4 (5→14) taken 16432 times.
16446 switch (drmModeGetPropertyType(prop)) {
276 4 case DRM_MODE_PROP_RANGE:
277 4 ret = check_range_prop(prop, value);
278 4 break;
279 3 case DRM_MODE_PROP_ENUM:
280 3 ret = check_enum_prop(prop, value);
281 3 break;
282 3 case DRM_MODE_PROP_BITMASK:
283 3 ret = check_bitmask_prop(prop, value);
284 3 break;
285 4 case DRM_MODE_PROP_SIGNED_RANGE:
286 4 ret = check_signed_range_prop(prop, value);
287 4 break;
288 }
289
2/2
✓ Branch 0 (14→15) taken 6 times.
✓ Branch 1 (14→16) taken 16440 times.
16446 if (ret != 0) {
290 6 return ret;
291 }
292
293 16440 ret = drmModeAtomicAddProperty(req, plane->id, prop->prop_id, value);
294
1/2
✗ Branch 0 (17→18) not taken.
✓ Branch 1 (17→21) taken 16440 times.
16440 if (ret < 0) {
295 liftoff_log(LIFTOFF_ERROR, "drmModeAtomicAddProperty: %s",
296 strerror(-ret));
297 return ret;
298 }
299
300 16440 return 0;
301 }
302
303 static int
304 1687 set_plane_core_prop(struct liftoff_plane *plane, drmModeAtomicReq *req,
305 enum liftoff_core_property core_prop, uint64_t value)
306 {
307 const drmModePropertyRes *prop;
308
309 1687 prop = plane->core_props[core_prop];
310
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→5) taken 1687 times.
1687 if (prop == NULL) {
311 liftoff_log(LIFTOFF_DEBUG,
312 "plane %"PRIu32" is missing core property %d",
313 plane->id, core_prop);
314 return -EINVAL;
315 }
316
317 1687 return plane_set_prop(plane, req, prop, value);
318 }
319
320 bool
321 328 plane_check_layer_fb(struct liftoff_plane *plane, struct liftoff_layer *layer)
322 {
323 const struct drm_format_modifier_blob *set;
324 const uint32_t *formats;
325 const struct drm_format_modifier *modifiers;
326 size_t i;
327 ssize_t format_index, modifier_index;
328 int format_shift;
329
330 /* TODO: add support for legacy format list with implicit modifier */
331
2/2
✓ Branch 0 (2→3) taken 5 times.
✓ Branch 1 (2→5) taken 323 times.
328 if (layer->fb_info.fb_id == 0 ||
332
1/2
✓ Branch 0 (3→4) taken 5 times.
✗ Branch 1 (3→5) not taken.
5 !(layer->fb_info.flags & DRM_MODE_FB_MODIFIERS) ||
333
2/2
✓ Branch 0 (4→5) taken 3 times.
✓ Branch 1 (4→6) taken 2 times.
5 plane->in_formats_blob == NULL) {
334 326 return true; /* not enough information to reject */
335 }
336
337 2 set = plane->in_formats_blob->data;
338
339 2 formats = (void *)((char *)set + set->formats_offset);
340 2 format_index = -1;
341
1/2
✓ Branch 0 (10→7) taken 2 times.
✗ Branch 1 (10→11) not taken.
2 for (i = 0; i < set->count_formats; ++i) {
342
1/2
✓ Branch 0 (7→8) taken 2 times.
✗ Branch 1 (7→9) not taken.
2 if (formats[i] == layer->fb_info.pixel_format) {
343 2 format_index = (ssize_t)i;
344 2 break;
345 }
346 }
347
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 2 times.
2 if (format_index < 0) {
348 return false;
349 }
350
351 2 modifiers = (void *)((char *)set + set->modifiers_offset);
352 2 modifier_index = -1;
353
2/2
✓ Branch 0 (17→14) taken 2 times.
✓ Branch 1 (17→18) taken 1 times.
3 for (i = 0; i < set->count_modifiers; i++) {
354
2/2
✓ Branch 0 (14→15) taken 1 times.
✓ Branch 1 (14→16) taken 1 times.
2 if (modifiers[i].modifier == layer->fb_info.modifier) {
355 1 modifier_index = (ssize_t)i;
356 1 break;
357 }
358 }
359
2/2
✓ Branch 0 (18→19) taken 1 times.
✓ Branch 1 (18→20) taken 1 times.
2 if (modifier_index < 0) {
360 1 return false;
361 }
362
363
1/2
✓ Branch 0 (20→21) taken 1 times.
✗ Branch 1 (20→22) not taken.
1 if ((size_t)format_index < modifiers[modifier_index].offset ||
364
1/2
✗ Branch 0 (21→22) not taken.
✓ Branch 1 (21→23) taken 1 times.
1 (size_t)format_index >= modifiers[modifier_index].offset + 64) {
365 return false;
366 }
367 1 format_shift = format_index - (int)modifiers[modifier_index].offset;
368 1 return (modifiers[modifier_index].formats & ((uint64_t)1 << format_shift)) != 0;
369 }
370
371 int
372 1661 plane_apply(struct liftoff_plane *plane, struct liftoff_layer *layer,
373 drmModeAtomicReq *req)
374 {
375 int cursor, ret;
376 size_t i;
377 struct liftoff_layer_property *layer_prop;
378 const drmModePropertyRes *plane_prop;
379
380 1661 cursor = drmModeAtomicGetCursor(req);
381
382
2/2
✓ Branch 0 (3→4) taken 26 times.
✓ Branch 1 (3→9) taken 1635 times.
1661 if (layer == NULL) {
383 26 ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_FB_ID, 0);
384
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 26 times.
26 if (ret != 0) {
385 return ret;
386 }
387 26 return set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID, 0);
388 }
389
390 1635 ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID,
391 1635 layer->output->crtc_id);
392
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→12) taken 1635 times.
1635 if (ret != 0) {
393 return ret;
394 }
395
396
2/2
✓ Branch 0 (38→13) taken 14974 times.
✓ Branch 1 (38→39) taken 1624 times.
16598 for (i = 0; i < layer->props_len; i++) {
397 14974 layer_prop = &layer->props[i];
398
2/2
✓ Branch 0 (13→14) taken 202 times.
✓ Branch 1 (13→15) taken 14772 times.
14974 if (layer_prop->core_index == LIFTOFF_PROP_ZPOS) {
399 /* We don't yet support setting the zpos property. We
400 * only use it (read-only) during plane allocation. */
401 202 continue;
402 }
403
404 14772 plane_prop = plane_get_property(plane, layer_prop);
405
2/2
✓ Branch 0 (16→17) taken 13 times.
✓ Branch 1 (16→33) taken 14759 times.
14772 if (plane_prop == NULL) {
406
2/2
✓ Branch 0 (17→18) taken 5 times.
✓ Branch 1 (17→20) taken 8 times.
13 if (layer_prop->core_index == LIFTOFF_PROP_ALPHA &&
407
2/2
✓ Branch 0 (18→19) taken 4 times.
✓ Branch 1 (18→20) taken 1 times.
5 layer_prop->value == 0xFFFF) {
408 4 continue; /* Layer is completely opaque */
409 }
410
2/2
✓ Branch 0 (20→21) taken 3 times.
✓ Branch 1 (20→23) taken 6 times.
9 if (layer_prop->core_index == LIFTOFF_PROP_ROTATION &&
411
2/2
✓ Branch 0 (21→22) taken 2 times.
✓ Branch 1 (21→23) taken 1 times.
3 layer_prop->value == DRM_MODE_ROTATE_0) {
412 2 continue; /* Layer isn't rotated */
413 }
414
1/2
✗ Branch 0 (23→24) not taken.
✓ Branch 1 (23→26) taken 7 times.
7 if (strcmp(layer_prop->name, "SCALING_FILTER") == 0 &&
415 layer_prop->value == 0) {
416 continue; /* Layer uses default scaling filter */
417 }
418
1/2
✗ Branch 0 (26→27) not taken.
✓ Branch 1 (26→29) taken 7 times.
7 if (strcmp(layer_prop->name, "pixel blend mode") == 0 &&
419 layer_prop->value == 0) {
420 continue; /* Layer uses pre-multiplied alpha */
421 }
422
2/2
✓ Branch 0 (29→30) taken 2 times.
✓ Branch 1 (29→31) taken 5 times.
7 if (strcmp(layer_prop->name, "FB_DAMAGE_CLIPS") == 0) {
423 2 continue; /* Damage can be omitted */
424 }
425 5 drmModeAtomicSetCursor(req, cursor);
426 5 return -EINVAL;
427 }
428
429 14759 ret = plane_set_prop(plane, req, plane_prop, layer_prop->value);
430
2/2
✓ Branch 0 (34→35) taken 6 times.
✓ Branch 1 (34→37) taken 14753 times.
14759 if (ret != 0) {
431 6 drmModeAtomicSetCursor(req, cursor);
432 6 return ret;
433 }
434 }
435
436 1624 return 0;
437 }
438