blob: f9f6aece8d34c901bbdfbba6320abd8cb465a1c5 [file] [log] [blame]
/*
* Copyright 2010 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.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
static int dump_info = 1;
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
struct type_name {
int type;
char *name;
};
#define type_name_fn(res) \
char * res##_str(int type) { \
int i; \
for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
if (res##_names[i].type == type) \
return res##_names[i].name; \
} \
return "(invalid)"; \
}
struct type_name encoder_type_names[] = {
{ DRM_MODE_ENCODER_NONE, "none" },
{ DRM_MODE_ENCODER_DAC, "DAC" },
{ DRM_MODE_ENCODER_TMDS, "TMDS" },
{ DRM_MODE_ENCODER_LVDS, "LVDS" },
{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
};
type_name_fn(encoder_type)
struct type_name connector_status_names[] = {
{ DRM_MODE_CONNECTED, "connected" },
{ DRM_MODE_DISCONNECTED, "disconnected" },
{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
};
type_name_fn(connector_status)
struct type_name connector_type_names[] = {
{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
{ DRM_MODE_CONNECTOR_VGA, "VGA" },
{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
{ DRM_MODE_CONNECTOR_Composite, "composite" },
{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
{ DRM_MODE_CONNECTOR_Component, "component" },
{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
{ DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
{ DRM_MODE_CONNECTOR_TV, "TV" },
{ DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
};
type_name_fn(connector_type)
/*
* 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.
*/
struct connector {
uint32_t id;
int mode_valid;
drmModeModeInfo mode;
drmModeEncoder *encoder;
drmModeConnector *connector;
int crtc;
};
static void dump_mode(drmModeModeInfo *mode)
{
printf(" %s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\n",
mode->name,
mode->vrefresh,
mode->hdisplay,
mode->hsync_start,
mode->hsync_end,
mode->htotal,
mode->vdisplay,
mode->vsync_start,
mode->vsync_end,
mode->vtotal,
mode->flags,
mode->type,
mode->clock);
}
static void dump_connectors(int fd, drmModeRes *resources)
{
int i, j;
printf("Connectors:\n");
printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n");
for (i = 0; i < resources->count_connectors; i++) {
drmModeConnector *connector;
connector = drmModeGetConnector(fd, resources->connectors[i]);
if (!connector) {
fprintf(stderr, "could not get connector %i: %s\n",
resources->connectors[i], strerror(errno));
continue;
}
printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n",
connector->connector_id,
connector->encoder_id,
connector_status_str(connector->connection),
connector_type_str(connector->connector_type),
connector->mmWidth, connector->mmHeight,
connector->count_modes);
if (!connector->count_modes)
continue;
printf(" modes:\n");
printf(" name refresh (Hz) hdisp hss hse htot vdisp "
"vss vse vtot flags type clock\n");
for (j = 0; j < connector->count_modes; j++)
dump_mode(&connector->modes[j]);
drmModeFreeConnector(connector);
}
printf("\n");
}
static void dump_crtcs(int fd, drmModeRes *resources)
{
int i;
printf("CRTCs:\n");
printf("id\tfb\tpos\tsize\n");
for (i = 0; i < resources->count_crtcs; i++) {
drmModeCrtc *crtc;
crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
if (!crtc) {
fprintf(stderr, "could not get crtc %i: %s\n",
resources->crtcs[i], strerror(errno));
continue;
}
printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
crtc->crtc_id,
crtc->buffer_id,
crtc->x, crtc->y,
crtc->width, crtc->height);
dump_mode(&crtc->mode);
drmModeFreeCrtc(crtc);
}
printf("\n");
}
static void connector_find_preferred_mode(int fd, drmModeRes *resources,
struct connector *c)
{
drmModeConnector *connector;
drmModeEncoder *encoder = NULL;
int i;
/* First, find the connector & mode */
c->mode_valid = 0;
connector = drmModeGetConnector(fd, c->id);
if (!connector) {
fprintf(stderr, "could not get connector %d: %s\n",
c->id, strerror(errno));
drmModeFreeConnector(connector);
return;
}
if (connector->connection != DRM_MODE_CONNECTED) {
drmModeFreeConnector(connector);
return;
}
if (!connector->count_modes) {
fprintf(stderr, "connector %d has no modes\n", c->id);
drmModeFreeConnector(connector);
return;
}
if (connector->connector_id != c->id) {
fprintf(stderr, "connector id doesn't match (%d != %d)\n",
connector->connector_id, c->id);
drmModeFreeConnector(connector);
return;
}
for (i = 0; i < connector->count_modes; i++) {
c->mode = connector->modes[i];
if (c->mode.type & DRM_MODE_TYPE_PREFERRED) {
c->mode_valid = 1;
break;
}
}
if (!c->mode_valid) {
if (connector->count_modes > 0) {
/* use the first mode as test mode */
c->mode = connector->modes[0];
c->mode_valid = 1;
} else {
return;
}
}
/* Now get the encoder */
for (i = 0; i < connector->count_encoders; i++) {
encoder = drmModeGetEncoder(fd, connector->encoders[i]);
if (!encoder) {
fprintf(stderr, "could not get encoder %i: %s\n",
resources->encoders[i], strerror(errno));
drmModeFreeEncoder(encoder);
continue;
}
break;
}
if (i == resources->count_encoders) {
fprintf(stderr, "failed to find encoder\n");
c->mode_valid = 0;
return;
}
c->encoder = encoder;
/* Find first CRTC not in use */
for (i = 0; i < resources->count_crtcs; i++)
if (resources->crtcs[i] &&
(c->encoder->possible_crtcs & (1<<i)))
break;
if (i == resources->count_crtcs) {
fprintf(stderr, "failed to find crtc\n");
c->mode_valid = 0;
return;
}
c->crtc = resources->crtcs[i];
c->connector = connector;
}
static int test_cursor(int fd, struct connector *c)
{
int i;
for(i = 0; i < 10; i++) {
drmModeMoveCursor(fd, c->crtc, i*100, 0);
sleep(1);
}
}
static int update_display(int fd)
{
drmModeRes *resources;
struct connector connector;
int c;
resources = drmModeGetResources(fd);
if (!resources) {
fprintf(stderr, "drmModeGetResources failed: %s\n",
strerror(errno));
return 0;
}
if (dump_info) {
dump_connectors(fd, resources);
dump_crtcs(fd, resources);
}
/* Find first connected displays */
for (c = 0; c < resources->count_connectors; c++) {
connector.id = resources->connectors[c];
connector_find_preferred_mode(fd, resources, &connector);
if (connector.mode_valid)
break;
}
if (!connector.mode_valid) {
fprintf(stderr, "failed: no valid mode\n");
return 0;
}
test_cursor(fd, &connector);
drmModeFreeResources(resources);
return 1;
}
int main(int argc, char **argv)
{
int fd = -1;
if ((fd = open("/dev/dri/card0", O_RDWR)) < 0) {
drmError(fd, argv[0]);
return 1;
}
if (update_display(fd))
printf("Success!\n");
return 0;
}