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
|
|
|
|
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>
|
2018-11-07 08:00:24 -07:00
|
|
|
#if HAVE_SYS_SELECT_H
|
2016-01-19 22:35:11 -07:00
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
2019-11-15 07:31:00 -07:00
|
|
|
#include <math.h>
|
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
|
|
|
|
2019-01-05 12:02:19 -07:00
|
|
|
static enum util_fill_pattern primary_fill = UTIL_PATTERN_SMPTE;
|
|
|
|
static enum util_fill_pattern secondary_fill = UTIL_PATTERN_TILES;
|
|
|
|
|
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 {
|
|
|
|
struct crtc *crtcs;
|
2020-04-13 04:26:18 -06:00
|
|
|
int count_crtcs;
|
2013-02-26 22:39:36 -07:00
|
|
|
struct encoder *encoders;
|
2020-04-13 04:26:18 -06:00
|
|
|
int count_encoders;
|
2013-02-26 22:39:36 -07:00
|
|
|
struct connector *connectors;
|
2020-04-13 04:26:18 -06:00
|
|
|
int count_connectors;
|
2013-02-26 22:39:36 -07:00
|
|
|
struct fb *fbs;
|
2020-04-13 04:26:18 -06:00
|
|
|
int count_fbs;
|
2013-02-26 22:39:36 -07:00
|
|
|
struct plane *planes;
|
2020-04-13 04:26:18 -06:00
|
|
|
uint32_t count_planes;
|
2013-02-26 22:39:36 -07:00
|
|
|
};
|
|
|
|
|
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;
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
int use_atomic;
|
|
|
|
drmModeAtomicReq *req;
|
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
|
|
|
|
2019-12-03 07:37:36 -07:00
|
|
|
static float mode_vrefresh(drmModeModeInfo *mode)
|
|
|
|
{
|
|
|
|
return mode->clock * 1000.00
|
|
|
|
/ (mode->htotal * mode->vtotal);
|
|
|
|
}
|
|
|
|
|
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");
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_encoders; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2019-06-27 15:37:07 -06:00
|
|
|
static void dump_mode(drmModeModeInfo *mode, int index)
|
2008-12-17 22:02:43 -07:00
|
|
|
{
|
2019-06-27 15:37:07 -06:00
|
|
|
printf(" #%i %s %.2f %d %d %d %d %d %d %d %d %d",
|
|
|
|
index,
|
2008-12-17 22:02:43 -07:00
|
|
|
mode->name,
|
2019-12-03 07:37:36 -07:00
|
|
|
mode_vrefresh(mode),
|
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";
|
2019-03-01 15:40:59 -07:00
|
|
|
case DRM_FORMAT_MOD_QCOM_COMPRESSED:
|
|
|
|
return "QCOM_COMPRESSED";
|
2017-09-28 17:02:09 -06:00
|
|
|
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");
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_connectors; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
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");
|
2019-06-27 15:37:07 -06:00
|
|
|
printf("\tindex name refresh (Hz) hdisp hss hse htot vdisp "
|
2020-11-23 22:35:28 -07:00
|
|
|
"vss vse vtot\n");
|
2012-04-21 14:51:51 -06:00
|
|
|
for (j = 0; j < connector->count_modes; j++)
|
2019-06-27 15:37:07 -06:00
|
|
|
dump_mode(&connector->modes[j], 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");
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_crtcs; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
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);
|
2019-06-27 15:37:07 -06:00
|
|
|
dump_mode(&crtc->mode, 0);
|
2008-12-17 22:02:43 -07:00
|
|
|
|
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");
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_fbs; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
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
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_planes; i++) {
|
2013-02-26 21:35:13 -07:00
|
|
|
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;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
#define free_resource(_res, type, Type) \
|
2013-02-26 22:39:36 -07:00
|
|
|
do { \
|
|
|
|
if (!(_res)->type##s) \
|
|
|
|
break; \
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
|
2013-02-26 22:39:36 -07:00
|
|
|
if (!(_res)->type##s[i].type) \
|
|
|
|
break; \
|
|
|
|
drmModeFree##Type((_res)->type##s[i].type); \
|
|
|
|
} \
|
|
|
|
free((_res)->type##s); \
|
|
|
|
} while (0)
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
#define free_properties(_res, type) \
|
2013-02-26 22:39:36 -07:00
|
|
|
do { \
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
|
|
|
|
unsigned int j; \
|
|
|
|
for (j = 0; j < res->type##s[i].props->count_props; ++j)\
|
|
|
|
drmModeFreeProperty(res->type##s[i].props_info[j]);\
|
2013-02-26 22:39:36 -07:00
|
|
|
free(res->type##s[i].props_info); \
|
2020-04-13 04:26:18 -06:00
|
|
|
drmModeFreeObjectProperties(res->type##s[i].props); \
|
2013-02-26 22:39:36 -07:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
free_properties(res, plane);
|
|
|
|
free_resource(res, plane, Plane);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
free_properties(res, connector);
|
|
|
|
free_properties(res, crtc);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < res->count_connectors; i++)
|
|
|
|
free(res->connectors[i].name);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
free_resource(res, fb, FB);
|
|
|
|
free_resource(res, connector, Connector);
|
|
|
|
free_resource(res, encoder, Encoder);
|
|
|
|
free_resource(res, crtc, Crtc);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2020-04-13 04:26:18 -06:00
|
|
|
drmModeRes *_res;
|
|
|
|
drmModePlaneRes *plane_res;
|
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);
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
_res = drmModeGetResources(dev->fd);
|
|
|
|
if (!_res) {
|
2013-02-26 22:39:36 -07:00
|
|
|
fprintf(stderr, "drmModeGetResources failed: %s\n",
|
|
|
|
strerror(errno));
|
2020-04-13 04:26:18 -06:00
|
|
|
free(res);
|
|
|
|
return NULL;
|
2013-02-26 22:39:36 -07:00
|
|
|
}
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
res->count_crtcs = _res->count_crtcs;
|
|
|
|
res->count_encoders = _res->count_encoders;
|
|
|
|
res->count_connectors = _res->count_connectors;
|
|
|
|
res->count_fbs = _res->count_fbs;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
|
|
|
|
res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
|
|
|
|
res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
|
|
|
|
res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
|
|
|
|
|
|
|
|
if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
|
|
|
|
drmModeFreeResources(_res);
|
2013-02-26 22:39:36 -07:00
|
|
|
goto error;
|
2020-04-13 04:26:18 -06:00
|
|
|
}
|
2013-02-26 22:39:36 -07:00
|
|
|
|
|
|
|
#define get_resource(_res, __res, type, Type) \
|
|
|
|
do { \
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
|
|
|
|
uint32_t type##id = (__res)->type##s[i]; \
|
|
|
|
(_res)->type##s[i].type = \
|
|
|
|
drmModeGet##Type(dev->fd, type##id); \
|
|
|
|
if (!(_res)->type##s[i].type) \
|
2013-02-26 22:39:36 -07:00
|
|
|
fprintf(stderr, "could not get %s %i: %s\n", \
|
2020-04-13 04:26:18 -06:00
|
|
|
#type, type##id, \
|
2013-02-26 22:39:36 -07:00
|
|
|
strerror(errno)); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
get_resource(res, _res, crtc, Crtc);
|
|
|
|
get_resource(res, _res, encoder, Encoder);
|
|
|
|
get_resource(res, _res, connector, Connector);
|
|
|
|
get_resource(res, _res, fb, FB);
|
|
|
|
|
|
|
|
drmModeFreeResources(_res);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2015-01-23 09:08:21 -07:00
|
|
|
/* Set the name of all connectors based on the type name and the per-type ID. */
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < res->count_connectors; i++) {
|
2015-01-23 09:08:21 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
#define get_properties(_res, type, Type) \
|
2013-02-26 22:39:36 -07:00
|
|
|
do { \
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
|
2013-02-26 22:39:36 -07:00
|
|
|
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)
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
get_properties(res, crtc, CRTC);
|
|
|
|
get_properties(res, connector, CONNECTOR);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < res->count_crtcs; ++i)
|
2013-02-26 21:35:13 -07:00
|
|
|
res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
plane_res = drmModeGetPlaneResources(dev->fd);
|
|
|
|
if (!plane_res) {
|
2013-02-26 22:39:36 -07:00
|
|
|
fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
res->count_planes = plane_res->count_planes;
|
|
|
|
|
|
|
|
res->planes = calloc(res->count_planes, sizeof(*res->planes));
|
|
|
|
if (!res->planes) {
|
|
|
|
drmModeFreePlaneResources(plane_res);
|
2013-02-26 22:39:36 -07:00
|
|
|
goto error;
|
2020-04-13 04:26:18 -06:00
|
|
|
}
|
2013-02-26 22:39:36 -07:00
|
|
|
|
|
|
|
get_resource(res, plane_res, plane, Plane);
|
2020-04-13 04:26:18 -06:00
|
|
|
drmModeFreePlaneResources(plane_res);
|
|
|
|
get_properties(res, plane, PLANE);
|
2013-02-26 22:39:36 -07:00
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
error:
|
|
|
|
free_resources(res);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-13 05:31:23 -06:00
|
|
|
static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
|
2013-03-19 08:36:09 -06:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_crtcs; ++i) {
|
2013-03-19 08:36:09 -06:00
|
|
|
drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
|
|
|
|
if (crtc && crtc->crtc_id == id)
|
2020-04-13 05:31:23 -06:00
|
|
|
return &dev->resources->crtcs[i];
|
2013-03-19 08:36:09 -06:00
|
|
|
}
|
|
|
|
|
2020-04-13 05:31:23 -06:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
|
|
|
|
if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
|
|
|
|
return 1 << i;
|
|
|
|
}
|
|
|
|
/* Unreachable: crtc->crtc is one of resources->crtcs[] */
|
|
|
|
/* Don't return zero or static analysers will complain */
|
|
|
|
abort();
|
|
|
|
return 0;
|
2013-03-19 08:36:09 -06:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_connectors; i++) {
|
2015-01-23 09:08:21 -07:00
|
|
|
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;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_connectors; i++) {
|
2013-03-19 06:48:36 -06:00
|
|
|
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;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->count_encoders; i++) {
|
2013-03-19 06:48:36 -06:00
|
|
|
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];
|
2019-11-15 07:31:00 -07:00
|
|
|
float 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;
|
2018-07-25 08:00:16 -06:00
|
|
|
unsigned int old_fb_id;
|
2015-04-28 04:41:39 -06:00
|
|
|
struct bo *bo;
|
2018-07-25 08:00:16 -06:00
|
|
|
struct bo *old_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,
|
2019-11-15 07:31:00 -07:00
|
|
|
const float 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;
|
|
|
|
|
2019-06-27 15:37:07 -06:00
|
|
|
/* Pick by Index */
|
|
|
|
if (mode_str[0] == '#') {
|
|
|
|
int index = atoi(mode_str + 1);
|
|
|
|
|
|
|
|
if (index >= connector->count_modes || index < 0)
|
|
|
|
return NULL;
|
|
|
|
return &connector->modes[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pick by Name */
|
2013-03-19 06:48:36 -06:00
|
|
|
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)) {
|
2019-12-03 07:37:36 -07:00
|
|
|
/* 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.
|
2014-01-10 03:02:33 -07:00
|
|
|
*/
|
|
|
|
if (vrefresh == 0)
|
|
|
|
return mode;
|
2019-12-03 07:37:36 -07:00
|
|
|
else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
|
2014-01-10 03:02:33 -07:00
|
|
|
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;
|
2020-04-13 05:31:23 -06:00
|
|
|
struct crtc *crtc;
|
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;
|
2020-04-13 05:31:23 -06:00
|
|
|
crtc = get_crtc_by_id(dev, encoder->crtc_id);
|
|
|
|
if (!crtc)
|
|
|
|
continue;
|
|
|
|
active_crtcs |= get_crtc_mask(dev, crtc);
|
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) {
|
2019-12-03 07:37:36 -07:00
|
|
|
if (pipe->vrefresh)
|
|
|
|
fprintf(stderr,
|
|
|
|
"failed to find mode "
|
|
|
|
"\"%s-%.2fHz\" for connector %s\n",
|
|
|
|
pipe->mode_str, pipe->vrefresh, pipe->cons[i]);
|
|
|
|
else
|
|
|
|
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) {
|
2020-04-13 05:31:23 -06:00
|
|
|
pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
|
2013-03-19 06:48:36 -06:00
|
|
|
} else {
|
|
|
|
pipe->crtc = pipe_find_crtc(dev, pipe);
|
2020-04-13 05:09:59 -06:00
|
|
|
pipe->crtc_id = pipe->crtc->crtc->crtc_id;
|
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;
|
2019-06-02 12:33:26 -06:00
|
|
|
bool optional;
|
2013-02-26 21:35:13 -07:00
|
|
|
};
|
|
|
|
|
2019-06-02 12:33:26 -06:00
|
|
|
static bool 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;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
#define find_object(_res, type, Type) \
|
2013-02-26 21:35:13 -07:00
|
|
|
do { \
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
|
2013-02-26 21:35:13 -07:00
|
|
|
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) \
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
find_object(dev->resources, crtc, CRTC);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->obj_type == 0)
|
2020-04-13 04:26:18 -06:00
|
|
|
find_object(dev->resources, connector, CONNECTOR);
|
2013-02-26 21:35:13 -07:00
|
|
|
if (p->obj_type == 0)
|
2020-04-13 04:26:18 -06:00
|
|
|
find_object(dev->resources, 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);
|
2019-06-02 12:33:26 -06:00
|
|
|
return false;
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!props) {
|
|
|
|
fprintf(stderr, "%s %i has no properties\n",
|
|
|
|
obj_type, p->obj_id);
|
2019-06-02 12:33:26 -06:00
|
|
|
return false;
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-06-02 12:33:26 -06:00
|
|
|
if (!p->optional)
|
|
|
|
fprintf(stderr, "%s %i has no %s property\n",
|
|
|
|
obj_type, p->obj_id, p->name);
|
|
|
|
return false;
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
p->prop_id = props->props[i];
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (!dev->use_atomic)
|
|
|
|
ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
|
|
|
|
p->prop_id, p->value);
|
|
|
|
else
|
|
|
|
ret = drmModeAtomicAddProperty(dev->req, p->obj_id, 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));
|
2019-06-02 12:33:26 -06:00
|
|
|
|
|
|
|
return true;
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2020-04-13 05:09:59 -06:00
|
|
|
drmModePageFlip(fd, pipe->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;
|
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
static void add_property(struct device *dev, uint32_t obj_id,
|
|
|
|
const char *name, uint64_t value)
|
|
|
|
{
|
|
|
|
struct property_arg p;
|
|
|
|
|
|
|
|
p.obj_id = obj_id;
|
|
|
|
strcpy(p.name, name);
|
|
|
|
p.value = value;
|
|
|
|
|
|
|
|
set_property(dev, &p);
|
|
|
|
}
|
|
|
|
|
2019-06-02 12:33:26 -06:00
|
|
|
static bool add_property_optional(struct device *dev, uint32_t obj_id,
|
|
|
|
const char *name, uint64_t value)
|
|
|
|
{
|
|
|
|
struct property_arg p;
|
|
|
|
|
|
|
|
p.obj_id = obj_id;
|
|
|
|
strcpy(p.name, name);
|
|
|
|
p.value = value;
|
|
|
|
p.optional = true;
|
|
|
|
|
|
|
|
return set_property(dev, &p);
|
|
|
|
}
|
|
|
|
|
2017-11-17 21:52:46 -07:00
|
|
|
static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
|
|
|
|
{
|
|
|
|
unsigned blob_id = 0;
|
|
|
|
/* TODO: support 1024-sized LUTs, when the use-case arises */
|
|
|
|
struct drm_color_lut gamma_lut[256];
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
if (fourcc == DRM_FORMAT_C8) {
|
|
|
|
/* TODO: Add C8 support for more patterns */
|
|
|
|
util_smpte_c8_gamma(256, gamma_lut);
|
|
|
|
drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
gamma_lut[i].red =
|
|
|
|
gamma_lut[i].green =
|
|
|
|
gamma_lut[i].blue = i << 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
|
|
|
|
add_property_optional(dev, crtc_id, "CTM", 0);
|
|
|
|
if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
|
|
|
|
uint16_t r[256], g[256], b[256];
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
r[i] = gamma_lut[i].red;
|
|
|
|
g[i] = gamma_lut[i].green;
|
|
|
|
b[i] = gamma_lut[i].blue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
|
|
|
|
if (ret)
|
|
|
|
fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-10 16:29:58 -06:00
|
|
|
static int
|
|
|
|
bo_fb_create(int fd, unsigned int fourcc, const uint32_t w, const uint32_t h,
|
|
|
|
enum util_fill_pattern pat, struct bo **out_bo, unsigned int *out_fb_id)
|
|
|
|
{
|
|
|
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
|
|
|
struct bo *bo;
|
|
|
|
unsigned int fb_id;
|
|
|
|
|
|
|
|
bo = bo_create(fd, fourcc, w, h, handles, pitches, offsets, pat);
|
|
|
|
|
|
|
|
if (bo == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (drmModeAddFB2(fd, w, h, fourcc, handles, pitches, offsets, &fb_id, 0)) {
|
|
|
|
fprintf(stderr, "failed to add fb (%ux%u): %s\n", w, h, strerror(errno));
|
|
|
|
bo_destroy(bo);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*out_bo = bo;
|
|
|
|
*out_fb_id = fb_id;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
static int atomic_set_plane(struct device *dev, struct plane_arg *p,
|
|
|
|
int pattern, bool update)
|
|
|
|
{
|
|
|
|
struct bo *plane_bo;
|
|
|
|
int crtc_x, crtc_y, crtc_w, crtc_h;
|
|
|
|
struct crtc *crtc = NULL;
|
|
|
|
unsigned int old_fb_id;
|
|
|
|
|
|
|
|
/* Find an unused plane which can be connected to our CRTC. Find the
|
|
|
|
* CRTC index first, then iterate over available planes.
|
|
|
|
*/
|
2020-04-13 05:31:23 -06:00
|
|
|
crtc = get_crtc_by_id(dev, p->crtc_id);
|
2018-07-25 08:00:16 -06:00
|
|
|
if (!crtc) {
|
|
|
|
fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!update)
|
|
|
|
fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
|
|
|
|
p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
|
|
|
|
|
|
|
|
plane_bo = p->old_bo;
|
|
|
|
p->old_bo = p->bo;
|
|
|
|
|
|
|
|
if (!plane_bo) {
|
2020-04-10 16:29:58 -06:00
|
|
|
if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
|
|
|
|
pattern, &plane_bo, &p->fb_id))
|
2018-07-25 08:00:16 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->bo = plane_bo;
|
|
|
|
|
|
|
|
old_fb_id = p->fb_id;
|
|
|
|
p->old_fb_id = old_fb_id;
|
|
|
|
|
|
|
|
crtc_w = p->w * p->scale;
|
|
|
|
crtc_h = p->h * p->scale;
|
|
|
|
if (!p->has_position) {
|
|
|
|
/* Default to the middle of the screen */
|
|
|
|
crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
|
|
|
|
crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
|
|
|
|
} else {
|
|
|
|
crtc_x = p->x;
|
|
|
|
crtc_y = p->y;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_property(dev, p->plane_id, "FB_ID", p->fb_id);
|
|
|
|
add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
|
|
|
|
add_property(dev, p->plane_id, "SRC_X", 0);
|
|
|
|
add_property(dev, p->plane_id, "SRC_Y", 0);
|
|
|
|
add_property(dev, p->plane_id, "SRC_W", p->w << 16);
|
|
|
|
add_property(dev, p->plane_id, "SRC_H", p->h << 16);
|
|
|
|
add_property(dev, p->plane_id, "CRTC_X", crtc_x);
|
|
|
|
add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
|
|
|
|
add_property(dev, p->plane_id, "CRTC_W", crtc_w);
|
|
|
|
add_property(dev, p->plane_id, "CRTC_H", crtc_h);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
2016-07-19 10:47:13 -06:00
|
|
|
uint32_t plane_id;
|
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;
|
2020-04-13 05:31:23 -06:00
|
|
|
unsigned int i, crtc_mask;
|
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.
|
|
|
|
*/
|
2020-04-13 05:31:23 -06:00
|
|
|
crtc = get_crtc_by_id(dev, p->crtc_id);
|
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;
|
|
|
|
}
|
2020-04-13 05:31:23 -06:00
|
|
|
crtc_mask = get_crtc_mask(dev, crtc);
|
2016-07-19 10:47:13 -06:00
|
|
|
plane_id = p->plane_id;
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
for (i = 0; i < dev->resources->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
|
|
|
|
2020-04-13 05:31:23 -06:00
|
|
|
if ((ovr->possible_crtcs & crtc_mask) &&
|
2017-10-11 08:58:11 -06:00
|
|
|
(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
|
|
|
}
|
|
|
|
|
2020-04-13 04:26:18 -06:00
|
|
|
if (i == dev->resources->count_planes) {
|
2013-02-26 21:35:13 -07:00
|
|
|
fprintf(stderr, "no unused plane available for CRTC %u\n",
|
2020-04-13 05:09:59 -06:00
|
|
|
p->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);
|
|
|
|
|
2011-12-14 20:06:43 -07:00
|
|
|
/* just use single plane format for now.. */
|
2020-04-10 16:29:58 -06:00
|
|
|
if (bo_fb_create(dev->fd, p->fourcc, p->w, p->h,
|
|
|
|
secondary_fill, &p->bo, &p->fb_id))
|
2011-12-14 20:06:43 -07:00
|
|
|
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 */
|
2020-04-13 05:09:59 -06:00
|
|
|
if (drmModeSetPlane(dev->fd, plane_id, p->crtc_id, p->fb_id,
|
2020-04-10 16:29:58 -06:00
|
|
|
0, crtc_x, crtc_y, crtc_w, crtc_h,
|
2011-12-14 20:06:43 -07:00
|
|
|
0, 0, p->w << 16, p->h << 16)) {
|
|
|
|
fprintf(stderr, "failed to enable plane: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-04-13 05:09:59 -06:00
|
|
|
ovr->crtc_id = p->crtc_id;
|
2013-02-26 22:39:36 -07:00
|
|
|
|
2011-12-14 20:06:43 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
static void atomic_set_planes(struct device *dev, struct plane_arg *p,
|
|
|
|
unsigned int count, bool update)
|
|
|
|
{
|
2019-01-05 12:02:19 -07:00
|
|
|
unsigned int i, pattern = primary_fill;
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
/* set up planes */
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (i > 0)
|
2019-01-05 12:02:19 -07:00
|
|
|
pattern = secondary_fill;
|
2017-11-17 21:52:46 -07:00
|
|
|
else
|
|
|
|
set_gamma(dev, p[i].crtc_id, p[i].fourcc);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
if (atomic_set_plane(dev, &p[i], pattern, update))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 03:20:07 -06:00
|
|
|
static void
|
|
|
|
atomic_test_page_flip(struct device *dev, struct pipe_arg *pipe_args,
|
|
|
|
struct plane_arg *plane_args, unsigned int plane_count)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
gettimeofday(&pipe_args->start, NULL);
|
|
|
|
pipe_args->swap_count = 0;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
drmModeAtomicFree(dev->req);
|
|
|
|
dev->req = drmModeAtomicAlloc();
|
|
|
|
atomic_set_planes(dev, plane_args, plane_count, true);
|
|
|
|
|
|
|
|
ret = drmModeAtomicCommit(dev->fd, dev->req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "Atomic Commit failed [2]\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipe_args->swap_count++;
|
|
|
|
if (pipe_args->swap_count == 60) {
|
|
|
|
struct timeval end;
|
|
|
|
double t;
|
|
|
|
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
t = end.tv_sec + end.tv_usec * 1e-6 -
|
|
|
|
(pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
|
|
|
|
fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
|
|
|
|
pipe_args->swap_count = 0;
|
|
|
|
pipe_args->start = end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
add_property(dev, p[i].plane_id, "FB_ID", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "CRTC_ID", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "SRC_X", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "SRC_Y", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "SRC_W", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "SRC_H", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "CRTC_X", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "CRTC_Y", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "CRTC_W", 0);
|
|
|
|
add_property(dev, p[i].plane_id, "CRTC_H", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void atomic_clear_FB(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);
|
|
|
|
p[i].fb_id = 0;
|
|
|
|
}
|
|
|
|
if (p[i].old_fb_id) {
|
|
|
|
drmModeRmFB(dev->fd, p[i].old_fb_id);
|
|
|
|
p[i].old_fb_id = 0;
|
|
|
|
}
|
|
|
|
if (p[i].bo) {
|
|
|
|
bo_destroy(p[i].bo);
|
|
|
|
p[i].bo = NULL;
|
|
|
|
}
|
|
|
|
if (p[i].old_bo) {
|
|
|
|
bo_destroy(p[i].old_bo);
|
|
|
|
p[i].old_bo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 02:57:09 -06: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;
|
|
|
|
}
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
static int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
|
|
|
|
struct pipe_arg *pipe)
|
|
|
|
{
|
|
|
|
char *con_str;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
con_str = calloc(8, sizeof(char));
|
|
|
|
if (!con_str)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
sprintf(con_str, "%d", con->connector_id);
|
|
|
|
strcpy(pipe->format_str, "XR24");
|
|
|
|
pipe->fourcc = util_format_fourcc(pipe->format_str);
|
|
|
|
pipe->num_cons = 1;
|
|
|
|
pipe->con_ids = calloc(1, sizeof(*pipe->con_ids));
|
|
|
|
pipe->cons = calloc(1, sizeof(*pipe->cons));
|
|
|
|
|
|
|
|
if (!pipe->con_ids || !pipe->cons)
|
|
|
|
goto free_con_str;
|
|
|
|
|
|
|
|
pipe->con_ids[0] = con->connector_id;
|
|
|
|
pipe->cons[0] = (const char*)con_str;
|
|
|
|
|
|
|
|
pipe->crtc = pipe_find_crtc(dev, pipe);
|
|
|
|
if (!pipe->crtc)
|
|
|
|
goto free_all;
|
|
|
|
|
|
|
|
pipe->crtc_id = pipe->crtc->crtc->crtc_id;
|
|
|
|
|
|
|
|
/* Return the first mode if no preferred. */
|
|
|
|
pipe->mode = &con->modes[0];
|
|
|
|
|
|
|
|
for (i = 0; i < con->count_modes; i++) {
|
|
|
|
drmModeModeInfo *current_mode = &con->modes[i];
|
|
|
|
|
|
|
|
if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
|
|
|
|
pipe->mode = current_mode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(pipe->mode_str, "%dx%d", pipe->mode->hdisplay, pipe->mode->vdisplay);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_all:
|
|
|
|
free(pipe->cons);
|
|
|
|
free(pipe->con_ids);
|
|
|
|
free_con_str:
|
|
|
|
free(con_str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
|
|
|
|
{
|
|
|
|
struct pipe_arg *pipes;
|
|
|
|
struct resources *res = dev->resources;
|
|
|
|
drmModeConnector *con = NULL;
|
|
|
|
int i, connected = 0, attempted = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < res->count_connectors; i++) {
|
|
|
|
con = res->connectors[i].connector;
|
|
|
|
if (!con || con->connection != DRM_MODE_CONNECTED)
|
|
|
|
continue;
|
|
|
|
connected++;
|
|
|
|
}
|
|
|
|
if (!connected) {
|
|
|
|
printf("no connected connector!\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipes = calloc(connected, sizeof(struct pipe_arg));
|
|
|
|
if (!pipes)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < res->count_connectors && attempted < connected; i++) {
|
|
|
|
con = res->connectors[i].connector;
|
|
|
|
if (!con || con->connection != DRM_MODE_CONNECTED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pipe_attempt_connector(dev, con, &pipes[attempted]) < 0) {
|
|
|
|
printf("failed fetching preferred mode for connector\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
attempted++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_pipes = pipes;
|
|
|
|
return attempted;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct plane *get_primary_plane_by_crtc(struct device *dev, struct crtc *crtc)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->resources->count_planes; i++) {
|
|
|
|
struct plane *plane = &dev->resources->planes[i];
|
|
|
|
drmModePlane *ovr = plane->plane;
|
|
|
|
if (!ovr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// XXX: add is_primary_plane and (?) format checks
|
|
|
|
|
|
|
|
if (ovr->possible_crtcs & get_crtc_mask(dev, crtc))
|
|
|
|
return plane;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
|
2018-07-25 08:00:16 -06:00
|
|
|
{
|
2020-04-13 09:05:52 -06:00
|
|
|
unsigned int i, j;
|
|
|
|
int ret, x = 0;
|
2019-07-22 10:08:23 -06:00
|
|
|
int preferred = count == 0;
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
|
2020-04-13 08:33:50 -06:00
|
|
|
ret = pipe_resolve_connectors(dev, pipe);
|
|
|
|
if (ret < 0)
|
|
|
|
return;
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
ret = pipe_find_crtc_and_mode(dev, pipe);
|
|
|
|
if (ret < 0)
|
|
|
|
continue;
|
|
|
|
}
|
2019-07-22 10:08:23 -06:00
|
|
|
if (preferred) {
|
|
|
|
struct pipe_arg *pipe_args;
|
|
|
|
|
|
|
|
count = pipe_find_preferred(dev, &pipe_args);
|
|
|
|
if (!count) {
|
|
|
|
fprintf(stderr, "can't find any preferred connector/mode.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pipes = pipe_args;
|
|
|
|
}
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
if (!dev->use_atomic) {
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
|
|
|
|
if (pipe->mode == NULL)
|
|
|
|
continue;
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (!preferred) {
|
|
|
|
dev->mode.width += pipe->mode->hdisplay;
|
|
|
|
if (dev->mode.height < pipe->mode->vdisplay)
|
|
|
|
dev->mode.height = pipe->mode->vdisplay;
|
|
|
|
} else {
|
|
|
|
/* XXX: Use a clone mode, more like atomic. We could do per
|
|
|
|
* connector bo/fb, so we don't have the stretched image.
|
|
|
|
*/
|
|
|
|
if (dev->mode.width < pipe->mode->hdisplay)
|
|
|
|
dev->mode.width = pipe->mode->hdisplay;
|
|
|
|
if (dev->mode.height < pipe->mode->vdisplay)
|
|
|
|
dev->mode.height = pipe->mode->vdisplay;
|
|
|
|
}
|
2020-04-13 09:05:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
|
|
|
|
primary_fill, &dev->mode.bo, &dev->mode.fb_id))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
struct pipe_arg *pipe = &pipes[i];
|
|
|
|
uint32_t blob_id;
|
|
|
|
|
|
|
|
if (pipe->mode == NULL)
|
|
|
|
continue;
|
|
|
|
|
2019-11-15 07:31:00 -07:00
|
|
|
printf("setting mode %s-%.2fHz on connectors ",
|
2019-12-03 07:37:36 -07:00
|
|
|
pipe->mode->name, mode_vrefresh(pipe->mode));
|
2018-07-25 08:00:16 -06:00
|
|
|
for (j = 0; j < pipe->num_cons; ++j) {
|
|
|
|
printf("%s, ", pipe->cons[j]);
|
2020-04-13 09:05:52 -06:00
|
|
|
if (dev->use_atomic)
|
|
|
|
add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
|
2018-07-25 08:00:16 -06:00
|
|
|
}
|
2020-04-13 05:09:59 -06:00
|
|
|
printf("crtc %d\n", pipe->crtc_id);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
if (!dev->use_atomic) {
|
|
|
|
ret = drmModeSetCrtc(dev->fd, pipe->crtc_id, dev->mode.fb_id,
|
|
|
|
x, 0, pipe->con_ids, pipe->num_cons,
|
|
|
|
pipe->mode);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
/* XXX: Actually check if this is needed */
|
|
|
|
drmModeDirtyFB(dev->fd, dev->mode.fb_id, NULL, 0);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (!preferred)
|
|
|
|
x += pipe->mode->hdisplay;
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
2018-07-25 08:00:16 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
set_gamma(dev, pipe->crtc_id, pipe->fourcc);
|
|
|
|
} else {
|
|
|
|
drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
|
|
|
|
add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
|
|
|
|
add_property(dev, pipe->crtc_id, "ACTIVE", 1);
|
2019-07-22 10:08:23 -06:00
|
|
|
|
|
|
|
/* By default atomic modeset does not set a primary plane, shrug */
|
|
|
|
if (preferred) {
|
|
|
|
struct plane *plane = get_primary_plane_by_crtc(dev, pipe->crtc);
|
|
|
|
struct plane_arg plane_args = {
|
|
|
|
.plane_id = plane->plane->plane_id,
|
|
|
|
.crtc_id = pipe->crtc_id,
|
|
|
|
.w = pipe->mode->hdisplay,
|
|
|
|
.h = pipe->mode->vdisplay,
|
|
|
|
.scale = 1.0,
|
|
|
|
.format_str = "XR24",
|
|
|
|
.fourcc = util_format_fourcc(pipe->format_str),
|
|
|
|
};
|
|
|
|
|
|
|
|
atomic_set_planes(dev, &plane_args, 1, false);
|
|
|
|
}
|
2020-04-13 09:05:52 -06:00
|
|
|
}
|
2018-07-25 08:00:16 -06:00
|
|
|
}
|
|
|
|
}
|
2015-04-28 04:41:39 -06:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
|
2009-02-03 13:03:41 -07:00
|
|
|
{
|
2013-02-26 21:35:13 -07:00
|
|
|
unsigned int i;
|
2013-03-19 06:48:36 -06:00
|
|
|
unsigned int j;
|
2009-02-03 13:03:41 -07:00
|
|
|
|
2009-02-03 12:00:00 -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-02-03 12:00:00 -07:00
|
|
|
continue;
|
2009-02-04 10:17:13 -07:00
|
|
|
|
2013-03-19 06:48:36 -06:00
|
|
|
for (j = 0; j < pipe->num_cons; ++j)
|
2020-04-13 09:05:52 -06:00
|
|
|
add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
|
2017-11-17 21:52:46 -07:00
|
|
|
|
2020-04-13 09:05:52 -06:00
|
|
|
add_property(dev, pipe->crtc_id, "MODE_ID", 0);
|
|
|
|
add_property(dev, pipe->crtc_id, "ACTIVE", 0);
|
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],
|
2020-04-13 05:09:59 -06:00
|
|
|
pipe->crtc_id,
|
2014-04-22 08:33:12 -06:00
|
|
|
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
|
|
|
{
|
|
|
|
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
|
|
|
|
2020-04-10 16:29:58 -06:00
|
|
|
if (bo_fb_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
|
|
|
|
UTIL_PATTERN_PLAIN, &other_bo, &other_fb_id))
|
2009-11-17 13:32:23 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-04-13 05:09:59 -06:00
|
|
|
ret = drmModePageFlip(dev->fd, pipe->crtc_id,
|
2013-03-19 06:48:36 -06:00
|
|
|
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);
|
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. */
|
2019-07-22 10:08:22 -06:00
|
|
|
if (!endp)
|
|
|
|
return -1;
|
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 == '-') {
|
2019-11-15 07:31:00 -07:00
|
|
|
pipe->vrefresh = strtof(p + 1, &endp);
|
2014-01-10 03:02:33 -07:00
|
|
|
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 == '@') {
|
2017-11-17 21:52:46 -07:00
|
|
|
strncpy(plane->format_str, end + 1, 4);
|
|
|
|
plane->format_str[4] = '\0';
|
2013-02-26 21:35:13 -07:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2019-01-05 12:02:19 -07:00
|
|
|
static void parse_fill_patterns(char *arg)
|
|
|
|
{
|
|
|
|
char *fill = strtok(arg, ",");
|
|
|
|
if (!fill)
|
|
|
|
return;
|
|
|
|
primary_fill = util_pattern_enum(fill);
|
|
|
|
fill = strtok(NULL, ",");
|
|
|
|
if (!fill)
|
|
|
|
return;
|
|
|
|
secondary_fill = util_pattern_enum(fill);
|
|
|
|
}
|
|
|
|
|
2013-02-11 13:36:30 -07:00
|
|
|
static void usage(char *name)
|
2008-12-17 11:09:49 -07:00
|
|
|
{
|
2019-07-22 10:08:23 -06:00
|
|
|
fprintf(stderr, "usage: %s [-acDdefMPpsCvrw]\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");
|
2019-06-27 15:37:07 -06:00
|
|
|
fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<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");
|
2019-07-22 10:08:23 -06:00
|
|
|
fprintf(stderr, "\t-r\tset the preferred mode for all connectors\n");
|
2013-02-26 21:35:13 -07:00
|
|
|
fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
|
2018-07-25 08:00:16 -06:00
|
|
|
fprintf(stderr, "\t-a \tuse atomic API\n");
|
2019-01-05 12:02:19 -07:00
|
|
|
fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\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);
|
|
|
|
}
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
static char optstr[] = "acdD:efF:M:P:ps:Cvrw:";
|
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;
|
2019-07-22 10:08:23 -06:00
|
|
|
int set_preferred = 0;
|
2018-07-25 08:00:16 -06:00
|
|
|
int use_atomic = 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) {
|
2018-07-25 08:00:16 -06:00
|
|
|
case 'a':
|
|
|
|
use_atomic = 1;
|
2020-04-10 13:12:54 -06:00
|
|
|
/* Preserve the default behaviour of dumping all information. */
|
|
|
|
args--;
|
2018-07-25 08:00:16 -06:00
|
|
|
break;
|
2008-12-17 11:09:49 -07:00
|
|
|
case 'c':
|
|
|
|
connectors = 1;
|
|
|
|
break;
|
2013-09-07 19:36:01 -06:00
|
|
|
case 'D':
|
|
|
|
device = optarg;
|
2020-04-10 13:12:54 -06:00
|
|
|
/* Preserve the default behaviour of dumping all information. */
|
2013-09-07 19:36:01 -06:00
|
|
|
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;
|
2019-01-05 12:02:19 -07:00
|
|
|
case 'F':
|
|
|
|
parse_fill_patterns(optarg);
|
|
|
|
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;
|
2019-07-22 10:08:23 -06:00
|
|
|
case 'r':
|
|
|
|
set_preferred = 1;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-10 13:12:54 -06:00
|
|
|
/* Dump all the details when no* arguments are provided. */
|
|
|
|
if (!args)
|
2013-02-26 21:35:13 -07:00
|
|
|
encoders = connectors = crtcs = planes = framebuffers = 1;
|
2008-12-17 11:09:49 -07:00
|
|
|
|
2020-04-10 13:17:07 -06:00
|
|
|
if (test_vsync && !count) {
|
|
|
|
fprintf(stderr, "page flipping requires at least one -s option.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred && count) {
|
|
|
|
fprintf(stderr, "cannot use -r (preferred) when -s (mode) is set\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set_preferred && plane_count) {
|
|
|
|
fprintf(stderr, "cannot use -r (preferred) when -P (plane) is set\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2020-04-10 13:17:07 -06: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
|
|
|
|
2020-04-10 11:31:18 -06:00
|
|
|
if (use_atomic) {
|
|
|
|
ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
|
|
|
|
drmClose(dev.fd);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-07-25 08:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
dev.use_atomic = use_atomic;
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (dev.use_atomic) {
|
|
|
|
dev.req = drmModeAtomicAlloc();
|
2014-12-09 13:00:58 -07:00
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred || (count && plane_count)) {
|
2018-07-25 08:00:16 -06: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");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred || count)
|
2020-04-15 03:37:24 -06:00
|
|
|
set_mode(&dev, pipe_args, count);
|
|
|
|
|
|
|
|
if (plane_count)
|
|
|
|
atomic_set_planes(&dev, plane_args, plane_count, false);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "Atomic Commit failed [1]\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-04-15 03:20:07 -06:00
|
|
|
if (test_vsync)
|
|
|
|
atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
|
2018-07-25 08:00:16 -06:00
|
|
|
|
|
|
|
if (drop_master)
|
|
|
|
drmDropMaster(dev.fd);
|
|
|
|
|
|
|
|
getchar();
|
|
|
|
|
|
|
|
drmModeAtomicFree(dev.req);
|
|
|
|
dev.req = drmModeAtomicAlloc();
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
/* XXX: properly teardown the preferred mode/plane state */
|
2020-04-15 03:37:24 -06:00
|
|
|
if (plane_count)
|
|
|
|
atomic_clear_planes(&dev, plane_args, plane_count);
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
atomic_clear_mode(&dev, pipe_args, count);
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
2020-04-15 03:28:36 -06:00
|
|
|
if (ret)
|
2018-07-25 08:00:16 -06:00
|
|
|
fprintf(stderr, "Atomic Commit failed\n");
|
|
|
|
|
2020-04-15 03:37:24 -06:00
|
|
|
if (plane_count)
|
|
|
|
atomic_clear_FB(&dev, plane_args, plane_count);
|
2013-02-26 21:35:13 -07:00
|
|
|
}
|
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
drmModeAtomicFree(dev.req);
|
|
|
|
} else {
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred || count || plane_count) {
|
2018-07-25 08:00:16 -06:00
|
|
|
uint64_t cap = 0;
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
|
|
|
|
if (ret || cap == 0) {
|
|
|
|
fprintf(stderr, "driver doesn't support the dumb buffer API\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred || count)
|
2018-07-25 08:00:16 -06:00
|
|
|
set_mode(&dev, pipe_args, count);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (plane_count)
|
|
|
|
set_planes(&dev, plane_args, plane_count);
|
2014-04-22 08:33:12 -06:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (test_cursor)
|
|
|
|
set_cursors(&dev, pipe_args, count);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (test_vsync)
|
|
|
|
test_page_flip(&dev, pipe_args, count);
|
2013-02-26 21:35:13 -07:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (drop_master)
|
|
|
|
drmDropMaster(dev.fd);
|
2014-04-22 08:33:12 -06:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
getchar();
|
2014-04-22 08:33:12 -06:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (test_cursor)
|
|
|
|
clear_cursors(&dev);
|
2015-04-28 04:41:39 -06:00
|
|
|
|
2018-07-25 08:00:16 -06:00
|
|
|
if (plane_count)
|
|
|
|
clear_planes(&dev, plane_args, plane_count);
|
|
|
|
|
2019-07-22 10:08:23 -06:00
|
|
|
if (set_preferred || count)
|
2018-07-25 08:00:16 -06:00
|
|
|
clear_mode(&dev);
|
|
|
|
}
|
2008-12-17 11:09:49 -07:00
|
|
|
}
|
|
|
|
|
2013-02-26 21:35:13 -07:00
|
|
|
free_resources(dev.resources);
|
2020-04-14 04:31:07 -06:00
|
|
|
drmClose(dev.fd);
|
2008-12-17 11:09:49 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|