GCC Code Coverage Report


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