118210155Smrg/* 218210155Smrg * DRM based mode setting test program 318210155Smrg * Copyright 2008 Tungsten Graphics 418210155Smrg * Jakob Bornecrantz <jakob@tungstengraphics.com> 518210155Smrg * Copyright 2008 Intel Corporation 618210155Smrg * Jesse Barnes <jesse.barnes@intel.com> 718210155Smrg * 818210155Smrg * Permission is hereby granted, free of charge, to any person obtaining a 918210155Smrg * copy of this software and associated documentation files (the "Software"), 1018210155Smrg * to deal in the Software without restriction, including without limitation 1118210155Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1218210155Smrg * and/or sell copies of the Software, and to permit persons to whom the 1318210155Smrg * Software is furnished to do so, subject to the following conditions: 1418210155Smrg * 1518210155Smrg * The above copyright notice and this permission notice shall be included in 1618210155Smrg * all copies or substantial portions of the Software. 1718210155Smrg * 1818210155Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1918210155Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2018210155Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2118210155Smrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2218210155Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2318210155Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2418210155Smrg * IN THE SOFTWARE. 2518210155Smrg */ 2618210155Smrg 2718210155Smrg/* 2818210155Smrg * This fairly simple test program dumps output in a similar format to the 2918210155Smrg * "xrandr" tool everyone knows & loves. It's necessarily slightly different 3018210155Smrg * since the kernel separates outputs into encoder and connector structures, 3118210155Smrg * each with their own unique ID. The program also allows test testing of the 3218210155Smrg * memory management and mode setting APIs by allowing the user to specify a 3318210155Smrg * connector and mode to use for mode setting. If all works as expected, a 3418210155Smrg * blue background should be painted on the monitor attached to the specified 3518210155Smrg * connector after the selected mode is set. 3618210155Smrg * 3718210155Smrg * TODO: use cairo to write the mode info on the selected output once 3818210155Smrg * the mode has been programmed, along with possible test patterns. 3918210155Smrg */ 40fe517fc9Smrg 4118210155Smrg#include <assert.h> 42e88f27b3Smrg#include <ctype.h> 43e88f27b3Smrg#include <stdbool.h> 4418210155Smrg#include <stdio.h> 4518210155Smrg#include <stdlib.h> 4618210155Smrg#include <stdint.h> 47e88f27b3Smrg#include <inttypes.h> 4818210155Smrg#include <unistd.h> 4918210155Smrg#include <string.h> 50424e9256Smrg#include <strings.h> 5118210155Smrg#include <errno.h> 52fe517fc9Smrg#include <poll.h> 5322944501Smrg#include <sys/time.h> 5487bf8e7cSmrg#if HAVE_SYS_SELECT_H 55fe517fc9Smrg#include <sys/select.h> 56fe517fc9Smrg#endif 5787bf8e7cSmrg#include <math.h> 5818210155Smrg 5918210155Smrg#include "xf86drm.h" 6018210155Smrg#include "xf86drmMode.h" 61e88f27b3Smrg#include "drm_fourcc.h" 6218210155Smrg 63fe517fc9Smrg#include "util/common.h" 64fe517fc9Smrg#include "util/format.h" 65fe517fc9Smrg#include "util/kms.h" 66fe517fc9Smrg#include "util/pattern.h" 67fe517fc9Smrg 68e88f27b3Smrg#include "buffers.h" 69a7d7de1eSmrg#include "cursor.h" 70e88f27b3Smrg 71bf6cc7dcSmrgstatic enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE; 72bf6cc7dcSmrgstatic enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES; 7348246ce7Smrgstatic drmModeModeInfo user_mode; 74bf6cc7dcSmrg 75e88f27b3Smrgstruct crtc { 76e88f27b3Smrg drmModeCrtc *crtc; 77e88f27b3Smrg drmModeObjectProperties *props; 78e88f27b3Smrg drmModePropertyRes **props_info; 79e88f27b3Smrg drmModeModeInfo *mode; 80e88f27b3Smrg}; 81e88f27b3Smrg 82e88f27b3Smrgstruct encoder { 83e88f27b3Smrg drmModeEncoder *encoder; 84e88f27b3Smrg}; 85e88f27b3Smrg 86e88f27b3Smrgstruct connector { 87e88f27b3Smrg drmModeConnector *connector; 88e88f27b3Smrg drmModeObjectProperties *props; 89e88f27b3Smrg drmModePropertyRes **props_info; 90fe517fc9Smrg char *name; 91e88f27b3Smrg}; 9218210155Smrg 93e88f27b3Smrgstruct fb { 94e88f27b3Smrg drmModeFB *fb; 95e88f27b3Smrg}; 96e88f27b3Smrg 97e88f27b3Smrgstruct plane { 98e88f27b3Smrg drmModePlane *plane; 99e88f27b3Smrg drmModeObjectProperties *props; 100e88f27b3Smrg drmModePropertyRes **props_info; 101e88f27b3Smrg}; 102e88f27b3Smrg 103e88f27b3Smrgstruct resources { 104e88f27b3Smrg struct crtc *crtcs; 10587bf8e7cSmrg int count_crtcs; 106e88f27b3Smrg struct encoder *encoders; 10787bf8e7cSmrg int count_encoders; 108e88f27b3Smrg struct connector *connectors; 10987bf8e7cSmrg int count_connectors; 110e88f27b3Smrg struct fb *fbs; 11187bf8e7cSmrg int count_fbs; 112e88f27b3Smrg struct plane *planes; 11387bf8e7cSmrg uint32_t count_planes; 114e88f27b3Smrg}; 115e88f27b3Smrg 116e88f27b3Smrgstruct device { 117e88f27b3Smrg int fd; 118e88f27b3Smrg 119e88f27b3Smrg struct resources *resources; 120e88f27b3Smrg 121e88f27b3Smrg struct { 122e88f27b3Smrg unsigned int width; 123e88f27b3Smrg unsigned int height; 124e88f27b3Smrg 125e88f27b3Smrg unsigned int fb_id; 1263c748557Ssnj struct bo *bo; 127424e9256Smrg struct bo *cursor_bo; 128e88f27b3Smrg } mode; 1296260e5d5Smrg 1306260e5d5Smrg int use_atomic; 1316260e5d5Smrg drmModeAtomicReq *req; 13248246ce7Smrg int32_t writeback_fence_fd; 133e88f27b3Smrg}; 13418210155Smrg 1353c748557Ssnjstatic inline int64_t U642I64(uint64_t val) 1363c748557Ssnj{ 1373c748557Ssnj return (int64_t)*((int64_t *)&val); 1383c748557Ssnj} 13918210155Smrg 14087bf8e7cSmrgstatic float mode_vrefresh(drmModeModeInfo *mode) 14187bf8e7cSmrg{ 14248246ce7Smrg unsigned int num, den; 14348246ce7Smrg 14448246ce7Smrg num = mode->clock; 14548246ce7Smrg den = mode->htotal * mode->vtotal; 14648246ce7Smrg 14748246ce7Smrg if (mode->flags & DRM_MODE_FLAG_INTERLACE) 14848246ce7Smrg num *= 2; 14948246ce7Smrg if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 15048246ce7Smrg den *= 2; 15148246ce7Smrg if (mode->vscan > 1) 15248246ce7Smrg den *= mode->vscan; 15348246ce7Smrg 15448246ce7Smrg return num * 1000.00 / den; 15587bf8e7cSmrg} 15687bf8e7cSmrg 157e88f27b3Smrg#define bit_name_fn(res) \ 158e88f27b3Smrgconst char * res##_str(int type) { \ 159e88f27b3Smrg unsigned int i; \ 160e88f27b3Smrg const char *sep = ""; \ 161e88f27b3Smrg for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 162e88f27b3Smrg if (type & (1 << i)) { \ 163e88f27b3Smrg printf("%s%s", sep, res##_names[i]); \ 164e88f27b3Smrg sep = ", "; \ 165e88f27b3Smrg } \ 166e88f27b3Smrg } \ 167e88f27b3Smrg return NULL; \ 168e88f27b3Smrg} 169e88f27b3Smrg 170e88f27b3Smrgstatic const char *mode_type_names[] = { 171e88f27b3Smrg "builtin", 172e88f27b3Smrg "clock_c", 173e88f27b3Smrg "crtc_c", 174e88f27b3Smrg "preferred", 175e88f27b3Smrg "default", 176e88f27b3Smrg "userdef", 177e88f27b3Smrg "driver", 178e88f27b3Smrg}; 179e88f27b3Smrg 180e88f27b3Smrgstatic bit_name_fn(mode_type) 181e88f27b3Smrg 182e88f27b3Smrgstatic const char *mode_flag_names[] = { 183e88f27b3Smrg "phsync", 184e88f27b3Smrg "nhsync", 185e88f27b3Smrg "pvsync", 186e88f27b3Smrg "nvsync", 187e88f27b3Smrg "interlace", 188e88f27b3Smrg "dblscan", 189e88f27b3Smrg "csync", 190e88f27b3Smrg "pcsync", 191e88f27b3Smrg "ncsync", 192e88f27b3Smrg "hskew", 193e88f27b3Smrg "bcast", 194e88f27b3Smrg "pixmux", 195e88f27b3Smrg "dblclk", 196e88f27b3Smrg "clkdiv2" 197e88f27b3Smrg}; 198e88f27b3Smrg 199e88f27b3Smrgstatic bit_name_fn(mode_flag) 20018210155Smrg 2010655efefSmrgstatic void dump_fourcc(uint32_t fourcc) 2020655efefSmrg{ 2033b115362Smrg char *name = drmGetFormatName(fourcc); 2043b115362Smrg printf(" %s", name); 2053b115362Smrg free(name); 2060655efefSmrg} 2070655efefSmrg 208e88f27b3Smrgstatic void dump_encoders(struct device *dev) 20918210155Smrg{ 21018210155Smrg drmModeEncoder *encoder; 21118210155Smrg int i; 21218210155Smrg 21318210155Smrg printf("Encoders:\n"); 21418210155Smrg printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 21587bf8e7cSmrg for (i = 0; i < dev->resources->count_encoders; i++) { 216e88f27b3Smrg encoder = dev->resources->encoders[i].encoder; 217e88f27b3Smrg if (!encoder) 21818210155Smrg continue; 219e88f27b3Smrg 22018210155Smrg printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 22118210155Smrg encoder->encoder_id, 22218210155Smrg encoder->crtc_id, 223fe517fc9Smrg util_lookup_encoder_type_name(encoder->encoder_type), 22418210155Smrg encoder->possible_crtcs, 22518210155Smrg encoder->possible_clones); 22618210155Smrg } 22718210155Smrg printf("\n"); 22818210155Smrg} 22918210155Smrg 23087bf8e7cSmrgstatic void dump_mode(drmModeModeInfo *mode, int index) 23118210155Smrg{ 23287bf8e7cSmrg printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d", 23387bf8e7cSmrg index, 23418210155Smrg mode->name, 23587bf8e7cSmrg mode_vrefresh(mode), 23618210155Smrg mode->hdisplay, 23718210155Smrg mode->hsync_start, 23818210155Smrg mode->hsync_end, 23918210155Smrg mode->htotal, 24018210155Smrg mode->vdisplay, 24118210155Smrg mode->vsync_start, 24218210155Smrg mode->vsync_end, 2432ee35494Smrg mode->vtotal, 2442ee35494Smrg mode->clock); 245e88f27b3Smrg 246e88f27b3Smrg printf(" flags: "); 247e88f27b3Smrg mode_flag_str(mode->flags); 248e88f27b3Smrg printf("; type: "); 249e88f27b3Smrg mode_type_str(mode->type); 250e88f27b3Smrg printf("\n"); 25118210155Smrg} 25218210155Smrg 253e88f27b3Smrgstatic void dump_blob(struct device *dev, uint32_t blob_id) 254e88f27b3Smrg{ 255e88f27b3Smrg uint32_t i; 256e88f27b3Smrg unsigned char *blob_data; 257e88f27b3Smrg drmModePropertyBlobPtr blob; 258e88f27b3Smrg 259e88f27b3Smrg blob = drmModeGetPropertyBlob(dev->fd, blob_id); 2603c748557Ssnj if (!blob) { 2613c748557Ssnj printf("\n"); 262e88f27b3Smrg return; 2633c748557Ssnj } 264e88f27b3Smrg 265e88f27b3Smrg blob_data = blob->data; 266e88f27b3Smrg 267e88f27b3Smrg for (i = 0; i < blob->length; i++) { 268e88f27b3Smrg if (i % 16 == 0) 269e88f27b3Smrg printf("\n\t\t\t"); 270e88f27b3Smrg printf("%.2hhx", blob_data[i]); 271e88f27b3Smrg } 272e88f27b3Smrg printf("\n"); 273e88f27b3Smrg 274e88f27b3Smrg drmModeFreePropertyBlob(blob); 275e88f27b3Smrg} 276e88f27b3Smrg 2772b90624aSmrgstatic const char *modifier_to_string(uint64_t modifier) 2782b90624aSmrg{ 279636d5e9fSmrg static char mod_string[4096]; 280636d5e9fSmrg 281636d5e9fSmrg char *modifier_name = drmGetFormatModifierName(modifier); 282636d5e9fSmrg char *vendor_name = drmGetFormatModifierVendor(modifier); 283636d5e9fSmrg memset(mod_string, 0x00, sizeof(mod_string)); 284636d5e9fSmrg 285636d5e9fSmrg if (!modifier_name) { 286636d5e9fSmrg if (vendor_name) 287636d5e9fSmrg snprintf(mod_string, sizeof(mod_string), "%s_%s", 288636d5e9fSmrg vendor_name, "UNKNOWN_MODIFIER"); 289636d5e9fSmrg else 290636d5e9fSmrg snprintf(mod_string, sizeof(mod_string), "%s_%s", 291636d5e9fSmrg "UNKNOWN_VENDOR", "UNKNOWN_MODIFIER"); 292636d5e9fSmrg /* safe, as free is no-op for NULL */ 293636d5e9fSmrg free(vendor_name); 294636d5e9fSmrg return mod_string; 2952b90624aSmrg } 296636d5e9fSmrg 297636d5e9fSmrg if (modifier == DRM_FORMAT_MOD_LINEAR) { 298636d5e9fSmrg snprintf(mod_string, sizeof(mod_string), "%s", modifier_name); 299636d5e9fSmrg free(modifier_name); 300636d5e9fSmrg free(vendor_name); 301636d5e9fSmrg return mod_string; 302636d5e9fSmrg } 303636d5e9fSmrg 304636d5e9fSmrg snprintf(mod_string, sizeof(mod_string), "%s_%s", 305636d5e9fSmrg vendor_name, modifier_name); 306636d5e9fSmrg 307636d5e9fSmrg free(modifier_name); 308636d5e9fSmrg free(vendor_name); 309636d5e9fSmrg return mod_string; 3102b90624aSmrg} 3112b90624aSmrg 3122b90624aSmrgstatic void dump_in_formats(struct device *dev, uint32_t blob_id) 3132b90624aSmrg{ 314adfa0b0cSmrg drmModeFormatModifierIterator iter = {0}; 3152b90624aSmrg drmModePropertyBlobPtr blob; 316adfa0b0cSmrg uint32_t fmt = 0; 3172b90624aSmrg 3182b90624aSmrg printf("\t\tin_formats blob decoded:\n"); 3192b90624aSmrg blob = drmModeGetPropertyBlob(dev->fd, blob_id); 3202b90624aSmrg if (!blob) { 3212b90624aSmrg printf("\n"); 3222b90624aSmrg return; 3232b90624aSmrg } 3242b90624aSmrg 325adfa0b0cSmrg while (drmModeFormatModifierBlobIterNext(blob, &iter)) { 326adfa0b0cSmrg if (!fmt || fmt != iter.fmt) { 327adfa0b0cSmrg printf("%s\t\t\t", !fmt ? "" : "\n"); 328adfa0b0cSmrg fmt = iter.fmt; 329adfa0b0cSmrg dump_fourcc(fmt); 330adfa0b0cSmrg printf(": "); 3312b90624aSmrg } 332adfa0b0cSmrg 33348246ce7Smrg printf(" %s(0x%"PRIx64")", modifier_to_string(iter.mod), iter.mod); 3342b90624aSmrg } 3352b90624aSmrg 336adfa0b0cSmrg printf("\n"); 337adfa0b0cSmrg 3382b90624aSmrg drmModeFreePropertyBlob(blob); 3392b90624aSmrg} 3402b90624aSmrg 341e88f27b3Smrgstatic void dump_prop(struct device *dev, drmModePropertyPtr prop, 342e88f27b3Smrg uint32_t prop_id, uint64_t value) 34318210155Smrg{ 34418210155Smrg int i; 345e88f27b3Smrg printf("\t%d", prop_id); 346e88f27b3Smrg if (!prop) { 347e88f27b3Smrg printf("\n"); 348e88f27b3Smrg return; 349e88f27b3Smrg } 350e88f27b3Smrg 351e88f27b3Smrg printf(" %s:\n", prop->name); 352e88f27b3Smrg 353e88f27b3Smrg printf("\t\tflags:"); 354e88f27b3Smrg if (prop->flags & DRM_MODE_PROP_PENDING) 355e88f27b3Smrg printf(" pending"); 356e88f27b3Smrg if (prop->flags & DRM_MODE_PROP_IMMUTABLE) 357e88f27b3Smrg printf(" immutable"); 3583c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 3593c748557Ssnj printf(" signed range"); 3603c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) 3613c748557Ssnj printf(" range"); 3623c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) 363e88f27b3Smrg printf(" enum"); 3643c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) 365e88f27b3Smrg printf(" bitmask"); 3663c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 367e88f27b3Smrg printf(" blob"); 3683c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT)) 3693c748557Ssnj printf(" object"); 370e88f27b3Smrg printf("\n"); 371e88f27b3Smrg 3723c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) { 3733c748557Ssnj printf("\t\tvalues:"); 3743c748557Ssnj for (i = 0; i < prop->count_values; i++) 3753c748557Ssnj printf(" %"PRId64, U642I64(prop->values[i])); 3763c748557Ssnj printf("\n"); 3773c748557Ssnj } 3783c748557Ssnj 3793c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) { 380e88f27b3Smrg printf("\t\tvalues:"); 381e88f27b3Smrg for (i = 0; i < prop->count_values; i++) 382e88f27b3Smrg printf(" %"PRIu64, prop->values[i]); 383e88f27b3Smrg printf("\n"); 384e88f27b3Smrg } 38518210155Smrg 3863c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) { 387e88f27b3Smrg printf("\t\tenums:"); 388e88f27b3Smrg for (i = 0; i < prop->count_enums; i++) 389adfa0b0cSmrg printf(" %s=%"PRIu64, prop->enums[i].name, 39050027b5bSmrg (uint64_t)prop->enums[i].value); 391e88f27b3Smrg printf("\n"); 3923c748557Ssnj } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) { 393e88f27b3Smrg printf("\t\tvalues:"); 394e88f27b3Smrg for (i = 0; i < prop->count_enums; i++) 395e88f27b3Smrg printf(" %s=0x%llx", prop->enums[i].name, 396e88f27b3Smrg (1LL << prop->enums[i].value)); 397e88f27b3Smrg printf("\n"); 398e88f27b3Smrg } else { 399e88f27b3Smrg assert(prop->count_enums == 0); 400e88f27b3Smrg } 401e88f27b3Smrg 4023c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) { 403e88f27b3Smrg printf("\t\tblobs:\n"); 404e88f27b3Smrg for (i = 0; i < prop->count_blobs; i++) 405e88f27b3Smrg dump_blob(dev, prop->blob_ids[i]); 406e88f27b3Smrg printf("\n"); 407e88f27b3Smrg } else { 408e88f27b3Smrg assert(prop->count_blobs == 0); 40918210155Smrg } 410e88f27b3Smrg 411e88f27b3Smrg printf("\t\tvalue:"); 4123c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 413e88f27b3Smrg dump_blob(dev, value); 414fe517fc9Smrg else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 415fe517fc9Smrg printf(" %"PRId64"\n", value); 416e88f27b3Smrg else 417e88f27b3Smrg printf(" %"PRIu64"\n", value); 4182b90624aSmrg 4192b90624aSmrg if (strcmp(prop->name, "IN_FORMATS") == 0) 4202b90624aSmrg dump_in_formats(dev, value); 42118210155Smrg} 42218210155Smrg 423e88f27b3Smrgstatic void dump_connectors(struct device *dev) 42418210155Smrg{ 42518210155Smrg int i, j; 42618210155Smrg 42718210155Smrg printf("Connectors:\n"); 428fe517fc9Smrg printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n"); 42987bf8e7cSmrg for (i = 0; i < dev->resources->count_connectors; i++) { 430e88f27b3Smrg struct connector *_connector = &dev->resources->connectors[i]; 431e88f27b3Smrg drmModeConnector *connector = _connector->connector; 432e88f27b3Smrg if (!connector) 43318210155Smrg continue; 43418210155Smrg 435fe517fc9Smrg printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t", 43618210155Smrg connector->connector_id, 43718210155Smrg connector->encoder_id, 438fe517fc9Smrg util_lookup_connector_status_name(connector->connection), 439fe517fc9Smrg _connector->name, 44018210155Smrg connector->mmWidth, connector->mmHeight, 44118210155Smrg connector->count_modes); 44218210155Smrg 44322944501Smrg for (j = 0; j < connector->count_encoders; j++) 44422944501Smrg printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]); 44522944501Smrg printf("\n"); 44622944501Smrg 447e88f27b3Smrg if (connector->count_modes) { 448e88f27b3Smrg printf(" modes:\n"); 44987bf8e7cSmrg printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp " 4504b3d3f37Smrg "vss vse vtot\n"); 451e88f27b3Smrg for (j = 0; j < connector->count_modes; j++) 45287bf8e7cSmrg dump_mode(&connector->modes[j], j); 453e88f27b3Smrg } 45422944501Smrg 455e88f27b3Smrg if (_connector->props) { 456e88f27b3Smrg printf(" props:\n"); 457e88f27b3Smrg for (j = 0; j < (int)_connector->props->count_props; j++) 458e88f27b3Smrg dump_prop(dev, _connector->props_info[j], 459e88f27b3Smrg _connector->props->props[j], 460e88f27b3Smrg _connector->props->prop_values[j]); 461e88f27b3Smrg } 46218210155Smrg } 46318210155Smrg printf("\n"); 46418210155Smrg} 46518210155Smrg 466e88f27b3Smrgstatic void dump_crtcs(struct device *dev) 46718210155Smrg{ 46818210155Smrg int i; 469e88f27b3Smrg uint32_t j; 47018210155Smrg 47118210155Smrg printf("CRTCs:\n"); 47218210155Smrg printf("id\tfb\tpos\tsize\n"); 47387bf8e7cSmrg for (i = 0; i < dev->resources->count_crtcs; i++) { 474e88f27b3Smrg struct crtc *_crtc = &dev->resources->crtcs[i]; 475e88f27b3Smrg drmModeCrtc *crtc = _crtc->crtc; 476e88f27b3Smrg if (!crtc) 47718210155Smrg continue; 478e88f27b3Smrg 47918210155Smrg printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", 48018210155Smrg crtc->crtc_id, 48118210155Smrg crtc->buffer_id, 48218210155Smrg crtc->x, crtc->y, 48318210155Smrg crtc->width, crtc->height); 48487bf8e7cSmrg dump_mode(&crtc->mode, 0); 48518210155Smrg 486e88f27b3Smrg if (_crtc->props) { 487e88f27b3Smrg printf(" props:\n"); 488e88f27b3Smrg for (j = 0; j < _crtc->props->count_props; j++) 489e88f27b3Smrg dump_prop(dev, _crtc->props_info[j], 490e88f27b3Smrg _crtc->props->props[j], 491e88f27b3Smrg _crtc->props->prop_values[j]); 492e88f27b3Smrg } else { 493e88f27b3Smrg printf(" no properties found\n"); 494e88f27b3Smrg } 49518210155Smrg } 49618210155Smrg printf("\n"); 49718210155Smrg} 49818210155Smrg 499e88f27b3Smrgstatic void dump_framebuffers(struct device *dev) 50018210155Smrg{ 50118210155Smrg drmModeFB *fb; 50218210155Smrg int i; 50318210155Smrg 50418210155Smrg printf("Frame buffers:\n"); 50518210155Smrg printf("id\tsize\tpitch\n"); 50687bf8e7cSmrg for (i = 0; i < dev->resources->count_fbs; i++) { 507e88f27b3Smrg fb = dev->resources->fbs[i].fb; 508e88f27b3Smrg if (!fb) 50918210155Smrg continue; 510e88f27b3Smrg 51122944501Smrg printf("%u\t(%ux%u)\t%u\n", 51218210155Smrg fb->fb_id, 51322944501Smrg fb->width, fb->height, 51422944501Smrg fb->pitch); 51518210155Smrg } 51618210155Smrg printf("\n"); 51718210155Smrg} 51818210155Smrg 519e88f27b3Smrgstatic void dump_planes(struct device *dev) 52018210155Smrg{ 521e88f27b3Smrg unsigned int i, j; 52218210155Smrg 523e88f27b3Smrg printf("Planes:\n"); 524e88f27b3Smrg printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n"); 52518210155Smrg 52687bf8e7cSmrg for (i = 0; i < dev->resources->count_planes; i++) { 527e88f27b3Smrg struct plane *plane = &dev->resources->planes[i]; 528e88f27b3Smrg drmModePlane *ovr = plane->plane; 529e88f27b3Smrg if (!ovr) 53018210155Smrg continue; 53118210155Smrg 532e88f27b3Smrg printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", 533e88f27b3Smrg ovr->plane_id, ovr->crtc_id, ovr->fb_id, 534e88f27b3Smrg ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, 535e88f27b3Smrg ovr->gamma_size, ovr->possible_crtcs); 536e88f27b3Smrg 537e88f27b3Smrg if (!ovr->count_formats) 53818210155Smrg continue; 53918210155Smrg 540e88f27b3Smrg printf(" formats:"); 541e88f27b3Smrg for (j = 0; j < ovr->count_formats; j++) 5420655efefSmrg dump_fourcc(ovr->formats[j]); 543e88f27b3Smrg printf("\n"); 544e88f27b3Smrg 545e88f27b3Smrg if (plane->props) { 546e88f27b3Smrg printf(" props:\n"); 547e88f27b3Smrg for (j = 0; j < plane->props->count_props; j++) 548e88f27b3Smrg dump_prop(dev, plane->props_info[j], 549e88f27b3Smrg plane->props->props[j], 550e88f27b3Smrg plane->props->prop_values[j]); 551e88f27b3Smrg } else { 552e88f27b3Smrg printf(" no properties found\n"); 55318210155Smrg } 554e88f27b3Smrg } 555e88f27b3Smrg printf("\n"); 55618210155Smrg 557e88f27b3Smrg return; 558e88f27b3Smrg} 559e88f27b3Smrg 560e88f27b3Smrgstatic void free_resources(struct resources *res) 561e88f27b3Smrg{ 562fe517fc9Smrg int i; 563fe517fc9Smrg 564e88f27b3Smrg if (!res) 565e88f27b3Smrg return; 56618210155Smrg 56787bf8e7cSmrg#define free_resource(_res, type, Type) \ 568e88f27b3Smrg do { \ 569e88f27b3Smrg if (!(_res)->type##s) \ 570e88f27b3Smrg break; \ 57187bf8e7cSmrg for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 572e88f27b3Smrg if (!(_res)->type##s[i].type) \ 573e88f27b3Smrg break; \ 574e88f27b3Smrg drmModeFree##Type((_res)->type##s[i].type); \ 575e88f27b3Smrg } \ 576e88f27b3Smrg free((_res)->type##s); \ 577e88f27b3Smrg } while (0) 578e88f27b3Smrg 57987bf8e7cSmrg#define free_properties(_res, type) \ 580e88f27b3Smrg do { \ 58187bf8e7cSmrg for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 58287bf8e7cSmrg unsigned int j; \ 58387bf8e7cSmrg for (j = 0; j < res->type##s[i].props->count_props; ++j)\ 58487bf8e7cSmrg drmModeFreeProperty(res->type##s[i].props_info[j]);\ 585e88f27b3Smrg free(res->type##s[i].props_info); \ 58687bf8e7cSmrg drmModeFreeObjectProperties(res->type##s[i].props); \ 587e88f27b3Smrg } \ 588e88f27b3Smrg } while (0) 589e88f27b3Smrg 59087bf8e7cSmrg free_properties(res, plane); 59187bf8e7cSmrg free_resource(res, plane, Plane); 592fe517fc9Smrg 59387bf8e7cSmrg free_properties(res, connector); 59487bf8e7cSmrg free_properties(res, crtc); 595fe517fc9Smrg 59687bf8e7cSmrg for (i = 0; i < res->count_connectors; i++) 59787bf8e7cSmrg free(res->connectors[i].name); 598e88f27b3Smrg 59987bf8e7cSmrg free_resource(res, fb, FB); 60087bf8e7cSmrg free_resource(res, connector, Connector); 60187bf8e7cSmrg free_resource(res, encoder, Encoder); 60287bf8e7cSmrg free_resource(res, crtc, Crtc); 60318210155Smrg 604e88f27b3Smrg free(res); 605e88f27b3Smrg} 60618210155Smrg 607e88f27b3Smrgstatic struct resources *get_resources(struct device *dev) 608e88f27b3Smrg{ 60987bf8e7cSmrg drmModeRes *_res; 61087bf8e7cSmrg drmModePlaneRes *plane_res; 611e88f27b3Smrg struct resources *res; 612e88f27b3Smrg int i; 61318210155Smrg 614424e9256Smrg res = calloc(1, sizeof(*res)); 615e88f27b3Smrg if (res == 0) 616e88f27b3Smrg return NULL; 617e88f27b3Smrg 618424e9256Smrg drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 619e88f27b3Smrg 62087bf8e7cSmrg _res = drmModeGetResources(dev->fd); 62187bf8e7cSmrg if (!_res) { 622e88f27b3Smrg fprintf(stderr, "drmModeGetResources failed: %s\n", 623e88f27b3Smrg strerror(errno)); 62487bf8e7cSmrg free(res); 62587bf8e7cSmrg return NULL; 626e88f27b3Smrg } 62718210155Smrg 62887bf8e7cSmrg res->count_crtcs = _res->count_crtcs; 62987bf8e7cSmrg res->count_encoders = _res->count_encoders; 63087bf8e7cSmrg res->count_connectors = _res->count_connectors; 63187bf8e7cSmrg res->count_fbs = _res->count_fbs; 632e88f27b3Smrg 63387bf8e7cSmrg res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs)); 63487bf8e7cSmrg res->encoders = calloc(res->count_encoders, sizeof(*res->encoders)); 63587bf8e7cSmrg res->connectors = calloc(res->count_connectors, sizeof(*res->connectors)); 63687bf8e7cSmrg res->fbs = calloc(res->count_fbs, sizeof(*res->fbs)); 63787bf8e7cSmrg 63887bf8e7cSmrg if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) { 63987bf8e7cSmrg drmModeFreeResources(_res); 640e88f27b3Smrg goto error; 64187bf8e7cSmrg } 642e88f27b3Smrg 643e88f27b3Smrg#define get_resource(_res, __res, type, Type) \ 644e88f27b3Smrg do { \ 64587bf8e7cSmrg for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 64687bf8e7cSmrg uint32_t type##id = (__res)->type##s[i]; \ 64787bf8e7cSmrg (_res)->type##s[i].type = \ 64887bf8e7cSmrg drmModeGet##Type(dev->fd, type##id); \ 64987bf8e7cSmrg if (!(_res)->type##s[i].type) \ 650e88f27b3Smrg fprintf(stderr, "could not get %s %i: %s\n", \ 65187bf8e7cSmrg #type, type##id, \ 652e88f27b3Smrg strerror(errno)); \ 653e88f27b3Smrg } \ 654e88f27b3Smrg } while (0) 655e88f27b3Smrg 65687bf8e7cSmrg get_resource(res, _res, crtc, Crtc); 65787bf8e7cSmrg get_resource(res, _res, encoder, Encoder); 65887bf8e7cSmrg get_resource(res, _res, connector, Connector); 65987bf8e7cSmrg get_resource(res, _res, fb, FB); 66087bf8e7cSmrg 66187bf8e7cSmrg drmModeFreeResources(_res); 662e88f27b3Smrg 663fe517fc9Smrg /* Set the name of all connectors based on the type name and the per-type ID. */ 66487bf8e7cSmrg for (i = 0; i < res->count_connectors; i++) { 665fe517fc9Smrg struct connector *connector = &res->connectors[i]; 666fe517fc9Smrg drmModeConnector *conn = connector->connector; 6672b90624aSmrg int num; 668fe517fc9Smrg 6692b90624aSmrg num = asprintf(&connector->name, "%s-%u", 67050027b5bSmrg drmModeGetConnectorTypeName(conn->connector_type), 671fe517fc9Smrg conn->connector_type_id); 6722b90624aSmrg if (num < 0) 6732b90624aSmrg goto error; 674fe517fc9Smrg } 675fe517fc9Smrg 67687bf8e7cSmrg#define get_properties(_res, type, Type) \ 677e88f27b3Smrg do { \ 67887bf8e7cSmrg for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 679e88f27b3Smrg struct type *obj = &res->type##s[i]; \ 680e88f27b3Smrg unsigned int j; \ 681e88f27b3Smrg obj->props = \ 682e88f27b3Smrg drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 683e88f27b3Smrg DRM_MODE_OBJECT_##Type); \ 684e88f27b3Smrg if (!obj->props) { \ 685e88f27b3Smrg fprintf(stderr, \ 686e88f27b3Smrg "could not get %s %i properties: %s\n", \ 687e88f27b3Smrg #type, obj->type->type##_id, \ 688e88f27b3Smrg strerror(errno)); \ 689e88f27b3Smrg continue; \ 690e88f27b3Smrg } \ 691424e9256Smrg obj->props_info = calloc(obj->props->count_props, \ 692424e9256Smrg sizeof(*obj->props_info)); \ 693e88f27b3Smrg if (!obj->props_info) \ 694e88f27b3Smrg continue; \ 695e88f27b3Smrg for (j = 0; j < obj->props->count_props; ++j) \ 696e88f27b3Smrg obj->props_info[j] = \ 697e88f27b3Smrg drmModeGetProperty(dev->fd, obj->props->props[j]); \ 698e88f27b3Smrg } \ 699e88f27b3Smrg } while (0) 700e88f27b3Smrg 70187bf8e7cSmrg get_properties(res, crtc, CRTC); 70287bf8e7cSmrg get_properties(res, connector, CONNECTOR); 703e88f27b3Smrg 70487bf8e7cSmrg for (i = 0; i < res->count_crtcs; ++i) 705e88f27b3Smrg res->crtcs[i].mode = &res->crtcs[i].crtc->mode; 706e88f27b3Smrg 70787bf8e7cSmrg plane_res = drmModeGetPlaneResources(dev->fd); 70887bf8e7cSmrg if (!plane_res) { 709e88f27b3Smrg fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 710e88f27b3Smrg strerror(errno)); 711e88f27b3Smrg return res; 71218210155Smrg } 71318210155Smrg 71487bf8e7cSmrg res->count_planes = plane_res->count_planes; 71587bf8e7cSmrg 71687bf8e7cSmrg res->planes = calloc(res->count_planes, sizeof(*res->planes)); 71787bf8e7cSmrg if (!res->planes) { 71887bf8e7cSmrg drmModeFreePlaneResources(plane_res); 719e88f27b3Smrg goto error; 72087bf8e7cSmrg } 721e88f27b3Smrg 722e88f27b3Smrg get_resource(res, plane_res, plane, Plane); 72387bf8e7cSmrg drmModeFreePlaneResources(plane_res); 72487bf8e7cSmrg get_properties(res, plane, PLANE); 725e88f27b3Smrg 726e88f27b3Smrg return res; 727e88f27b3Smrg 728e88f27b3Smrgerror: 729e88f27b3Smrg free_resources(res); 730e88f27b3Smrg return NULL; 73118210155Smrg} 73218210155Smrg 73387bf8e7cSmrgstatic struct crtc *get_crtc_by_id(struct device *dev, uint32_t id) 734d049871aSmrg{ 735e88f27b3Smrg int i; 73618210155Smrg 73787bf8e7cSmrg for (i = 0; i < dev->resources->count_crtcs; ++i) { 738e88f27b3Smrg drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; 739e88f27b3Smrg if (crtc && crtc->crtc_id == id) 74087bf8e7cSmrg return &dev->resources->crtcs[i]; 741e88f27b3Smrg } 742d049871aSmrg 74387bf8e7cSmrg return NULL; 74487bf8e7cSmrg} 74587bf8e7cSmrg 74687bf8e7cSmrgstatic uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc) 74787bf8e7cSmrg{ 74887bf8e7cSmrg unsigned int i; 74987bf8e7cSmrg 75087bf8e7cSmrg for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) { 75187bf8e7cSmrg if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id) 75287bf8e7cSmrg return 1 << i; 75387bf8e7cSmrg } 75487bf8e7cSmrg /* Unreachable: crtc->crtc is one of resources->crtcs[] */ 75587bf8e7cSmrg /* Don't return zero or static analysers will complain */ 75687bf8e7cSmrg abort(); 75787bf8e7cSmrg return 0; 758d049871aSmrg} 759d049871aSmrg 760fe517fc9Smrgstatic drmModeConnector *get_connector_by_name(struct device *dev, const char *name) 761fe517fc9Smrg{ 762fe517fc9Smrg struct connector *connector; 763fe517fc9Smrg int i; 764fe517fc9Smrg 76587bf8e7cSmrg for (i = 0; i < dev->resources->count_connectors; i++) { 766fe517fc9Smrg connector = &dev->resources->connectors[i]; 767fe517fc9Smrg 768fe517fc9Smrg if (strcmp(connector->name, name) == 0) 769fe517fc9Smrg return connector->connector; 770fe517fc9Smrg } 771fe517fc9Smrg 772fe517fc9Smrg return NULL; 773fe517fc9Smrg} 774fe517fc9Smrg 775e88f27b3Smrgstatic drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) 77618210155Smrg{ 777e88f27b3Smrg drmModeConnector *connector; 778e88f27b3Smrg int i; 779e88f27b3Smrg 78087bf8e7cSmrg for (i = 0; i < dev->resources->count_connectors; i++) { 781e88f27b3Smrg connector = dev->resources->connectors[i].connector; 782e88f27b3Smrg if (connector && connector->connector_id == id) 783e88f27b3Smrg return connector; 784e88f27b3Smrg } 785e88f27b3Smrg 786e88f27b3Smrg return NULL; 787e88f27b3Smrg} 788e88f27b3Smrg 789e88f27b3Smrgstatic drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) 790e88f27b3Smrg{ 791e88f27b3Smrg drmModeEncoder *encoder; 792e88f27b3Smrg int i; 793e88f27b3Smrg 79487bf8e7cSmrg for (i = 0; i < dev->resources->count_encoders; i++) { 795e88f27b3Smrg encoder = dev->resources->encoders[i].encoder; 796e88f27b3Smrg if (encoder && encoder->encoder_id == id) 797e88f27b3Smrg return encoder; 798e88f27b3Smrg } 799e88f27b3Smrg 800e88f27b3Smrg return NULL; 801e88f27b3Smrg} 802e88f27b3Smrg 803e88f27b3Smrg/* ----------------------------------------------------------------------------- 804e88f27b3Smrg * Pipes and planes 805e88f27b3Smrg */ 806e88f27b3Smrg 807e88f27b3Smrg/* 808e88f27b3Smrg * Mode setting with the kernel interfaces is a bit of a chore. 809e88f27b3Smrg * First you have to find the connector in question and make sure the 810e88f27b3Smrg * requested mode is available. 811e88f27b3Smrg * Then you need to find the encoder attached to that connector so you 812e88f27b3Smrg * can bind it with a free crtc. 813e88f27b3Smrg */ 814e88f27b3Smrgstruct pipe_arg { 815fe517fc9Smrg const char **cons; 816e88f27b3Smrg uint32_t *con_ids; 817e88f27b3Smrg unsigned int num_cons; 818e88f27b3Smrg uint32_t crtc_id; 819e88f27b3Smrg char mode_str[64]; 82048246ce7Smrg char format_str[8]; /* need to leave room for "_BE" and terminating \0 */ 82187bf8e7cSmrg float vrefresh; 822e88f27b3Smrg unsigned int fourcc; 823e88f27b3Smrg drmModeModeInfo *mode; 824e88f27b3Smrg struct crtc *crtc; 825e88f27b3Smrg unsigned int fb_id[2], current_fb_id; 826e88f27b3Smrg struct timeval start; 82748246ce7Smrg unsigned int out_fb_id; 82848246ce7Smrg struct bo *out_bo; 829e88f27b3Smrg 830e88f27b3Smrg int swap_count; 831e88f27b3Smrg}; 832e88f27b3Smrg 833e88f27b3Smrgstruct plane_arg { 8342ee35494Smrg uint32_t plane_id; /* the id of plane to use */ 835e88f27b3Smrg uint32_t crtc_id; /* the id of CRTC to bind to */ 836e88f27b3Smrg bool has_position; 837e88f27b3Smrg int32_t x, y; 838e88f27b3Smrg uint32_t w, h; 839e88f27b3Smrg double scale; 840e88f27b3Smrg unsigned int fb_id; 8416260e5d5Smrg unsigned int old_fb_id; 842424e9256Smrg struct bo *bo; 8436260e5d5Smrg struct bo *old_bo; 84448246ce7Smrg char format_str[8]; /* need to leave room for "_BE" and terminating \0 */ 845e88f27b3Smrg unsigned int fourcc; 846e88f27b3Smrg}; 847e88f27b3Smrg 848e88f27b3Smrgstatic drmModeModeInfo * 849e88f27b3Smrgconnector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, 85087bf8e7cSmrg const float vrefresh) 851e88f27b3Smrg{ 852e88f27b3Smrg drmModeConnector *connector; 853e88f27b3Smrg drmModeModeInfo *mode; 854e88f27b3Smrg int i; 855e88f27b3Smrg 856e88f27b3Smrg connector = get_connector_by_id(dev, con_id); 85748246ce7Smrg if (!connector) 85848246ce7Smrg return NULL; 85948246ce7Smrg 86048246ce7Smrg if (strchr(mode_str, ',')) { 86148246ce7Smrg i = sscanf(mode_str, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", 86248246ce7Smrg &user_mode.hdisplay, &user_mode.hsync_start, 86348246ce7Smrg &user_mode.hsync_end, &user_mode.htotal, 86448246ce7Smrg &user_mode.vdisplay, &user_mode.vsync_start, 86548246ce7Smrg &user_mode.vsync_end, &user_mode.vtotal); 86648246ce7Smrg if (i == 8) { 86748246ce7Smrg user_mode.clock = roundf(user_mode.htotal * user_mode.vtotal * vrefresh / 1000); 86848246ce7Smrg user_mode.vrefresh = roundf(vrefresh); 86948246ce7Smrg snprintf(user_mode.name, sizeof(user_mode.name), "custom%dx%d", user_mode.hdisplay, user_mode.vdisplay); 87048246ce7Smrg 87148246ce7Smrg return &user_mode; 87248246ce7Smrg } 87348246ce7Smrg } 87448246ce7Smrg 87548246ce7Smrg if (!connector->count_modes) 876e88f27b3Smrg return NULL; 877e88f27b3Smrg 87887bf8e7cSmrg /* Pick by Index */ 87987bf8e7cSmrg if (mode_str[0] == '#') { 88087bf8e7cSmrg int index = atoi(mode_str + 1); 88187bf8e7cSmrg 88287bf8e7cSmrg if (index >= connector->count_modes || index < 0) 88387bf8e7cSmrg return NULL; 88487bf8e7cSmrg return &connector->modes[index]; 88587bf8e7cSmrg } 88687bf8e7cSmrg 88787bf8e7cSmrg /* Pick by Name */ 888e88f27b3Smrg for (i = 0; i < connector->count_modes; i++) { 889e88f27b3Smrg mode = &connector->modes[i]; 890e88f27b3Smrg if (!strcmp(mode->name, mode_str)) { 89187bf8e7cSmrg /* If the vertical refresh frequency is not specified 89287bf8e7cSmrg * then return the first mode that match with the name. 89387bf8e7cSmrg * Else, return the mode that match the name and 89487bf8e7cSmrg * the specified vertical refresh frequency. 895e88f27b3Smrg */ 896e88f27b3Smrg if (vrefresh == 0) 897e88f27b3Smrg return mode; 89887bf8e7cSmrg else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005) 899e88f27b3Smrg return mode; 90018210155Smrg } 901e88f27b3Smrg } 90218210155Smrg 903e88f27b3Smrg return NULL; 90418210155Smrg} 90518210155Smrg 906e88f27b3Smrgstatic struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) 90718210155Smrg{ 908e88f27b3Smrg uint32_t possible_crtcs = ~0; 909e88f27b3Smrg uint32_t active_crtcs = 0; 910e88f27b3Smrg unsigned int crtc_idx; 911e88f27b3Smrg unsigned int i; 912e88f27b3Smrg int j; 913e88f27b3Smrg 914e88f27b3Smrg for (i = 0; i < pipe->num_cons; ++i) { 915e88f27b3Smrg uint32_t crtcs_for_connector = 0; 916e88f27b3Smrg drmModeConnector *connector; 917e88f27b3Smrg drmModeEncoder *encoder; 91887bf8e7cSmrg struct crtc *crtc; 919e88f27b3Smrg 920e88f27b3Smrg connector = get_connector_by_id(dev, pipe->con_ids[i]); 921e88f27b3Smrg if (!connector) 922e88f27b3Smrg return NULL; 923e88f27b3Smrg 924e88f27b3Smrg for (j = 0; j < connector->count_encoders; ++j) { 925e88f27b3Smrg encoder = get_encoder_by_id(dev, connector->encoders[j]); 926e88f27b3Smrg if (!encoder) 927e88f27b3Smrg continue; 928e88f27b3Smrg 929e88f27b3Smrg crtcs_for_connector |= encoder->possible_crtcs; 93087bf8e7cSmrg crtc = get_crtc_by_id(dev, encoder->crtc_id); 93187bf8e7cSmrg if (!crtc) 93287bf8e7cSmrg continue; 93387bf8e7cSmrg active_crtcs |= get_crtc_mask(dev, crtc); 934e88f27b3Smrg } 93518210155Smrg 936e88f27b3Smrg possible_crtcs &= crtcs_for_connector; 93718210155Smrg } 93818210155Smrg 939e88f27b3Smrg if (!possible_crtcs) 940e88f27b3Smrg return NULL; 941e88f27b3Smrg 942e88f27b3Smrg /* Return the first possible and active CRTC if one exists, or the first 943e88f27b3Smrg * possible CRTC otherwise. 944e88f27b3Smrg */ 945e88f27b3Smrg if (possible_crtcs & active_crtcs) 946e88f27b3Smrg crtc_idx = ffs(possible_crtcs & active_crtcs); 947e88f27b3Smrg else 948e88f27b3Smrg crtc_idx = ffs(possible_crtcs); 949e88f27b3Smrg 950e88f27b3Smrg return &dev->resources->crtcs[crtc_idx - 1]; 951e88f27b3Smrg} 952e88f27b3Smrg 953e88f27b3Smrgstatic int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) 954e88f27b3Smrg{ 955e88f27b3Smrg drmModeModeInfo *mode = NULL; 956e88f27b3Smrg int i; 957e88f27b3Smrg 958e88f27b3Smrg pipe->mode = NULL; 959e88f27b3Smrg 960e88f27b3Smrg for (i = 0; i < (int)pipe->num_cons; i++) { 961e88f27b3Smrg mode = connector_find_mode(dev, pipe->con_ids[i], 962e88f27b3Smrg pipe->mode_str, pipe->vrefresh); 963e88f27b3Smrg if (mode == NULL) { 96487bf8e7cSmrg if (pipe->vrefresh) 96587bf8e7cSmrg fprintf(stderr, 96687bf8e7cSmrg "failed to find mode " 96787bf8e7cSmrg "\"%s-%.2fHz\" for connector %s\n", 96887bf8e7cSmrg pipe->mode_str, pipe->vrefresh, pipe->cons[i]); 96987bf8e7cSmrg else 97087bf8e7cSmrg fprintf(stderr, 971fe517fc9Smrg "failed to find mode \"%s\" for connector %s\n", 972fe517fc9Smrg pipe->mode_str, pipe->cons[i]); 973e88f27b3Smrg return -EINVAL; 974e88f27b3Smrg } 97518210155Smrg } 97618210155Smrg 977e88f27b3Smrg /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise 978e88f27b3Smrg * locate a CRTC that can be attached to all the connectors. 979e88f27b3Smrg */ 980e88f27b3Smrg if (pipe->crtc_id != (uint32_t)-1) { 98187bf8e7cSmrg pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id); 982e88f27b3Smrg } else { 983e88f27b3Smrg pipe->crtc = pipe_find_crtc(dev, pipe); 98487bf8e7cSmrg pipe->crtc_id = pipe->crtc->crtc->crtc_id; 98518210155Smrg } 986d049871aSmrg 987e88f27b3Smrg if (!pipe->crtc) { 988e88f27b3Smrg fprintf(stderr, "failed to find CRTC for pipe\n"); 989e88f27b3Smrg return -EINVAL; 990e88f27b3Smrg } 991d049871aSmrg 992e88f27b3Smrg pipe->mode = mode; 993e88f27b3Smrg pipe->crtc->mode = mode; 99418210155Smrg 99518210155Smrg return 0; 99618210155Smrg} 99718210155Smrg 998e88f27b3Smrg/* ----------------------------------------------------------------------------- 999e88f27b3Smrg * Properties 1000e88f27b3Smrg */ 1001e88f27b3Smrg 1002e88f27b3Smrgstruct property_arg { 1003e88f27b3Smrg uint32_t obj_id; 1004e88f27b3Smrg uint32_t obj_type; 1005e88f27b3Smrg char name[DRM_PROP_NAME_LEN+1]; 1006e88f27b3Smrg uint32_t prop_id; 1007e88f27b3Smrg uint64_t value; 1008bf6cc7dcSmrg bool optional; 1009e88f27b3Smrg}; 1010e88f27b3Smrg 1011bf6cc7dcSmrgstatic bool set_property(struct device *dev, struct property_arg *p) 101222944501Smrg{ 1013e88f27b3Smrg drmModeObjectProperties *props = NULL; 1014e88f27b3Smrg drmModePropertyRes **props_info = NULL; 1015e88f27b3Smrg const char *obj_type; 1016e88f27b3Smrg int ret; 1017e88f27b3Smrg int i; 101822944501Smrg 1019e88f27b3Smrg p->obj_type = 0; 1020e88f27b3Smrg p->prop_id = 0; 1021e88f27b3Smrg 102287bf8e7cSmrg#define find_object(_res, type, Type) \ 1023e88f27b3Smrg do { \ 102487bf8e7cSmrg for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \ 1025e88f27b3Smrg struct type *obj = &(_res)->type##s[i]; \ 1026e88f27b3Smrg if (obj->type->type##_id != p->obj_id) \ 1027e88f27b3Smrg continue; \ 1028e88f27b3Smrg p->obj_type = DRM_MODE_OBJECT_##Type; \ 1029e88f27b3Smrg obj_type = #Type; \ 1030e88f27b3Smrg props = obj->props; \ 1031e88f27b3Smrg props_info = obj->props_info; \ 1032e88f27b3Smrg } \ 1033e88f27b3Smrg } while(0) \ 1034e88f27b3Smrg 103587bf8e7cSmrg find_object(dev->resources, crtc, CRTC); 1036e88f27b3Smrg if (p->obj_type == 0) 103787bf8e7cSmrg find_object(dev->resources, connector, CONNECTOR); 1038e88f27b3Smrg if (p->obj_type == 0) 103987bf8e7cSmrg find_object(dev->resources, plane, PLANE); 1040e88f27b3Smrg if (p->obj_type == 0) { 1041e88f27b3Smrg fprintf(stderr, "Object %i not found, can't set property\n", 1042e88f27b3Smrg p->obj_id); 1043bf6cc7dcSmrg return false; 1044e88f27b3Smrg } 104522944501Smrg 1046e88f27b3Smrg if (!props) { 1047e88f27b3Smrg fprintf(stderr, "%s %i has no properties\n", 1048e88f27b3Smrg obj_type, p->obj_id); 1049bf6cc7dcSmrg return false; 105022944501Smrg } 105122944501Smrg 1052e88f27b3Smrg for (i = 0; i < (int)props->count_props; ++i) { 1053e88f27b3Smrg if (!props_info[i]) 1054e88f27b3Smrg continue; 1055e88f27b3Smrg if (strcmp(props_info[i]->name, p->name) == 0) 1056e88f27b3Smrg break; 105722944501Smrg } 105822944501Smrg 1059e88f27b3Smrg if (i == (int)props->count_props) { 1060bf6cc7dcSmrg if (!p->optional) 1061bf6cc7dcSmrg fprintf(stderr, "%s %i has no %s property\n", 1062bf6cc7dcSmrg obj_type, p->obj_id, p->name); 1063bf6cc7dcSmrg return false; 1064e88f27b3Smrg } 106522944501Smrg 1066e88f27b3Smrg p->prop_id = props->props[i]; 106722944501Smrg 10686260e5d5Smrg if (!dev->use_atomic) 10696260e5d5Smrg ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 10706260e5d5Smrg p->prop_id, p->value); 10716260e5d5Smrg else 10726260e5d5Smrg ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value); 10736260e5d5Smrg 1074e88f27b3Smrg if (ret < 0) 1075e88f27b3Smrg fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 107648246ce7Smrg obj_type, p->obj_id, p->name, p->value, strerror(-ret)); 1077bf6cc7dcSmrg 1078bf6cc7dcSmrg return true; 107922944501Smrg} 108022944501Smrg 1081e88f27b3Smrg/* -------------------------------------------------------------------------- */ 1082e88f27b3Smrg 1083e88f27b3Smrgstatic void 108422944501Smrgpage_flip_handler(int fd, unsigned int frame, 108522944501Smrg unsigned int sec, unsigned int usec, void *data) 108622944501Smrg{ 1087e88f27b3Smrg struct pipe_arg *pipe; 108822944501Smrg unsigned int new_fb_id; 108922944501Smrg struct timeval end; 109022944501Smrg double t; 109122944501Smrg 1092e88f27b3Smrg pipe = data; 1093e88f27b3Smrg if (pipe->current_fb_id == pipe->fb_id[0]) 1094e88f27b3Smrg new_fb_id = pipe->fb_id[1]; 109522944501Smrg else 1096e88f27b3Smrg new_fb_id = pipe->fb_id[0]; 1097e88f27b3Smrg 109887bf8e7cSmrg drmModePageFlip(fd, pipe->crtc_id, new_fb_id, 1099e88f27b3Smrg DRM_MODE_PAGE_FLIP_EVENT, pipe); 1100e88f27b3Smrg pipe->current_fb_id = new_fb_id; 1101e88f27b3Smrg pipe->swap_count++; 1102e88f27b3Smrg if (pipe->swap_count == 60) { 110322944501Smrg gettimeofday(&end, NULL); 110422944501Smrg t = end.tv_sec + end.tv_usec * 1e-6 - 1105e88f27b3Smrg (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); 1106e88f27b3Smrg fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); 1107e88f27b3Smrg pipe->swap_count = 0; 1108e88f27b3Smrg pipe->start = end; 110922944501Smrg } 111022944501Smrg} 111122944501Smrg 1112424e9256Smrgstatic bool format_support(const drmModePlanePtr ovr, uint32_t fmt) 1113424e9256Smrg{ 1114424e9256Smrg unsigned int i; 1115424e9256Smrg 1116424e9256Smrg for (i = 0; i < ovr->count_formats; ++i) { 1117424e9256Smrg if (ovr->formats[i] == fmt) 1118424e9256Smrg return true; 1119424e9256Smrg } 1120424e9256Smrg 1121424e9256Smrg return false; 1122424e9256Smrg} 1123424e9256Smrg 11246260e5d5Smrgstatic void add_property(struct device *dev, uint32_t obj_id, 11256260e5d5Smrg const char *name, uint64_t value) 11266260e5d5Smrg{ 11276260e5d5Smrg struct property_arg p; 11286260e5d5Smrg 11296260e5d5Smrg p.obj_id = obj_id; 11306260e5d5Smrg strcpy(p.name, name); 11316260e5d5Smrg p.value = value; 11326260e5d5Smrg 11336260e5d5Smrg set_property(dev, &p); 11346260e5d5Smrg} 11356260e5d5Smrg 1136bf6cc7dcSmrgstatic bool add_property_optional(struct device *dev, uint32_t obj_id, 1137bf6cc7dcSmrg const char *name, uint64_t value) 1138bf6cc7dcSmrg{ 1139bf6cc7dcSmrg struct property_arg p; 1140bf6cc7dcSmrg 1141bf6cc7dcSmrg p.obj_id = obj_id; 1142bf6cc7dcSmrg strcpy(p.name, name); 1143bf6cc7dcSmrg p.value = value; 1144bf6cc7dcSmrg p.optional = true; 1145bf6cc7dcSmrg 1146bf6cc7dcSmrg return set_property(dev, &p); 1147bf6cc7dcSmrg} 1148bf6cc7dcSmrg 1149bf6cc7dcSmrgstatic void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc) 1150bf6cc7dcSmrg{ 1151bf6cc7dcSmrg unsigned blob_id = 0; 115248246ce7Smrg const struct util_format_info *info; 1153bf6cc7dcSmrg /* TODO: support 1024-sized LUTs, when the use-case arises */ 1154bf6cc7dcSmrg struct drm_color_lut gamma_lut[256]; 1155bf6cc7dcSmrg int i, ret; 1156bf6cc7dcSmrg 115748246ce7Smrg info = util_format_info_find(fourcc); 115848246ce7Smrg if (info->ncolors) { 115948246ce7Smrg memset(gamma_lut, 0, sizeof(gamma_lut)); 116048246ce7Smrg /* TODO: Add index support for more patterns */ 116148246ce7Smrg util_smpte_fill_lut(info->ncolors, gamma_lut); 1162bf6cc7dcSmrg drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id); 1163bf6cc7dcSmrg } else { 116448246ce7Smrg /* 116548246ce7Smrg * Initialize gamma_lut to a linear table for the legacy API below. 116648246ce7Smrg * The modern property API resets to a linear/pass-thru table if blob_id 116748246ce7Smrg * is 0, hence no PropertyBlob is created here. 116848246ce7Smrg */ 1169bf6cc7dcSmrg for (i = 0; i < 256; i++) { 1170bf6cc7dcSmrg gamma_lut[i].red = 1171bf6cc7dcSmrg gamma_lut[i].green = 1172bf6cc7dcSmrg gamma_lut[i].blue = i << 8; 1173bf6cc7dcSmrg } 1174bf6cc7dcSmrg } 1175bf6cc7dcSmrg 1176bf6cc7dcSmrg add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0); 1177bf6cc7dcSmrg add_property_optional(dev, crtc_id, "CTM", 0); 1178bf6cc7dcSmrg if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) { 117948246ce7Smrg /* If we can't add the GAMMA_LUT property, try the legacy API. */ 1180bf6cc7dcSmrg uint16_t r[256], g[256], b[256]; 1181bf6cc7dcSmrg 1182bf6cc7dcSmrg for (i = 0; i < 256; i++) { 1183bf6cc7dcSmrg r[i] = gamma_lut[i].red; 1184bf6cc7dcSmrg g[i] = gamma_lut[i].green; 1185bf6cc7dcSmrg b[i] = gamma_lut[i].blue; 1186bf6cc7dcSmrg } 1187bf6cc7dcSmrg 1188bf6cc7dcSmrg ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b); 118948246ce7Smrg if (ret && errno != ENOSYS) 1190bf6cc7dcSmrg fprintf(stderr, "failed to set gamma: %s\n", strerror(errno)); 1191bf6cc7dcSmrg } 1192bf6cc7dcSmrg} 1193bf6cc7dcSmrg 119487bf8e7cSmrgstatic int 119587bf8e7cSmrgbo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h, 119687bf8e7cSmrg enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id) 119787bf8e7cSmrg{ 119887bf8e7cSmrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 119987bf8e7cSmrg struct bo *bo; 120087bf8e7cSmrg unsigned int fb_id; 120187bf8e7cSmrg 120287bf8e7cSmrg bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat); 120387bf8e7cSmrg 120487bf8e7cSmrg if (bo == NULL) 120587bf8e7cSmrg return -1; 120687bf8e7cSmrg 120787bf8e7cSmrg if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) { 120887bf8e7cSmrg fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno)); 120987bf8e7cSmrg bo_destroy(bo); 121087bf8e7cSmrg return -1; 121187bf8e7cSmrg } 121287bf8e7cSmrg *out_bo = bo; 121387bf8e7cSmrg *out_fb_id = fb_id; 121487bf8e7cSmrg return 0; 121587bf8e7cSmrg} 121687bf8e7cSmrg 12176260e5d5Smrgstatic int atomic_set_plane(struct device *dev, struct plane_arg *p, 12186260e5d5Smrg int pattern, bool update) 12196260e5d5Smrg{ 12206260e5d5Smrg struct bo *plane_bo; 12216260e5d5Smrg int crtc_x, crtc_y, crtc_w, crtc_h; 12226260e5d5Smrg struct crtc *crtc = NULL; 12236260e5d5Smrg unsigned int old_fb_id; 12246260e5d5Smrg 12256260e5d5Smrg /* Find an unused plane which can be connected to our CRTC. Find the 12266260e5d5Smrg * CRTC index first, then iterate over available planes. 12276260e5d5Smrg */ 122887bf8e7cSmrg crtc = get_crtc_by_id(dev, p->crtc_id); 12296260e5d5Smrg if (!crtc) { 12306260e5d5Smrg fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 12316260e5d5Smrg return -1; 12326260e5d5Smrg } 12336260e5d5Smrg 12346260e5d5Smrg if (!update) 12356260e5d5Smrg fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n", 12366260e5d5Smrg p->w, p->h, p->format_str, p->plane_id, p->crtc_id); 12376260e5d5Smrg 12386260e5d5Smrg plane_bo = p->old_bo; 12396260e5d5Smrg p->old_bo = p->bo; 12406260e5d5Smrg 12416260e5d5Smrg if (!plane_bo) { 124287bf8e7cSmrg if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h, 124387bf8e7cSmrg pattern, &plane_bo, &p->fb_id)) 12446260e5d5Smrg return -1; 12456260e5d5Smrg } 12466260e5d5Smrg 12476260e5d5Smrg p->bo = plane_bo; 12486260e5d5Smrg 12496260e5d5Smrg old_fb_id = p->fb_id; 12506260e5d5Smrg p->old_fb_id = old_fb_id; 12516260e5d5Smrg 12526260e5d5Smrg crtc_w = p->w * p->scale; 12536260e5d5Smrg crtc_h = p->h * p->scale; 12546260e5d5Smrg if (!p->has_position) { 12556260e5d5Smrg /* Default to the middle of the screen */ 12566260e5d5Smrg crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 12576260e5d5Smrg crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 12586260e5d5Smrg } else { 12596260e5d5Smrg crtc_x = p->x; 12606260e5d5Smrg crtc_y = p->y; 12616260e5d5Smrg } 12626260e5d5Smrg 12636260e5d5Smrg add_property(dev, p->plane_id, "FB_ID", p->fb_id); 12646260e5d5Smrg add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id); 12656260e5d5Smrg add_property(dev, p->plane_id, "SRC_X", 0); 12666260e5d5Smrg add_property(dev, p->plane_id, "SRC_Y", 0); 12676260e5d5Smrg add_property(dev, p->plane_id, "SRC_W", p->w << 16); 12686260e5d5Smrg add_property(dev, p->plane_id, "SRC_H", p->h << 16); 12696260e5d5Smrg add_property(dev, p->plane_id, "CRTC_X", crtc_x); 12706260e5d5Smrg add_property(dev, p->plane_id, "CRTC_Y", crtc_y); 12716260e5d5Smrg add_property(dev, p->plane_id, "CRTC_W", crtc_w); 12726260e5d5Smrg add_property(dev, p->plane_id, "CRTC_H", crtc_h); 12736260e5d5Smrg 12746260e5d5Smrg return 0; 12756260e5d5Smrg} 12766260e5d5Smrg 1277e88f27b3Smrgstatic int set_plane(struct device *dev, struct plane_arg *p) 127818210155Smrg{ 1279e88f27b3Smrg drmModePlane *ovr; 12802ee35494Smrg uint32_t plane_id; 1281e88f27b3Smrg int crtc_x, crtc_y, crtc_w, crtc_h; 1282e88f27b3Smrg struct crtc *crtc = NULL; 128387bf8e7cSmrg unsigned int i, crtc_mask; 1284e88f27b3Smrg 1285e88f27b3Smrg /* Find an unused plane which can be connected to our CRTC. Find the 1286e88f27b3Smrg * CRTC index first, then iterate over available planes. 1287e88f27b3Smrg */ 128887bf8e7cSmrg crtc = get_crtc_by_id(dev, p->crtc_id); 1289e88f27b3Smrg if (!crtc) { 1290e88f27b3Smrg fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1291e88f27b3Smrg return -1; 1292e88f27b3Smrg } 129387bf8e7cSmrg crtc_mask = get_crtc_mask(dev, crtc); 12942ee35494Smrg plane_id = p->plane_id; 12952ee35494Smrg 129687bf8e7cSmrg for (i = 0; i < dev->resources->count_planes; i++) { 1297e88f27b3Smrg ovr = dev->resources->planes[i].plane; 12982ee35494Smrg if (!ovr) 12992ee35494Smrg continue; 13002ee35494Smrg 13012ee35494Smrg if (plane_id && plane_id != ovr->plane_id) 13022ee35494Smrg continue; 13032ee35494Smrg 13042ee35494Smrg if (!format_support(ovr, p->fourcc)) 130518210155Smrg continue; 1306e88f27b3Smrg 130787bf8e7cSmrg if ((ovr->possible_crtcs & crtc_mask) && 13082b90624aSmrg (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) { 1309e88f27b3Smrg plane_id = ovr->plane_id; 13102ee35494Smrg break; 13112ee35494Smrg } 131218210155Smrg } 131318210155Smrg 131487bf8e7cSmrg if (i == dev->resources->count_planes) { 1315e88f27b3Smrg fprintf(stderr, "no unused plane available for CRTC %u\n", 131687bf8e7cSmrg p->crtc_id); 1317e88f27b3Smrg return -1; 1318e88f27b3Smrg } 1319e88f27b3Smrg 1320e88f27b3Smrg fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", 1321e88f27b3Smrg p->w, p->h, p->format_str, plane_id); 1322e88f27b3Smrg 1323e88f27b3Smrg /* just use single plane format for now.. */ 132487bf8e7cSmrg if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h, 132587bf8e7cSmrg secondary_fill, &p->bo, &p->fb_id)) 1326e88f27b3Smrg return -1; 1327e88f27b3Smrg 1328e88f27b3Smrg crtc_w = p->w * p->scale; 1329e88f27b3Smrg crtc_h = p->h * p->scale; 1330e88f27b3Smrg if (!p->has_position) { 1331e88f27b3Smrg /* Default to the middle of the screen */ 1332e88f27b3Smrg crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1333e88f27b3Smrg crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1334e88f27b3Smrg } else { 1335e88f27b3Smrg crtc_x = p->x; 1336e88f27b3Smrg crtc_y = p->y; 133718210155Smrg } 133818210155Smrg 1339e88f27b3Smrg /* note src coords (last 4 args) are in Q16 format */ 134087bf8e7cSmrg if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id, 134187bf8e7cSmrg 0, crtc_x, crtc_y, crtc_w, crtc_h, 1342e88f27b3Smrg 0, 0, p->w << 16, p->h << 16)) { 1343e88f27b3Smrg fprintf(stderr, "failed to enable plane: %s\n", 1344e88f27b3Smrg strerror(errno)); 1345e88f27b3Smrg return -1; 1346e88f27b3Smrg } 1347e88f27b3Smrg 134887bf8e7cSmrg ovr->crtc_id = p->crtc_id; 1349e88f27b3Smrg 1350e88f27b3Smrg return 0; 1351e88f27b3Smrg} 1352e88f27b3Smrg 13536260e5d5Smrgstatic void atomic_set_planes(struct device *dev, struct plane_arg *p, 13546260e5d5Smrg unsigned int count, bool update) 13556260e5d5Smrg{ 1356bf6cc7dcSmrg unsigned int i, pattern = primary_fill; 13576260e5d5Smrg 13586260e5d5Smrg /* set up planes */ 13596260e5d5Smrg for (i = 0; i < count; i++) { 13606260e5d5Smrg if (i > 0) 1361bf6cc7dcSmrg pattern = secondary_fill; 1362bf6cc7dcSmrg else 1363bf6cc7dcSmrg set_gamma(dev, p[i].crtc_id, p[i].fourcc); 13646260e5d5Smrg 13656260e5d5Smrg if (atomic_set_plane(dev, &p[i], pattern, update)) 13666260e5d5Smrg return; 13676260e5d5Smrg } 13686260e5d5Smrg} 13696260e5d5Smrg 137087bf8e7cSmrgstatic void 137187bf8e7cSmrgatomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args, 137287bf8e7cSmrg struct plane_arg *plane_args, unsigned int plane_count) 137387bf8e7cSmrg{ 137487bf8e7cSmrg int ret; 137587bf8e7cSmrg 137687bf8e7cSmrg gettimeofday(&pipe_args->start, NULL); 137787bf8e7cSmrg pipe_args->swap_count = 0; 137887bf8e7cSmrg 137987bf8e7cSmrg while (true) { 138087bf8e7cSmrg drmModeAtomicFree(dev->req); 138187bf8e7cSmrg dev->req = drmModeAtomicAlloc(); 138287bf8e7cSmrg atomic_set_planes(dev, plane_args, plane_count, true); 138387bf8e7cSmrg 138487bf8e7cSmrg ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 138587bf8e7cSmrg if (ret) { 138687bf8e7cSmrg fprintf(stderr, "Atomic Commit failed [2]\n"); 138787bf8e7cSmrg return; 138887bf8e7cSmrg } 138987bf8e7cSmrg 139087bf8e7cSmrg pipe_args->swap_count++; 139187bf8e7cSmrg if (pipe_args->swap_count == 60) { 139287bf8e7cSmrg struct timeval end; 139387bf8e7cSmrg double t; 139487bf8e7cSmrg 139587bf8e7cSmrg gettimeofday(&end, NULL); 139687bf8e7cSmrg t = end.tv_sec + end.tv_usec * 1e-6 - 139787bf8e7cSmrg (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6); 139887bf8e7cSmrg fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t); 139987bf8e7cSmrg pipe_args->swap_count = 0; 140087bf8e7cSmrg pipe_args->start = end; 140187bf8e7cSmrg } 140287bf8e7cSmrg } 140387bf8e7cSmrg} 140487bf8e7cSmrg 14056260e5d5Smrgstatic void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 14066260e5d5Smrg{ 14076260e5d5Smrg unsigned int i; 14086260e5d5Smrg 14096260e5d5Smrg for (i = 0; i < count; i++) { 14106260e5d5Smrg add_property(dev, p[i].plane_id, "FB_ID", 0); 14116260e5d5Smrg add_property(dev, p[i].plane_id, "CRTC_ID", 0); 14126260e5d5Smrg add_property(dev, p[i].plane_id, "SRC_X", 0); 14136260e5d5Smrg add_property(dev, p[i].plane_id, "SRC_Y", 0); 14146260e5d5Smrg add_property(dev, p[i].plane_id, "SRC_W", 0); 14156260e5d5Smrg add_property(dev, p[i].plane_id, "SRC_H", 0); 14166260e5d5Smrg add_property(dev, p[i].plane_id, "CRTC_X", 0); 14176260e5d5Smrg add_property(dev, p[i].plane_id, "CRTC_Y", 0); 14186260e5d5Smrg add_property(dev, p[i].plane_id, "CRTC_W", 0); 14196260e5d5Smrg add_property(dev, p[i].plane_id, "CRTC_H", 0); 14206260e5d5Smrg } 14216260e5d5Smrg} 14226260e5d5Smrg 14236260e5d5Smrgstatic void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count) 14246260e5d5Smrg{ 14256260e5d5Smrg unsigned int i; 14266260e5d5Smrg 14276260e5d5Smrg for (i = 0; i < count; i++) { 14286260e5d5Smrg if (p[i].fb_id) { 14296260e5d5Smrg drmModeRmFB(dev->fd, p[i].fb_id); 14306260e5d5Smrg p[i].fb_id = 0; 14316260e5d5Smrg } 14326260e5d5Smrg if (p[i].old_fb_id) { 14336260e5d5Smrg drmModeRmFB(dev->fd, p[i].old_fb_id); 14346260e5d5Smrg p[i].old_fb_id = 0; 14356260e5d5Smrg } 14366260e5d5Smrg if (p[i].bo) { 14376260e5d5Smrg bo_destroy(p[i].bo); 14386260e5d5Smrg p[i].bo = NULL; 14396260e5d5Smrg } 14406260e5d5Smrg if (p[i].old_bo) { 14416260e5d5Smrg bo_destroy(p[i].old_bo); 14426260e5d5Smrg p[i].old_bo = NULL; 14436260e5d5Smrg } 14446260e5d5Smrg 14456260e5d5Smrg } 14466260e5d5Smrg} 14476260e5d5Smrg 1448424e9256Smrgstatic void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1449424e9256Smrg{ 1450424e9256Smrg unsigned int i; 1451424e9256Smrg 1452424e9256Smrg for (i = 0; i < count; i++) { 1453424e9256Smrg if (p[i].fb_id) 1454424e9256Smrg drmModeRmFB(dev->fd, p[i].fb_id); 1455424e9256Smrg if (p[i].bo) 1456424e9256Smrg bo_destroy(p[i].bo); 1457424e9256Smrg } 1458424e9256Smrg} 1459424e9256Smrg 146087bf8e7cSmrgstatic int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe) 14616260e5d5Smrg{ 146287bf8e7cSmrg drmModeConnector *connector; 14636260e5d5Smrg unsigned int i; 146487bf8e7cSmrg uint32_t id; 146587bf8e7cSmrg char *endp; 14666260e5d5Smrg 146787bf8e7cSmrg for (i = 0; i < pipe->num_cons; i++) { 146887bf8e7cSmrg id = strtoul(pipe->cons[i], &endp, 10); 146987bf8e7cSmrg if (endp == pipe->cons[i]) { 147087bf8e7cSmrg connector = get_connector_by_name(dev, pipe->cons[i]); 147187bf8e7cSmrg if (!connector) { 147287bf8e7cSmrg fprintf(stderr, "no connector named '%s'\n", 147387bf8e7cSmrg pipe->cons[i]); 147487bf8e7cSmrg return -ENODEV; 147587bf8e7cSmrg } 14766260e5d5Smrg 147787bf8e7cSmrg id = connector->connector_id; 147887bf8e7cSmrg } 147987bf8e7cSmrg 148087bf8e7cSmrg pipe->con_ids[i] = id; 148187bf8e7cSmrg } 148287bf8e7cSmrg 148387bf8e7cSmrg return 0; 148487bf8e7cSmrg} 148587bf8e7cSmrg 148648246ce7Smrgstatic bool pipe_has_writeback_connector(struct device *dev, struct pipe_arg *pipes, 148748246ce7Smrg unsigned int count) 148848246ce7Smrg{ 148948246ce7Smrg drmModeConnector *connector; 149048246ce7Smrg unsigned int i, j; 149148246ce7Smrg 149248246ce7Smrg for (j = 0; j < count; j++) { 149348246ce7Smrg struct pipe_arg *pipe = &pipes[j]; 149448246ce7Smrg 149548246ce7Smrg for (i = 0; i < pipe->num_cons; i++) { 149648246ce7Smrg connector = get_connector_by_id(dev, pipe->con_ids[i]); 149748246ce7Smrg if (connector && connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 149848246ce7Smrg return true; 149948246ce7Smrg } 150048246ce7Smrg } 150148246ce7Smrg return false; 150248246ce7Smrg} 150348246ce7Smrg 150487bf8e7cSmrgstatic int pipe_attempt_connector(struct device *dev, drmModeConnector *con, 150587bf8e7cSmrg struct pipe_arg *pipe) 150687bf8e7cSmrg{ 150787bf8e7cSmrg char *con_str; 150887bf8e7cSmrg int i; 150987bf8e7cSmrg 151087bf8e7cSmrg con_str = calloc(8, sizeof(char)); 151187bf8e7cSmrg if (!con_str) 151287bf8e7cSmrg return -1; 151387bf8e7cSmrg 151487bf8e7cSmrg sprintf(con_str, "%d", con->connector_id); 151587bf8e7cSmrg strcpy(pipe->format_str, "XR24"); 151687bf8e7cSmrg pipe->fourcc = util_format_fourcc(pipe->format_str); 151787bf8e7cSmrg pipe->num_cons = 1; 151887bf8e7cSmrg pipe->con_ids = calloc(1, sizeof(*pipe->con_ids)); 151987bf8e7cSmrg pipe->cons = calloc(1, sizeof(*pipe->cons)); 152087bf8e7cSmrg 152187bf8e7cSmrg if (!pipe->con_ids || !pipe->cons) 152287bf8e7cSmrg goto free_con_str; 152387bf8e7cSmrg 152487bf8e7cSmrg pipe->con_ids[0] = con->connector_id; 152587bf8e7cSmrg pipe->cons[0] = (const char*)con_str; 152687bf8e7cSmrg 152787bf8e7cSmrg pipe->crtc = pipe_find_crtc(dev, pipe); 152887bf8e7cSmrg if (!pipe->crtc) 152987bf8e7cSmrg goto free_all; 153087bf8e7cSmrg 153187bf8e7cSmrg pipe->crtc_id = pipe->crtc->crtc->crtc_id; 153287bf8e7cSmrg 153387bf8e7cSmrg /* Return the first mode if no preferred. */ 153487bf8e7cSmrg pipe->mode = &con->modes[0]; 153587bf8e7cSmrg 153687bf8e7cSmrg for (i = 0; i < con->count_modes; i++) { 153787bf8e7cSmrg drmModeModeInfo *current_mode = &con->modes[i]; 153887bf8e7cSmrg 153987bf8e7cSmrg if (current_mode->type & DRM_MODE_TYPE_PREFERRED) { 154087bf8e7cSmrg pipe->mode = current_mode; 154187bf8e7cSmrg break; 154287bf8e7cSmrg } 154387bf8e7cSmrg } 154487bf8e7cSmrg 154587bf8e7cSmrg sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay); 154687bf8e7cSmrg 154787bf8e7cSmrg return 0; 154887bf8e7cSmrg 154987bf8e7cSmrgfree_all: 155087bf8e7cSmrg free(pipe->cons); 155187bf8e7cSmrg free(pipe->con_ids); 155287bf8e7cSmrgfree_con_str: 155387bf8e7cSmrg free(con_str); 155487bf8e7cSmrg return -1; 155587bf8e7cSmrg} 155687bf8e7cSmrg 155787bf8e7cSmrgstatic int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes) 155887bf8e7cSmrg{ 155987bf8e7cSmrg struct pipe_arg *pipes; 156087bf8e7cSmrg struct resources *res = dev->resources; 156187bf8e7cSmrg drmModeConnector *con = NULL; 156287bf8e7cSmrg int i, connected = 0, attempted = 0; 156387bf8e7cSmrg 156487bf8e7cSmrg for (i = 0; i < res->count_connectors; i++) { 156587bf8e7cSmrg con = res->connectors[i].connector; 156648246ce7Smrg if (!con || con->connection != DRM_MODE_CONNECTED || 156748246ce7Smrg con->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 15686260e5d5Smrg continue; 156987bf8e7cSmrg connected++; 157087bf8e7cSmrg } 157187bf8e7cSmrg if (!connected) { 157287bf8e7cSmrg printf("no connected connector!\n"); 157387bf8e7cSmrg return 0; 15746260e5d5Smrg } 15756260e5d5Smrg 157687bf8e7cSmrg pipes = calloc(connected, sizeof(struct pipe_arg)); 157787bf8e7cSmrg if (!pipes) 157887bf8e7cSmrg return 0; 15796260e5d5Smrg 158087bf8e7cSmrg for (i = 0; i < res->count_connectors && attempted < connected; i++) { 158187bf8e7cSmrg con = res->connectors[i].connector; 158287bf8e7cSmrg if (!con || con->connection != DRM_MODE_CONNECTED) 15836260e5d5Smrg continue; 15846260e5d5Smrg 158587bf8e7cSmrg if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) { 158687bf8e7cSmrg printf("failed fetching preferred mode for connector\n"); 158787bf8e7cSmrg continue; 15886260e5d5Smrg } 158987bf8e7cSmrg attempted++; 15906260e5d5Smrg } 159187bf8e7cSmrg 159287bf8e7cSmrg *out_pipes = pipes; 159387bf8e7cSmrg return attempted; 15946260e5d5Smrg} 15956260e5d5Smrg 159687bf8e7cSmrgstatic struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc) 15976260e5d5Smrg{ 15986260e5d5Smrg unsigned int i; 15996260e5d5Smrg 160087bf8e7cSmrg for (i = 0; i < dev->resources->count_planes; i++) { 160187bf8e7cSmrg struct plane *plane = &dev->resources->planes[i]; 160287bf8e7cSmrg drmModePlane *ovr = plane->plane; 160387bf8e7cSmrg if (!ovr) 16046260e5d5Smrg continue; 16056260e5d5Smrg 160687bf8e7cSmrg // XXX: add is_primary_plane and (?) format checks 16076260e5d5Smrg 160887bf8e7cSmrg if (ovr->possible_crtcs & get_crtc_mask(dev, crtc)) 160987bf8e7cSmrg return plane; 16106260e5d5Smrg } 161187bf8e7cSmrg return NULL; 16126260e5d5Smrg} 1613424e9256Smrg 161448246ce7Smrgstatic unsigned int set_mode(struct device *dev, struct pipe_arg **pipe_args, unsigned int count) 1615e88f27b3Smrg{ 161687bf8e7cSmrg unsigned int i, j; 161787bf8e7cSmrg int ret, x = 0; 161887bf8e7cSmrg int preferred = count == 0; 161948246ce7Smrg struct pipe_arg *pipes; 1620e88f27b3Smrg 162187bf8e7cSmrg if (preferred) { 162248246ce7Smrg count = pipe_find_preferred(dev, pipe_args); 162387bf8e7cSmrg if (!count) { 162487bf8e7cSmrg fprintf(stderr, "can't find any preferred connector/mode.\n"); 162548246ce7Smrg return 0; 162648246ce7Smrg } 162748246ce7Smrg 162848246ce7Smrg pipes = *pipe_args; 162948246ce7Smrg } else { 163048246ce7Smrg pipes = *pipe_args; 163148246ce7Smrg 163248246ce7Smrg for (i = 0; i < count; i++) { 163348246ce7Smrg struct pipe_arg *pipe = &pipes[i]; 163448246ce7Smrg 163548246ce7Smrg ret = pipe_resolve_connectors(dev, pipe); 163648246ce7Smrg if (ret < 0) 163748246ce7Smrg return 0; 163848246ce7Smrg 163948246ce7Smrg ret = pipe_find_crtc_and_mode(dev, pipe); 164048246ce7Smrg if (ret < 0) 164148246ce7Smrg continue; 164287bf8e7cSmrg } 1643e88f27b3Smrg } 1644e88f27b3Smrg 164587bf8e7cSmrg if (!dev->use_atomic) { 164687bf8e7cSmrg for (i = 0; i < count; i++) { 164787bf8e7cSmrg struct pipe_arg *pipe = &pipes[i]; 164818210155Smrg 164987bf8e7cSmrg if (pipe->mode == NULL) 165087bf8e7cSmrg continue; 1651424e9256Smrg 165287bf8e7cSmrg if (!preferred) { 165387bf8e7cSmrg dev->mode.width += pipe->mode->hdisplay; 165487bf8e7cSmrg if (dev->mode.height < pipe->mode->vdisplay) 165587bf8e7cSmrg dev->mode.height = pipe->mode->vdisplay; 165687bf8e7cSmrg } else { 165787bf8e7cSmrg /* XXX: Use a clone mode, more like atomic. We could do per 165887bf8e7cSmrg * connector bo/fb, so we don't have the stretched image. 165987bf8e7cSmrg */ 166087bf8e7cSmrg if (dev->mode.width < pipe->mode->hdisplay) 166187bf8e7cSmrg dev->mode.width = pipe->mode->hdisplay; 166287bf8e7cSmrg if (dev->mode.height < pipe->mode->vdisplay) 166387bf8e7cSmrg dev->mode.height = pipe->mode->vdisplay; 166487bf8e7cSmrg } 166587bf8e7cSmrg } 166618210155Smrg 166787bf8e7cSmrg if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height, 166887bf8e7cSmrg primary_fill, &dev->mode.bo, &dev->mode.fb_id)) 166948246ce7Smrg return 0; 167087bf8e7cSmrg } 1671424e9256Smrg 167218210155Smrg for (i = 0; i < count; i++) { 1673e88f27b3Smrg struct pipe_arg *pipe = &pipes[i]; 167487bf8e7cSmrg uint32_t blob_id; 1675e88f27b3Smrg 1676e88f27b3Smrg if (pipe->mode == NULL) 167718210155Smrg continue; 167818210155Smrg 167987bf8e7cSmrg printf("setting mode %s-%.2fHz on connectors ", 168087bf8e7cSmrg pipe->mode->name, mode_vrefresh(pipe->mode)); 168187bf8e7cSmrg for (j = 0; j < pipe->num_cons; ++j) { 1682fe517fc9Smrg printf("%s, ", pipe->cons[j]); 168387bf8e7cSmrg if (dev->use_atomic) 168487bf8e7cSmrg add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id); 168587bf8e7cSmrg } 168687bf8e7cSmrg printf("crtc %d\n", pipe->crtc_id); 1687e88f27b3Smrg 168887bf8e7cSmrg if (!dev->use_atomic) { 168987bf8e7cSmrg ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id, 169087bf8e7cSmrg x, 0, pipe->con_ids, pipe->num_cons, 169187bf8e7cSmrg pipe->mode); 1692e88f27b3Smrg 169387bf8e7cSmrg /* XXX: Actually check if this is needed */ 169487bf8e7cSmrg drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0); 169518210155Smrg 169687bf8e7cSmrg if (!preferred) 169787bf8e7cSmrg x += pipe->mode->hdisplay; 169818210155Smrg 169987bf8e7cSmrg if (ret) { 170087bf8e7cSmrg fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 170148246ce7Smrg return 0; 170287bf8e7cSmrg } 170387bf8e7cSmrg 170487bf8e7cSmrg set_gamma(dev, pipe->crtc_id, pipe->fourcc); 170587bf8e7cSmrg } else { 170687bf8e7cSmrg drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id); 170787bf8e7cSmrg add_property(dev, pipe->crtc_id, "MODE_ID", blob_id); 170887bf8e7cSmrg add_property(dev, pipe->crtc_id, "ACTIVE", 1); 170987bf8e7cSmrg 171087bf8e7cSmrg /* By default atomic modeset does not set a primary plane, shrug */ 171187bf8e7cSmrg if (preferred) { 171287bf8e7cSmrg struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc); 171387bf8e7cSmrg struct plane_arg plane_args = { 171487bf8e7cSmrg .plane_id = plane->plane->plane_id, 171587bf8e7cSmrg .crtc_id = pipe->crtc_id, 171687bf8e7cSmrg .w = pipe->mode->hdisplay, 171787bf8e7cSmrg .h = pipe->mode->vdisplay, 171887bf8e7cSmrg .scale = 1.0, 171987bf8e7cSmrg .format_str = "XR24", 172087bf8e7cSmrg .fourcc = util_format_fourcc(pipe->format_str), 172187bf8e7cSmrg }; 172287bf8e7cSmrg 172387bf8e7cSmrg atomic_set_planes(dev, &plane_args, 1, false); 172487bf8e7cSmrg } 172518210155Smrg } 172687bf8e7cSmrg } 172748246ce7Smrg 172848246ce7Smrg return count; 172948246ce7Smrg} 173048246ce7Smrg 173148246ce7Smrgstatic void writeback_config(struct device *dev, struct pipe_arg *pipes, unsigned int count) 173248246ce7Smrg{ 173348246ce7Smrg drmModeConnector *connector; 173448246ce7Smrg unsigned int i, j; 173548246ce7Smrg 173648246ce7Smrg for (j = 0; j < count; j++) { 173748246ce7Smrg struct pipe_arg *pipe = &pipes[j]; 173848246ce7Smrg 173948246ce7Smrg for (i = 0; i < pipe->num_cons; i++) { 174048246ce7Smrg connector = get_connector_by_id(dev, pipe->con_ids[i]); 174148246ce7Smrg if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { 174248246ce7Smrg if (!pipe->mode) { 174348246ce7Smrg fprintf(stderr, "no mode for writeback\n"); 174448246ce7Smrg return; 174548246ce7Smrg } 174648246ce7Smrg bo_fb_create(dev->fd, pipes[j].fourcc, 174748246ce7Smrg pipe->mode->hdisplay, pipe->mode->vdisplay, 174848246ce7Smrg UTIL_PATTERN_PLAIN, 174948246ce7Smrg &pipe->out_bo, &pipe->out_fb_id); 175048246ce7Smrg add_property(dev, pipe->con_ids[i], "WRITEBACK_FB_ID", 175148246ce7Smrg pipe->out_fb_id); 175248246ce7Smrg add_property(dev, pipe->con_ids[i], "WRITEBACK_OUT_FENCE_PTR", 175348246ce7Smrg (uintptr_t)(&dev->writeback_fence_fd)); 175448246ce7Smrg } 175548246ce7Smrg } 175648246ce7Smrg } 175748246ce7Smrg} 175848246ce7Smrg 175948246ce7Smrgstatic int poll_writeback_fence(int fd, int timeout) 176048246ce7Smrg{ 176148246ce7Smrg struct pollfd fds = { fd, POLLIN }; 176248246ce7Smrg int ret; 176348246ce7Smrg 176448246ce7Smrg do { 176548246ce7Smrg ret = poll(&fds, 1, timeout); 176648246ce7Smrg if (ret > 0) { 176748246ce7Smrg if (fds.revents & (POLLERR | POLLNVAL)) 176848246ce7Smrg return -EINVAL; 176948246ce7Smrg 177048246ce7Smrg return 0; 177148246ce7Smrg } else if (ret == 0) { 177248246ce7Smrg return -ETIMEDOUT; 177348246ce7Smrg } else { 177448246ce7Smrg ret = -errno; 177548246ce7Smrg if (ret == -EINTR || ret == -EAGAIN) 177648246ce7Smrg continue; 177748246ce7Smrg return ret; 177848246ce7Smrg } 177948246ce7Smrg } while (1); 178048246ce7Smrg 178148246ce7Smrg} 178248246ce7Smrg 178348246ce7Smrgstatic void dump_output_fb(struct device *dev, struct pipe_arg *pipes, char *dump_path, 178448246ce7Smrg unsigned int count) 178548246ce7Smrg{ 178648246ce7Smrg drmModeConnector *connector; 178748246ce7Smrg unsigned int i, j; 178848246ce7Smrg 178948246ce7Smrg for (j = 0; j < count; j++) { 179048246ce7Smrg struct pipe_arg *pipe = &pipes[j]; 179148246ce7Smrg 179248246ce7Smrg for (i = 0; i < pipe->num_cons; i++) { 179348246ce7Smrg connector = get_connector_by_id(dev, pipe->con_ids[i]); 179448246ce7Smrg if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 179548246ce7Smrg bo_dump(pipe->out_bo, dump_path); 179648246ce7Smrg } 179748246ce7Smrg } 179887bf8e7cSmrg} 179987bf8e7cSmrg 180087bf8e7cSmrgstatic void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 180187bf8e7cSmrg{ 180287bf8e7cSmrg unsigned int i; 180387bf8e7cSmrg unsigned int j; 180487bf8e7cSmrg 180587bf8e7cSmrg for (i = 0; i < count; i++) { 180687bf8e7cSmrg struct pipe_arg *pipe = &pipes[i]; 1807bf6cc7dcSmrg 180887bf8e7cSmrg if (pipe->mode == NULL) 180987bf8e7cSmrg continue; 181087bf8e7cSmrg 181187bf8e7cSmrg for (j = 0; j < pipe->num_cons; ++j) 181287bf8e7cSmrg add_property(dev, pipe->con_ids[j], "CRTC_ID",0); 181387bf8e7cSmrg 181487bf8e7cSmrg add_property(dev, pipe->crtc_id, "MODE_ID", 0); 181587bf8e7cSmrg add_property(dev, pipe->crtc_id, "ACTIVE", 0); 181618210155Smrg } 1817424e9256Smrg} 181822944501Smrg 1819424e9256Smrgstatic void clear_mode(struct device *dev) 1820424e9256Smrg{ 1821424e9256Smrg if (dev->mode.fb_id) 1822424e9256Smrg drmModeRmFB(dev->fd, dev->mode.fb_id); 1823424e9256Smrg if (dev->mode.bo) 1824424e9256Smrg bo_destroy(dev->mode.bo); 1825e88f27b3Smrg} 182622944501Smrg 1827e88f27b3Smrgstatic void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1828e88f27b3Smrg{ 1829e88f27b3Smrg unsigned int i; 1830e88f27b3Smrg 1831e88f27b3Smrg /* set up planes/overlays */ 1832e88f27b3Smrg for (i = 0; i < count; i++) 1833e88f27b3Smrg if (set_plane(dev, &p[i])) 1834e88f27b3Smrg return; 1835e88f27b3Smrg} 1836e88f27b3Smrg 1837a7d7de1eSmrgstatic void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1838a7d7de1eSmrg{ 1839424e9256Smrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1840adfa0b0cSmrg uint32_t cw = 64; 1841adfa0b0cSmrg uint32_t ch = 64; 18423c748557Ssnj struct bo *bo; 1843adfa0b0cSmrg uint64_t value; 1844a7d7de1eSmrg unsigned int i; 1845a7d7de1eSmrg int ret; 1846a7d7de1eSmrg 1847adfa0b0cSmrg ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &value); 1848adfa0b0cSmrg if (!ret) 1849adfa0b0cSmrg cw = value; 1850adfa0b0cSmrg 1851adfa0b0cSmrg ret = drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &value); 1852adfa0b0cSmrg if (!ret) 1853adfa0b0cSmrg ch = value; 1854adfa0b0cSmrg 1855a7d7de1eSmrg 1856a7d7de1eSmrg /* create cursor bo.. just using PATTERN_PLAIN as it has 1857a7d7de1eSmrg * translucent alpha 1858a7d7de1eSmrg */ 18593c748557Ssnj bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches, 1860fe517fc9Smrg offsets, UTIL_PATTERN_PLAIN); 1861a7d7de1eSmrg if (bo == NULL) 1862a7d7de1eSmrg return; 1863a7d7de1eSmrg 1864424e9256Smrg dev->mode.cursor_bo = bo; 1865424e9256Smrg 1866a7d7de1eSmrg for (i = 0; i < count; i++) { 1867a7d7de1eSmrg struct pipe_arg *pipe = &pipes[i]; 1868a7d7de1eSmrg ret = cursor_init(dev->fd, handles[0], 186987bf8e7cSmrg pipe->crtc_id, 1870a7d7de1eSmrg pipe->mode->hdisplay, pipe->mode->vdisplay, 1871a7d7de1eSmrg cw, ch); 1872a7d7de1eSmrg if (ret) { 1873a7d7de1eSmrg fprintf(stderr, "failed to init cursor for CRTC[%u]\n", 1874a7d7de1eSmrg pipe->crtc_id); 1875a7d7de1eSmrg return; 1876a7d7de1eSmrg } 1877a7d7de1eSmrg } 1878a7d7de1eSmrg 1879a7d7de1eSmrg cursor_start(); 1880a7d7de1eSmrg} 1881a7d7de1eSmrg 1882a7d7de1eSmrgstatic void clear_cursors(struct device *dev) 1883a7d7de1eSmrg{ 1884a7d7de1eSmrg cursor_stop(); 1885424e9256Smrg 1886424e9256Smrg if (dev->mode.cursor_bo) 1887424e9256Smrg bo_destroy(dev->mode.cursor_bo); 1888a7d7de1eSmrg} 1889a7d7de1eSmrg 1890e88f27b3Smrgstatic void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1891e88f27b3Smrg{ 1892e88f27b3Smrg unsigned int other_fb_id; 18933c748557Ssnj struct bo *other_bo; 1894e88f27b3Smrg drmEventContext evctx; 1895e88f27b3Smrg unsigned int i; 1896e88f27b3Smrg int ret; 1897e88f27b3Smrg 189887bf8e7cSmrg if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height, 189987bf8e7cSmrg UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id)) 190022944501Smrg return; 190122944501Smrg 190222944501Smrg for (i = 0; i < count; i++) { 1903e88f27b3Smrg struct pipe_arg *pipe = &pipes[i]; 1904e88f27b3Smrg 1905e88f27b3Smrg if (pipe->mode == NULL) 190622944501Smrg continue; 190722944501Smrg 190887bf8e7cSmrg ret = drmModePageFlip(dev->fd, pipe->crtc_id, 1909e88f27b3Smrg other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 1910e88f27b3Smrg pipe); 1911e88f27b3Smrg if (ret) { 1912e88f27b3Smrg fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 1913424e9256Smrg goto err_rmfb; 1914e88f27b3Smrg } 1915e88f27b3Smrg gettimeofday(&pipe->start, NULL); 1916e88f27b3Smrg pipe->swap_count = 0; 1917e88f27b3Smrg pipe->fb_id[0] = dev->mode.fb_id; 1918e88f27b3Smrg pipe->fb_id[1] = other_fb_id; 1919e88f27b3Smrg pipe->current_fb_id = other_fb_id; 192022944501Smrg } 192122944501Smrg 192222944501Smrg memset(&evctx, 0, sizeof evctx); 192322944501Smrg evctx.version = DRM_EVENT_CONTEXT_VERSION; 192422944501Smrg evctx.vblank_handler = NULL; 192522944501Smrg evctx.page_flip_handler = page_flip_handler; 1926fe517fc9Smrg 192722944501Smrg while (1) { 192822944501Smrg#if 0 192922944501Smrg struct pollfd pfd[2]; 193022944501Smrg 193122944501Smrg pfd[0].fd = 0; 193222944501Smrg pfd[0].events = POLLIN; 193322944501Smrg pfd[1].fd = fd; 193422944501Smrg pfd[1].events = POLLIN; 193522944501Smrg 193622944501Smrg if (poll(pfd, 2, -1) < 0) { 193722944501Smrg fprintf(stderr, "poll error\n"); 193822944501Smrg break; 193922944501Smrg } 194022944501Smrg 194122944501Smrg if (pfd[0].revents) 194222944501Smrg break; 194322944501Smrg#else 194422944501Smrg struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 194522944501Smrg fd_set fds; 194622944501Smrg 194722944501Smrg FD_ZERO(&fds); 194822944501Smrg FD_SET(0, &fds); 1949e88f27b3Smrg FD_SET(dev->fd, &fds); 1950e88f27b3Smrg ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 195122944501Smrg 195222944501Smrg if (ret <= 0) { 195322944501Smrg fprintf(stderr, "select timed out or error (ret %d)\n", 195422944501Smrg ret); 195522944501Smrg continue; 195622944501Smrg } else if (FD_ISSET(0, &fds)) { 195722944501Smrg break; 195822944501Smrg } 195922944501Smrg#endif 196022944501Smrg 1961e88f27b3Smrg drmHandleEvent(dev->fd, &evctx); 196222944501Smrg } 1963e88f27b3Smrg 1964424e9256Smrgerr_rmfb: 1965424e9256Smrg drmModeRmFB(dev->fd, other_fb_id); 19663c748557Ssnj bo_destroy(other_bo); 196718210155Smrg} 196818210155Smrg 1969e88f27b3Smrg#define min(a, b) ((a) < (b) ? (a) : (b)) 197018210155Smrg 1971e88f27b3Smrgstatic int parse_connector(struct pipe_arg *pipe, const char *arg) 197218210155Smrg{ 1973e88f27b3Smrg unsigned int len; 1974e88f27b3Smrg unsigned int i; 1975e88f27b3Smrg const char *p; 1976e88f27b3Smrg char *endp; 1977e88f27b3Smrg 1978e88f27b3Smrg pipe->vrefresh = 0; 1979e88f27b3Smrg pipe->crtc_id = (uint32_t)-1; 1980e88f27b3Smrg strcpy(pipe->format_str, "XR24"); 1981e88f27b3Smrg 1982e88f27b3Smrg /* Count the number of connectors and allocate them. */ 1983e88f27b3Smrg pipe->num_cons = 1; 1984fe517fc9Smrg for (p = arg; *p && *p != ':' && *p != '@'; ++p) { 1985e88f27b3Smrg if (*p == ',') 1986e88f27b3Smrg pipe->num_cons++; 1987e88f27b3Smrg } 1988e88f27b3Smrg 1989424e9256Smrg pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids)); 1990fe517fc9Smrg pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons)); 1991fe517fc9Smrg if (pipe->con_ids == NULL || pipe->cons == NULL) 1992e88f27b3Smrg return -1; 1993e88f27b3Smrg 1994e88f27b3Smrg /* Parse the connectors. */ 1995e88f27b3Smrg for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { 1996fe517fc9Smrg endp = strpbrk(p, ",@:"); 1997fe517fc9Smrg if (!endp) 1998fe517fc9Smrg break; 1999fe517fc9Smrg 2000fe517fc9Smrg pipe->cons[i] = strndup(p, endp - p); 2001fe517fc9Smrg 2002e88f27b3Smrg if (*endp != ',') 2003e88f27b3Smrg break; 2004e88f27b3Smrg } 2005e88f27b3Smrg 2006e88f27b3Smrg if (i != pipe->num_cons - 1) 2007e88f27b3Smrg return -1; 2008e88f27b3Smrg 2009e88f27b3Smrg /* Parse the remaining parameters. */ 201087bf8e7cSmrg if (!endp) 201187bf8e7cSmrg return -1; 2012e88f27b3Smrg if (*endp == '@') { 2013e88f27b3Smrg arg = endp + 1; 2014e88f27b3Smrg pipe->crtc_id = strtoul(arg, &endp, 10); 2015e88f27b3Smrg } 2016e88f27b3Smrg if (*endp != ':') 2017e88f27b3Smrg return -1; 2018e88f27b3Smrg 2019e88f27b3Smrg arg = endp + 1; 2020e88f27b3Smrg 2021e88f27b3Smrg /* Search for the vertical refresh or the format. */ 2022e88f27b3Smrg p = strpbrk(arg, "-@"); 2023e88f27b3Smrg if (p == NULL) 2024e88f27b3Smrg p = arg + strlen(arg); 2025e88f27b3Smrg len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); 2026e88f27b3Smrg strncpy(pipe->mode_str, arg, len); 2027e88f27b3Smrg pipe->mode_str[len] = '\0'; 2028e88f27b3Smrg 2029e88f27b3Smrg if (*p == '-') { 203087bf8e7cSmrg pipe->vrefresh = strtof(p + 1, &endp); 2031e88f27b3Smrg p = endp; 2032e88f27b3Smrg } 2033e88f27b3Smrg 2034e88f27b3Smrg if (*p == '@') { 203548246ce7Smrg len = sizeof(pipe->format_str) - 1; 203648246ce7Smrg strncpy(pipe->format_str, p + 1, len); 203748246ce7Smrg pipe->format_str[len] = '\0'; 2038e88f27b3Smrg } 2039e88f27b3Smrg 2040fe517fc9Smrg pipe->fourcc = util_format_fourcc(pipe->format_str); 2041e88f27b3Smrg if (pipe->fourcc == 0) { 2042e88f27b3Smrg fprintf(stderr, "unknown format %s\n", pipe->format_str); 2043e88f27b3Smrg return -1; 2044e88f27b3Smrg } 2045e88f27b3Smrg 2046e88f27b3Smrg return 0; 2047e88f27b3Smrg} 2048e88f27b3Smrg 2049e88f27b3Smrgstatic int parse_plane(struct plane_arg *plane, const char *p) 2050e88f27b3Smrg{ 205148246ce7Smrg unsigned int len; 2052e88f27b3Smrg char *end; 2053e88f27b3Smrg 20542ee35494Smrg plane->plane_id = strtoul(p, &end, 10); 20552ee35494Smrg if (*end != '@') 20562ee35494Smrg return -EINVAL; 20572ee35494Smrg 20582ee35494Smrg p = end + 1; 2059e88f27b3Smrg plane->crtc_id = strtoul(p, &end, 10); 2060e88f27b3Smrg if (*end != ':') 2061e88f27b3Smrg return -EINVAL; 2062e88f27b3Smrg 2063e88f27b3Smrg p = end + 1; 2064e88f27b3Smrg plane->w = strtoul(p, &end, 10); 2065e88f27b3Smrg if (*end != 'x') 2066e88f27b3Smrg return -EINVAL; 2067e88f27b3Smrg 2068e88f27b3Smrg p = end + 1; 2069e88f27b3Smrg plane->h = strtoul(p, &end, 10); 2070e88f27b3Smrg 2071e88f27b3Smrg if (*end == '+' || *end == '-') { 2072e88f27b3Smrg plane->x = strtol(end, &end, 10); 2073e88f27b3Smrg if (*end != '+' && *end != '-') 2074e88f27b3Smrg return -EINVAL; 2075e88f27b3Smrg plane->y = strtol(end, &end, 10); 2076e88f27b3Smrg 2077e88f27b3Smrg plane->has_position = true; 2078e88f27b3Smrg } 2079e88f27b3Smrg 2080e88f27b3Smrg if (*end == '*') { 2081e88f27b3Smrg p = end + 1; 2082e88f27b3Smrg plane->scale = strtod(p, &end); 2083e88f27b3Smrg if (plane->scale <= 0.0) 2084e88f27b3Smrg return -EINVAL; 2085e88f27b3Smrg } else { 2086e88f27b3Smrg plane->scale = 1.0; 2087e88f27b3Smrg } 2088e88f27b3Smrg 2089e88f27b3Smrg if (*end == '@') { 209048246ce7Smrg len = sizeof(plane->format_str) - 1; 209148246ce7Smrg strncpy(plane->format_str, end + 1, len); 209248246ce7Smrg plane->format_str[len] = '\0'; 2093e88f27b3Smrg } else { 2094e88f27b3Smrg strcpy(plane->format_str, "XR24"); 2095e88f27b3Smrg } 2096e88f27b3Smrg 2097fe517fc9Smrg plane->fourcc = util_format_fourcc(plane->format_str); 2098e88f27b3Smrg if (plane->fourcc == 0) { 2099e88f27b3Smrg fprintf(stderr, "unknown format %s\n", plane->format_str); 2100e88f27b3Smrg return -EINVAL; 2101e88f27b3Smrg } 2102e88f27b3Smrg 2103e88f27b3Smrg return 0; 2104e88f27b3Smrg} 2105e88f27b3Smrg 2106e88f27b3Smrgstatic int parse_property(struct property_arg *p, const char *arg) 2107e88f27b3Smrg{ 2108e88f27b3Smrg if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 2109e88f27b3Smrg return -1; 2110e88f27b3Smrg 2111e88f27b3Smrg p->obj_type = 0; 2112e88f27b3Smrg p->name[DRM_PROP_NAME_LEN] = '\0'; 2113e88f27b3Smrg 2114e88f27b3Smrg return 0; 2115e88f27b3Smrg} 2116e88f27b3Smrg 2117bf6cc7dcSmrgstatic void parse_fill_patterns(char *arg) 2118bf6cc7dcSmrg{ 2119bf6cc7dcSmrg char *fill = strtok(arg, ","); 2120bf6cc7dcSmrg if (!fill) 2121bf6cc7dcSmrg return; 2122bf6cc7dcSmrg primary_fill = util_pattern_enum(fill); 2123bf6cc7dcSmrg fill = strtok(NULL, ","); 2124bf6cc7dcSmrg if (!fill) 2125bf6cc7dcSmrg return; 2126bf6cc7dcSmrg secondary_fill = util_pattern_enum(fill); 2127bf6cc7dcSmrg} 2128bf6cc7dcSmrg 2129e88f27b3Smrgstatic void usage(char *name) 2130e88f27b3Smrg{ 213148246ce7Smrg fprintf(stderr, "usage: %s [-acDdefMoPpsCvrw]\n", name); 2132e88f27b3Smrg 2133e88f27b3Smrg fprintf(stderr, "\n Query options:\n\n"); 213418210155Smrg fprintf(stderr, "\t-c\tlist connectors\n"); 2135e88f27b3Smrg fprintf(stderr, "\t-e\tlist encoders\n"); 213618210155Smrg fprintf(stderr, "\t-f\tlist framebuffers\n"); 2137e88f27b3Smrg fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 2138e88f27b3Smrg 2139e88f27b3Smrg fprintf(stderr, "\n Test options:\n\n"); 214048246ce7Smrg fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane, see 'plane-topology'\n"); 214148246ce7Smrg fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:mode[@<format>]\tset a mode, see 'mode-topology'\n"); 214248246ce7Smrg fprintf(stderr, "\t\twhere mode can be specified as:\n"); 214348246ce7Smrg fprintf(stderr, "\t\t<hdisp>x<vdisp>[-<vrefresh>]\n"); 214448246ce7Smrg fprintf(stderr, "\t\t<hdisp>,<hss>,<hse>,<htot>,<vdisp>,<vss>,<vse>,<vtot>-<vrefresh>\n"); 214548246ce7Smrg fprintf(stderr, "\t\t#<mode index>\n"); 2146a7d7de1eSmrg fprintf(stderr, "\t-C\ttest hw cursor\n"); 214722944501Smrg fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 214887bf8e7cSmrg fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n"); 214948246ce7Smrg fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property, see 'property'\n"); 21506260e5d5Smrg fprintf(stderr, "\t-a \tuse atomic API\n"); 2151bf6cc7dcSmrg fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n"); 215248246ce7Smrg fprintf(stderr, "\t-o <desired file path> \t Dump writeback output buffer to file\n"); 2153e88f27b3Smrg 2154e88f27b3Smrg fprintf(stderr, "\n Generic options:\n\n"); 2155e88f27b3Smrg fprintf(stderr, "\t-d\tdrop master after mode set\n"); 2156e88f27b3Smrg fprintf(stderr, "\t-M module\tuse the given driver\n"); 2157e88f27b3Smrg fprintf(stderr, "\t-D device\tuse the given device\n"); 2158e88f27b3Smrg 215918210155Smrg fprintf(stderr, "\n\tDefault is to dump all info.\n"); 216048246ce7Smrg 216148246ce7Smrg fprintf(stderr, "\n"); 216248246ce7Smrg fprintf(stderr, "Plane Topology is defined as:\n"); 216348246ce7Smrg fprintf(stderr, "\tplane-topology\t::= plane-id '@' crtc-id ':' width 'x' height ( <plane-offsets> )? ;\n"); 216448246ce7Smrg fprintf(stderr, "\tplane-offsets\t::= '+' x-offset '+' y-offset ( <plane-scale> )? ;\n"); 216548246ce7Smrg fprintf(stderr, "\tplane-scale\t::= '*' scale ( <plane-format> )? ;\n"); 216648246ce7Smrg fprintf(stderr, "\tplane-format\t::= '@' format ;\n"); 216748246ce7Smrg 216848246ce7Smrg fprintf(stderr, "\n"); 216948246ce7Smrg fprintf(stderr, "Mode Topology is defined as:\n"); 217048246ce7Smrg fprintf(stderr, "\tmode-topology\t::= connector-id ( ',' connector-id )* ( '@' crtc-id )? ':' <mode-selection> ( '@' format )? ;\n"); 217148246ce7Smrg fprintf(stderr, "\tmode-selection\t::= <indexed-mode> | <named-mode> | <custom-mode> ;\n"); 217248246ce7Smrg fprintf(stderr, "\tindexed-mode\t::= '#' mode-index ;\n"); 217348246ce7Smrg fprintf(stderr, "\tnamed-mode\t::= width 'x' height ( '-' vrefresh )? ;\n"); 217448246ce7Smrg fprintf(stderr, "\tcustom-mode\t::= hdisplay ',' hsyncstart ',' hsyncend ',' htotal ',' vdisplay ',' vsyncstart ',' vsyncend ',' vtotal '-' vrefresh ;\n"); 217548246ce7Smrg 217648246ce7Smrg fprintf(stderr, "\n"); 217748246ce7Smrg fprintf(stderr, "Property is defined as:\n"); 217848246ce7Smrg fprintf(stderr, "\tproperty\t::= object-id ':' property-name ':' value ;\n"); 217918210155Smrg exit(0); 218018210155Smrg} 218118210155Smrg 218248246ce7Smrgstatic char optstr[] = "acdD:efF:M:P:ps:Cvrw:o:"; 2183e88f27b3Smrg 218418210155Smrgint main(int argc, char **argv) 218518210155Smrg{ 2186e88f27b3Smrg struct device dev; 2187e88f27b3Smrg 218818210155Smrg int c; 2189e88f27b3Smrg int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 2190e88f27b3Smrg int drop_master = 0; 219122944501Smrg int test_vsync = 0; 2192a7d7de1eSmrg int test_cursor = 0; 219387bf8e7cSmrg int set_preferred = 0; 21946260e5d5Smrg int use_atomic = 0; 2195e88f27b3Smrg char *device = NULL; 2196e88f27b3Smrg char *module = NULL; 2197e88f27b3Smrg unsigned int i; 2198fe517fc9Smrg unsigned int count = 0, plane_count = 0; 2199e88f27b3Smrg unsigned int prop_count = 0; 2200e88f27b3Smrg struct pipe_arg *pipe_args = NULL; 2201e88f27b3Smrg struct plane_arg *plane_args = NULL; 2202e88f27b3Smrg struct property_arg *prop_args = NULL; 2203e88f27b3Smrg unsigned int args = 0; 2204e88f27b3Smrg int ret; 220548246ce7Smrg char *dump_path = NULL; 2206e88f27b3Smrg 2207e88f27b3Smrg memset(&dev, 0, sizeof dev); 2208e88f27b3Smrg 220918210155Smrg opterr = 0; 221018210155Smrg while ((c = getopt(argc, argv, optstr)) != -1) { 2211e88f27b3Smrg args++; 2212e88f27b3Smrg 221318210155Smrg switch (c) { 22146260e5d5Smrg case 'a': 22156260e5d5Smrg use_atomic = 1; 221687bf8e7cSmrg /* Preserve the default behaviour of dumping all information. */ 221787bf8e7cSmrg args--; 22186260e5d5Smrg break; 221918210155Smrg case 'c': 222018210155Smrg connectors = 1; 222118210155Smrg break; 2222e88f27b3Smrg case 'D': 2223e88f27b3Smrg device = optarg; 222487bf8e7cSmrg /* Preserve the default behaviour of dumping all information. */ 2225e88f27b3Smrg args--; 2226e88f27b3Smrg break; 2227e88f27b3Smrg case 'd': 2228e88f27b3Smrg drop_master = 1; 222918210155Smrg break; 2230e88f27b3Smrg case 'e': 2231e88f27b3Smrg encoders = 1; 223218210155Smrg break; 223318210155Smrg case 'f': 223418210155Smrg framebuffers = 1; 223518210155Smrg break; 2236bf6cc7dcSmrg case 'F': 2237bf6cc7dcSmrg parse_fill_patterns(optarg); 2238bf6cc7dcSmrg break; 2239e88f27b3Smrg case 'M': 2240e88f27b3Smrg module = optarg; 2241e88f27b3Smrg /* Preserve the default behaviour of dumping all information. */ 2242e88f27b3Smrg args--; 2243e88f27b3Smrg break; 224448246ce7Smrg case 'o': 224548246ce7Smrg dump_path = optarg; 224648246ce7Smrg break; 2247e88f27b3Smrg case 'P': 2248e88f27b3Smrg plane_args = realloc(plane_args, 2249e88f27b3Smrg (plane_count + 1) * sizeof *plane_args); 2250e88f27b3Smrg if (plane_args == NULL) { 2251e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 2252e88f27b3Smrg return 1; 2253e88f27b3Smrg } 2254424e9256Smrg memset(&plane_args[plane_count], 0, sizeof(*plane_args)); 2255e88f27b3Smrg 2256e88f27b3Smrg if (parse_plane(&plane_args[plane_count], optarg) < 0) 2257e88f27b3Smrg usage(argv[0]); 2258e88f27b3Smrg 2259e88f27b3Smrg plane_count++; 2260e88f27b3Smrg break; 2261e88f27b3Smrg case 'p': 2262e88f27b3Smrg crtcs = 1; 2263e88f27b3Smrg planes = 1; 226422944501Smrg break; 226518210155Smrg case 's': 2266e88f27b3Smrg pipe_args = realloc(pipe_args, 2267e88f27b3Smrg (count + 1) * sizeof *pipe_args); 2268e88f27b3Smrg if (pipe_args == NULL) { 2269e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 2270e88f27b3Smrg return 1; 2271e88f27b3Smrg } 2272424e9256Smrg memset(&pipe_args[count], 0, sizeof(*pipe_args)); 2273e88f27b3Smrg 2274e88f27b3Smrg if (parse_connector(&pipe_args[count], optarg) < 0) 227518210155Smrg usage(argv[0]); 2276e88f27b3Smrg 2277fe517fc9Smrg count++; 227818210155Smrg break; 2279a7d7de1eSmrg case 'C': 2280a7d7de1eSmrg test_cursor = 1; 2281a7d7de1eSmrg break; 2282e88f27b3Smrg case 'v': 2283e88f27b3Smrg test_vsync = 1; 2284e88f27b3Smrg break; 228587bf8e7cSmrg case 'r': 228687bf8e7cSmrg set_preferred = 1; 228787bf8e7cSmrg break; 2288e88f27b3Smrg case 'w': 2289e88f27b3Smrg prop_args = realloc(prop_args, 2290e88f27b3Smrg (prop_count + 1) * sizeof *prop_args); 2291e88f27b3Smrg if (prop_args == NULL) { 2292e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 2293e88f27b3Smrg return 1; 2294e88f27b3Smrg } 2295424e9256Smrg memset(&prop_args[prop_count], 0, sizeof(*prop_args)); 2296e88f27b3Smrg 2297e88f27b3Smrg if (parse_property(&prop_args[prop_count], optarg) < 0) 2298e88f27b3Smrg usage(argv[0]); 2299e88f27b3Smrg 2300e88f27b3Smrg prop_count++; 2301e88f27b3Smrg break; 230218210155Smrg default: 230318210155Smrg usage(argv[0]); 230418210155Smrg break; 230518210155Smrg } 230618210155Smrg } 230718210155Smrg 230887bf8e7cSmrg /* Dump all the details when no* arguments are provided. */ 230987bf8e7cSmrg if (!args) 2310e88f27b3Smrg encoders = connectors = crtcs = planes = framebuffers = 1; 231118210155Smrg 231248246ce7Smrg if (test_vsync && !count && !set_preferred) { 231348246ce7Smrg fprintf(stderr, "page flipping requires at least one -s or -r option.\n"); 23146260e5d5Smrg return -1; 23156260e5d5Smrg } 231687bf8e7cSmrg if (set_preferred && count) { 231787bf8e7cSmrg fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n"); 231822944501Smrg return -1; 231922944501Smrg } 232022944501Smrg 232187bf8e7cSmrg dev.fd = util_open(device, module); 232287bf8e7cSmrg if (dev.fd < 0) 2323a7d7de1eSmrg return -1; 232487bf8e7cSmrg 232587bf8e7cSmrg if (use_atomic) { 232687bf8e7cSmrg ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1); 232748246ce7Smrg drmSetClientCap(dev.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); 232887bf8e7cSmrg if (ret) { 232987bf8e7cSmrg fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno)); 233087bf8e7cSmrg drmClose(dev.fd); 233187bf8e7cSmrg return -1; 233287bf8e7cSmrg } 2333a7d7de1eSmrg } 2334a7d7de1eSmrg 233587bf8e7cSmrg dev.use_atomic = use_atomic; 233687bf8e7cSmrg 2337e88f27b3Smrg dev.resources = get_resources(&dev); 2338e88f27b3Smrg if (!dev.resources) { 2339e88f27b3Smrg drmClose(dev.fd); 234018210155Smrg return 1; 234118210155Smrg } 234218210155Smrg 2343e88f27b3Smrg#define dump_resource(dev, res) if (res) dump_##res(dev) 2344e88f27b3Smrg 2345e88f27b3Smrg dump_resource(&dev, encoders); 2346e88f27b3Smrg dump_resource(&dev, connectors); 2347e88f27b3Smrg dump_resource(&dev, crtcs); 2348e88f27b3Smrg dump_resource(&dev, planes); 2349e88f27b3Smrg dump_resource(&dev, framebuffers); 2350e88f27b3Smrg 235148246ce7Smrg if (dev.use_atomic) 235248246ce7Smrg dev.req = drmModeAtomicAlloc(); 235348246ce7Smrg 2354e88f27b3Smrg for (i = 0; i < prop_count; ++i) 2355e88f27b3Smrg set_property(&dev, &prop_args[i]); 2356e88f27b3Smrg 23576260e5d5Smrg if (dev.use_atomic) { 235887bf8e7cSmrg if (set_preferred || (count && plane_count)) { 23596260e5d5Smrg uint64_t cap = 0; 23606260e5d5Smrg 23616260e5d5Smrg ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 23626260e5d5Smrg if (ret || cap == 0) { 23636260e5d5Smrg fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 23646260e5d5Smrg return 1; 23656260e5d5Smrg } 23666260e5d5Smrg 236787bf8e7cSmrg if (set_preferred || count) 236848246ce7Smrg count = set_mode(&dev, &pipe_args, count); 236948246ce7Smrg 237048246ce7Smrg if (dump_path) { 237148246ce7Smrg if (!pipe_has_writeback_connector(&dev, pipe_args, count)) { 237248246ce7Smrg fprintf(stderr, "No writeback connector found, can not dump.\n"); 237348246ce7Smrg return 1; 237448246ce7Smrg } 237548246ce7Smrg 237648246ce7Smrg writeback_config(&dev, pipe_args, count); 237748246ce7Smrg } 237887bf8e7cSmrg 237987bf8e7cSmrg if (plane_count) 238087bf8e7cSmrg atomic_set_planes(&dev, plane_args, plane_count, false); 23816260e5d5Smrg 23826260e5d5Smrg ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 23836260e5d5Smrg if (ret) { 23846260e5d5Smrg fprintf(stderr, "Atomic Commit failed [1]\n"); 23856260e5d5Smrg return 1; 23866260e5d5Smrg } 23876260e5d5Smrg 238848246ce7Smrg /* 238948246ce7Smrg * Since only writeback connectors have an output fb, this should only be 239048246ce7Smrg * called for writeback. 239148246ce7Smrg */ 239248246ce7Smrg if (dump_path) { 239348246ce7Smrg ret = poll_writeback_fence(dev.writeback_fence_fd, 1000); 239448246ce7Smrg if (ret) 239548246ce7Smrg fprintf(stderr, "Poll for writeback error: %d. Skipping Dump.\n", 239648246ce7Smrg ret); 239748246ce7Smrg dump_output_fb(&dev, pipe_args, dump_path, count); 239848246ce7Smrg } 239948246ce7Smrg 240087bf8e7cSmrg if (test_vsync) 240187bf8e7cSmrg atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count); 24026260e5d5Smrg 24036260e5d5Smrg if (drop_master) 24046260e5d5Smrg drmDropMaster(dev.fd); 24056260e5d5Smrg 24066260e5d5Smrg getchar(); 24076260e5d5Smrg 24086260e5d5Smrg drmModeAtomicFree(dev.req); 24096260e5d5Smrg dev.req = drmModeAtomicAlloc(); 24106260e5d5Smrg 241187bf8e7cSmrg /* XXX: properly teardown the preferred mode/plane state */ 241287bf8e7cSmrg if (plane_count) 241387bf8e7cSmrg atomic_clear_planes(&dev, plane_args, plane_count); 241487bf8e7cSmrg 241587bf8e7cSmrg if (count) 241687bf8e7cSmrg atomic_clear_mode(&dev, pipe_args, count); 241748246ce7Smrg } 241887bf8e7cSmrg 241948246ce7Smrg ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 242048246ce7Smrg if (ret) 242148246ce7Smrg fprintf(stderr, "Atomic Commit failed\n"); 24226260e5d5Smrg 242348246ce7Smrg if (count && plane_count) 242448246ce7Smrg atomic_clear_FB(&dev, plane_args, plane_count); 2425e88f27b3Smrg 24266260e5d5Smrg drmModeAtomicFree(dev.req); 24276260e5d5Smrg } else { 242848246ce7Smrg if (dump_path) { 242948246ce7Smrg fprintf(stderr, "writeback / dump is only supported in atomic mode\n"); 243048246ce7Smrg return 1; 243148246ce7Smrg } 243248246ce7Smrg 243387bf8e7cSmrg if (set_preferred || count || plane_count) { 24346260e5d5Smrg uint64_t cap = 0; 24356260e5d5Smrg 24366260e5d5Smrg ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 24376260e5d5Smrg if (ret || cap == 0) { 24386260e5d5Smrg fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 24396260e5d5Smrg return 1; 24406260e5d5Smrg } 24416260e5d5Smrg 244287bf8e7cSmrg if (set_preferred || count) 244348246ce7Smrg count = set_mode(&dev, &pipe_args, count); 2444e88f27b3Smrg 24456260e5d5Smrg if (plane_count) 24466260e5d5Smrg set_planes(&dev, plane_args, plane_count); 2447e88f27b3Smrg 24486260e5d5Smrg if (test_cursor) 24496260e5d5Smrg set_cursors(&dev, pipe_args, count); 2450a7d7de1eSmrg 24516260e5d5Smrg if (test_vsync) 24526260e5d5Smrg test_page_flip(&dev, pipe_args, count); 2453e88f27b3Smrg 24546260e5d5Smrg if (drop_master) 24556260e5d5Smrg drmDropMaster(dev.fd); 2456e88f27b3Smrg 24576260e5d5Smrg getchar(); 2458a7d7de1eSmrg 24596260e5d5Smrg if (test_cursor) 24606260e5d5Smrg clear_cursors(&dev); 2461a7d7de1eSmrg 24626260e5d5Smrg if (plane_count) 24636260e5d5Smrg clear_planes(&dev, plane_args, plane_count); 24646260e5d5Smrg 246587bf8e7cSmrg if (set_preferred || count) 24666260e5d5Smrg clear_mode(&dev); 24676260e5d5Smrg } 246818210155Smrg } 246918210155Smrg 2470e88f27b3Smrg free_resources(dev.resources); 247187bf8e7cSmrg drmClose(dev.fd); 247218210155Smrg 247318210155Smrg return 0; 247418210155Smrg} 2475