GCC Code Coverage Report


Directory: ./
File: plane.c
Date: 2025-02-19 14:16:04
Exec Total Coverage
Lines: 182 219 83.1%
Functions: 13 13 100.0%
Branches: 113 143 79.0%

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