modetest.c revision 2b90624a
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 41a884aba1Smrg#ifdef HAVE_CONFIG_H 4218210155Smrg#include "config.h" 43a884aba1Smrg#endif 4418210155Smrg 4518210155Smrg#include <assert.h> 46e88f27b3Smrg#include <ctype.h> 47e88f27b3Smrg#include <stdbool.h> 4818210155Smrg#include <stdio.h> 4918210155Smrg#include <stdlib.h> 5018210155Smrg#include <stdint.h> 51e88f27b3Smrg#include <inttypes.h> 5218210155Smrg#include <unistd.h> 5318210155Smrg#include <string.h> 54424e9256Smrg#include <strings.h> 5518210155Smrg#include <errno.h> 56fe517fc9Smrg#include <poll.h> 5722944501Smrg#include <sys/time.h> 58fe517fc9Smrg#ifdef HAVE_SYS_SELECT_H 59fe517fc9Smrg#include <sys/select.h> 60fe517fc9Smrg#endif 6118210155Smrg 6218210155Smrg#include "xf86drm.h" 6318210155Smrg#include "xf86drmMode.h" 64e88f27b3Smrg#include "drm_fourcc.h" 6518210155Smrg 66fe517fc9Smrg#include "util/common.h" 67fe517fc9Smrg#include "util/format.h" 68fe517fc9Smrg#include "util/kms.h" 69fe517fc9Smrg#include "util/pattern.h" 70fe517fc9Smrg 71e88f27b3Smrg#include "buffers.h" 72a7d7de1eSmrg#include "cursor.h" 73e88f27b3Smrg 74e88f27b3Smrgstruct crtc { 75e88f27b3Smrg drmModeCrtc *crtc; 76e88f27b3Smrg drmModeObjectProperties *props; 77e88f27b3Smrg drmModePropertyRes **props_info; 78e88f27b3Smrg drmModeModeInfo *mode; 79e88f27b3Smrg}; 80e88f27b3Smrg 81e88f27b3Smrgstruct encoder { 82e88f27b3Smrg drmModeEncoder *encoder; 83e88f27b3Smrg}; 84e88f27b3Smrg 85e88f27b3Smrgstruct connector { 86e88f27b3Smrg drmModeConnector *connector; 87e88f27b3Smrg drmModeObjectProperties *props; 88e88f27b3Smrg drmModePropertyRes **props_info; 89fe517fc9Smrg char *name; 90e88f27b3Smrg}; 9118210155Smrg 92e88f27b3Smrgstruct fb { 93e88f27b3Smrg drmModeFB *fb; 94e88f27b3Smrg}; 95e88f27b3Smrg 96e88f27b3Smrgstruct plane { 97e88f27b3Smrg drmModePlane *plane; 98e88f27b3Smrg drmModeObjectProperties *props; 99e88f27b3Smrg drmModePropertyRes **props_info; 100e88f27b3Smrg}; 101e88f27b3Smrg 102e88f27b3Smrgstruct resources { 103e88f27b3Smrg drmModeRes *res; 104e88f27b3Smrg drmModePlaneRes *plane_res; 105e88f27b3Smrg 106e88f27b3Smrg struct crtc *crtcs; 107e88f27b3Smrg struct encoder *encoders; 108e88f27b3Smrg struct connector *connectors; 109e88f27b3Smrg struct fb *fbs; 110e88f27b3Smrg struct plane *planes; 111e88f27b3Smrg}; 112e88f27b3Smrg 113e88f27b3Smrgstruct device { 114e88f27b3Smrg int fd; 115e88f27b3Smrg 116e88f27b3Smrg struct resources *resources; 117e88f27b3Smrg 118e88f27b3Smrg struct { 119e88f27b3Smrg unsigned int width; 120e88f27b3Smrg unsigned int height; 121e88f27b3Smrg 122e88f27b3Smrg unsigned int fb_id; 1233c748557Ssnj struct bo *bo; 124424e9256Smrg struct bo *cursor_bo; 125e88f27b3Smrg } mode; 126e88f27b3Smrg}; 12718210155Smrg 1283c748557Ssnjstatic inline int64_t U642I64(uint64_t val) 1293c748557Ssnj{ 1303c748557Ssnj return (int64_t)*((int64_t *)&val); 1313c748557Ssnj} 13218210155Smrg 133e88f27b3Smrg#define bit_name_fn(res) \ 134e88f27b3Smrgconst char * res##_str(int type) { \ 135e88f27b3Smrg unsigned int i; \ 136e88f27b3Smrg const char *sep = ""; \ 137e88f27b3Smrg for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ 138e88f27b3Smrg if (type & (1 << i)) { \ 139e88f27b3Smrg printf("%s%s", sep, res##_names[i]); \ 140e88f27b3Smrg sep = ", "; \ 141e88f27b3Smrg } \ 142e88f27b3Smrg } \ 143e88f27b3Smrg return NULL; \ 144e88f27b3Smrg} 145e88f27b3Smrg 146e88f27b3Smrgstatic const char *mode_type_names[] = { 147e88f27b3Smrg "builtin", 148e88f27b3Smrg "clock_c", 149e88f27b3Smrg "crtc_c", 150e88f27b3Smrg "preferred", 151e88f27b3Smrg "default", 152e88f27b3Smrg "userdef", 153e88f27b3Smrg "driver", 154e88f27b3Smrg}; 155e88f27b3Smrg 156e88f27b3Smrgstatic bit_name_fn(mode_type) 157e88f27b3Smrg 158e88f27b3Smrgstatic const char *mode_flag_names[] = { 159e88f27b3Smrg "phsync", 160e88f27b3Smrg "nhsync", 161e88f27b3Smrg "pvsync", 162e88f27b3Smrg "nvsync", 163e88f27b3Smrg "interlace", 164e88f27b3Smrg "dblscan", 165e88f27b3Smrg "csync", 166e88f27b3Smrg "pcsync", 167e88f27b3Smrg "ncsync", 168e88f27b3Smrg "hskew", 169e88f27b3Smrg "bcast", 170e88f27b3Smrg "pixmux", 171e88f27b3Smrg "dblclk", 172e88f27b3Smrg "clkdiv2" 173e88f27b3Smrg}; 174e88f27b3Smrg 175e88f27b3Smrgstatic bit_name_fn(mode_flag) 17618210155Smrg 1770655efefSmrgstatic void dump_fourcc(uint32_t fourcc) 1780655efefSmrg{ 1790655efefSmrg printf(" %c%c%c%c", 1800655efefSmrg fourcc, 1810655efefSmrg fourcc >> 8, 1820655efefSmrg fourcc >> 16, 1830655efefSmrg fourcc >> 24); 1840655efefSmrg} 1850655efefSmrg 186e88f27b3Smrgstatic void dump_encoders(struct device *dev) 18718210155Smrg{ 18818210155Smrg drmModeEncoder *encoder; 18918210155Smrg int i; 19018210155Smrg 19118210155Smrg printf("Encoders:\n"); 19218210155Smrg printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); 193e88f27b3Smrg for (i = 0; i < dev->resources->res->count_encoders; i++) { 194e88f27b3Smrg encoder = dev->resources->encoders[i].encoder; 195e88f27b3Smrg if (!encoder) 19618210155Smrg continue; 197e88f27b3Smrg 19818210155Smrg printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", 19918210155Smrg encoder->encoder_id, 20018210155Smrg encoder->crtc_id, 201fe517fc9Smrg util_lookup_encoder_type_name(encoder->encoder_type), 20218210155Smrg encoder->possible_crtcs, 20318210155Smrg encoder->possible_clones); 20418210155Smrg } 20518210155Smrg printf("\n"); 20618210155Smrg} 20718210155Smrg 208e88f27b3Smrgstatic void dump_mode(drmModeModeInfo *mode) 20918210155Smrg{ 2102ee35494Smrg printf(" %s %d %d %d %d %d %d %d %d %d %d", 21118210155Smrg mode->name, 21222944501Smrg mode->vrefresh, 21318210155Smrg mode->hdisplay, 21418210155Smrg mode->hsync_start, 21518210155Smrg mode->hsync_end, 21618210155Smrg mode->htotal, 21718210155Smrg mode->vdisplay, 21818210155Smrg mode->vsync_start, 21918210155Smrg mode->vsync_end, 2202ee35494Smrg mode->vtotal, 2212ee35494Smrg mode->clock); 222e88f27b3Smrg 223e88f27b3Smrg printf(" flags: "); 224e88f27b3Smrg mode_flag_str(mode->flags); 225e88f27b3Smrg printf("; type: "); 226e88f27b3Smrg mode_type_str(mode->type); 227e88f27b3Smrg printf("\n"); 22818210155Smrg} 22918210155Smrg 230e88f27b3Smrgstatic void dump_blob(struct device *dev, uint32_t blob_id) 231e88f27b3Smrg{ 232e88f27b3Smrg uint32_t i; 233e88f27b3Smrg unsigned char *blob_data; 234e88f27b3Smrg drmModePropertyBlobPtr blob; 235e88f27b3Smrg 236e88f27b3Smrg blob = drmModeGetPropertyBlob(dev->fd, blob_id); 2373c748557Ssnj if (!blob) { 2383c748557Ssnj printf("\n"); 239e88f27b3Smrg return; 2403c748557Ssnj } 241e88f27b3Smrg 242e88f27b3Smrg blob_data = blob->data; 243e88f27b3Smrg 244e88f27b3Smrg for (i = 0; i < blob->length; i++) { 245e88f27b3Smrg if (i % 16 == 0) 246e88f27b3Smrg printf("\n\t\t\t"); 247e88f27b3Smrg printf("%.2hhx", blob_data[i]); 248e88f27b3Smrg } 249e88f27b3Smrg printf("\n"); 250e88f27b3Smrg 251e88f27b3Smrg drmModeFreePropertyBlob(blob); 252e88f27b3Smrg} 253e88f27b3Smrg 2542b90624aSmrgstatic const char *modifier_to_string(uint64_t modifier) 2552b90624aSmrg{ 2562b90624aSmrg switch (modifier) { 2572b90624aSmrg case DRM_FORMAT_MOD_INVALID: 2582b90624aSmrg return "INVALID"; 2592b90624aSmrg case DRM_FORMAT_MOD_LINEAR: 2602b90624aSmrg return "LINEAR"; 2612b90624aSmrg case I915_FORMAT_MOD_X_TILED: 2622b90624aSmrg return "X_TILED"; 2632b90624aSmrg case I915_FORMAT_MOD_Y_TILED: 2642b90624aSmrg return "Y_TILED"; 2652b90624aSmrg case I915_FORMAT_MOD_Yf_TILED: 2662b90624aSmrg return "Yf_TILED"; 2672b90624aSmrg case I915_FORMAT_MOD_Y_TILED_CCS: 2682b90624aSmrg return "Y_TILED_CCS"; 2692b90624aSmrg case I915_FORMAT_MOD_Yf_TILED_CCS: 2702b90624aSmrg return "Yf_TILED_CCS"; 2712b90624aSmrg case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 2722b90624aSmrg return "SAMSUNG_64_32_TILE"; 2732b90624aSmrg case DRM_FORMAT_MOD_VIVANTE_TILED: 2742b90624aSmrg return "VIVANTE_TILED"; 2752b90624aSmrg case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: 2762b90624aSmrg return "VIVANTE_SUPER_TILED"; 2772b90624aSmrg case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED: 2782b90624aSmrg return "VIVANTE_SPLIT_TILED"; 2792b90624aSmrg case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED: 2802b90624aSmrg return "VIVANTE_SPLIT_SUPER_TILED"; 2812b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: 2822b90624aSmrg return "NVIDIA_TEGRA_TILED"; 2832b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): 2842b90624aSmrg return "NVIDIA_16BX2_BLOCK(0)"; 2852b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): 2862b90624aSmrg return "NVIDIA_16BX2_BLOCK(1)"; 2872b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): 2882b90624aSmrg return "NVIDIA_16BX2_BLOCK(2)"; 2892b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): 2902b90624aSmrg return "NVIDIA_16BX2_BLOCK(3)"; 2912b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): 2922b90624aSmrg return "NVIDIA_16BX2_BLOCK(4)"; 2932b90624aSmrg case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): 2942b90624aSmrg return "NVIDIA_16BX2_BLOCK(5)"; 2952b90624aSmrg case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 2962b90624aSmrg return "MOD_BROADCOM_VC4_T_TILED"; 2972b90624aSmrg default: 2982b90624aSmrg return "(UNKNOWN MODIFIER)"; 2992b90624aSmrg } 3002b90624aSmrg} 3012b90624aSmrg 3022b90624aSmrgstatic void dump_in_formats(struct device *dev, uint32_t blob_id) 3032b90624aSmrg{ 3042b90624aSmrg uint32_t i, j; 3052b90624aSmrg drmModePropertyBlobPtr blob; 3062b90624aSmrg struct drm_format_modifier_blob *header; 3072b90624aSmrg uint32_t *formats; 3082b90624aSmrg struct drm_format_modifier *modifiers; 3092b90624aSmrg 3102b90624aSmrg printf("\t\tin_formats blob decoded:\n"); 3112b90624aSmrg blob = drmModeGetPropertyBlob(dev->fd, blob_id); 3122b90624aSmrg if (!blob) { 3132b90624aSmrg printf("\n"); 3142b90624aSmrg return; 3152b90624aSmrg } 3162b90624aSmrg 3172b90624aSmrg header = blob->data; 3182b90624aSmrg formats = (uint32_t *) ((char *) header + header->formats_offset); 3192b90624aSmrg modifiers = (struct drm_format_modifier *) 3202b90624aSmrg ((char *) header + header->modifiers_offset); 3212b90624aSmrg 3222b90624aSmrg for (i = 0; i < header->count_formats; i++) { 3232b90624aSmrg printf("\t\t\t"); 3242b90624aSmrg dump_fourcc(formats[i]); 3252b90624aSmrg printf(": "); 3262b90624aSmrg for (j = 0; j < header->count_modifiers; j++) { 3272b90624aSmrg uint64_t mask = 1ULL << i; 3282b90624aSmrg if (modifiers[j].formats & mask) 3292b90624aSmrg printf(" %s", modifier_to_string(modifiers[j].modifier)); 3302b90624aSmrg } 3312b90624aSmrg printf("\n"); 3322b90624aSmrg } 3332b90624aSmrg 3342b90624aSmrg drmModeFreePropertyBlob(blob); 3352b90624aSmrg} 3362b90624aSmrg 337e88f27b3Smrgstatic void dump_prop(struct device *dev, drmModePropertyPtr prop, 338e88f27b3Smrg uint32_t prop_id, uint64_t value) 33918210155Smrg{ 34018210155Smrg int i; 341e88f27b3Smrg printf("\t%d", prop_id); 342e88f27b3Smrg if (!prop) { 343e88f27b3Smrg printf("\n"); 344e88f27b3Smrg return; 345e88f27b3Smrg } 346e88f27b3Smrg 347e88f27b3Smrg printf(" %s:\n", prop->name); 348e88f27b3Smrg 349e88f27b3Smrg printf("\t\tflags:"); 350e88f27b3Smrg if (prop->flags & DRM_MODE_PROP_PENDING) 351e88f27b3Smrg printf(" pending"); 352e88f27b3Smrg if (prop->flags & DRM_MODE_PROP_IMMUTABLE) 353e88f27b3Smrg printf(" immutable"); 3543c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 3553c748557Ssnj printf(" signed range"); 3563c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) 3573c748557Ssnj printf(" range"); 3583c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) 359e88f27b3Smrg printf(" enum"); 3603c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) 361e88f27b3Smrg printf(" bitmask"); 3623c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 363e88f27b3Smrg printf(" blob"); 3643c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT)) 3653c748557Ssnj printf(" object"); 366e88f27b3Smrg printf("\n"); 367e88f27b3Smrg 3683c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) { 3693c748557Ssnj printf("\t\tvalues:"); 3703c748557Ssnj for (i = 0; i < prop->count_values; i++) 3713c748557Ssnj printf(" %"PRId64, U642I64(prop->values[i])); 3723c748557Ssnj printf("\n"); 3733c748557Ssnj } 3743c748557Ssnj 3753c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) { 376e88f27b3Smrg printf("\t\tvalues:"); 377e88f27b3Smrg for (i = 0; i < prop->count_values; i++) 378e88f27b3Smrg printf(" %"PRIu64, prop->values[i]); 379e88f27b3Smrg printf("\n"); 380e88f27b3Smrg } 38118210155Smrg 3823c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) { 383e88f27b3Smrg printf("\t\tenums:"); 384e88f27b3Smrg for (i = 0; i < prop->count_enums; i++) 385e88f27b3Smrg printf(" %s=%llu", prop->enums[i].name, 386e88f27b3Smrg prop->enums[i].value); 387e88f27b3Smrg printf("\n"); 3883c748557Ssnj } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) { 389e88f27b3Smrg printf("\t\tvalues:"); 390e88f27b3Smrg for (i = 0; i < prop->count_enums; i++) 391e88f27b3Smrg printf(" %s=0x%llx", prop->enums[i].name, 392e88f27b3Smrg (1LL << prop->enums[i].value)); 393e88f27b3Smrg printf("\n"); 394e88f27b3Smrg } else { 395e88f27b3Smrg assert(prop->count_enums == 0); 396e88f27b3Smrg } 397e88f27b3Smrg 3983c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) { 399e88f27b3Smrg printf("\t\tblobs:\n"); 400e88f27b3Smrg for (i = 0; i < prop->count_blobs; i++) 401e88f27b3Smrg dump_blob(dev, prop->blob_ids[i]); 402e88f27b3Smrg printf("\n"); 403e88f27b3Smrg } else { 404e88f27b3Smrg assert(prop->count_blobs == 0); 40518210155Smrg } 406e88f27b3Smrg 407e88f27b3Smrg printf("\t\tvalue:"); 4083c748557Ssnj if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) 409e88f27b3Smrg dump_blob(dev, value); 410fe517fc9Smrg else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) 411fe517fc9Smrg printf(" %"PRId64"\n", value); 412e88f27b3Smrg else 413e88f27b3Smrg printf(" %"PRIu64"\n", value); 4142b90624aSmrg 4152b90624aSmrg if (strcmp(prop->name, "IN_FORMATS") == 0) 4162b90624aSmrg dump_in_formats(dev, value); 41718210155Smrg} 41818210155Smrg 419e88f27b3Smrgstatic void dump_connectors(struct device *dev) 42018210155Smrg{ 42118210155Smrg int i, j; 42218210155Smrg 42318210155Smrg printf("Connectors:\n"); 424fe517fc9Smrg printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n"); 425e88f27b3Smrg for (i = 0; i < dev->resources->res->count_connectors; i++) { 426e88f27b3Smrg struct connector *_connector = &dev->resources->connectors[i]; 427e88f27b3Smrg drmModeConnector *connector = _connector->connector; 428e88f27b3Smrg if (!connector) 42918210155Smrg continue; 43018210155Smrg 431fe517fc9Smrg printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t", 43218210155Smrg connector->connector_id, 43318210155Smrg connector->encoder_id, 434fe517fc9Smrg util_lookup_connector_status_name(connector->connection), 435fe517fc9Smrg _connector->name, 43618210155Smrg connector->mmWidth, connector->mmHeight, 43718210155Smrg connector->count_modes); 43818210155Smrg 43922944501Smrg for (j = 0; j < connector->count_encoders; j++) 44022944501Smrg printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]); 44122944501Smrg printf("\n"); 44222944501Smrg 443e88f27b3Smrg if (connector->count_modes) { 444e88f27b3Smrg printf(" modes:\n"); 445e88f27b3Smrg printf("\tname refresh (Hz) hdisp hss hse htot vdisp " 446e88f27b3Smrg "vss vse vtot)\n"); 447e88f27b3Smrg for (j = 0; j < connector->count_modes; j++) 448e88f27b3Smrg dump_mode(&connector->modes[j]); 449e88f27b3Smrg } 45022944501Smrg 451e88f27b3Smrg if (_connector->props) { 452e88f27b3Smrg printf(" props:\n"); 453e88f27b3Smrg for (j = 0; j < (int)_connector->props->count_props; j++) 454e88f27b3Smrg dump_prop(dev, _connector->props_info[j], 455e88f27b3Smrg _connector->props->props[j], 456e88f27b3Smrg _connector->props->prop_values[j]); 457e88f27b3Smrg } 45818210155Smrg } 45918210155Smrg printf("\n"); 46018210155Smrg} 46118210155Smrg 462e88f27b3Smrgstatic void dump_crtcs(struct device *dev) 46318210155Smrg{ 46418210155Smrg int i; 465e88f27b3Smrg uint32_t j; 46618210155Smrg 46718210155Smrg printf("CRTCs:\n"); 46818210155Smrg printf("id\tfb\tpos\tsize\n"); 469e88f27b3Smrg for (i = 0; i < dev->resources->res->count_crtcs; i++) { 470e88f27b3Smrg struct crtc *_crtc = &dev->resources->crtcs[i]; 471e88f27b3Smrg drmModeCrtc *crtc = _crtc->crtc; 472e88f27b3Smrg if (!crtc) 47318210155Smrg continue; 474e88f27b3Smrg 47518210155Smrg printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", 47618210155Smrg crtc->crtc_id, 47718210155Smrg crtc->buffer_id, 47818210155Smrg crtc->x, crtc->y, 47918210155Smrg crtc->width, crtc->height); 48018210155Smrg dump_mode(&crtc->mode); 48118210155Smrg 482e88f27b3Smrg if (_crtc->props) { 483e88f27b3Smrg printf(" props:\n"); 484e88f27b3Smrg for (j = 0; j < _crtc->props->count_props; j++) 485e88f27b3Smrg dump_prop(dev, _crtc->props_info[j], 486e88f27b3Smrg _crtc->props->props[j], 487e88f27b3Smrg _crtc->props->prop_values[j]); 488e88f27b3Smrg } else { 489e88f27b3Smrg printf(" no properties found\n"); 490e88f27b3Smrg } 49118210155Smrg } 49218210155Smrg printf("\n"); 49318210155Smrg} 49418210155Smrg 495e88f27b3Smrgstatic void dump_framebuffers(struct device *dev) 49618210155Smrg{ 49718210155Smrg drmModeFB *fb; 49818210155Smrg int i; 49918210155Smrg 50018210155Smrg printf("Frame buffers:\n"); 50118210155Smrg printf("id\tsize\tpitch\n"); 502e88f27b3Smrg for (i = 0; i < dev->resources->res->count_fbs; i++) { 503e88f27b3Smrg fb = dev->resources->fbs[i].fb; 504e88f27b3Smrg if (!fb) 50518210155Smrg continue; 506e88f27b3Smrg 50722944501Smrg printf("%u\t(%ux%u)\t%u\n", 50818210155Smrg fb->fb_id, 50922944501Smrg fb->width, fb->height, 51022944501Smrg fb->pitch); 51118210155Smrg } 51218210155Smrg printf("\n"); 51318210155Smrg} 51418210155Smrg 515e88f27b3Smrgstatic void dump_planes(struct device *dev) 51618210155Smrg{ 517e88f27b3Smrg unsigned int i, j; 51818210155Smrg 519e88f27b3Smrg printf("Planes:\n"); 520e88f27b3Smrg printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n"); 52118210155Smrg 522e88f27b3Smrg if (!dev->resources->plane_res) 523e88f27b3Smrg return; 52418210155Smrg 525e88f27b3Smrg for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 526e88f27b3Smrg struct plane *plane = &dev->resources->planes[i]; 527e88f27b3Smrg drmModePlane *ovr = plane->plane; 528e88f27b3Smrg if (!ovr) 52918210155Smrg continue; 53018210155Smrg 531e88f27b3Smrg printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", 532e88f27b3Smrg ovr->plane_id, ovr->crtc_id, ovr->fb_id, 533e88f27b3Smrg ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, 534e88f27b3Smrg ovr->gamma_size, ovr->possible_crtcs); 535e88f27b3Smrg 536e88f27b3Smrg if (!ovr->count_formats) 53718210155Smrg continue; 53818210155Smrg 539e88f27b3Smrg printf(" formats:"); 540e88f27b3Smrg for (j = 0; j < ovr->count_formats; j++) 5410655efefSmrg dump_fourcc(ovr->formats[j]); 542e88f27b3Smrg printf("\n"); 543e88f27b3Smrg 544e88f27b3Smrg if (plane->props) { 545e88f27b3Smrg printf(" props:\n"); 546e88f27b3Smrg for (j = 0; j < plane->props->count_props; j++) 547e88f27b3Smrg dump_prop(dev, plane->props_info[j], 548e88f27b3Smrg plane->props->props[j], 549e88f27b3Smrg plane->props->prop_values[j]); 550e88f27b3Smrg } else { 551e88f27b3Smrg printf(" no properties found\n"); 55218210155Smrg } 553e88f27b3Smrg } 554e88f27b3Smrg printf("\n"); 55518210155Smrg 556e88f27b3Smrg return; 557e88f27b3Smrg} 558e88f27b3Smrg 559e88f27b3Smrgstatic void free_resources(struct resources *res) 560e88f27b3Smrg{ 561fe517fc9Smrg int i; 562fe517fc9Smrg 563e88f27b3Smrg if (!res) 564e88f27b3Smrg return; 56518210155Smrg 566e88f27b3Smrg#define free_resource(_res, __res, type, Type) \ 567e88f27b3Smrg do { \ 568e88f27b3Smrg if (!(_res)->type##s) \ 569e88f27b3Smrg break; \ 570e88f27b3Smrg for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 571e88f27b3Smrg if (!(_res)->type##s[i].type) \ 572e88f27b3Smrg break; \ 573e88f27b3Smrg drmModeFree##Type((_res)->type##s[i].type); \ 574e88f27b3Smrg } \ 575e88f27b3Smrg free((_res)->type##s); \ 576e88f27b3Smrg } while (0) 577e88f27b3Smrg 578e88f27b3Smrg#define free_properties(_res, __res, type) \ 579e88f27b3Smrg do { \ 580e88f27b3Smrg for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 581e88f27b3Smrg drmModeFreeObjectProperties(res->type##s[i].props); \ 582e88f27b3Smrg free(res->type##s[i].props_info); \ 583e88f27b3Smrg } \ 584e88f27b3Smrg } while (0) 585e88f27b3Smrg 586e88f27b3Smrg if (res->res) { 587e88f27b3Smrg free_properties(res, res, crtc); 588e88f27b3Smrg 589e88f27b3Smrg free_resource(res, res, crtc, Crtc); 590e88f27b3Smrg free_resource(res, res, encoder, Encoder); 591fe517fc9Smrg 592fe517fc9Smrg for (i = 0; i < res->res->count_connectors; i++) 593fe517fc9Smrg free(res->connectors[i].name); 594fe517fc9Smrg 595e88f27b3Smrg free_resource(res, res, connector, Connector); 596e88f27b3Smrg free_resource(res, res, fb, FB); 597e88f27b3Smrg 598e88f27b3Smrg drmModeFreeResources(res->res); 59918210155Smrg } 60018210155Smrg 601e88f27b3Smrg if (res->plane_res) { 602e88f27b3Smrg free_properties(res, plane_res, plane); 603e88f27b3Smrg 604e88f27b3Smrg free_resource(res, plane_res, plane, Plane); 605e88f27b3Smrg 606e88f27b3Smrg drmModeFreePlaneResources(res->plane_res); 60718210155Smrg } 60818210155Smrg 609e88f27b3Smrg free(res); 610e88f27b3Smrg} 61118210155Smrg 612e88f27b3Smrgstatic struct resources *get_resources(struct device *dev) 613e88f27b3Smrg{ 614e88f27b3Smrg struct resources *res; 615e88f27b3Smrg int i; 61618210155Smrg 617424e9256Smrg res = calloc(1, sizeof(*res)); 618e88f27b3Smrg if (res == 0) 619e88f27b3Smrg return NULL; 620e88f27b3Smrg 621424e9256Smrg drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 622e88f27b3Smrg 6233c748557Ssnj drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); 6243c748557Ssnj 625e88f27b3Smrg res->res = drmModeGetResources(dev->fd); 626e88f27b3Smrg if (!res->res) { 627e88f27b3Smrg fprintf(stderr, "drmModeGetResources failed: %s\n", 628e88f27b3Smrg strerror(errno)); 629e88f27b3Smrg goto error; 630e88f27b3Smrg } 63118210155Smrg 632424e9256Smrg res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs)); 633424e9256Smrg res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders)); 634424e9256Smrg res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors)); 635424e9256Smrg res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs)); 636e88f27b3Smrg 637e88f27b3Smrg if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) 638e88f27b3Smrg goto error; 639e88f27b3Smrg 640e88f27b3Smrg#define get_resource(_res, __res, type, Type) \ 641e88f27b3Smrg do { \ 642e88f27b3Smrg for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 643e88f27b3Smrg (_res)->type##s[i].type = \ 644e88f27b3Smrg drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \ 645e88f27b3Smrg if (!(_res)->type##s[i].type) \ 646e88f27b3Smrg fprintf(stderr, "could not get %s %i: %s\n", \ 647e88f27b3Smrg #type, (_res)->__res->type##s[i], \ 648e88f27b3Smrg strerror(errno)); \ 649e88f27b3Smrg } \ 650e88f27b3Smrg } while (0) 651e88f27b3Smrg 652e88f27b3Smrg get_resource(res, res, crtc, Crtc); 653e88f27b3Smrg get_resource(res, res, encoder, Encoder); 654e88f27b3Smrg get_resource(res, res, connector, Connector); 655e88f27b3Smrg get_resource(res, res, fb, FB); 656e88f27b3Smrg 657fe517fc9Smrg /* Set the name of all connectors based on the type name and the per-type ID. */ 658fe517fc9Smrg for (i = 0; i < res->res->count_connectors; i++) { 659fe517fc9Smrg struct connector *connector = &res->connectors[i]; 660fe517fc9Smrg drmModeConnector *conn = connector->connector; 6612b90624aSmrg int num; 662fe517fc9Smrg 6632b90624aSmrg num = asprintf(&connector->name, "%s-%u", 664fe517fc9Smrg util_lookup_connector_type_name(conn->connector_type), 665fe517fc9Smrg conn->connector_type_id); 6662b90624aSmrg if (num < 0) 6672b90624aSmrg goto error; 668fe517fc9Smrg } 669fe517fc9Smrg 670e88f27b3Smrg#define get_properties(_res, __res, type, Type) \ 671e88f27b3Smrg do { \ 672e88f27b3Smrg for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 673e88f27b3Smrg struct type *obj = &res->type##s[i]; \ 674e88f27b3Smrg unsigned int j; \ 675e88f27b3Smrg obj->props = \ 676e88f27b3Smrg drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ 677e88f27b3Smrg DRM_MODE_OBJECT_##Type); \ 678e88f27b3Smrg if (!obj->props) { \ 679e88f27b3Smrg fprintf(stderr, \ 680e88f27b3Smrg "could not get %s %i properties: %s\n", \ 681e88f27b3Smrg #type, obj->type->type##_id, \ 682e88f27b3Smrg strerror(errno)); \ 683e88f27b3Smrg continue; \ 684e88f27b3Smrg } \ 685424e9256Smrg obj->props_info = calloc(obj->props->count_props, \ 686424e9256Smrg sizeof(*obj->props_info)); \ 687e88f27b3Smrg if (!obj->props_info) \ 688e88f27b3Smrg continue; \ 689e88f27b3Smrg for (j = 0; j < obj->props->count_props; ++j) \ 690e88f27b3Smrg obj->props_info[j] = \ 691e88f27b3Smrg drmModeGetProperty(dev->fd, obj->props->props[j]); \ 692e88f27b3Smrg } \ 693e88f27b3Smrg } while (0) 694e88f27b3Smrg 695e88f27b3Smrg get_properties(res, res, crtc, CRTC); 696e88f27b3Smrg get_properties(res, res, connector, CONNECTOR); 697e88f27b3Smrg 698e88f27b3Smrg for (i = 0; i < res->res->count_crtcs; ++i) 699e88f27b3Smrg res->crtcs[i].mode = &res->crtcs[i].crtc->mode; 700e88f27b3Smrg 701e88f27b3Smrg res->plane_res = drmModeGetPlaneResources(dev->fd); 702e88f27b3Smrg if (!res->plane_res) { 703e88f27b3Smrg fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", 704e88f27b3Smrg strerror(errno)); 705e88f27b3Smrg return res; 70618210155Smrg } 70718210155Smrg 708424e9256Smrg res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes)); 709e88f27b3Smrg if (!res->planes) 710e88f27b3Smrg goto error; 711e88f27b3Smrg 712e88f27b3Smrg get_resource(res, plane_res, plane, Plane); 713e88f27b3Smrg get_properties(res, plane_res, plane, PLANE); 714e88f27b3Smrg 715e88f27b3Smrg return res; 716e88f27b3Smrg 717e88f27b3Smrgerror: 718e88f27b3Smrg free_resources(res); 719e88f27b3Smrg return NULL; 72018210155Smrg} 72118210155Smrg 722e88f27b3Smrgstatic int get_crtc_index(struct device *dev, uint32_t id) 723d049871aSmrg{ 724e88f27b3Smrg int i; 72518210155Smrg 726e88f27b3Smrg for (i = 0; i < dev->resources->res->count_crtcs; ++i) { 727e88f27b3Smrg drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; 728e88f27b3Smrg if (crtc && crtc->crtc_id == id) 729e88f27b3Smrg return i; 730e88f27b3Smrg } 731d049871aSmrg 732e88f27b3Smrg return -1; 733d049871aSmrg} 734d049871aSmrg 735fe517fc9Smrgstatic drmModeConnector *get_connector_by_name(struct device *dev, const char *name) 736fe517fc9Smrg{ 737fe517fc9Smrg struct connector *connector; 738fe517fc9Smrg int i; 739fe517fc9Smrg 740fe517fc9Smrg for (i = 0; i < dev->resources->res->count_connectors; i++) { 741fe517fc9Smrg connector = &dev->resources->connectors[i]; 742fe517fc9Smrg 743fe517fc9Smrg if (strcmp(connector->name, name) == 0) 744fe517fc9Smrg return connector->connector; 745fe517fc9Smrg } 746fe517fc9Smrg 747fe517fc9Smrg return NULL; 748fe517fc9Smrg} 749fe517fc9Smrg 750e88f27b3Smrgstatic drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) 75118210155Smrg{ 752e88f27b3Smrg drmModeConnector *connector; 753e88f27b3Smrg int i; 754e88f27b3Smrg 755e88f27b3Smrg for (i = 0; i < dev->resources->res->count_connectors; i++) { 756e88f27b3Smrg connector = dev->resources->connectors[i].connector; 757e88f27b3Smrg if (connector && connector->connector_id == id) 758e88f27b3Smrg return connector; 759e88f27b3Smrg } 760e88f27b3Smrg 761e88f27b3Smrg return NULL; 762e88f27b3Smrg} 763e88f27b3Smrg 764e88f27b3Smrgstatic drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) 765e88f27b3Smrg{ 766e88f27b3Smrg drmModeEncoder *encoder; 767e88f27b3Smrg int i; 768e88f27b3Smrg 769e88f27b3Smrg for (i = 0; i < dev->resources->res->count_encoders; i++) { 770e88f27b3Smrg encoder = dev->resources->encoders[i].encoder; 771e88f27b3Smrg if (encoder && encoder->encoder_id == id) 772e88f27b3Smrg return encoder; 773e88f27b3Smrg } 774e88f27b3Smrg 775e88f27b3Smrg return NULL; 776e88f27b3Smrg} 777e88f27b3Smrg 778e88f27b3Smrg/* ----------------------------------------------------------------------------- 779e88f27b3Smrg * Pipes and planes 780e88f27b3Smrg */ 781e88f27b3Smrg 782e88f27b3Smrg/* 783e88f27b3Smrg * Mode setting with the kernel interfaces is a bit of a chore. 784e88f27b3Smrg * First you have to find the connector in question and make sure the 785e88f27b3Smrg * requested mode is available. 786e88f27b3Smrg * Then you need to find the encoder attached to that connector so you 787e88f27b3Smrg * can bind it with a free crtc. 788e88f27b3Smrg */ 789e88f27b3Smrgstruct pipe_arg { 790fe517fc9Smrg const char **cons; 791e88f27b3Smrg uint32_t *con_ids; 792e88f27b3Smrg unsigned int num_cons; 793e88f27b3Smrg uint32_t crtc_id; 794e88f27b3Smrg char mode_str[64]; 795e88f27b3Smrg char format_str[5]; 796e88f27b3Smrg unsigned int vrefresh; 797e88f27b3Smrg unsigned int fourcc; 798e88f27b3Smrg drmModeModeInfo *mode; 799e88f27b3Smrg struct crtc *crtc; 800e88f27b3Smrg unsigned int fb_id[2], current_fb_id; 801e88f27b3Smrg struct timeval start; 802e88f27b3Smrg 803e88f27b3Smrg int swap_count; 804e88f27b3Smrg}; 805e88f27b3Smrg 806e88f27b3Smrgstruct plane_arg { 8072ee35494Smrg uint32_t plane_id; /* the id of plane to use */ 808e88f27b3Smrg uint32_t crtc_id; /* the id of CRTC to bind to */ 809e88f27b3Smrg bool has_position; 810e88f27b3Smrg int32_t x, y; 811e88f27b3Smrg uint32_t w, h; 812e88f27b3Smrg double scale; 813e88f27b3Smrg unsigned int fb_id; 814424e9256Smrg struct bo *bo; 815e88f27b3Smrg char format_str[5]; /* need to leave room for terminating \0 */ 816e88f27b3Smrg unsigned int fourcc; 817e88f27b3Smrg}; 818e88f27b3Smrg 819e88f27b3Smrgstatic drmModeModeInfo * 820e88f27b3Smrgconnector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, 821e88f27b3Smrg const unsigned int vrefresh) 822e88f27b3Smrg{ 823e88f27b3Smrg drmModeConnector *connector; 824e88f27b3Smrg drmModeModeInfo *mode; 825e88f27b3Smrg int i; 826e88f27b3Smrg 827e88f27b3Smrg connector = get_connector_by_id(dev, con_id); 828e88f27b3Smrg if (!connector || !connector->count_modes) 829e88f27b3Smrg return NULL; 830e88f27b3Smrg 831e88f27b3Smrg for (i = 0; i < connector->count_modes; i++) { 832e88f27b3Smrg mode = &connector->modes[i]; 833e88f27b3Smrg if (!strcmp(mode->name, mode_str)) { 834e88f27b3Smrg /* If the vertical refresh frequency is not specified then return the 835e88f27b3Smrg * first mode that match with the name. Else, return the mode that match 836e88f27b3Smrg * the name and the specified vertical refresh frequency. 837e88f27b3Smrg */ 838e88f27b3Smrg if (vrefresh == 0) 839e88f27b3Smrg return mode; 840e88f27b3Smrg else if (mode->vrefresh == vrefresh) 841e88f27b3Smrg return mode; 84218210155Smrg } 843e88f27b3Smrg } 84418210155Smrg 845e88f27b3Smrg return NULL; 84618210155Smrg} 84718210155Smrg 848e88f27b3Smrgstatic struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) 84918210155Smrg{ 850e88f27b3Smrg uint32_t possible_crtcs = ~0; 851e88f27b3Smrg uint32_t active_crtcs = 0; 852e88f27b3Smrg unsigned int crtc_idx; 853e88f27b3Smrg unsigned int i; 854e88f27b3Smrg int j; 855e88f27b3Smrg 856e88f27b3Smrg for (i = 0; i < pipe->num_cons; ++i) { 857e88f27b3Smrg uint32_t crtcs_for_connector = 0; 858e88f27b3Smrg drmModeConnector *connector; 859e88f27b3Smrg drmModeEncoder *encoder; 860e88f27b3Smrg int idx; 861e88f27b3Smrg 862e88f27b3Smrg connector = get_connector_by_id(dev, pipe->con_ids[i]); 863e88f27b3Smrg if (!connector) 864e88f27b3Smrg return NULL; 865e88f27b3Smrg 866e88f27b3Smrg for (j = 0; j < connector->count_encoders; ++j) { 867e88f27b3Smrg encoder = get_encoder_by_id(dev, connector->encoders[j]); 868e88f27b3Smrg if (!encoder) 869e88f27b3Smrg continue; 870e88f27b3Smrg 871e88f27b3Smrg crtcs_for_connector |= encoder->possible_crtcs; 872e88f27b3Smrg 873e88f27b3Smrg idx = get_crtc_index(dev, encoder->crtc_id); 874e88f27b3Smrg if (idx >= 0) 875e88f27b3Smrg active_crtcs |= 1 << idx; 876e88f27b3Smrg } 87718210155Smrg 878e88f27b3Smrg possible_crtcs &= crtcs_for_connector; 87918210155Smrg } 88018210155Smrg 881e88f27b3Smrg if (!possible_crtcs) 882e88f27b3Smrg return NULL; 883e88f27b3Smrg 884e88f27b3Smrg /* Return the first possible and active CRTC if one exists, or the first 885e88f27b3Smrg * possible CRTC otherwise. 886e88f27b3Smrg */ 887e88f27b3Smrg if (possible_crtcs & active_crtcs) 888e88f27b3Smrg crtc_idx = ffs(possible_crtcs & active_crtcs); 889e88f27b3Smrg else 890e88f27b3Smrg crtc_idx = ffs(possible_crtcs); 891e88f27b3Smrg 892e88f27b3Smrg return &dev->resources->crtcs[crtc_idx - 1]; 893e88f27b3Smrg} 894e88f27b3Smrg 895e88f27b3Smrgstatic int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) 896e88f27b3Smrg{ 897e88f27b3Smrg drmModeModeInfo *mode = NULL; 898e88f27b3Smrg int i; 899e88f27b3Smrg 900e88f27b3Smrg pipe->mode = NULL; 901e88f27b3Smrg 902e88f27b3Smrg for (i = 0; i < (int)pipe->num_cons; i++) { 903e88f27b3Smrg mode = connector_find_mode(dev, pipe->con_ids[i], 904e88f27b3Smrg pipe->mode_str, pipe->vrefresh); 905e88f27b3Smrg if (mode == NULL) { 906e88f27b3Smrg fprintf(stderr, 907fe517fc9Smrg "failed to find mode \"%s\" for connector %s\n", 908fe517fc9Smrg pipe->mode_str, pipe->cons[i]); 909e88f27b3Smrg return -EINVAL; 910e88f27b3Smrg } 91118210155Smrg } 91218210155Smrg 913e88f27b3Smrg /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise 914e88f27b3Smrg * locate a CRTC that can be attached to all the connectors. 915e88f27b3Smrg */ 916e88f27b3Smrg if (pipe->crtc_id != (uint32_t)-1) { 917e88f27b3Smrg for (i = 0; i < dev->resources->res->count_crtcs; i++) { 918e88f27b3Smrg struct crtc *crtc = &dev->resources->crtcs[i]; 919e88f27b3Smrg 920e88f27b3Smrg if (pipe->crtc_id == crtc->crtc->crtc_id) { 921e88f27b3Smrg pipe->crtc = crtc; 922e88f27b3Smrg break; 923e88f27b3Smrg } 924d049871aSmrg } 925e88f27b3Smrg } else { 926e88f27b3Smrg pipe->crtc = pipe_find_crtc(dev, pipe); 92718210155Smrg } 928d049871aSmrg 929e88f27b3Smrg if (!pipe->crtc) { 930e88f27b3Smrg fprintf(stderr, "failed to find CRTC for pipe\n"); 931e88f27b3Smrg return -EINVAL; 932e88f27b3Smrg } 933d049871aSmrg 934e88f27b3Smrg pipe->mode = mode; 935e88f27b3Smrg pipe->crtc->mode = mode; 93618210155Smrg 93718210155Smrg return 0; 93818210155Smrg} 93918210155Smrg 940e88f27b3Smrg/* ----------------------------------------------------------------------------- 941e88f27b3Smrg * Properties 942e88f27b3Smrg */ 943e88f27b3Smrg 944e88f27b3Smrgstruct property_arg { 945e88f27b3Smrg uint32_t obj_id; 946e88f27b3Smrg uint32_t obj_type; 947e88f27b3Smrg char name[DRM_PROP_NAME_LEN+1]; 948e88f27b3Smrg uint32_t prop_id; 949e88f27b3Smrg uint64_t value; 950e88f27b3Smrg}; 951e88f27b3Smrg 952e88f27b3Smrgstatic void set_property(struct device *dev, struct property_arg *p) 95322944501Smrg{ 954e88f27b3Smrg drmModeObjectProperties *props = NULL; 955e88f27b3Smrg drmModePropertyRes **props_info = NULL; 956e88f27b3Smrg const char *obj_type; 957e88f27b3Smrg int ret; 958e88f27b3Smrg int i; 95922944501Smrg 960e88f27b3Smrg p->obj_type = 0; 961e88f27b3Smrg p->prop_id = 0; 962e88f27b3Smrg 963e88f27b3Smrg#define find_object(_res, __res, type, Type) \ 964e88f27b3Smrg do { \ 965e88f27b3Smrg for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ 966e88f27b3Smrg struct type *obj = &(_res)->type##s[i]; \ 967e88f27b3Smrg if (obj->type->type##_id != p->obj_id) \ 968e88f27b3Smrg continue; \ 969e88f27b3Smrg p->obj_type = DRM_MODE_OBJECT_##Type; \ 970e88f27b3Smrg obj_type = #Type; \ 971e88f27b3Smrg props = obj->props; \ 972e88f27b3Smrg props_info = obj->props_info; \ 973e88f27b3Smrg } \ 974e88f27b3Smrg } while(0) \ 975e88f27b3Smrg 976e88f27b3Smrg find_object(dev->resources, res, crtc, CRTC); 977e88f27b3Smrg if (p->obj_type == 0) 978e88f27b3Smrg find_object(dev->resources, res, connector, CONNECTOR); 979e88f27b3Smrg if (p->obj_type == 0) 980e88f27b3Smrg find_object(dev->resources, plane_res, plane, PLANE); 981e88f27b3Smrg if (p->obj_type == 0) { 982e88f27b3Smrg fprintf(stderr, "Object %i not found, can't set property\n", 983e88f27b3Smrg p->obj_id); 984e88f27b3Smrg return; 985e88f27b3Smrg } 98622944501Smrg 987e88f27b3Smrg if (!props) { 988e88f27b3Smrg fprintf(stderr, "%s %i has no properties\n", 989e88f27b3Smrg obj_type, p->obj_id); 990e88f27b3Smrg return; 99122944501Smrg } 99222944501Smrg 993e88f27b3Smrg for (i = 0; i < (int)props->count_props; ++i) { 994e88f27b3Smrg if (!props_info[i]) 995e88f27b3Smrg continue; 996e88f27b3Smrg if (strcmp(props_info[i]->name, p->name) == 0) 997e88f27b3Smrg break; 99822944501Smrg } 99922944501Smrg 1000e88f27b3Smrg if (i == (int)props->count_props) { 1001e88f27b3Smrg fprintf(stderr, "%s %i has no %s property\n", 1002e88f27b3Smrg obj_type, p->obj_id, p->name); 1003e88f27b3Smrg return; 1004e88f27b3Smrg } 100522944501Smrg 1006e88f27b3Smrg p->prop_id = props->props[i]; 100722944501Smrg 1008e88f27b3Smrg ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, 1009e88f27b3Smrg p->prop_id, p->value); 1010e88f27b3Smrg if (ret < 0) 1011e88f27b3Smrg fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", 1012e88f27b3Smrg obj_type, p->obj_id, p->name, p->value, strerror(errno)); 101322944501Smrg} 101422944501Smrg 1015e88f27b3Smrg/* -------------------------------------------------------------------------- */ 1016e88f27b3Smrg 1017e88f27b3Smrgstatic void 101822944501Smrgpage_flip_handler(int fd, unsigned int frame, 101922944501Smrg unsigned int sec, unsigned int usec, void *data) 102022944501Smrg{ 1021e88f27b3Smrg struct pipe_arg *pipe; 102222944501Smrg unsigned int new_fb_id; 102322944501Smrg struct timeval end; 102422944501Smrg double t; 102522944501Smrg 1026e88f27b3Smrg pipe = data; 1027e88f27b3Smrg if (pipe->current_fb_id == pipe->fb_id[0]) 1028e88f27b3Smrg new_fb_id = pipe->fb_id[1]; 102922944501Smrg else 1030e88f27b3Smrg new_fb_id = pipe->fb_id[0]; 1031e88f27b3Smrg 1032e88f27b3Smrg drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id, 1033e88f27b3Smrg DRM_MODE_PAGE_FLIP_EVENT, pipe); 1034e88f27b3Smrg pipe->current_fb_id = new_fb_id; 1035e88f27b3Smrg pipe->swap_count++; 1036e88f27b3Smrg if (pipe->swap_count == 60) { 103722944501Smrg gettimeofday(&end, NULL); 103822944501Smrg t = end.tv_sec + end.tv_usec * 1e-6 - 1039e88f27b3Smrg (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); 1040e88f27b3Smrg fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); 1041e88f27b3Smrg pipe->swap_count = 0; 1042e88f27b3Smrg pipe->start = end; 104322944501Smrg } 104422944501Smrg} 104522944501Smrg 1046424e9256Smrgstatic bool format_support(const drmModePlanePtr ovr, uint32_t fmt) 1047424e9256Smrg{ 1048424e9256Smrg unsigned int i; 1049424e9256Smrg 1050424e9256Smrg for (i = 0; i < ovr->count_formats; ++i) { 1051424e9256Smrg if (ovr->formats[i] == fmt) 1052424e9256Smrg return true; 1053424e9256Smrg } 1054424e9256Smrg 1055424e9256Smrg return false; 1056424e9256Smrg} 1057424e9256Smrg 1058e88f27b3Smrgstatic int set_plane(struct device *dev, struct plane_arg *p) 105918210155Smrg{ 1060e88f27b3Smrg drmModePlane *ovr; 1061424e9256Smrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 10622ee35494Smrg uint32_t plane_id; 10633c748557Ssnj struct bo *plane_bo; 1064e88f27b3Smrg uint32_t plane_flags = 0; 1065e88f27b3Smrg int crtc_x, crtc_y, crtc_w, crtc_h; 1066e88f27b3Smrg struct crtc *crtc = NULL; 1067e88f27b3Smrg unsigned int pipe; 1068e88f27b3Smrg unsigned int i; 1069e88f27b3Smrg 1070e88f27b3Smrg /* Find an unused plane which can be connected to our CRTC. Find the 1071e88f27b3Smrg * CRTC index first, then iterate over available planes. 1072e88f27b3Smrg */ 1073e88f27b3Smrg for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { 1074e88f27b3Smrg if (p->crtc_id == dev->resources->res->crtcs[i]) { 1075e88f27b3Smrg crtc = &dev->resources->crtcs[i]; 1076e88f27b3Smrg pipe = i; 1077e88f27b3Smrg break; 1078e88f27b3Smrg } 1079e88f27b3Smrg } 108018210155Smrg 1081e88f27b3Smrg if (!crtc) { 1082e88f27b3Smrg fprintf(stderr, "CRTC %u not found\n", p->crtc_id); 1083e88f27b3Smrg return -1; 1084e88f27b3Smrg } 1085e88f27b3Smrg 10862ee35494Smrg plane_id = p->plane_id; 10872ee35494Smrg 10882ee35494Smrg for (i = 0; i < dev->resources->plane_res->count_planes; i++) { 1089e88f27b3Smrg ovr = dev->resources->planes[i].plane; 10902ee35494Smrg if (!ovr) 10912ee35494Smrg continue; 10922ee35494Smrg 10932ee35494Smrg if (plane_id && plane_id != ovr->plane_id) 10942ee35494Smrg continue; 10952ee35494Smrg 10962ee35494Smrg if (!format_support(ovr, p->fourcc)) 109718210155Smrg continue; 1098e88f27b3Smrg 10992b90624aSmrg if ((ovr->possible_crtcs & (1 << pipe)) && 11002b90624aSmrg (ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) { 1101e88f27b3Smrg plane_id = ovr->plane_id; 11022ee35494Smrg break; 11032ee35494Smrg } 110418210155Smrg } 110518210155Smrg 11062ee35494Smrg if (i == dev->resources->plane_res->count_planes) { 1107e88f27b3Smrg fprintf(stderr, "no unused plane available for CRTC %u\n", 1108e88f27b3Smrg crtc->crtc->crtc_id); 1109e88f27b3Smrg return -1; 1110e88f27b3Smrg } 1111e88f27b3Smrg 1112e88f27b3Smrg fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", 1113e88f27b3Smrg p->w, p->h, p->format_str, plane_id); 1114e88f27b3Smrg 11153c748557Ssnj plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles, 1116fe517fc9Smrg pitches, offsets, UTIL_PATTERN_TILES); 1117e88f27b3Smrg if (plane_bo == NULL) 1118e88f27b3Smrg return -1; 1119e88f27b3Smrg 1120424e9256Smrg p->bo = plane_bo; 1121424e9256Smrg 1122e88f27b3Smrg /* just use single plane format for now.. */ 1123e88f27b3Smrg if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, 1124e88f27b3Smrg handles, pitches, offsets, &p->fb_id, plane_flags)) { 1125e88f27b3Smrg fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1126e88f27b3Smrg return -1; 1127e88f27b3Smrg } 1128e88f27b3Smrg 1129e88f27b3Smrg crtc_w = p->w * p->scale; 1130e88f27b3Smrg crtc_h = p->h * p->scale; 1131e88f27b3Smrg if (!p->has_position) { 1132e88f27b3Smrg /* Default to the middle of the screen */ 1133e88f27b3Smrg crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; 1134e88f27b3Smrg crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; 1135e88f27b3Smrg } else { 1136e88f27b3Smrg crtc_x = p->x; 1137e88f27b3Smrg crtc_y = p->y; 113818210155Smrg } 113918210155Smrg 1140e88f27b3Smrg /* note src coords (last 4 args) are in Q16 format */ 1141e88f27b3Smrg if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id, 1142e88f27b3Smrg plane_flags, crtc_x, crtc_y, crtc_w, crtc_h, 1143e88f27b3Smrg 0, 0, p->w << 16, p->h << 16)) { 1144e88f27b3Smrg fprintf(stderr, "failed to enable plane: %s\n", 1145e88f27b3Smrg strerror(errno)); 1146e88f27b3Smrg return -1; 1147e88f27b3Smrg } 1148e88f27b3Smrg 1149e88f27b3Smrg ovr->crtc_id = crtc->crtc->crtc_id; 1150e88f27b3Smrg 1151e88f27b3Smrg return 0; 1152e88f27b3Smrg} 1153e88f27b3Smrg 1154424e9256Smrgstatic void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1155424e9256Smrg{ 1156424e9256Smrg unsigned int i; 1157424e9256Smrg 1158424e9256Smrg for (i = 0; i < count; i++) { 1159424e9256Smrg if (p[i].fb_id) 1160424e9256Smrg drmModeRmFB(dev->fd, p[i].fb_id); 1161424e9256Smrg if (p[i].bo) 1162424e9256Smrg bo_destroy(p[i].bo); 1163424e9256Smrg } 1164424e9256Smrg} 1165424e9256Smrg 1166424e9256Smrg 1167e88f27b3Smrgstatic void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1168e88f27b3Smrg{ 1169424e9256Smrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1170e88f27b3Smrg unsigned int fb_id; 11713c748557Ssnj struct bo *bo; 1172e88f27b3Smrg unsigned int i; 1173e88f27b3Smrg unsigned int j; 1174e88f27b3Smrg int ret, x; 1175e88f27b3Smrg 1176e88f27b3Smrg dev->mode.width = 0; 1177e88f27b3Smrg dev->mode.height = 0; 1178424e9256Smrg dev->mode.fb_id = 0; 1179e88f27b3Smrg 1180e88f27b3Smrg for (i = 0; i < count; i++) { 1181e88f27b3Smrg struct pipe_arg *pipe = &pipes[i]; 1182e88f27b3Smrg 1183e88f27b3Smrg ret = pipe_find_crtc_and_mode(dev, pipe); 1184e88f27b3Smrg if (ret < 0) 1185e88f27b3Smrg continue; 1186e88f27b3Smrg 1187e88f27b3Smrg dev->mode.width += pipe->mode->hdisplay; 1188e88f27b3Smrg if (dev->mode.height < pipe->mode->vdisplay) 1189e88f27b3Smrg dev->mode.height = pipe->mode->vdisplay; 1190e88f27b3Smrg } 1191e88f27b3Smrg 1192fe517fc9Smrg bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1193fe517fc9Smrg dev->mode.height, handles, pitches, offsets, 1194fe517fc9Smrg UTIL_PATTERN_SMPTE); 1195e88f27b3Smrg if (bo == NULL) 119618210155Smrg return; 119718210155Smrg 1198424e9256Smrg dev->mode.bo = bo; 1199424e9256Smrg 1200e88f27b3Smrg ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1201e88f27b3Smrg pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0); 120218210155Smrg if (ret) { 1203e88f27b3Smrg fprintf(stderr, "failed to add fb (%ux%u): %s\n", 1204e88f27b3Smrg dev->mode.width, dev->mode.height, strerror(errno)); 120518210155Smrg return; 120618210155Smrg } 120718210155Smrg 1208424e9256Smrg dev->mode.fb_id = fb_id; 1209424e9256Smrg 121018210155Smrg x = 0; 121118210155Smrg for (i = 0; i < count; i++) { 1212e88f27b3Smrg struct pipe_arg *pipe = &pipes[i]; 1213e88f27b3Smrg 1214e88f27b3Smrg if (pipe->mode == NULL) 121518210155Smrg continue; 121618210155Smrg 1217e88f27b3Smrg printf("setting mode %s-%dHz@%s on connectors ", 1218e88f27b3Smrg pipe->mode_str, pipe->mode->vrefresh, pipe->format_str); 1219e88f27b3Smrg for (j = 0; j < pipe->num_cons; ++j) 1220fe517fc9Smrg printf("%s, ", pipe->cons[j]); 1221e88f27b3Smrg printf("crtc %d\n", pipe->crtc->crtc->crtc_id); 1222e88f27b3Smrg 1223e88f27b3Smrg ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id, 1224e88f27b3Smrg x, 0, pipe->con_ids, pipe->num_cons, 1225e88f27b3Smrg pipe->mode); 1226e88f27b3Smrg 1227e88f27b3Smrg /* XXX: Actually check if this is needed */ 1228e88f27b3Smrg drmModeDirtyFB(dev->fd, fb_id, NULL, 0); 122918210155Smrg 1230e88f27b3Smrg x += pipe->mode->hdisplay; 123118210155Smrg 123218210155Smrg if (ret) { 123318210155Smrg fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); 123418210155Smrg return; 123518210155Smrg } 123618210155Smrg } 1237424e9256Smrg} 123822944501Smrg 1239424e9256Smrgstatic void clear_mode(struct device *dev) 1240424e9256Smrg{ 1241424e9256Smrg if (dev->mode.fb_id) 1242424e9256Smrg drmModeRmFB(dev->fd, dev->mode.fb_id); 1243424e9256Smrg if (dev->mode.bo) 1244424e9256Smrg bo_destroy(dev->mode.bo); 1245e88f27b3Smrg} 124622944501Smrg 1247e88f27b3Smrgstatic void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) 1248e88f27b3Smrg{ 1249e88f27b3Smrg unsigned int i; 1250e88f27b3Smrg 1251e88f27b3Smrg /* set up planes/overlays */ 1252e88f27b3Smrg for (i = 0; i < count; i++) 1253e88f27b3Smrg if (set_plane(dev, &p[i])) 1254e88f27b3Smrg return; 1255e88f27b3Smrg} 1256e88f27b3Smrg 1257a7d7de1eSmrgstatic void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1258a7d7de1eSmrg{ 1259424e9256Smrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 12603c748557Ssnj struct bo *bo; 1261a7d7de1eSmrg unsigned int i; 1262a7d7de1eSmrg int ret; 1263a7d7de1eSmrg 1264a7d7de1eSmrg /* maybe make cursor width/height configurable some day */ 1265a7d7de1eSmrg uint32_t cw = 64; 1266a7d7de1eSmrg uint32_t ch = 64; 1267a7d7de1eSmrg 1268a7d7de1eSmrg /* create cursor bo.. just using PATTERN_PLAIN as it has 1269a7d7de1eSmrg * translucent alpha 1270a7d7de1eSmrg */ 12713c748557Ssnj bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches, 1272fe517fc9Smrg offsets, UTIL_PATTERN_PLAIN); 1273a7d7de1eSmrg if (bo == NULL) 1274a7d7de1eSmrg return; 1275a7d7de1eSmrg 1276424e9256Smrg dev->mode.cursor_bo = bo; 1277424e9256Smrg 1278a7d7de1eSmrg for (i = 0; i < count; i++) { 1279a7d7de1eSmrg struct pipe_arg *pipe = &pipes[i]; 1280a7d7de1eSmrg ret = cursor_init(dev->fd, handles[0], 1281a7d7de1eSmrg pipe->crtc->crtc->crtc_id, 1282a7d7de1eSmrg pipe->mode->hdisplay, pipe->mode->vdisplay, 1283a7d7de1eSmrg cw, ch); 1284a7d7de1eSmrg if (ret) { 1285a7d7de1eSmrg fprintf(stderr, "failed to init cursor for CRTC[%u]\n", 1286a7d7de1eSmrg pipe->crtc_id); 1287a7d7de1eSmrg return; 1288a7d7de1eSmrg } 1289a7d7de1eSmrg } 1290a7d7de1eSmrg 1291a7d7de1eSmrg cursor_start(); 1292a7d7de1eSmrg} 1293a7d7de1eSmrg 1294a7d7de1eSmrgstatic void clear_cursors(struct device *dev) 1295a7d7de1eSmrg{ 1296a7d7de1eSmrg cursor_stop(); 1297424e9256Smrg 1298424e9256Smrg if (dev->mode.cursor_bo) 1299424e9256Smrg bo_destroy(dev->mode.cursor_bo); 1300a7d7de1eSmrg} 1301a7d7de1eSmrg 1302e88f27b3Smrgstatic void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) 1303e88f27b3Smrg{ 1304424e9256Smrg uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; 1305e88f27b3Smrg unsigned int other_fb_id; 13063c748557Ssnj struct bo *other_bo; 1307e88f27b3Smrg drmEventContext evctx; 1308e88f27b3Smrg unsigned int i; 1309e88f27b3Smrg int ret; 1310e88f27b3Smrg 1311fe517fc9Smrg other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, 1312fe517fc9Smrg dev->mode.height, handles, pitches, offsets, 1313fe517fc9Smrg UTIL_PATTERN_PLAIN); 1314e88f27b3Smrg if (other_bo == NULL) 131522944501Smrg return; 131622944501Smrg 1317e88f27b3Smrg ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, 1318e88f27b3Smrg pipes[0].fourcc, handles, pitches, offsets, 1319e88f27b3Smrg &other_fb_id, 0); 132022944501Smrg if (ret) { 132122944501Smrg fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); 1322424e9256Smrg goto err; 132322944501Smrg } 132422944501Smrg 132522944501Smrg for (i = 0; i < count; i++) { 1326e88f27b3Smrg struct pipe_arg *pipe = &pipes[i]; 1327e88f27b3Smrg 1328e88f27b3Smrg if (pipe->mode == NULL) 132922944501Smrg continue; 133022944501Smrg 1331e88f27b3Smrg ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id, 1332e88f27b3Smrg other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 1333e88f27b3Smrg pipe); 1334e88f27b3Smrg if (ret) { 1335e88f27b3Smrg fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); 1336424e9256Smrg goto err_rmfb; 1337e88f27b3Smrg } 1338e88f27b3Smrg gettimeofday(&pipe->start, NULL); 1339e88f27b3Smrg pipe->swap_count = 0; 1340e88f27b3Smrg pipe->fb_id[0] = dev->mode.fb_id; 1341e88f27b3Smrg pipe->fb_id[1] = other_fb_id; 1342e88f27b3Smrg pipe->current_fb_id = other_fb_id; 134322944501Smrg } 134422944501Smrg 134522944501Smrg memset(&evctx, 0, sizeof evctx); 134622944501Smrg evctx.version = DRM_EVENT_CONTEXT_VERSION; 134722944501Smrg evctx.vblank_handler = NULL; 134822944501Smrg evctx.page_flip_handler = page_flip_handler; 1349fe517fc9Smrg 135022944501Smrg while (1) { 135122944501Smrg#if 0 135222944501Smrg struct pollfd pfd[2]; 135322944501Smrg 135422944501Smrg pfd[0].fd = 0; 135522944501Smrg pfd[0].events = POLLIN; 135622944501Smrg pfd[1].fd = fd; 135722944501Smrg pfd[1].events = POLLIN; 135822944501Smrg 135922944501Smrg if (poll(pfd, 2, -1) < 0) { 136022944501Smrg fprintf(stderr, "poll error\n"); 136122944501Smrg break; 136222944501Smrg } 136322944501Smrg 136422944501Smrg if (pfd[0].revents) 136522944501Smrg break; 136622944501Smrg#else 136722944501Smrg struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; 136822944501Smrg fd_set fds; 136922944501Smrg 137022944501Smrg FD_ZERO(&fds); 137122944501Smrg FD_SET(0, &fds); 1372e88f27b3Smrg FD_SET(dev->fd, &fds); 1373e88f27b3Smrg ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); 137422944501Smrg 137522944501Smrg if (ret <= 0) { 137622944501Smrg fprintf(stderr, "select timed out or error (ret %d)\n", 137722944501Smrg ret); 137822944501Smrg continue; 137922944501Smrg } else if (FD_ISSET(0, &fds)) { 138022944501Smrg break; 138122944501Smrg } 138222944501Smrg#endif 138322944501Smrg 1384e88f27b3Smrg drmHandleEvent(dev->fd, &evctx); 138522944501Smrg } 1386e88f27b3Smrg 1387424e9256Smrgerr_rmfb: 1388424e9256Smrg drmModeRmFB(dev->fd, other_fb_id); 1389424e9256Smrgerr: 13903c748557Ssnj bo_destroy(other_bo); 139118210155Smrg} 139218210155Smrg 1393e88f27b3Smrg#define min(a, b) ((a) < (b) ? (a) : (b)) 139418210155Smrg 1395e88f27b3Smrgstatic int parse_connector(struct pipe_arg *pipe, const char *arg) 139618210155Smrg{ 1397e88f27b3Smrg unsigned int len; 1398e88f27b3Smrg unsigned int i; 1399e88f27b3Smrg const char *p; 1400e88f27b3Smrg char *endp; 1401e88f27b3Smrg 1402e88f27b3Smrg pipe->vrefresh = 0; 1403e88f27b3Smrg pipe->crtc_id = (uint32_t)-1; 1404e88f27b3Smrg strcpy(pipe->format_str, "XR24"); 1405e88f27b3Smrg 1406e88f27b3Smrg /* Count the number of connectors and allocate them. */ 1407e88f27b3Smrg pipe->num_cons = 1; 1408fe517fc9Smrg for (p = arg; *p && *p != ':' && *p != '@'; ++p) { 1409e88f27b3Smrg if (*p == ',') 1410e88f27b3Smrg pipe->num_cons++; 1411e88f27b3Smrg } 1412e88f27b3Smrg 1413424e9256Smrg pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids)); 1414fe517fc9Smrg pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons)); 1415fe517fc9Smrg if (pipe->con_ids == NULL || pipe->cons == NULL) 1416e88f27b3Smrg return -1; 1417e88f27b3Smrg 1418e88f27b3Smrg /* Parse the connectors. */ 1419e88f27b3Smrg for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { 1420fe517fc9Smrg endp = strpbrk(p, ",@:"); 1421fe517fc9Smrg if (!endp) 1422fe517fc9Smrg break; 1423fe517fc9Smrg 1424fe517fc9Smrg pipe->cons[i] = strndup(p, endp - p); 1425fe517fc9Smrg 1426e88f27b3Smrg if (*endp != ',') 1427e88f27b3Smrg break; 1428e88f27b3Smrg } 1429e88f27b3Smrg 1430e88f27b3Smrg if (i != pipe->num_cons - 1) 1431e88f27b3Smrg return -1; 1432e88f27b3Smrg 1433e88f27b3Smrg /* Parse the remaining parameters. */ 1434e88f27b3Smrg if (*endp == '@') { 1435e88f27b3Smrg arg = endp + 1; 1436e88f27b3Smrg pipe->crtc_id = strtoul(arg, &endp, 10); 1437e88f27b3Smrg } 1438e88f27b3Smrg if (*endp != ':') 1439e88f27b3Smrg return -1; 1440e88f27b3Smrg 1441e88f27b3Smrg arg = endp + 1; 1442e88f27b3Smrg 1443e88f27b3Smrg /* Search for the vertical refresh or the format. */ 1444e88f27b3Smrg p = strpbrk(arg, "-@"); 1445e88f27b3Smrg if (p == NULL) 1446e88f27b3Smrg p = arg + strlen(arg); 1447e88f27b3Smrg len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); 1448e88f27b3Smrg strncpy(pipe->mode_str, arg, len); 1449e88f27b3Smrg pipe->mode_str[len] = '\0'; 1450e88f27b3Smrg 1451e88f27b3Smrg if (*p == '-') { 1452e88f27b3Smrg pipe->vrefresh = strtoul(p + 1, &endp, 10); 1453e88f27b3Smrg p = endp; 1454e88f27b3Smrg } 1455e88f27b3Smrg 1456e88f27b3Smrg if (*p == '@') { 1457e88f27b3Smrg strncpy(pipe->format_str, p + 1, 4); 1458e88f27b3Smrg pipe->format_str[4] = '\0'; 1459e88f27b3Smrg } 1460e88f27b3Smrg 1461fe517fc9Smrg pipe->fourcc = util_format_fourcc(pipe->format_str); 1462e88f27b3Smrg if (pipe->fourcc == 0) { 1463e88f27b3Smrg fprintf(stderr, "unknown format %s\n", pipe->format_str); 1464e88f27b3Smrg return -1; 1465e88f27b3Smrg } 1466e88f27b3Smrg 1467e88f27b3Smrg return 0; 1468e88f27b3Smrg} 1469e88f27b3Smrg 1470e88f27b3Smrgstatic int parse_plane(struct plane_arg *plane, const char *p) 1471e88f27b3Smrg{ 1472e88f27b3Smrg char *end; 1473e88f27b3Smrg 14742ee35494Smrg plane->plane_id = strtoul(p, &end, 10); 14752ee35494Smrg if (*end != '@') 14762ee35494Smrg return -EINVAL; 14772ee35494Smrg 14782ee35494Smrg p = end + 1; 1479e88f27b3Smrg plane->crtc_id = strtoul(p, &end, 10); 1480e88f27b3Smrg if (*end != ':') 1481e88f27b3Smrg return -EINVAL; 1482e88f27b3Smrg 1483e88f27b3Smrg p = end + 1; 1484e88f27b3Smrg plane->w = strtoul(p, &end, 10); 1485e88f27b3Smrg if (*end != 'x') 1486e88f27b3Smrg return -EINVAL; 1487e88f27b3Smrg 1488e88f27b3Smrg p = end + 1; 1489e88f27b3Smrg plane->h = strtoul(p, &end, 10); 1490e88f27b3Smrg 1491e88f27b3Smrg if (*end == '+' || *end == '-') { 1492e88f27b3Smrg plane->x = strtol(end, &end, 10); 1493e88f27b3Smrg if (*end != '+' && *end != '-') 1494e88f27b3Smrg return -EINVAL; 1495e88f27b3Smrg plane->y = strtol(end, &end, 10); 1496e88f27b3Smrg 1497e88f27b3Smrg plane->has_position = true; 1498e88f27b3Smrg } 1499e88f27b3Smrg 1500e88f27b3Smrg if (*end == '*') { 1501e88f27b3Smrg p = end + 1; 1502e88f27b3Smrg plane->scale = strtod(p, &end); 1503e88f27b3Smrg if (plane->scale <= 0.0) 1504e88f27b3Smrg return -EINVAL; 1505e88f27b3Smrg } else { 1506e88f27b3Smrg plane->scale = 1.0; 1507e88f27b3Smrg } 1508e88f27b3Smrg 1509e88f27b3Smrg if (*end == '@') { 1510e88f27b3Smrg p = end + 1; 1511e88f27b3Smrg if (strlen(p) != 4) 1512e88f27b3Smrg return -EINVAL; 1513e88f27b3Smrg 1514e88f27b3Smrg strcpy(plane->format_str, p); 1515e88f27b3Smrg } else { 1516e88f27b3Smrg strcpy(plane->format_str, "XR24"); 1517e88f27b3Smrg } 1518e88f27b3Smrg 1519fe517fc9Smrg plane->fourcc = util_format_fourcc(plane->format_str); 1520e88f27b3Smrg if (plane->fourcc == 0) { 1521e88f27b3Smrg fprintf(stderr, "unknown format %s\n", plane->format_str); 1522e88f27b3Smrg return -EINVAL; 1523e88f27b3Smrg } 1524e88f27b3Smrg 1525e88f27b3Smrg return 0; 1526e88f27b3Smrg} 1527e88f27b3Smrg 1528e88f27b3Smrgstatic int parse_property(struct property_arg *p, const char *arg) 1529e88f27b3Smrg{ 1530e88f27b3Smrg if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) 1531e88f27b3Smrg return -1; 1532e88f27b3Smrg 1533e88f27b3Smrg p->obj_type = 0; 1534e88f27b3Smrg p->name[DRM_PROP_NAME_LEN] = '\0'; 1535e88f27b3Smrg 1536e88f27b3Smrg return 0; 1537e88f27b3Smrg} 1538e88f27b3Smrg 1539e88f27b3Smrgstatic void usage(char *name) 1540e88f27b3Smrg{ 1541a7d7de1eSmrg fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name); 1542e88f27b3Smrg 1543e88f27b3Smrg fprintf(stderr, "\n Query options:\n\n"); 154418210155Smrg fprintf(stderr, "\t-c\tlist connectors\n"); 1545e88f27b3Smrg fprintf(stderr, "\t-e\tlist encoders\n"); 154618210155Smrg fprintf(stderr, "\t-f\tlist framebuffers\n"); 1547e88f27b3Smrg fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); 1548e88f27b3Smrg 1549e88f27b3Smrg fprintf(stderr, "\n Test options:\n\n"); 15502ee35494Smrg fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n"); 1551e88f27b3Smrg fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n"); 1552a7d7de1eSmrg fprintf(stderr, "\t-C\ttest hw cursor\n"); 155322944501Smrg fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); 1554e88f27b3Smrg fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); 1555e88f27b3Smrg 1556e88f27b3Smrg fprintf(stderr, "\n Generic options:\n\n"); 1557e88f27b3Smrg fprintf(stderr, "\t-d\tdrop master after mode set\n"); 1558e88f27b3Smrg fprintf(stderr, "\t-M module\tuse the given driver\n"); 1559e88f27b3Smrg fprintf(stderr, "\t-D device\tuse the given device\n"); 1560e88f27b3Smrg 156118210155Smrg fprintf(stderr, "\n\tDefault is to dump all info.\n"); 156218210155Smrg exit(0); 156318210155Smrg} 156418210155Smrg 1565e88f27b3Smrgstatic int page_flipping_supported(void) 156622944501Smrg{ 1567e88f27b3Smrg /*FIXME: generic ioctl needed? */ 1568e88f27b3Smrg return 1; 1569e88f27b3Smrg#if 0 157022944501Smrg int ret, value; 157122944501Smrg struct drm_i915_getparam gp; 157222944501Smrg 157322944501Smrg gp.param = I915_PARAM_HAS_PAGEFLIPPING; 157422944501Smrg gp.value = &value; 157522944501Smrg 157622944501Smrg ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); 157722944501Smrg if (ret) { 157822944501Smrg fprintf(stderr, "drm_i915_getparam: %m\n"); 157922944501Smrg return 0; 158022944501Smrg } 158122944501Smrg 158222944501Smrg return *gp.value; 1583e88f27b3Smrg#endif 158422944501Smrg} 158522944501Smrg 1586a7d7de1eSmrgstatic int cursor_supported(void) 1587a7d7de1eSmrg{ 1588a7d7de1eSmrg /*FIXME: generic ioctl needed? */ 1589a7d7de1eSmrg return 1; 1590a7d7de1eSmrg} 1591a7d7de1eSmrg 1592fe517fc9Smrgstatic int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe) 1593fe517fc9Smrg{ 1594fe517fc9Smrg drmModeConnector *connector; 1595fe517fc9Smrg unsigned int i; 1596fe517fc9Smrg uint32_t id; 1597fe517fc9Smrg char *endp; 1598fe517fc9Smrg 1599fe517fc9Smrg for (i = 0; i < pipe->num_cons; i++) { 1600fe517fc9Smrg id = strtoul(pipe->cons[i], &endp, 10); 1601fe517fc9Smrg if (endp == pipe->cons[i]) { 1602fe517fc9Smrg connector = get_connector_by_name(dev, pipe->cons[i]); 1603fe517fc9Smrg if (!connector) { 1604fe517fc9Smrg fprintf(stderr, "no connector named '%s'\n", 1605fe517fc9Smrg pipe->cons[i]); 1606fe517fc9Smrg return -ENODEV; 1607fe517fc9Smrg } 1608fe517fc9Smrg 1609fe517fc9Smrg id = connector->connector_id; 1610fe517fc9Smrg } 1611fe517fc9Smrg 1612fe517fc9Smrg pipe->con_ids[i] = id; 1613fe517fc9Smrg } 1614fe517fc9Smrg 1615fe517fc9Smrg return 0; 1616fe517fc9Smrg} 1617fe517fc9Smrg 1618a7d7de1eSmrgstatic char optstr[] = "cdD:efM:P:ps:Cvw:"; 1619e88f27b3Smrg 162018210155Smrgint main(int argc, char **argv) 162118210155Smrg{ 1622e88f27b3Smrg struct device dev; 1623e88f27b3Smrg 162418210155Smrg int c; 1625e88f27b3Smrg int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; 1626e88f27b3Smrg int drop_master = 0; 162722944501Smrg int test_vsync = 0; 1628a7d7de1eSmrg int test_cursor = 0; 1629e88f27b3Smrg char *device = NULL; 1630e88f27b3Smrg char *module = NULL; 1631e88f27b3Smrg unsigned int i; 1632fe517fc9Smrg unsigned int count = 0, plane_count = 0; 1633e88f27b3Smrg unsigned int prop_count = 0; 1634e88f27b3Smrg struct pipe_arg *pipe_args = NULL; 1635e88f27b3Smrg struct plane_arg *plane_args = NULL; 1636e88f27b3Smrg struct property_arg *prop_args = NULL; 1637e88f27b3Smrg unsigned int args = 0; 1638e88f27b3Smrg int ret; 1639e88f27b3Smrg 1640e88f27b3Smrg memset(&dev, 0, sizeof dev); 1641e88f27b3Smrg 164218210155Smrg opterr = 0; 164318210155Smrg while ((c = getopt(argc, argv, optstr)) != -1) { 1644e88f27b3Smrg args++; 1645e88f27b3Smrg 164618210155Smrg switch (c) { 164718210155Smrg case 'c': 164818210155Smrg connectors = 1; 164918210155Smrg break; 1650e88f27b3Smrg case 'D': 1651e88f27b3Smrg device = optarg; 1652e88f27b3Smrg args--; 1653e88f27b3Smrg break; 1654e88f27b3Smrg case 'd': 1655e88f27b3Smrg drop_master = 1; 165618210155Smrg break; 1657e88f27b3Smrg case 'e': 1658e88f27b3Smrg encoders = 1; 165918210155Smrg break; 166018210155Smrg case 'f': 166118210155Smrg framebuffers = 1; 166218210155Smrg break; 1663e88f27b3Smrg case 'M': 1664e88f27b3Smrg module = optarg; 1665e88f27b3Smrg /* Preserve the default behaviour of dumping all information. */ 1666e88f27b3Smrg args--; 1667e88f27b3Smrg break; 1668e88f27b3Smrg case 'P': 1669e88f27b3Smrg plane_args = realloc(plane_args, 1670e88f27b3Smrg (plane_count + 1) * sizeof *plane_args); 1671e88f27b3Smrg if (plane_args == NULL) { 1672e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 1673e88f27b3Smrg return 1; 1674e88f27b3Smrg } 1675424e9256Smrg memset(&plane_args[plane_count], 0, sizeof(*plane_args)); 1676e88f27b3Smrg 1677e88f27b3Smrg if (parse_plane(&plane_args[plane_count], optarg) < 0) 1678e88f27b3Smrg usage(argv[0]); 1679e88f27b3Smrg 1680e88f27b3Smrg plane_count++; 1681e88f27b3Smrg break; 1682e88f27b3Smrg case 'p': 1683e88f27b3Smrg crtcs = 1; 1684e88f27b3Smrg planes = 1; 168522944501Smrg break; 168618210155Smrg case 's': 1687e88f27b3Smrg pipe_args = realloc(pipe_args, 1688e88f27b3Smrg (count + 1) * sizeof *pipe_args); 1689e88f27b3Smrg if (pipe_args == NULL) { 1690e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 1691e88f27b3Smrg return 1; 1692e88f27b3Smrg } 1693424e9256Smrg memset(&pipe_args[count], 0, sizeof(*pipe_args)); 1694e88f27b3Smrg 1695e88f27b3Smrg if (parse_connector(&pipe_args[count], optarg) < 0) 169618210155Smrg usage(argv[0]); 1697e88f27b3Smrg 1698fe517fc9Smrg count++; 169918210155Smrg break; 1700a7d7de1eSmrg case 'C': 1701a7d7de1eSmrg test_cursor = 1; 1702a7d7de1eSmrg break; 1703e88f27b3Smrg case 'v': 1704e88f27b3Smrg test_vsync = 1; 1705e88f27b3Smrg break; 1706e88f27b3Smrg case 'w': 1707e88f27b3Smrg prop_args = realloc(prop_args, 1708e88f27b3Smrg (prop_count + 1) * sizeof *prop_args); 1709e88f27b3Smrg if (prop_args == NULL) { 1710e88f27b3Smrg fprintf(stderr, "memory allocation failed\n"); 1711e88f27b3Smrg return 1; 1712e88f27b3Smrg } 1713424e9256Smrg memset(&prop_args[prop_count], 0, sizeof(*prop_args)); 1714e88f27b3Smrg 1715e88f27b3Smrg if (parse_property(&prop_args[prop_count], optarg) < 0) 1716e88f27b3Smrg usage(argv[0]); 1717e88f27b3Smrg 1718e88f27b3Smrg prop_count++; 1719e88f27b3Smrg break; 172018210155Smrg default: 172118210155Smrg usage(argv[0]); 172218210155Smrg break; 172318210155Smrg } 172418210155Smrg } 172518210155Smrg 1726e88f27b3Smrg if (!args) 1727e88f27b3Smrg encoders = connectors = crtcs = planes = framebuffers = 1; 172818210155Smrg 1729fe517fc9Smrg dev.fd = util_open(device, module); 1730fe517fc9Smrg if (dev.fd < 0) 1731fe517fc9Smrg return -1; 173218210155Smrg 1733e88f27b3Smrg if (test_vsync && !page_flipping_supported()) { 173422944501Smrg fprintf(stderr, "page flipping not supported by drm.\n"); 173522944501Smrg return -1; 173622944501Smrg } 173722944501Smrg 1738e88f27b3Smrg if (test_vsync && !count) { 1739e88f27b3Smrg fprintf(stderr, "page flipping requires at least one -s option.\n"); 174018210155Smrg return -1; 174118210155Smrg } 174218210155Smrg 1743a7d7de1eSmrg if (test_cursor && !cursor_supported()) { 1744a7d7de1eSmrg fprintf(stderr, "hw cursor not supported by drm.\n"); 1745a7d7de1eSmrg return -1; 1746a7d7de1eSmrg } 1747a7d7de1eSmrg 1748e88f27b3Smrg dev.resources = get_resources(&dev); 1749e88f27b3Smrg if (!dev.resources) { 1750e88f27b3Smrg drmClose(dev.fd); 175118210155Smrg return 1; 175218210155Smrg } 175318210155Smrg 1754fe517fc9Smrg for (i = 0; i < count; i++) { 1755fe517fc9Smrg if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) { 1756fe517fc9Smrg free_resources(dev.resources); 1757fe517fc9Smrg drmClose(dev.fd); 1758fe517fc9Smrg return 1; 1759fe517fc9Smrg } 1760fe517fc9Smrg } 1761fe517fc9Smrg 1762e88f27b3Smrg#define dump_resource(dev, res) if (res) dump_##res(dev) 1763e88f27b3Smrg 1764e88f27b3Smrg dump_resource(&dev, encoders); 1765e88f27b3Smrg dump_resource(&dev, connectors); 1766e88f27b3Smrg dump_resource(&dev, crtcs); 1767e88f27b3Smrg dump_resource(&dev, planes); 1768e88f27b3Smrg dump_resource(&dev, framebuffers); 1769e88f27b3Smrg 1770e88f27b3Smrg for (i = 0; i < prop_count; ++i) 1771e88f27b3Smrg set_property(&dev, &prop_args[i]); 1772e88f27b3Smrg 1773e88f27b3Smrg if (count || plane_count) { 17743c748557Ssnj uint64_t cap = 0; 17753c748557Ssnj 17763c748557Ssnj ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap); 17773c748557Ssnj if (ret || cap == 0) { 17783c748557Ssnj fprintf(stderr, "driver doesn't support the dumb buffer API\n"); 1779e88f27b3Smrg return 1; 1780e88f27b3Smrg } 1781e88f27b3Smrg 1782e88f27b3Smrg if (count) 1783e88f27b3Smrg set_mode(&dev, pipe_args, count); 1784e88f27b3Smrg 1785e88f27b3Smrg if (plane_count) 1786e88f27b3Smrg set_planes(&dev, plane_args, plane_count); 1787e88f27b3Smrg 1788a7d7de1eSmrg if (test_cursor) 1789a7d7de1eSmrg set_cursors(&dev, pipe_args, count); 1790a7d7de1eSmrg 1791e88f27b3Smrg if (test_vsync) 1792e88f27b3Smrg test_page_flip(&dev, pipe_args, count); 1793e88f27b3Smrg 1794e88f27b3Smrg if (drop_master) 1795e88f27b3Smrg drmDropMaster(dev.fd); 1796e88f27b3Smrg 1797a7d7de1eSmrg getchar(); 1798a7d7de1eSmrg 1799a7d7de1eSmrg if (test_cursor) 1800a7d7de1eSmrg clear_cursors(&dev); 1801a7d7de1eSmrg 18023c748557Ssnj bo_destroy(dev.mode.bo); 180318210155Smrg } 180418210155Smrg 1805e88f27b3Smrg free_resources(dev.resources); 180618210155Smrg 180718210155Smrg return 0; 180818210155Smrg} 1809