2008-12-17 11:09:49 -07:00
|
|
|
/*
|
|
|
|
* DRM based mode setting test program
|
|
|
|
* Copyright 2008 Tungsten Graphics
|
|
|
|
* Jakob Bornecrantz <jakob@tungstengraphics.com>
|
|
|
|
* Copyright 2008 Intel Corporation
|
|
|
|
* Jesse Barnes <jesse.barnes@intel.com>
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This fairly simple test program dumps output in a similar format to the
|
|
|
|
* "xrandr" tool everyone knows & loves. It's necessarily slightly different
|
|
|
|
* since the kernel separates outputs into encoder and connector structures,
|
|
|
|
* each with their own unique ID. The program also allows test testing of the
|
|
|
|
* memory management and mode setting APIs by allowing the user to specify a
|
|
|
|
* connector and mode to use for mode setting. If all works as expected, a
|
|
|
|
* blue background should be painted on the monitor attached to the specified
|
|
|
|
* connector after the selected mode is set.
|
|
|
|
*
|
|
|
|
* TODO: use cairo to write the mode info on the selected output once
|
|
|
|
* the mode has been programmed, along with possible test patterns.
|
|
|
|
*/
|
2015-12-09 10:37:39 -07:00
|
|
|
|
2014-07-27 07:46:45 -06:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2009-02-03 13:03:41 -07:00
|
|
|
#include "config.h"
|
2014-07-27 07:46:45 -06:00
|
|
|
#endif
|
2009-02-03 13:03:41 -07:00
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
#include <assert.h>
|
2013-03-19 06:48:36 -06:00
|
|
|
#include <ctype.h>
|
2013-02-26 21:35:13 -07:00
|
|
|
#include <stdbool.h>
|
2008-12-17 11:09:49 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
2012-04-21 14:51:53 -06:00
|
|
|
#include <inttypes.h>
|
2008-12-17 11:09:49 -07:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
2015-04-16 11:55:40 -06:00
|
|
|
#include <strings.h>
|
2008-12-17 11:09:49 -07:00
|
|
|
#include <errno.h>
|
2016-01-19 20:27:28 -07:00
|
|
|
#include <poll.h>
|
2010-01-29 19:14:44 -07:00
|
|
|
#include <sys/time.h>
|
2016-01-19 22:35:11 -07:00
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
2008-12-17 11:09:49 -07:00
|
|
|
|
|
|
|
#include "xf86drm.h"
|
|
|
|
#include "xf86drmMode.h"
|
2011-12-14 20:06:43 -07:00
|
|
|
#include "drm_fourcc.h"
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2015-12-09 10:37:39 -07:00
|
|
|
#include "util/common.h"
|
|
|
|
#include "util/format.h"
|
2015-12-09 10:37:40 -07:00
|
|
|
#include "util/kms.h"
|
2015-12-09 10:37:39 -07:00
|
|
|
#include "util/pattern.h"
|
|
|
|
|
2012-07-20 08:37:00 -06:00
|
|
|
#include "buffers.h"
|
2014-04-22 08:33:12 -06:00
|
|
|
#include "cursor.h"
|
2009-02-03 13:03:41 -07:00
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
struct crtc {
|
|
|
|
drmModeCrtc *crtc;
|
|
|
|
drmModeObjectProperties *props;
|
|
|
|
drmModePropertyRes **props_info;
|
2013-02-26 21:35:13 -07:00
|
|
|
drmModeModeInfo *mode;
|
2013-02-26 22:39:36 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct encoder {
|
|
|
|
drmModeEncoder *encoder;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct connector {
|
|
|
|
drmModeConnector *connector;
|
|
|
|
drmModeObjectProperties *props;
|
|
|
|
drmModePropertyRes **props_info;
|
2015-01-23 09:08:21 -07:00
|
|
|
char *name;
|
2013-02-26 22:39:36 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct fb {
|
|
|
|
drmModeFB *fb;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct plane {
|
|
|
|
drmModePlane *plane;
|
|
|
|
drmModeObjectProperties *props;
|
|
|
|
drmModePropertyRes **props_info;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct resources {
|
|
|
|
drmModeRes *res;
|
|
|
|
drmModePlaneRes *plane_res;
|
|
|
|
|
|
|
|
struct crtc *crtcs;
|
|
|
|
struct encoder *encoders;
|
|
|
|
struct connector *connectors;
|
|
|
|
struct fb *fbs;
|
|
|
|
struct plane *planes;
|
|
|
|
};
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
struct device {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
struct resources *resources;
|
2013-02-26 21:35:13 -07:00
|
|
|
|
|
|
|
struct {
|
|
|
|
unsigned int width;
|
|
|
|
unsigned int height;
|
|
|
|
|
|
|
|
unsigned int fb_id;
|
2014-12-09 13:00:58 -07:00
|
|
|
struct bo *bo;
|
2015-04-13 02:32:18 -06:00
|
|
|
struct bo *cursor_bo;
|
2013-02-26 21:35:13 -07:00
|
|
|
} mode;
|
2013-02-26 21:35:13 -07:00
|
|
|
};
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-10-12 10:16:44 -06:00
|
|
|
static inline int64_t U642I64(uint64_t val)
|
|
|
|
{
|
|
|
|
return (int64_t)*((int64_t *)&val);
|
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2012-06-28 08:48:31 -06:00
|
|
|
#define bit_name_fn(res) \
|
2013-02-11 13:36:30 -07:00
|
|
|
const char * res##_str(int type) { \
|
|
|
|
unsigned int i; \
|
2012-06-28 08:48:31 -06:00
|
|
|
const char *sep = ""; \
|
|
|
|
for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
|
|
|
|
if (type & (1 << i)) { \
|
|
|
|
printf("%s%s", sep, res##_names[i]); \
|
|
|
|
sep = ", "; \
|
|
|
|
} \
|
|
|
|
} \
|
2012-08-11 16:00:40 -06:00
|
|
|
return NULL; \
|
2012-06-28 08:48:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *mode_type_names[] = {
|
|
|
|
"builtin",
|
|
|
|
"clock_c",
|
|
|
|
"crtc_c",
|
|
|
|
"preferred",
|
|
|
|
"default",
|
|
|
|
"userdef",
|
|
|
|
"driver",
|
|
|
|
};
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static bit_name_fn(mode_type)
|
2012-06-28 08:48:31 -06:00
|
|
|
|
|
|
|
static const char *mode_flag_names[] = {
|
|
|
|
"phsync",
|
|
|
|
"nhsync",
|
|
|
|
"pvsync",
|
|
|
|
"nvsync",
|
|
|
|
"interlace",
|
|
|
|
"dblscan",
|
|
|
|
"csync",
|
|
|
|
"pcsync",
|
|
|
|
"ncsync",
|
|
|
|
"hskew",
|
|
|
|
"bcast",
|
|
|
|
"pixmux",
|
|
|
|
"dblclk",
|
|
|
|
"clkdiv2"
|
|
|
|
};
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static bit_name_fn(mode_flag)
|
2012-06-28 08:48:31 -06:00
|
|
|
|
2017-04-18 06:54:11 -06:00
|
|
|
static void dump_fourcc(uint32_t fourcc)
|
|
|
|
{
|
|
|
|
printf(" %c%c%c%c",
|
|
|
|
fourcc,
|
|
|
|
fourcc >> 8,
|
|
|
|
fourcc >> 16,
|
|
|
|
fourcc >> 24);
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_encoders(struct device *dev)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
|
|
|
drmModeEncoder *encoder;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("Encoders:\n");
|
|
|
|
printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < dev->resources->res->count_encoders; i++) {
|
|
|
|
encoder = dev->resources->encoders[i].encoder;
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!encoder)
|
2008-12-17 11:09:49 -07:00
|
|
|
continue;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
|
|
|
|
encoder->encoder_id,
|
|
|
|
encoder->crtc_id,
|
2015-12-09 10:37:40 -07:00
|
|
|
util_lookup_encoder_type_name(encoder->encoder_type),
|
2008-12-17 11:09:49 -07:00
|
|
|
encoder->possible_crtcs,
|
|
|
|
encoder->possible_clones);
|
|
|
|
}
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static void dump_mode(drmModeModeInfo *mode)
|
2008-12-17 22:02:43 -07:00
|
|
|
{
|
2016-08-31 20:45:45 -06:00
|
|
|
printf(" %s %d %d %d %d %d %d %d %d %d %d",
|
2008-12-17 22:02:43 -07:00
|
|
|
mode->name,
|
2010-02-27 08:04:42 -07:00
|
|
|
mode->vrefresh,
|
2008-12-17 22:02:43 -07:00
|
|
|
mode->hdisplay,
|
|
|
|
mode->hsync_start,
|
|
|
|
mode->hsync_end,
|
|
|
|
mode->htotal,
|
|
|
|
mode->vdisplay,
|
|
|
|
mode->vsync_start,
|
|
|
|
mode->vsync_end,
|
2016-08-31 20:45:45 -06:00
|
|
|
mode->vtotal,
|
|
|
|
mode->clock);
|
2012-06-28 08:48:31 -06:00
|
|
|
|
|
|
|
printf(" flags: ");
|
|
|
|
mode_flag_str(mode->flags);
|
|
|
|
printf("; type: ");
|
|
|
|
mode_type_str(mode->type);
|
|
|
|
printf("\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_blob(struct device *dev, uint32_t blob_id)
|
2012-04-21 14:51:53 -06:00
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
unsigned char *blob_data;
|
|
|
|
drmModePropertyBlobPtr blob;
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
blob = drmModeGetPropertyBlob(dev->fd, blob_id);
|
2012-06-08 04:28:16 -06:00
|
|
|
if (!blob) {
|
|
|
|
printf("\n");
|
2012-04-21 14:51:53 -06:00
|
|
|
return;
|
2012-06-08 04:28:16 -06:00
|
|
|
}
|
2012-04-21 14:51:53 -06:00
|
|
|
|
|
|
|
blob_data = blob->data;
|
|
|
|
|
|
|
|
for (i = 0; i < blob->length; i++) {
|
|
|
|
if (i % 16 == 0)
|
|
|
|
printf("\n\t\t\t");
|
|
|
|
printf("%.2hhx", blob_data[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
drmModeFreePropertyBlob(blob);
|
|
|
|
}
|
|
|
|
|
2017-09-28 17:02:09 -06:00
|
|
|
static const char *modifier_to_string(uint64_t modifier)
|
|
|
|
{
|
|
|
|
switch (modifier) {
|
|
|
|
case DRM_FORMAT_MOD_INVALID:
|
|
|
|
return "INVALID";
|
|
|
|
case DRM_FORMAT_MOD_LINEAR:
|
|
|
|
return "LINEAR";
|
|
|
|
case I915_FORMAT_MOD_X_TILED:
|
|
|
|
return "X_TILED";
|
|
|
|
case I915_FORMAT_MOD_Y_TILED:
|
|
|
|
return "Y_TILED";
|
|
|
|
case I915_FORMAT_MOD_Yf_TILED:
|
|
|
|
return "Yf_TILED";
|
|
|
|
case I915_FORMAT_MOD_Y_TILED_CCS:
|
|
|
|
return "Y_TILED_CCS";
|
|
|
|
case I915_FORMAT_MOD_Yf_TILED_CCS:
|
|
|
|
return "Yf_TILED_CCS";
|
|
|
|
case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
|
|
|
|
return "SAMSUNG_64_32_TILE";
|
|
|
|
case DRM_FORMAT_MOD_VIVANTE_TILED:
|
|
|
|
return "VIVANTE_TILED";
|
|
|
|
case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
|
|
|
|
return "VIVANTE_SUPER_TILED";
|
|
|
|
case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED:
|
|
|
|
return "VIVANTE_SPLIT_TILED";
|
|
|
|
case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED:
|
|
|
|
return "VIVANTE_SPLIT_SUPER_TILED";
|
drm/tegra: Sanitize format modifiers
The existing format modifier definitions were merged prematurely, and
recent work has unveiled that the definitions are suboptimal in several
ways:
- The format specifiers, except for one, are not Tegra specific, but
the names don't reflect that.
- The number space is split into two, reserving 32 bits for some
"parameter" which most of the modifiers are not going to have.
- Symbolic names for the modifiers are not using the standard
DRM_FORMAT_MOD_* prefix, which makes them awkward to use.
- The vendor prefix NV is somewhat ambiguous.
Fortunately, nobody's started using these modifiers, so we can still fix
the above issues. Do so by using the standard prefix. Also, remove TEGRA
from the name of those modifiers that exist on NVIDIA GPUs as well. In
case of the block linear modifiers, make the "parameter" smaller (4
bits, though only 6 values are valid) and don't let that leak into any
of the other modifiers.
Finally, also use the more canonical NVIDIA instead of the ambiguous NV
prefix.
This is based on commit 5843f4e02fbe86a59981e35adc6cabebee46fdc0 from
Linux v4.16-rc1 and also updates modetest to use the new defines.
Acked-by: Emil Velikov <emil.velikov@collabora.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
2017-11-14 10:50:30 -07:00
|
|
|
case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
|
|
|
|
return "NVIDIA_TEGRA_TILED";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(0)";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(1)";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(2)";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(3)";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(4)";
|
|
|
|
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5):
|
|
|
|
return "NVIDIA_16BX2_BLOCK(5)";
|
2017-09-28 17:02:09 -06:00
|
|
|
case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
|
|
|
|
return "MOD_BROADCOM_VC4_T_TILED";
|
|
|
|
default:
|
|
|
|
return "(UNKNOWN MODIFIER)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_in_formats(struct device *dev, uint32_t blob_id)
|
|
|
|
{
|
|
|
|
uint32_t i, j;
|
|
|
|
drmModePropertyBlobPtr blob;
|
|
|
|
struct drm_format_modifier_blob *header;
|
|
|
|
uint32_t *formats;
|
|
|
|
struct drm_format_modifier *modifiers;
|
|
|
|
|
|
|
|
printf("\t\tin_formats blob decoded:\n");
|
|
|
|
blob = drmModeGetPropertyBlob(dev->fd, blob_id);
|
|
|
|
if (!blob) {
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
header = blob->data;
|
|
|
|
formats = (uint32_t *) ((char *) header + header->formats_offset);
|
|
|
|
modifiers = (struct drm_format_modifier *)
|
|
|
|
((char *) header + header->modifiers_offset);
|
|
|
|
|
|
|
|
for (i = 0; i < header->count_formats; i++) {
|
|
|
|
printf("\t\t\t");
|
|
|
|
dump_fourcc(formats[i]);
|
|
|
|
printf(": ");
|
|
|
|
for (j = 0; j < header->count_modifiers; j++) {
|
|
|
|
uint64_t mask = 1ULL << i;
|
|
|
|
if (modifiers[j].formats & mask)
|
|
|
|
printf(" %s", modifier_to_string(modifiers[j].modifier));
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
drmModeFreePropertyBlob(blob);
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_prop(struct device *dev, drmModePropertyPtr prop,
|
|
|
|
uint32_t prop_id, uint64_t value)
|
2009-02-23 13:08:03 -07:00
|
|
|
{
|
|
|
|
int i;
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\t%d", prop_id);
|
|
|
|
if (!prop) {
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" %s:\n", prop->name);
|
|
|
|
|
|
|
|
printf("\t\tflags:");
|
|
|
|
if (prop->flags & DRM_MODE_PROP_PENDING)
|
|
|
|
printf(" pending");
|
|
|
|
if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
|
|
|
|
printf(" immutable");
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
|
|
|
|
printf(" signed range");
|
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
|
|
|
|
printf(" range");
|
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
|
2012-04-21 14:51:53 -06:00
|
|
|
printf(" enum");
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
|
2012-06-05 11:28:38 -06:00
|
|
|
printf(" bitmask");
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
|
2012-04-21 14:51:53 -06:00
|
|
|
printf(" blob");
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
|
|
|
|
printf(" object");
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\n");
|
2009-02-23 13:08:03 -07:00
|
|
|
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
|
|
|
|
printf("\t\tvalues:");
|
|
|
|
for (i = 0; i < prop->count_values; i++)
|
|
|
|
printf(" %"PRId64, U642I64(prop->values[i]));
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\t\tvalues:");
|
|
|
|
for (i = 0; i < prop->count_values; i++)
|
|
|
|
printf(" %"PRIu64, prop->values[i]);
|
|
|
|
printf("\n");
|
2009-02-23 13:08:03 -07:00
|
|
|
}
|
2012-04-21 14:51:53 -06:00
|
|
|
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\t\tenums:");
|
|
|
|
for (i = 0; i < prop->count_enums; i++)
|
|
|
|
printf(" %s=%llu", prop->enums[i].name,
|
|
|
|
prop->enums[i].value);
|
|
|
|
printf("\n");
|
2013-10-12 10:16:44 -06:00
|
|
|
} else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
|
2012-06-05 11:28:38 -06:00
|
|
|
printf("\t\tvalues:");
|
|
|
|
for (i = 0; i < prop->count_enums; i++)
|
|
|
|
printf(" %s=0x%llx", prop->enums[i].name,
|
|
|
|
(1LL << prop->enums[i].value));
|
|
|
|
printf("\n");
|
2012-04-21 14:51:53 -06:00
|
|
|
} else {
|
|
|
|
assert(prop->count_enums == 0);
|
|
|
|
}
|
|
|
|
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\t\tblobs:\n");
|
|
|
|
for (i = 0; i < prop->count_blobs; i++)
|
2013-02-26 21:35:13 -07:00
|
|
|
dump_blob(dev, prop->blob_ids[i]);
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\n");
|
|
|
|
} else {
|
|
|
|
assert(prop->count_blobs == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\t\tvalue:");
|
2013-10-12 10:16:44 -06:00
|
|
|
if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
|
2013-02-26 21:35:13 -07:00
|
|
|
dump_blob(dev, value);
|
2016-03-22 15:42:52 -06:00
|
|
|
else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
|
|
|
|
printf(" %"PRId64"\n", value);
|
2012-04-21 14:51:53 -06:00
|
|
|
else
|
|
|
|
printf(" %"PRIu64"\n", value);
|
2017-09-28 17:02:09 -06:00
|
|
|
|
|
|
|
if (strcmp(prop->name, "IN_FORMATS") == 0)
|
|
|
|
dump_in_formats(dev, value);
|
2009-02-23 13:08:03 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_connectors(struct device *dev)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
printf("Connectors:\n");
|
2015-01-23 09:08:21 -07:00
|
|
|
printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < dev->resources->res->count_connectors; i++) {
|
|
|
|
struct connector *_connector = &dev->resources->connectors[i];
|
2013-02-26 22:39:36 -07:00
|
|
|
drmModeConnector *connector = _connector->connector;
|
|
|
|
if (!connector)
|
2008-12-17 11:09:49 -07:00
|
|
|
continue;
|
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
|
2008-12-17 11:09:49 -07:00
|
|
|
connector->connector_id,
|
|
|
|
connector->encoder_id,
|
2015-12-09 10:37:40 -07:00
|
|
|
util_lookup_connector_status_name(connector->connection),
|
2015-01-23 09:08:21 -07:00
|
|
|
_connector->name,
|
2008-12-17 11:09:49 -07:00
|
|
|
connector->mmWidth, connector->mmHeight,
|
|
|
|
connector->count_modes);
|
|
|
|
|
2009-11-17 13:32:23 -07:00
|
|
|
for (j = 0; j < connector->count_encoders; j++)
|
|
|
|
printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
|
|
|
|
printf("\n");
|
|
|
|
|
2012-04-21 14:51:51 -06:00
|
|
|
if (connector->count_modes) {
|
|
|
|
printf(" modes:\n");
|
2012-04-21 14:51:53 -06:00
|
|
|
printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
|
2012-04-21 14:51:51 -06:00
|
|
|
"vss vse vtot)\n");
|
|
|
|
for (j = 0; j < connector->count_modes; j++)
|
|
|
|
dump_mode(&connector->modes[j]);
|
2013-02-26 22:39:36 -07:00
|
|
|
}
|
2012-04-21 14:51:51 -06:00
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
if (_connector->props) {
|
2012-04-21 14:51:51 -06:00
|
|
|
printf(" props:\n");
|
2013-02-26 22:39:36 -07:00
|
|
|
for (j = 0; j < (int)_connector->props->count_props; j++)
|
2013-02-26 21:35:13 -07:00
|
|
|
dump_prop(dev, _connector->props_info[j],
|
2013-02-26 22:39:36 -07:00
|
|
|
_connector->props->props[j],
|
|
|
|
_connector->props->prop_values[j]);
|
2012-04-21 14:51:51 -06:00
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_crtcs(struct device *dev)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
|
|
|
int i;
|
2012-05-15 15:38:29 -06:00
|
|
|
uint32_t j;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("CRTCs:\n");
|
|
|
|
printf("id\tfb\tpos\tsize\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < dev->resources->res->count_crtcs; i++) {
|
|
|
|
struct crtc *_crtc = &dev->resources->crtcs[i];
|
2013-02-26 22:39:36 -07:00
|
|
|
drmModeCrtc *crtc = _crtc->crtc;
|
|
|
|
if (!crtc)
|
2008-12-17 11:09:49 -07:00
|
|
|
continue;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
|
|
|
|
crtc->crtc_id,
|
|
|
|
crtc->buffer_id,
|
|
|
|
crtc->x, crtc->y,
|
|
|
|
crtc->width, crtc->height);
|
|
|
|
dump_mode(&crtc->mode);
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
if (_crtc->props) {
|
|
|
|
printf(" props:\n");
|
|
|
|
for (j = 0; j < _crtc->props->count_props; j++)
|
2013-02-26 21:35:13 -07:00
|
|
|
dump_prop(dev, _crtc->props_info[j],
|
2013-02-26 22:39:36 -07:00
|
|
|
_crtc->props->props[j],
|
|
|
|
_crtc->props->prop_values[j]);
|
2012-05-15 15:38:29 -06:00
|
|
|
} else {
|
2013-02-26 22:39:36 -07:00
|
|
|
printf(" no properties found\n");
|
2012-05-15 15:38:29 -06:00
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_framebuffers(struct device *dev)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
|
|
|
drmModeFB *fb;
|
|
|
|
int i;
|
|
|
|
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("Frame buffers:\n");
|
|
|
|
printf("id\tsize\tpitch\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < dev->resources->res->count_fbs; i++) {
|
|
|
|
fb = dev->resources->fbs[i].fb;
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!fb)
|
2008-12-17 11:09:49 -07:00
|
|
|
continue;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2010-01-29 19:14:44 -07:00
|
|
|
printf("%u\t(%ux%u)\t%u\n",
|
2008-12-17 22:02:43 -07:00
|
|
|
fb->fb_id,
|
2010-01-29 19:14:44 -07:00
|
|
|
fb->width, fb->height,
|
|
|
|
fb->pitch);
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
2008-12-17 22:02:43 -07:00
|
|
|
printf("\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void dump_planes(struct device *dev)
|
2011-12-14 20:06:43 -07:00
|
|
|
{
|
2012-04-21 14:51:50 -06:00
|
|
|
unsigned int i, j;
|
2011-12-14 20:06:43 -07:00
|
|
|
|
|
|
|
printf("Planes:\n");
|
2013-04-17 13:18:04 -06:00
|
|
|
printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (!dev->resources->plane_res)
|
2013-02-26 22:39:36 -07:00
|
|
|
return;
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
|
|
|
|
struct plane *plane = &dev->resources->planes[i];
|
2013-02-26 22:39:36 -07:00
|
|
|
drmModePlane *ovr = plane->plane;
|
|
|
|
if (!ovr)
|
2011-12-14 20:06:43 -07:00
|
|
|
continue;
|
|
|
|
|
2013-04-17 13:18:04 -06:00
|
|
|
printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
|
2011-12-14 20:06:43 -07:00
|
|
|
ovr->plane_id, ovr->crtc_id, ovr->fb_id,
|
|
|
|
ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
|
2013-04-17 13:18:04 -06:00
|
|
|
ovr->gamma_size, ovr->possible_crtcs);
|
2011-12-14 20:06:43 -07:00
|
|
|
|
|
|
|
if (!ovr->count_formats)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
printf(" formats:");
|
|
|
|
for (j = 0; j < ovr->count_formats; j++)
|
2017-04-18 06:54:11 -06:00
|
|
|
dump_fourcc(ovr->formats[j]);
|
2011-12-14 20:06:43 -07:00
|
|
|
printf("\n");
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
if (plane->props) {
|
|
|
|
printf(" props:\n");
|
|
|
|
for (j = 0; j < plane->props->count_props; j++)
|
2013-02-26 21:35:13 -07:00
|
|
|
dump_prop(dev, plane->props_info[j],
|
2013-02-26 22:39:36 -07:00
|
|
|
plane->props->props[j],
|
|
|
|
plane->props->prop_values[j]);
|
2012-06-05 11:28:47 -06:00
|
|
|
} else {
|
2013-02-26 22:39:36 -07:00
|
|
|
printf(" no properties found\n");
|
2012-06-05 11:28:47 -06:00
|
|
|
}
|
2011-12-14 20:06:43 -07:00
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
static void free_resources(struct resources *res)
|
|
|
|
{
|
2015-01-23 09:08:21 -07:00
|
|
|
int i;
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!res)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#define free_resource(_res, __res, type, Type) \
|
|
|
|
do { \
|
|
|
|
if (!(_res)->type##s) \
|
|
|
|
break; \
|
|
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
|
|
if (!(_res)->type##s[i].type) \
|
|
|
|
break; \
|
|
|
|
drmModeFree##Type((_res)->type##s[i].type); \
|
|
|
|
} \
|
|
|
|
free((_res)->type##s); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define free_properties(_res, __res, type) \
|
|
|
|
do { \
|
|
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
|
|
drmModeFreeObjectProperties(res->type##s[i].props); \
|
|
|
|
free(res->type##s[i].props_info); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
if (res->res) {
|
|
|
|
free_properties(res, res, crtc);
|
|
|
|
|
|
|
|
free_resource(res, res, crtc, Crtc);
|
|
|
|
free_resource(res, res, encoder, Encoder);
|
2015-01-23 09:08:21 -07:00
|
|
|
|
|
|
|
for (i = 0; i < res->res->count_connectors; i++)
|
|
|
|
free(res->connectors[i].name);
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
free_resource(res, res, connector, Connector);
|
|
|
|
free_resource(res, res, fb, FB);
|
|
|
|
|
|
|
|
drmModeFreeResources(res->res);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res->plane_res) {
|
|
|
|
free_properties(res, plane_res, plane);
|
|
|
|
|
|
|
|
free_resource(res, plane_res, plane, Plane);
|
|
|
|
|
|
|
|
drmModeFreePlaneResources(res->plane_res);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(res);
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static struct resources *get_resources(struct device *dev)
|
2013-02-26 22:39:36 -07:00
|
|
|
{
|
|
|
|
struct resources *res;
|
2013-02-26 21:35:13 -07:00
|
|
|
int i;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2015-04-28 06:25:24 -06:00
|
|
|
res = calloc(1, sizeof(*res));
|
2013-02-26 22:39:36 -07:00
|
|
|
if (res == 0)
|
|
|
|
return NULL;
|
|
|
|
|
2014-11-24 11:43:10 -07:00
|
|
|
drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
res->res = drmModeGetResources(dev->fd);
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!res->res) {
|
|
|
|
fprintf(stderr, "drmModeGetResources failed: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2015-04-28 06:25:24 -06:00
|
|
|
res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs));
|
|
|
|
res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders));
|
|
|
|
res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors));
|
|
|
|
res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs));
|
2013-02-26 22:39:36 -07:00
|
|
|
|
|
|
|
if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
#define get_resource(_res, __res, type, Type) \
|
|
|
|
do { \
|
|
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
|
|
(_res)->type##s[i].type = \
|
2013-02-26 21:35:13 -07:00
|
|
|
drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!(_res)->type##s[i].type) \
|
|
|
|
fprintf(stderr, "could not get %s %i: %s\n", \
|
|
|
|
#type, (_res)->__res->type##s[i], \
|
|
|
|
strerror(errno)); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
get_resource(res, res, crtc, Crtc);
|
|
|
|
get_resource(res, res, encoder, Encoder);
|
|
|
|
get_resource(res, res, connector, Connector);
|
|
|
|
get_resource(res, res, fb, FB);
|
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
/* Set the name of all connectors based on the type name and the per-type ID. */
|
|
|
|
for (i = 0; i < res->res->count_connectors; i++) {
|
|
|
|
struct connector *connector = &res->connectors[i];
|
2015-12-09 10:37:40 -07:00
|
|
|
drmModeConnector *conn = connector->connector;
|
2018-01-09 19:16:41 -07:00
|
|
|
int num;
|
2015-01-23 09:08:21 -07:00
|
|
|
|
2018-01-09 19:16:41 -07:00
|
|
|
num = asprintf(&connector->name, "%s-%u",
|
2015-12-09 10:37:40 -07:00
|
|
|
util_lookup_connector_type_name(conn->connector_type),
|
|
|
|
conn->connector_type_id);
|
2018-01-09 19:16:41 -07:00
|
|
|
if (num < 0)
|
|
|
|
goto error;
|
2015-01-23 09:08:21 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
#define get_properties(_res, __res, type, Type) \
|
|
|
|
do { \
|
|
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
|
|
struct type *obj = &res->type##s[i]; \
|
|
|
|
unsigned int j; \
|
|
|
|
obj->props = \
|
2013-02-26 21:35:13 -07:00
|
|
|
drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
|
2013-02-26 22:39:36 -07:00
|
|
|
DRM_MODE_OBJECT_##Type); \
|
|
|
|
if (!obj->props) { \
|
|
|
|
fprintf(stderr, \
|
|
|
|
"could not get %s %i properties: %s\n", \
|
|
|
|
#type, obj->type->type##_id, \
|
|
|
|
strerror(errno)); \
|
|
|
|
continue; \
|
|
|
|
} \
|
2015-04-28 06:25:24 -06:00
|
|
|
obj->props_info = calloc(obj->props->count_props, \
|
|
|
|
sizeof(*obj->props_info)); \
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!obj->props_info) \
|
|
|
|
continue; \
|
|
|
|
for (j = 0; j < obj->props->count_props; ++j) \
|
|
|
|
obj->props_info[j] = \
|
2013-02-26 21:35:13 -07:00
|
|
|
drmModeGetProperty(dev->fd, obj->props->props[j]); \
|
2013-02-26 22:39:36 -07:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
get_properties(res, res, crtc, CRTC);
|
|
|
|
get_properties(res, res, connector, CONNECTOR);
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < res->res->count_crtcs; ++i)
|
|
|
|
res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
res->plane_res = drmModeGetPlaneResources(dev->fd);
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!res->plane_res) {
|
|
|
|
fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-04-28 06:25:24 -06:00
|
|
|
res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes));
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!res->planes)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
get_resource(res, plane_res, plane, Plane);
|
|
|
|
get_properties(res, plane_res, plane, PLANE);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
error:
|
|
|
|
free_resources(res);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-19 08:36:09 -06:00
|
|
|
static int get_crtc_index(struct device *dev, uint32_t id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
|
|
|
|
drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
|
|
|
|
if (crtc && crtc->crtc_id == id)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
|
|
|
|
{
|
|
|
|
struct connector *connector;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->res->count_connectors; i++) {
|
|
|
|
connector = &dev->resources->connectors[i];
|
|
|
|
|
|
|
|
if (strcmp(connector->name, name) == 0)
|
|
|
|
return connector->connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
|
|
|
|
{
|
|
|
|
drmModeConnector *connector;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->res->count_connectors; i++) {
|
|
|
|
connector = dev->resources->connectors[i].connector;
|
|
|
|
if (connector && connector->connector_id == id)
|
|
|
|
return connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
|
|
|
|
{
|
|
|
|
drmModeEncoder *encoder;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->res->count_encoders; i++) {
|
|
|
|
encoder = dev->resources->encoders[i].encoder;
|
|
|
|
if (encoder && encoder->encoder_id == id)
|
|
|
|
return encoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-20 06:50:42 -06:00
|
|
|
/* -----------------------------------------------------------------------------
|
2013-03-19 06:47:39 -06:00
|
|
|
* Pipes and planes
|
2012-07-20 06:50:42 -06:00
|
|
|
*/
|
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
/*
|
|
|
|
* Mode setting with the kernel interfaces is a bit of a chore.
|
|
|
|
* First you have to find the connector in question and make sure the
|
|
|
|
* requested mode is available.
|
|
|
|
* Then you need to find the encoder attached to that connector so you
|
|
|
|
* can bind it with a free crtc.
|
|
|
|
*/
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg {
|
2015-01-23 09:08:21 -07:00
|
|
|
const char **cons;
|
2013-03-19 06:48:36 -06:00
|
|
|
uint32_t *con_ids;
|
|
|
|
unsigned int num_cons;
|
2013-02-26 21:35:13 -07:00
|
|
|
uint32_t crtc_id;
|
2009-02-03 12:00:00 -07:00
|
|
|
char mode_str[64];
|
2012-07-20 06:50:48 -06:00
|
|
|
char format_str[5];
|
2014-01-10 03:02:33 -07:00
|
|
|
unsigned int vrefresh;
|
2012-07-20 06:50:48 -06:00
|
|
|
unsigned int fourcc;
|
2009-02-23 13:08:03 -07:00
|
|
|
drmModeModeInfo *mode;
|
2013-02-26 21:35:13 -07:00
|
|
|
struct crtc *crtc;
|
2009-11-17 13:32:23 -07:00
|
|
|
unsigned int fb_id[2], current_fb_id;
|
|
|
|
struct timeval start;
|
|
|
|
|
|
|
|
int swap_count;
|
2011-12-14 20:06:43 -07:00
|
|
|
};
|
|
|
|
|
2013-02-26 22:39:36 -07:00
|
|
|
struct plane_arg {
|
2016-07-19 10:47:13 -06:00
|
|
|
uint32_t plane_id; /* the id of plane to use */
|
2013-02-26 21:35:13 -07:00
|
|
|
uint32_t crtc_id; /* the id of CRTC to bind to */
|
2013-02-26 21:35:13 -07:00
|
|
|
bool has_position;
|
|
|
|
int32_t x, y;
|
2011-12-14 20:06:43 -07:00
|
|
|
uint32_t w, h;
|
2013-09-07 19:36:02 -06:00
|
|
|
double scale;
|
2011-12-14 20:06:43 -07:00
|
|
|
unsigned int fb_id;
|
2015-04-28 04:41:39 -06:00
|
|
|
struct bo *bo;
|
2011-12-14 21:24:14 -07:00
|
|
|
char format_str[5]; /* need to leave room for terminating \0 */
|
2012-07-20 06:50:47 -06:00
|
|
|
unsigned int fourcc;
|
2011-12-14 20:06:43 -07:00
|
|
|
};
|
2009-02-03 12:00:00 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
static drmModeModeInfo *
|
2014-01-10 03:02:33 -07:00
|
|
|
connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
|
|
|
|
const unsigned int vrefresh)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
|
|
|
drmModeConnector *connector;
|
2013-03-19 06:48:36 -06:00
|
|
|
drmModeModeInfo *mode;
|
|
|
|
int i;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
connector = get_connector_by_id(dev, con_id);
|
|
|
|
if (!connector || !connector->count_modes)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < connector->count_modes; i++) {
|
|
|
|
mode = &connector->modes[i];
|
2014-01-10 03:02:33 -07:00
|
|
|
if (!strcmp(mode->name, mode_str)) {
|
|
|
|
/* If the vertical refresh frequency is not specified then return the
|
|
|
|
* first mode that match with the name. Else, return the mode that match
|
|
|
|
* the name and the specified vertical refresh frequency.
|
|
|
|
*/
|
|
|
|
if (vrefresh == 0)
|
|
|
|
return mode;
|
|
|
|
else if (mode->vrefresh == vrefresh)
|
|
|
|
return mode;
|
|
|
|
}
|
2013-03-19 06:48:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
|
|
|
|
{
|
|
|
|
uint32_t possible_crtcs = ~0;
|
|
|
|
uint32_t active_crtcs = 0;
|
|
|
|
unsigned int crtc_idx;
|
|
|
|
unsigned int i;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (i = 0; i < pipe->num_cons; ++i) {
|
2013-03-19 08:36:09 -06:00
|
|
|
uint32_t crtcs_for_connector = 0;
|
2013-03-19 06:48:36 -06:00
|
|
|
drmModeConnector *connector;
|
|
|
|
drmModeEncoder *encoder;
|
2013-03-19 08:36:09 -06:00
|
|
|
int idx;
|
2013-03-19 06:48:36 -06:00
|
|
|
|
|
|
|
connector = get_connector_by_id(dev, pipe->con_ids[i]);
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!connector)
|
2013-03-19 06:48:36 -06:00
|
|
|
return NULL;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-03-19 08:36:09 -06:00
|
|
|
for (j = 0; j < connector->count_encoders; ++j) {
|
|
|
|
encoder = get_encoder_by_id(dev, connector->encoders[j]);
|
|
|
|
if (!encoder)
|
|
|
|
continue;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-03-19 08:36:09 -06:00
|
|
|
crtcs_for_connector |= encoder->possible_crtcs;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-03-19 08:36:09 -06:00
|
|
|
idx = get_crtc_index(dev, encoder->crtc_id);
|
|
|
|
if (idx >= 0)
|
|
|
|
active_crtcs |= 1 << idx;
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
2013-03-19 08:36:09 -06:00
|
|
|
|
|
|
|
possible_crtcs &= crtcs_for_connector;
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
if (!possible_crtcs)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Return the first possible and active CRTC if one exists, or the first
|
|
|
|
* possible CRTC otherwise.
|
|
|
|
*/
|
|
|
|
if (possible_crtcs & active_crtcs)
|
|
|
|
crtc_idx = ffs(possible_crtcs & active_crtcs);
|
|
|
|
else
|
|
|
|
crtc_idx = ffs(possible_crtcs);
|
|
|
|
|
|
|
|
return &dev->resources->crtcs[crtc_idx - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
|
|
|
|
{
|
2013-08-29 14:31:51 -06:00
|
|
|
drmModeModeInfo *mode = NULL;
|
2013-03-19 06:48:36 -06:00
|
|
|
int i;
|
|
|
|
|
|
|
|
pipe->mode = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < (int)pipe->num_cons; i++) {
|
|
|
|
mode = connector_find_mode(dev, pipe->con_ids[i],
|
2014-01-10 03:02:33 -07:00
|
|
|
pipe->mode_str, pipe->vrefresh);
|
2013-03-19 06:48:36 -06:00
|
|
|
if (mode == NULL) {
|
|
|
|
fprintf(stderr,
|
2015-01-23 09:08:21 -07:00
|
|
|
"failed to find mode \"%s\" for connector %s\n",
|
|
|
|
pipe->mode_str, pipe->cons[i]);
|
2013-03-19 06:48:36 -06:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
|
2013-03-19 06:48:36 -06:00
|
|
|
* locate a CRTC that can be attached to all the connectors.
|
2013-02-26 21:35:13 -07:00
|
|
|
*/
|
2013-03-19 06:48:36 -06:00
|
|
|
if (pipe->crtc_id != (uint32_t)-1) {
|
|
|
|
for (i = 0; i < dev->resources->res->count_crtcs; i++) {
|
|
|
|
struct crtc *crtc = &dev->resources->crtcs[i];
|
|
|
|
|
|
|
|
if (pipe->crtc_id == crtc->crtc->crtc_id) {
|
|
|
|
pipe->crtc = crtc;
|
2013-02-26 21:35:13 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-03-19 06:48:36 -06:00
|
|
|
} else {
|
|
|
|
pipe->crtc = pipe_find_crtc(dev, pipe);
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
if (!pipe->crtc) {
|
|
|
|
fprintf(stderr, "failed to find CRTC for pipe\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
pipe->mode = mode;
|
|
|
|
pipe->crtc->mode = mode;
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
return 0;
|
2009-02-03 12:00:00 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
/* -----------------------------------------------------------------------------
|
|
|
|
* Properties
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct property_arg {
|
|
|
|
uint32_t obj_id;
|
|
|
|
uint32_t obj_type;
|
|
|
|
char name[DRM_PROP_NAME_LEN+1];
|
|
|
|
uint32_t prop_id;
|
|
|
|
uint64_t value;
|
|
|
|
};
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void set_property(struct device *dev, struct property_arg *p)
|
2013-02-26 21:35:13 -07:00
|
|
|
{
|
2013-08-29 14:31:51 -06:00
|
|
|
drmModeObjectProperties *props = NULL;
|
|
|
|
drmModePropertyRes **props_info = NULL;
|
2013-02-26 21:35:13 -07:00
|
|
|
const char *obj_type;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
p->obj_type = 0;
|
|
|
|
p->prop_id = 0;
|
|
|
|
|
|
|
|
#define find_object(_res, __res, type, Type) \
|
|
|
|
do { \
|
|
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
|
|
struct type *obj = &(_res)->type##s[i]; \
|
|
|
|
if (obj->type->type##_id != p->obj_id) \
|
|
|
|
continue; \
|
|
|
|
p->obj_type = DRM_MODE_OBJECT_##Type; \
|
|
|
|
obj_type = #Type; \
|
|
|
|
props = obj->props; \
|
|
|
|
props_info = obj->props_info; \
|
|
|
|
} \
|
|
|
|
} while(0) \
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
find_object(dev->resources, res, crtc, CRTC);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->obj_type == 0)
|
2013-02-26 21:35:13 -07:00
|
|
|
find_object(dev->resources, res, connector, CONNECTOR);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->obj_type == 0)
|
2013-02-26 21:35:13 -07:00
|
|
|
find_object(dev->resources, plane_res, plane, PLANE);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->obj_type == 0) {
|
|
|
|
fprintf(stderr, "Object %i not found, can't set property\n",
|
|
|
|
p->obj_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!props) {
|
|
|
|
fprintf(stderr, "%s %i has no properties\n",
|
|
|
|
obj_type, p->obj_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < (int)props->count_props; ++i) {
|
|
|
|
if (!props_info[i])
|
|
|
|
continue;
|
|
|
|
if (strcmp(props_info[i]->name, p->name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == (int)props->count_props) {
|
|
|
|
fprintf(stderr, "%s %i has no %s property\n",
|
|
|
|
obj_type, p->obj_id, p->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->prop_id = props->props[i];
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
|
|
|
|
p->prop_id, p->value);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (ret < 0)
|
|
|
|
fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
|
|
|
|
obj_type, p->obj_id, p->name, p->value, strerror(errno));
|
|
|
|
}
|
|
|
|
|
2012-07-20 06:50:41 -06:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static void
|
2012-07-20 06:50:41 -06:00
|
|
|
page_flip_handler(int fd, unsigned int frame,
|
|
|
|
unsigned int sec, unsigned int usec, void *data)
|
|
|
|
{
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg *pipe;
|
2012-07-20 06:50:41 -06:00
|
|
|
unsigned int new_fb_id;
|
|
|
|
struct timeval end;
|
|
|
|
double t;
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
pipe = data;
|
|
|
|
if (pipe->current_fb_id == pipe->fb_id[0])
|
|
|
|
new_fb_id = pipe->fb_id[1];
|
2012-07-20 06:50:41 -06:00
|
|
|
else
|
2013-03-19 06:47:39 -06:00
|
|
|
new_fb_id = pipe->fb_id[0];
|
2012-07-20 06:50:41 -06:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id,
|
2013-03-19 06:47:39 -06:00
|
|
|
DRM_MODE_PAGE_FLIP_EVENT, pipe);
|
|
|
|
pipe->current_fb_id = new_fb_id;
|
|
|
|
pipe->swap_count++;
|
|
|
|
if (pipe->swap_count == 60) {
|
2012-07-20 06:50:41 -06:00
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
t = end.tv_sec + end.tv_usec * 1e-6 -
|
2013-03-19 06:47:39 -06:00
|
|
|
(pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
|
|
|
|
fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
|
|
|
|
pipe->swap_count = 0;
|
|
|
|
pipe->start = end;
|
2012-07-20 06:50:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-06 06:29:33 -06:00
|
|
|
static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ovr->count_formats; ++i) {
|
|
|
|
if (ovr->formats[i] == fmt)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static int set_plane(struct device *dev, struct plane_arg *p)
|
2011-12-14 20:06:43 -07:00
|
|
|
{
|
|
|
|
drmModePlane *ovr;
|
2015-04-20 13:50:45 -06:00
|
|
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
2016-07-19 10:47:13 -06:00
|
|
|
uint32_t plane_id;
|
2014-12-09 13:00:58 -07:00
|
|
|
struct bo *plane_bo;
|
2012-07-20 06:50:47 -06:00
|
|
|
uint32_t plane_flags = 0;
|
2013-02-11 13:36:30 -07:00
|
|
|
int crtc_x, crtc_y, crtc_w, crtc_h;
|
2013-02-26 21:35:13 -07:00
|
|
|
struct crtc *crtc = NULL;
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int pipe;
|
2012-04-21 14:51:50 -06:00
|
|
|
unsigned int i;
|
2011-12-14 20:06:43 -07:00
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
/* Find an unused plane which can be connected to our CRTC. Find the
|
|
|
|
* CRTC index first, then iterate over available planes.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->crtc_id == dev->resources->res->crtcs[i]) {
|
|
|
|
crtc = &dev->resources->crtcs[i];
|
2013-02-26 21:35:13 -07:00
|
|
|
pipe = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (!crtc) {
|
|
|
|
fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
|
2013-02-26 21:35:13 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-07-19 10:47:13 -06:00
|
|
|
plane_id = p->plane_id;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
ovr = dev->resources->planes[i].plane;
|
2016-07-19 10:47:13 -06:00
|
|
|
if (!ovr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (plane_id && plane_id != ovr->plane_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!format_support(ovr, p->fourcc))
|
2013-02-26 22:39:36 -07:00
|
|
|
continue;
|
2011-12-14 20:06:43 -07:00
|
|
|
|
2017-10-11 08:58:11 -06:00
|
|
|
if ((ovr->possible_crtcs & (1 << pipe)) &&
|
|
|
|
(ovr->crtc_id == 0 || ovr->crtc_id == p->crtc_id)) {
|
2011-12-14 20:06:43 -07:00
|
|
|
plane_id = ovr->plane_id;
|
2016-07-19 10:47:13 -06:00
|
|
|
break;
|
|
|
|
}
|
2011-12-14 20:06:43 -07:00
|
|
|
}
|
|
|
|
|
2016-07-19 10:47:13 -06:00
|
|
|
if (i == dev->resources->plane_res->count_planes) {
|
2013-02-26 21:35:13 -07:00
|
|
|
fprintf(stderr, "no unused plane available for CRTC %u\n",
|
|
|
|
crtc->crtc->crtc_id);
|
2011-12-14 20:06:43 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
|
|
|
|
p->w, p->h, p->format_str, plane_id);
|
|
|
|
|
2014-12-09 13:00:58 -07:00
|
|
|
plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles,
|
2015-12-09 10:37:39 -07:00
|
|
|
pitches, offsets, UTIL_PATTERN_TILES);
|
2012-07-20 06:50:41 -06:00
|
|
|
if (plane_bo == NULL)
|
|
|
|
return -1;
|
2011-12-14 20:06:43 -07:00
|
|
|
|
2015-04-28 04:41:39 -06:00
|
|
|
p->bo = plane_bo;
|
|
|
|
|
2011-12-14 20:06:43 -07:00
|
|
|
/* just use single plane format for now.. */
|
2013-02-26 21:35:13 -07:00
|
|
|
if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
|
2011-12-14 20:06:43 -07:00
|
|
|
handles, pitches, offsets, &p->fb_id, plane_flags)) {
|
|
|
|
fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-09-07 19:36:02 -06:00
|
|
|
crtc_w = p->w * p->scale;
|
|
|
|
crtc_h = p->h * p->scale;
|
2013-02-26 21:35:13 -07:00
|
|
|
if (!p->has_position) {
|
|
|
|
/* Default to the middle of the screen */
|
2013-09-07 19:36:02 -06:00
|
|
|
crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
|
|
|
|
crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
|
2013-02-26 21:35:13 -07:00
|
|
|
} else {
|
|
|
|
crtc_x = p->x;
|
|
|
|
crtc_y = p->y;
|
|
|
|
}
|
2011-12-14 20:06:43 -07:00
|
|
|
|
|
|
|
/* note src coords (last 4 args) are in Q16 format */
|
2013-02-26 21:35:13 -07:00
|
|
|
if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id,
|
2011-12-14 20:06:43 -07:00
|
|
|
plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
|
|
|
|
0, 0, p->w << 16, p->h << 16)) {
|
|
|
|
fprintf(stderr, "failed to enable plane: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
ovr->crtc_id = crtc->crtc->crtc_id;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2011-12-14 20:06:43 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-28 04:41:39 -06:00
|
|
|
static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (p[i].fb_id)
|
|
|
|
drmModeRmFB(dev->fd, p[i].fb_id);
|
|
|
|
if (p[i].bo)
|
|
|
|
bo_destroy(p[i].bo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
|
2009-02-03 13:03:41 -07:00
|
|
|
{
|
2015-04-20 13:50:45 -06:00
|
|
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int fb_id;
|
2014-12-09 13:00:58 -07:00
|
|
|
struct bo *bo;
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int i;
|
2013-03-19 06:48:36 -06:00
|
|
|
unsigned int j;
|
2013-02-26 21:35:13 -07:00
|
|
|
int ret, x;
|
|
|
|
|
|
|
|
dev->mode.width = 0;
|
|
|
|
dev->mode.height = 0;
|
2015-04-13 02:32:15 -06:00
|
|
|
dev->mode.fb_id = 0;
|
2009-02-03 13:03:41 -07:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
ret = pipe_find_crtc_and_mode(dev, pipe);
|
|
|
|
if (ret < 0)
|
2009-02-03 13:03:41 -07:00
|
|
|
continue;
|
2013-03-19 06:48:36 -06:00
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
dev->mode.width += pipe->mode->hdisplay;
|
|
|
|
if (dev->mode.height < pipe->mode->vdisplay)
|
|
|
|
dev->mode.height = pipe->mode->vdisplay;
|
2009-02-03 13:03:41 -07:00
|
|
|
}
|
|
|
|
|
2015-12-09 10:37:39 -07:00
|
|
|
bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
|
|
|
|
dev->mode.height, handles, pitches, offsets,
|
|
|
|
UTIL_PATTERN_SMPTE);
|
2012-07-20 06:50:41 -06:00
|
|
|
if (bo == NULL)
|
2009-02-03 13:03:41 -07:00
|
|
|
return;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2015-04-13 02:32:16 -06:00
|
|
|
dev->mode.bo = bo;
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
|
|
|
|
pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0);
|
2008-12-17 11:09:49 -07:00
|
|
|
if (ret) {
|
2011-10-19 05:32:26 -06:00
|
|
|
fprintf(stderr, "failed to add fb (%ux%u): %s\n",
|
2013-02-26 21:35:13 -07:00
|
|
|
dev->mode.width, dev->mode.height, strerror(errno));
|
2008-12-17 11:09:49 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-13 02:32:16 -06:00
|
|
|
dev->mode.fb_id = fb_id;
|
|
|
|
|
2009-02-03 12:00:00 -07:00
|
|
|
x = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
|
|
|
|
if (pipe->mode == NULL)
|
2009-02-03 12:00:00 -07:00
|
|
|
continue;
|
2009-02-04 10:17:13 -07:00
|
|
|
|
2014-01-10 03:02:33 -07:00
|
|
|
printf("setting mode %s-%dHz@%s on connectors ",
|
|
|
|
pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
|
2013-03-19 06:48:36 -06:00
|
|
|
for (j = 0; j < pipe->num_cons; ++j)
|
2015-01-23 09:08:21 -07:00
|
|
|
printf("%s, ", pipe->cons[j]);
|
2013-03-19 06:48:36 -06:00
|
|
|
printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
|
2009-02-04 10:17:13 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id,
|
|
|
|
x, 0, pipe->con_ids, pipe->num_cons,
|
|
|
|
pipe->mode);
|
2011-10-19 05:32:43 -06:00
|
|
|
|
|
|
|
/* XXX: Actually check if this is needed */
|
2013-02-26 21:35:13 -07:00
|
|
|
drmModeDirtyFB(dev->fd, fb_id, NULL, 0);
|
2011-10-19 05:32:43 -06:00
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
x += pipe->mode->hdisplay;
|
2009-02-03 12:00:00 -07:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
2015-04-13 02:32:14 -06:00
|
|
|
static void clear_mode(struct device *dev)
|
|
|
|
{
|
2015-04-13 02:32:15 -06:00
|
|
|
if (dev->mode.fb_id)
|
|
|
|
drmModeRmFB(dev->fd, dev->mode.fb_id);
|
2015-04-13 02:32:14 -06:00
|
|
|
if (dev->mode.bo)
|
|
|
|
bo_destroy(dev->mode.bo);
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* set up planes/overlays */
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
if (set_plane(dev, &p[i]))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
|
|
|
|
{
|
2015-04-20 13:50:45 -06:00
|
|
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
2014-12-09 13:00:58 -07:00
|
|
|
struct bo *bo;
|
2014-04-22 08:33:12 -06:00
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* maybe make cursor width/height configurable some day */
|
|
|
|
uint32_t cw = 64;
|
|
|
|
uint32_t ch = 64;
|
|
|
|
|
|
|
|
/* create cursor bo.. just using PATTERN_PLAIN as it has
|
|
|
|
* translucent alpha
|
|
|
|
*/
|
2014-12-09 13:00:58 -07:00
|
|
|
bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
|
2015-12-09 10:37:39 -07:00
|
|
|
offsets, UTIL_PATTERN_PLAIN);
|
2014-04-22 08:33:12 -06:00
|
|
|
if (bo == NULL)
|
|
|
|
return;
|
|
|
|
|
2015-04-13 02:32:18 -06:00
|
|
|
dev->mode.cursor_bo = bo;
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
ret = cursor_init(dev->fd, handles[0],
|
|
|
|
pipe->crtc->crtc->crtc_id,
|
|
|
|
pipe->mode->hdisplay, pipe->mode->vdisplay,
|
|
|
|
cw, ch);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
|
|
|
|
pipe->crtc_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor_start();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_cursors(struct device *dev)
|
|
|
|
{
|
|
|
|
cursor_stop();
|
2015-04-13 02:32:18 -06:00
|
|
|
|
|
|
|
if (dev->mode.cursor_bo)
|
|
|
|
bo_destroy(dev->mode.cursor_bo);
|
2014-04-22 08:33:12 -06:00
|
|
|
}
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
|
2013-02-26 21:35:13 -07:00
|
|
|
{
|
2015-04-20 13:50:45 -06:00
|
|
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int other_fb_id;
|
2014-12-09 13:00:58 -07:00
|
|
|
struct bo *other_bo;
|
2013-02-26 21:35:13 -07:00
|
|
|
drmEventContext evctx;
|
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2015-12-09 10:37:39 -07:00
|
|
|
other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
|
|
|
|
dev->mode.height, handles, pitches, offsets,
|
|
|
|
UTIL_PATTERN_PLAIN);
|
2012-07-20 06:50:41 -06:00
|
|
|
if (other_bo == NULL)
|
2009-11-17 13:32:23 -07:00
|
|
|
return;
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
|
|
|
|
pipes[0].fourcc, handles, pitches, offsets,
|
|
|
|
&other_fb_id, 0);
|
2009-11-17 13:32:23 -07:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
|
2015-04-13 02:32:16 -06:00
|
|
|
goto err;
|
2009-11-17 13:32:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
|
|
|
|
if (pipe->mode == NULL)
|
2009-11-17 13:32:23 -07:00
|
|
|
continue;
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id,
|
|
|
|
other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
|
|
|
|
pipe);
|
2011-09-28 15:34:09 -06:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
|
2015-04-13 02:32:16 -06:00
|
|
|
goto err_rmfb;
|
2011-09-28 15:34:09 -06:00
|
|
|
}
|
2013-03-19 06:47:39 -06:00
|
|
|
gettimeofday(&pipe->start, NULL);
|
|
|
|
pipe->swap_count = 0;
|
|
|
|
pipe->fb_id[0] = dev->mode.fb_id;
|
|
|
|
pipe->fb_id[1] = other_fb_id;
|
|
|
|
pipe->current_fb_id = other_fb_id;
|
2009-11-17 13:32:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&evctx, 0, sizeof evctx);
|
|
|
|
evctx.version = DRM_EVENT_CONTEXT_VERSION;
|
|
|
|
evctx.vblank_handler = NULL;
|
2009-12-04 10:09:19 -07:00
|
|
|
evctx.page_flip_handler = page_flip_handler;
|
2015-08-18 18:58:42 -06:00
|
|
|
|
2009-11-17 13:32:23 -07:00
|
|
|
while (1) {
|
2010-03-26 14:13:57 -06:00
|
|
|
#if 0
|
2009-11-17 13:32:23 -07:00
|
|
|
struct pollfd pfd[2];
|
|
|
|
|
|
|
|
pfd[0].fd = 0;
|
|
|
|
pfd[0].events = POLLIN;
|
|
|
|
pfd[1].fd = fd;
|
|
|
|
pfd[1].events = POLLIN;
|
|
|
|
|
|
|
|
if (poll(pfd, 2, -1) < 0) {
|
|
|
|
fprintf(stderr, "poll error\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pfd[0].revents)
|
|
|
|
break;
|
2010-03-26 14:13:57 -06:00
|
|
|
#else
|
|
|
|
struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
|
|
|
|
fd_set fds;
|
|
|
|
|
|
|
|
FD_ZERO(&fds);
|
|
|
|
FD_SET(0, &fds);
|
2013-02-26 21:35:13 -07:00
|
|
|
FD_SET(dev->fd, &fds);
|
|
|
|
ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
|
2010-03-26 14:13:57 -06:00
|
|
|
|
|
|
|
if (ret <= 0) {
|
|
|
|
fprintf(stderr, "select timed out or error (ret %d)\n",
|
|
|
|
ret);
|
|
|
|
continue;
|
|
|
|
} else if (FD_ISSET(0, &fds)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2009-11-17 13:32:23 -07:00
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
drmHandleEvent(dev->fd, &evctx);
|
2009-11-17 13:32:23 -07:00
|
|
|
}
|
2011-02-17 02:47:47 -07:00
|
|
|
|
2015-04-13 02:32:16 -06:00
|
|
|
err_rmfb:
|
2015-04-13 02:32:15 -06:00
|
|
|
drmModeRmFB(dev->fd, other_fb_id);
|
2015-04-13 02:32:16 -06:00
|
|
|
err:
|
2014-12-09 13:00:58 -07:00
|
|
|
bo_destroy(other_bo);
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2012-07-20 06:50:48 -06:00
|
|
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
static int parse_connector(struct pipe_arg *pipe, const char *arg)
|
2012-07-20 06:50:47 -06:00
|
|
|
{
|
2012-07-20 06:50:48 -06:00
|
|
|
unsigned int len;
|
2013-03-19 06:48:36 -06:00
|
|
|
unsigned int i;
|
2012-07-20 06:50:48 -06:00
|
|
|
const char *p;
|
|
|
|
char *endp;
|
|
|
|
|
2014-01-10 03:02:33 -07:00
|
|
|
pipe->vrefresh = 0;
|
2013-03-19 06:47:39 -06:00
|
|
|
pipe->crtc_id = (uint32_t)-1;
|
|
|
|
strcpy(pipe->format_str, "XR24");
|
2012-07-20 06:50:48 -06:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
/* Count the number of connectors and allocate them. */
|
|
|
|
pipe->num_cons = 1;
|
2015-01-23 09:08:21 -07:00
|
|
|
for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
|
2013-03-19 06:48:36 -06:00
|
|
|
if (*p == ',')
|
|
|
|
pipe->num_cons++;
|
|
|
|
}
|
|
|
|
|
2015-04-28 06:25:24 -06:00
|
|
|
pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
|
2015-01-23 09:08:21 -07:00
|
|
|
pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
|
|
|
|
if (pipe->con_ids == NULL || pipe->cons == NULL)
|
2013-03-19 06:48:36 -06:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Parse the connectors. */
|
|
|
|
for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
|
2015-01-23 09:08:21 -07:00
|
|
|
endp = strpbrk(p, ",@:");
|
|
|
|
if (!endp)
|
|
|
|
break;
|
|
|
|
|
|
|
|
pipe->cons[i] = strndup(p, endp - p);
|
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
if (*endp != ',')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != pipe->num_cons - 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Parse the remaining parameters. */
|
2012-07-20 06:50:48 -06:00
|
|
|
if (*endp == '@') {
|
|
|
|
arg = endp + 1;
|
2013-03-19 06:47:39 -06:00
|
|
|
pipe->crtc_id = strtoul(arg, &endp, 10);
|
2012-07-20 06:50:48 -06:00
|
|
|
}
|
|
|
|
if (*endp != ':')
|
|
|
|
return -1;
|
2012-07-20 06:50:47 -06:00
|
|
|
|
2012-07-20 06:50:48 -06:00
|
|
|
arg = endp + 1;
|
2012-07-20 06:50:47 -06:00
|
|
|
|
2014-01-10 03:02:33 -07:00
|
|
|
/* Search for the vertical refresh or the format. */
|
|
|
|
p = strpbrk(arg, "-@");
|
|
|
|
if (p == NULL)
|
|
|
|
p = arg + strlen(arg);
|
2013-03-19 06:47:39 -06:00
|
|
|
len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
|
|
|
|
strncpy(pipe->mode_str, arg, len);
|
|
|
|
pipe->mode_str[len] = '\0';
|
2012-07-20 06:50:47 -06:00
|
|
|
|
2014-01-10 03:02:33 -07:00
|
|
|
if (*p == '-') {
|
|
|
|
pipe->vrefresh = strtoul(p + 1, &endp, 10);
|
|
|
|
p = endp;
|
|
|
|
}
|
|
|
|
|
2012-07-20 06:50:48 -06:00
|
|
|
if (*p == '@') {
|
2013-03-19 06:47:39 -06:00
|
|
|
strncpy(pipe->format_str, p + 1, 4);
|
|
|
|
pipe->format_str[4] = '\0';
|
2012-07-23 10:35:06 -06:00
|
|
|
}
|
2012-07-20 06:50:48 -06:00
|
|
|
|
2015-12-09 10:37:39 -07:00
|
|
|
pipe->fourcc = util_format_fourcc(pipe->format_str);
|
2013-03-19 06:47:39 -06:00
|
|
|
if (pipe->fourcc == 0) {
|
|
|
|
fprintf(stderr, "unknown format %s\n", pipe->format_str);
|
2012-07-23 10:35:06 -06:00
|
|
|
return -1;
|
2012-07-20 06:50:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-07-20 06:50:47 -06:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static int parse_plane(struct plane_arg *plane, const char *p)
|
2012-07-20 06:50:47 -06:00
|
|
|
{
|
2013-02-26 21:35:13 -07:00
|
|
|
char *end;
|
2012-07-20 06:50:47 -06:00
|
|
|
|
2016-07-19 10:47:13 -06:00
|
|
|
plane->plane_id = strtoul(p, &end, 10);
|
|
|
|
if (*end != '@')
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
p = end + 1;
|
2013-02-26 21:35:13 -07:00
|
|
|
plane->crtc_id = strtoul(p, &end, 10);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (*end != ':')
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
p = end + 1;
|
|
|
|
plane->w = strtoul(p, &end, 10);
|
|
|
|
if (*end != 'x')
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
p = end + 1;
|
|
|
|
plane->h = strtoul(p, &end, 10);
|
|
|
|
|
|
|
|
if (*end == '+' || *end == '-') {
|
|
|
|
plane->x = strtol(end, &end, 10);
|
|
|
|
if (*end != '+' && *end != '-')
|
|
|
|
return -EINVAL;
|
|
|
|
plane->y = strtol(end, &end, 10);
|
|
|
|
|
|
|
|
plane->has_position = true;
|
|
|
|
}
|
|
|
|
|
2013-09-07 19:36:02 -06:00
|
|
|
if (*end == '*') {
|
|
|
|
p = end + 1;
|
|
|
|
plane->scale = strtod(p, &end);
|
|
|
|
if (plane->scale <= 0.0)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
plane->scale = 1.0;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (*end == '@') {
|
|
|
|
p = end + 1;
|
|
|
|
if (strlen(p) != 4)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
strcpy(plane->format_str, p);
|
|
|
|
} else {
|
|
|
|
strcpy(plane->format_str, "XR24");
|
|
|
|
}
|
|
|
|
|
2015-12-09 10:37:39 -07:00
|
|
|
plane->fourcc = util_format_fourcc(plane->format_str);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (plane->fourcc == 0) {
|
|
|
|
fprintf(stderr, "unknown format %s\n", plane->format_str);
|
|
|
|
return -EINVAL;
|
2012-07-20 06:50:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
static int parse_property(struct property_arg *p, const char *arg)
|
|
|
|
{
|
|
|
|
if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
p->obj_type = 0;
|
|
|
|
p->name[DRM_PROP_NAME_LEN] = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static void usage(char *name)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
2014-04-22 08:33:12 -06:00
|
|
|
fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
|
2013-02-08 03:12:03 -07:00
|
|
|
|
|
|
|
fprintf(stderr, "\n Query options:\n\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
fprintf(stderr, "\t-c\tlist connectors\n");
|
2013-02-08 03:12:03 -07:00
|
|
|
fprintf(stderr, "\t-e\tlist encoders\n");
|
2008-12-17 11:09:49 -07:00
|
|
|
fprintf(stderr, "\t-f\tlist framebuffers\n");
|
2013-02-08 03:12:03 -07:00
|
|
|
fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
|
|
|
|
|
|
|
|
fprintf(stderr, "\n Test options:\n\n");
|
2016-07-19 10:47:13 -06:00
|
|
|
fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
|
2014-01-10 03:02:33 -07:00
|
|
|
fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
|
2014-04-22 08:33:12 -06:00
|
|
|
fprintf(stderr, "\t-C\ttest hw cursor\n");
|
2013-02-08 03:12:03 -07:00
|
|
|
fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
|
2013-02-08 03:12:03 -07:00
|
|
|
|
2013-02-08 05:42:45 -07:00
|
|
|
fprintf(stderr, "\n Generic options:\n\n");
|
2013-02-26 21:19:44 -07:00
|
|
|
fprintf(stderr, "\t-d\tdrop master after mode set\n");
|
2013-02-08 05:42:45 -07:00
|
|
|
fprintf(stderr, "\t-M module\tuse the given driver\n");
|
2013-09-07 19:36:01 -06:00
|
|
|
fprintf(stderr, "\t-D device\tuse the given device\n");
|
2013-02-08 05:42:45 -07:00
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
fprintf(stderr, "\n\tDefault is to dump all info.\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2012-04-21 14:51:50 -06:00
|
|
|
static int page_flipping_supported(void)
|
2009-12-09 08:36:53 -07:00
|
|
|
{
|
2011-02-17 02:47:47 -07:00
|
|
|
/*FIXME: generic ioctl needed? */
|
|
|
|
return 1;
|
|
|
|
#if 0
|
2009-12-09 08:36:53 -07:00
|
|
|
int ret, value;
|
|
|
|
struct drm_i915_getparam gp;
|
|
|
|
|
|
|
|
gp.param = I915_PARAM_HAS_PAGEFLIPPING;
|
|
|
|
gp.value = &value;
|
|
|
|
|
|
|
|
ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "drm_i915_getparam: %m\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-29 19:14:44 -07:00
|
|
|
return *gp.value;
|
2011-02-17 02:47:47 -07:00
|
|
|
#endif
|
2009-12-09 08:36:53 -07:00
|
|
|
}
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
static int cursor_supported(void)
|
|
|
|
{
|
|
|
|
/*FIXME: generic ioctl needed? */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
|
|
|
|
{
|
|
|
|
drmModeConnector *connector;
|
|
|
|
unsigned int i;
|
|
|
|
uint32_t id;
|
|
|
|
char *endp;
|
|
|
|
|
|
|
|
for (i = 0; i < pipe->num_cons; i++) {
|
|
|
|
id = strtoul(pipe->cons[i], &endp, 10);
|
|
|
|
if (endp == pipe->cons[i]) {
|
|
|
|
connector = get_connector_by_name(dev, pipe->cons[i]);
|
|
|
|
if (!connector) {
|
|
|
|
fprintf(stderr, "no connector named '%s'\n",
|
|
|
|
pipe->cons[i]);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
id = connector->connector_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipe->con_ids[i] = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
static char optstr[] = "cdD:efM:P:ps:Cvw:";
|
2013-02-08 03:12:03 -07:00
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2013-02-26 21:35:13 -07:00
|
|
|
struct device dev;
|
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
int c;
|
2011-12-14 20:06:43 -07:00
|
|
|
int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
|
2013-02-26 21:19:44 -07:00
|
|
|
int drop_master = 0;
|
2009-11-17 13:32:23 -07:00
|
|
|
int test_vsync = 0;
|
2014-04-22 08:33:12 -06:00
|
|
|
int test_cursor = 0;
|
2013-09-07 19:36:01 -06:00
|
|
|
char *device = NULL;
|
2013-02-08 05:42:45 -07:00
|
|
|
char *module = NULL;
|
2012-04-21 14:51:50 -06:00
|
|
|
unsigned int i;
|
2015-01-23 09:08:21 -07:00
|
|
|
unsigned int count = 0, plane_count = 0;
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int prop_count = 0;
|
2013-03-19 06:47:39 -06:00
|
|
|
struct pipe_arg *pipe_args = NULL;
|
2013-02-28 18:28:42 -07:00
|
|
|
struct plane_arg *plane_args = NULL;
|
2013-02-26 21:35:13 -07:00
|
|
|
struct property_arg *prop_args = NULL;
|
2013-02-08 05:42:45 -07:00
|
|
|
unsigned int args = 0;
|
2013-02-26 21:35:13 -07:00
|
|
|
int ret;
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
memset(&dev, 0, sizeof dev);
|
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
opterr = 0;
|
|
|
|
while ((c = getopt(argc, argv, optstr)) != -1) {
|
2013-02-08 05:42:45 -07:00
|
|
|
args++;
|
|
|
|
|
2008-12-17 11:09:49 -07:00
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
|
|
connectors = 1;
|
|
|
|
break;
|
2013-09-07 19:36:01 -06:00
|
|
|
case 'D':
|
|
|
|
device = optarg;
|
|
|
|
args--;
|
|
|
|
break;
|
2013-02-26 21:19:44 -07:00
|
|
|
case 'd':
|
|
|
|
drop_master = 1;
|
|
|
|
break;
|
2013-02-08 03:12:03 -07:00
|
|
|
case 'e':
|
|
|
|
encoders = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
framebuffers = 1;
|
2008-12-17 11:09:49 -07:00
|
|
|
break;
|
2013-02-08 05:42:45 -07:00
|
|
|
case 'M':
|
|
|
|
module = optarg;
|
|
|
|
/* Preserve the default behaviour of dumping all information. */
|
|
|
|
args--;
|
|
|
|
break;
|
2013-02-08 03:12:03 -07:00
|
|
|
case 'P':
|
2013-02-28 18:28:42 -07:00
|
|
|
plane_args = realloc(plane_args,
|
|
|
|
(plane_count + 1) * sizeof *plane_args);
|
|
|
|
if (plane_args == NULL) {
|
|
|
|
fprintf(stderr, "memory allocation failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-04-28 07:20:30 -06:00
|
|
|
memset(&plane_args[plane_count], 0, sizeof(*plane_args));
|
2013-02-28 18:28:42 -07:00
|
|
|
|
2013-02-08 03:12:03 -07:00
|
|
|
if (parse_plane(&plane_args[plane_count], optarg) < 0)
|
|
|
|
usage(argv[0]);
|
2013-02-28 18:28:42 -07:00
|
|
|
|
2013-02-08 03:12:03 -07:00
|
|
|
plane_count++;
|
2008-12-17 11:09:49 -07:00
|
|
|
break;
|
2013-02-08 03:12:03 -07:00
|
|
|
case 'p':
|
|
|
|
crtcs = 1;
|
|
|
|
planes = 1;
|
2009-11-17 13:32:23 -07:00
|
|
|
break;
|
2008-12-17 11:09:49 -07:00
|
|
|
case 's':
|
2013-03-19 06:47:39 -06:00
|
|
|
pipe_args = realloc(pipe_args,
|
|
|
|
(count + 1) * sizeof *pipe_args);
|
|
|
|
if (pipe_args == NULL) {
|
2013-02-28 18:28:42 -07:00
|
|
|
fprintf(stderr, "memory allocation failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-04-28 07:20:30 -06:00
|
|
|
memset(&pipe_args[count], 0, sizeof(*pipe_args));
|
2013-02-28 18:28:42 -07:00
|
|
|
|
2013-03-19 06:47:39 -06:00
|
|
|
if (parse_connector(&pipe_args[count], optarg) < 0)
|
2009-02-03 12:00:00 -07:00
|
|
|
usage(argv[0]);
|
2013-02-28 18:28:42 -07:00
|
|
|
|
2015-08-18 18:58:42 -06:00
|
|
|
count++;
|
2008-12-17 11:09:49 -07:00
|
|
|
break;
|
2014-04-22 08:33:12 -06:00
|
|
|
case 'C':
|
|
|
|
test_cursor = 1;
|
|
|
|
break;
|
2013-02-08 03:12:03 -07:00
|
|
|
case 'v':
|
|
|
|
test_vsync = 1;
|
2011-12-14 20:06:43 -07:00
|
|
|
break;
|
2013-02-26 21:35:13 -07:00
|
|
|
case 'w':
|
|
|
|
prop_args = realloc(prop_args,
|
|
|
|
(prop_count + 1) * sizeof *prop_args);
|
|
|
|
if (prop_args == NULL) {
|
|
|
|
fprintf(stderr, "memory allocation failed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-04-28 07:20:30 -06:00
|
|
|
memset(&prop_args[prop_count], 0, sizeof(*prop_args));
|
2013-02-26 21:35:13 -07:00
|
|
|
|
|
|
|
if (parse_property(&prop_args[prop_count], optarg) < 0)
|
|
|
|
usage(argv[0]);
|
|
|
|
|
|
|
|
prop_count++;
|
|
|
|
break;
|
2008-12-17 11:09:49 -07:00
|
|
|
default:
|
|
|
|
usage(argv[0]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 05:42:45 -07:00
|
|
|
if (!args)
|
2013-02-26 21:35:13 -07:00
|
|
|
encoders = connectors = crtcs = planes = framebuffers = 1;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2016-01-05 07:21:23 -07:00
|
|
|
dev.fd = util_open(device, module);
|
2015-12-09 10:37:46 -07:00
|
|
|
if (dev.fd < 0)
|
|
|
|
return -1;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2012-04-21 14:51:50 -06:00
|
|
|
if (test_vsync && !page_flipping_supported()) {
|
2009-12-09 08:36:53 -07:00
|
|
|
fprintf(stderr, "page flipping not supported by drm.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (test_vsync && !count) {
|
|
|
|
fprintf(stderr, "page flipping requires at least one -s option.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
if (test_cursor && !cursor_supported()) {
|
|
|
|
fprintf(stderr, "hw cursor not supported by drm.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
dev.resources = get_resources(&dev);
|
|
|
|
if (!dev.resources) {
|
|
|
|
drmClose(dev.fd);
|
2008-12-17 11:09:49 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) {
|
|
|
|
free_resources(dev.resources);
|
|
|
|
drmClose(dev.fd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
#define dump_resource(dev, res) if (res) dump_##res(dev)
|
|
|
|
|
|
|
|
dump_resource(&dev, encoders);
|
|
|
|
dump_resource(&dev, connectors);
|
|
|
|
dump_resource(&dev, crtcs);
|
|
|
|
dump_resource(&dev, planes);
|
|
|
|
dump_resource(&dev, framebuffers);
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
for (i = 0; i < prop_count; ++i)
|
2013-02-26 21:35:13 -07:00
|
|
|
set_property(&dev, &prop_args[i]);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (count || plane_count) {
|
2014-12-09 13:00:58 -07:00
|
|
|
uint64_t cap = 0;
|
|
|
|
|
|
|
|
ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
|
|
|
|
if (ret || cap == 0) {
|
|
|
|
fprintf(stderr, "driver doesn't support the dumb buffer API\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (count)
|
2013-03-19 06:47:39 -06:00
|
|
|
set_mode(&dev, pipe_args, count);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
|
|
|
if (plane_count)
|
|
|
|
set_planes(&dev, plane_args, plane_count);
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
if (test_cursor)
|
|
|
|
set_cursors(&dev, pipe_args, count);
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
if (test_vsync)
|
2013-03-19 06:47:39 -06:00
|
|
|
test_page_flip(&dev, pipe_args, count);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2013-02-26 21:19:44 -07:00
|
|
|
if (drop_master)
|
2013-02-26 21:35:13 -07:00
|
|
|
drmDropMaster(dev.fd);
|
|
|
|
|
2014-04-22 08:33:12 -06:00
|
|
|
getchar();
|
|
|
|
|
|
|
|
if (test_cursor)
|
|
|
|
clear_cursors(&dev);
|
|
|
|
|
2015-04-28 04:41:39 -06:00
|
|
|
if (plane_count)
|
|
|
|
clear_planes(&dev, plane_args, plane_count);
|
|
|
|
|
2015-04-13 02:32:14 -06:00
|
|
|
if (count)
|
|
|
|
clear_mode(&dev);
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
free_resources(dev.resources);
|
2008-12-17 11:09:49 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|