621 lines
17 KiB
C
621 lines
17 KiB
C
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <drm.h>
|
|
#include <drm_fourcc.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "xf86drm.h"
|
|
#include "xf86drmMode.h"
|
|
#include <signal.h>
|
|
|
|
#define FORMAT DRM_FORMAT_ARGB8888 //bpp 32
|
|
|
|
struct crtc {
|
|
drmModeCrtc *crtc;
|
|
drmModeObjectProperties *props;
|
|
drmModePropertyRes **props_info;
|
|
drmModeModeInfo *mode;
|
|
};
|
|
|
|
struct encoder {
|
|
drmModeEncoder *encoder;
|
|
};
|
|
|
|
struct connector {
|
|
drmModeConnector *connector;
|
|
drmModeObjectProperties *props;
|
|
drmModePropertyRes **props_info;
|
|
char *name;
|
|
};
|
|
|
|
struct fb {
|
|
drmModeFB *fb;
|
|
};
|
|
|
|
struct plane {
|
|
drmModePlane *plane;
|
|
drmModeObjectProperties *props;
|
|
drmModePropertyRes **props_info;
|
|
};
|
|
|
|
struct resources {
|
|
drmModeRes *res;
|
|
drmModePlaneRes *plane_res;
|
|
|
|
struct crtc *crtcs;
|
|
struct encoder *encoders;
|
|
struct connector *connectors;
|
|
struct fb *fbs;
|
|
struct plane *planes;
|
|
};
|
|
|
|
struct device {
|
|
int fd;
|
|
struct resources *resources;
|
|
|
|
struct {
|
|
unsigned int width;
|
|
unsigned int height;
|
|
} mode;
|
|
|
|
drmModeAtomicReq *req;
|
|
};
|
|
|
|
|
|
static void free_resources(struct resources *res)
|
|
{
|
|
int i;
|
|
|
|
if (!res)
|
|
return;
|
|
|
|
#define free_resource(_res, __res, type, Type) \
|
|
do { \
|
|
if (!(_res)->type##s) \
|
|
break; \
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
if (!(_res)->type##s[i].type) \
|
|
break; \
|
|
drmModeFree##Type((_res)->type##s[i].type); \
|
|
} \
|
|
free((_res)->type##s); \
|
|
} while (0)
|
|
|
|
#define free_properties(_res, __res, type) \
|
|
do { \
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
drmModeFreeObjectProperties(res->type##s[i].props); \
|
|
free(res->type##s[i].props_info); \
|
|
} \
|
|
} while (0)
|
|
|
|
if (res->res) {
|
|
free_properties(res, res, crtc);
|
|
|
|
free_resource(res, res, crtc, Crtc);
|
|
free_resource(res, res, encoder, Encoder);
|
|
|
|
for (i = 0; i < res->res->count_connectors; i++)
|
|
free(res->connectors[i].name);
|
|
|
|
free_resource(res, res, connector, Connector);
|
|
free_resource(res, res, fb, FB);
|
|
|
|
drmModeFreeResources(res->res);
|
|
}
|
|
|
|
if (res->plane_res) {
|
|
free_properties(res, plane_res, plane);
|
|
|
|
free_resource(res, plane_res, plane, Plane);
|
|
|
|
drmModeFreePlaneResources(res->plane_res);
|
|
}
|
|
|
|
free(res);
|
|
}
|
|
|
|
struct type_name {
|
|
unsigned int type;
|
|
const char *name;
|
|
};
|
|
static const 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, "DP" },
|
|
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
|
|
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
|
|
{ DRM_MODE_CONNECTOR_TV, "TV" },
|
|
{ DRM_MODE_CONNECTOR_eDP, "eDP" },
|
|
{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
|
|
{ DRM_MODE_CONNECTOR_DSI, "DSI" },
|
|
{ DRM_MODE_CONNECTOR_DPI, "DPI" },
|
|
};
|
|
|
|
static const char *util_lookup_type_name(unsigned int type,
|
|
const struct type_name *table,
|
|
unsigned int count)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
if (table[i].type == type)
|
|
return table[i].name;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
const char *util_lookup_connector_type_name(unsigned int type)
|
|
{
|
|
return util_lookup_type_name(type, connector_type_names,
|
|
ARRAY_SIZE(connector_type_names));
|
|
}
|
|
|
|
/*Copy from libdrm - modetest*/
|
|
static struct resources *get_resources(struct device *dev)
|
|
{
|
|
struct resources *res;
|
|
int i;
|
|
|
|
res = calloc(1, sizeof(*res));
|
|
if (res == 0)
|
|
return NULL;
|
|
|
|
drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
|
res->res = drmModeGetResources(dev->fd);
|
|
if (!res->res) {
|
|
fprintf(stderr, "drmModeGetResources failed: %s\n",
|
|
strerror(errno));
|
|
goto error;
|
|
}
|
|
|
|
res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs));
|
|
res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders));
|
|
res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors));
|
|
res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs));
|
|
|
|
if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
|
|
goto error;
|
|
|
|
#define get_resource(_res, __res, type, Type) \
|
|
do { \
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
(_res)->type##s[i].type = \
|
|
drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
|
|
if (!(_res)->type##s[i].type) \
|
|
fprintf(stderr, "could not get %s %i: %s\n", \
|
|
#type, (_res)->__res->type##s[i], \
|
|
strerror(errno)); \
|
|
} \
|
|
} while (0)
|
|
|
|
get_resource(res, res, crtc, Crtc);
|
|
get_resource(res, res, encoder, Encoder);
|
|
get_resource(res, res, connector, Connector);
|
|
get_resource(res, res, fb, FB);
|
|
|
|
/* Set the name of all connectors based on the type name and the per-type ID. */
|
|
for (i = 0; i < res->res->count_connectors; i++) {
|
|
struct connector *connector = &res->connectors[i];
|
|
drmModeConnector *conn = connector->connector;
|
|
int num;
|
|
|
|
num = asprintf(&connector->name, "%s-%u",
|
|
util_lookup_connector_type_name(conn->connector_type),
|
|
conn->connector_type_id);
|
|
if (num < 0)
|
|
goto error;
|
|
}
|
|
|
|
#define get_properties(_res, __res, type, Type) \
|
|
do { \
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
struct type *obj = &res->type##s[i]; \
|
|
unsigned int j; \
|
|
obj->props = \
|
|
drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
|
|
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; \
|
|
} \
|
|
obj->props_info = calloc(obj->props->count_props, \
|
|
sizeof(*obj->props_info)); \
|
|
if (!obj->props_info) \
|
|
continue; \
|
|
for (j = 0; j < obj->props->count_props; ++j) \
|
|
obj->props_info[j] = \
|
|
drmModeGetProperty(dev->fd, obj->props->props[j]); \
|
|
} \
|
|
} while (0)
|
|
|
|
get_properties(res, res, crtc, CRTC);
|
|
get_properties(res, res, connector, CONNECTOR);
|
|
|
|
for (i = 0; i < res->res->count_crtcs; ++i)
|
|
res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
|
|
|
|
res->plane_res = drmModeGetPlaneResources(dev->fd);
|
|
if (!res->plane_res) {
|
|
fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
|
|
strerror(errno));
|
|
return res;
|
|
}
|
|
|
|
res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes));
|
|
if (!res->planes)
|
|
goto error;
|
|
|
|
get_resource(res, plane_res, plane, Plane);
|
|
get_properties(res, plane_res, plane, PLANE);
|
|
|
|
return res;
|
|
|
|
error:
|
|
free_resources(res);
|
|
return NULL;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Properties: from libdrm modetest
|
|
*/
|
|
|
|
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;
|
|
bool optional;
|
|
};
|
|
|
|
static bool set_property(struct device *dev, struct property_arg *p)
|
|
{
|
|
drmModeObjectProperties *props = NULL;
|
|
drmModePropertyRes **props_info = NULL;
|
|
const char *obj_type;
|
|
int ret;
|
|
int i;
|
|
|
|
p->obj_type = 0;
|
|
p->prop_id = 0;
|
|
|
|
#define find_object(_res, __res, type, Type) \
|
|
do { \
|
|
for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
|
|
struct type *obj = &(_res)->type##s[i]; \
|
|
if (obj->type->type##_id != p->obj_id) \
|
|
continue; \
|
|
p->obj_type = DRM_MODE_OBJECT_##Type; \
|
|
obj_type = #Type; \
|
|
props = obj->props; \
|
|
props_info = obj->props_info; \
|
|
} \
|
|
} while(0) \
|
|
|
|
find_object(dev->resources, res, crtc, CRTC);
|
|
if (p->obj_type == 0)
|
|
find_object(dev->resources, res, connector, CONNECTOR);
|
|
if (p->obj_type == 0)
|
|
find_object(dev->resources, plane_res, plane, PLANE);
|
|
if (p->obj_type == 0) {
|
|
fprintf(stderr, "Object %i not found, can't set property\n",
|
|
p->obj_id);
|
|
return false;
|
|
}
|
|
|
|
if (!props) {
|
|
fprintf(stderr, "%s %i has no properties\n",
|
|
obj_type, p->obj_id);
|
|
return false;
|
|
}
|
|
|
|
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) {
|
|
if (!p->optional)
|
|
fprintf(stderr, "%s %i has no %s property\n",
|
|
obj_type, p->obj_id, p->name);
|
|
return false;
|
|
}
|
|
|
|
p->prop_id = props->props[i];
|
|
|
|
ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
|
|
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));
|
|
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static drmModeModeInfo * connector_find_mode(struct device *dev, uint32_t con_id,
|
|
const char *mode_str, const unsigned int vrefresh)
|
|
{
|
|
drmModeConnector *connector = NULL;;
|
|
drmModeModeInfo *mode = NULL;
|
|
bool use_preferred = false;
|
|
int i;
|
|
|
|
for (i = 0; i < dev->resources->res->count_connectors; i++) {
|
|
if (dev->resources->connectors[i].connector->connector_id == con_id) {
|
|
connector = dev->resources->connectors[i].connector;
|
|
break;
|
|
}
|
|
}
|
|
if (!connector || !connector->count_modes)
|
|
return NULL;
|
|
|
|
if (NULL == mode_str || 0 == strlen(mode_str)) {
|
|
use_preferred = true;
|
|
}
|
|
for (i = 0; i < connector->count_modes; i++) {
|
|
mode = &connector->modes[i];
|
|
if (use_preferred) {
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED)
|
|
return mode;
|
|
} else if (!strcmp(mode->name, mode_str)) {
|
|
/* If the vertical refresh frequency is not specified then return the
|
|
* first mode that match with the name. Else, return the mode that match
|
|
* the name and the specified vertical refresh frequency.
|
|
*/
|
|
if (vrefresh == 0)
|
|
return mode;
|
|
else if (mode->vrefresh == vrefresh)
|
|
return mode;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
pthread_mutex_t main_mutex;
|
|
pthread_cond_t main_cond;
|
|
static bool user_stop = false;
|
|
|
|
void signal_handle(int signal) {
|
|
(void) signal;
|
|
fprintf(stderr, "User interrupt handling\n");
|
|
pthread_mutex_lock(&main_mutex);
|
|
user_stop = true;
|
|
pthread_mutex_unlock(&main_mutex);
|
|
pthread_cond_signal(&main_cond);
|
|
}
|
|
|
|
void wait_connection() {
|
|
//Hark for hotplugin
|
|
//the hot plugin not work in same platform
|
|
sleep(3);
|
|
}
|
|
|
|
char str[] = "d:c:m:p:C:s:";
|
|
int main(int argc, char** argv) {
|
|
int c;
|
|
int ret = 0;
|
|
const char* device_name = "meson";
|
|
const char* display_mode = "";
|
|
int display_refresh = 0;
|
|
int connector_id = -1;
|
|
int crtc_id = -1;
|
|
struct device dev;
|
|
int run_as_service = 1;
|
|
|
|
pthread_mutex_init(&main_mutex, NULL);
|
|
pthread_cond_init(&main_cond, NULL);
|
|
user_stop = false;
|
|
drmModeModeInfo *mode = NULL;
|
|
|
|
memset(&dev, 0, sizeof dev);
|
|
signal(SIGINT, signal_handle);
|
|
signal(SIGTERM, signal_handle);
|
|
|
|
while ((c = getopt(argc, argv, str)) !=-1) {
|
|
switch (c) {
|
|
case 'd':
|
|
device_name = optarg;
|
|
break;
|
|
case 'm':
|
|
display_mode = optarg;
|
|
break;
|
|
case 'p':
|
|
display_refresh = atoi(optarg);
|
|
break;
|
|
case 'c':
|
|
connector_id = atoi(optarg);
|
|
break;
|
|
case 'C':
|
|
crtc_id = atoi(optarg);
|
|
break;
|
|
case 's':
|
|
run_as_service = atoi(optarg);
|
|
break;
|
|
default:
|
|
printf("Unknown Usage:-%c\nUsage %s: -d device_path -c connector_id -C crtc_id -m mode_str -p refresh_rate\n\tegl: %s -d %s -m 1920x1080 -p 60\n",c, argv[0], argv[0], device_name);
|
|
exit(-1);
|
|
}
|
|
}
|
|
fprintf(stderr, "Use device:%s with connector mode:%s\n", device_name, strlen(display_mode)? display_mode : "preferred");
|
|
|
|
//The busid can be NULL.
|
|
//If no /dev/dri, may be caused by the udev not enabed.
|
|
//Need disable udev on libdrm build configuration.
|
|
dev.fd = drmOpen(device_name, NULL);
|
|
if (dev.fd < 0) {
|
|
fprintf(stderr, "failed to open device %s\n", strerror(errno));
|
|
exit(-1);
|
|
}
|
|
/*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);
|
|
exit(-1);
|
|
}
|
|
|
|
bool need_wait_connection = false;
|
|
do {
|
|
need_wait_connection = false;
|
|
if (dev.resources) {
|
|
free_resources(dev.resources);
|
|
}
|
|
dev.resources = get_resources(&dev);
|
|
|
|
if (dev.resources == NULL || dev.resources->res->count_crtcs < 1 || dev.resources->res->count_connectors < 1 ) {
|
|
fprintf(stderr, "failed get connector or crtc\n");
|
|
ret = -1;
|
|
need_wait_connection = true;
|
|
wait_connection();
|
|
continue;
|
|
}
|
|
fprintf(stderr, "find crtcs:%d, connectors:%d\n", dev.resources->res->count_crtcs, dev.resources->res->count_connectors );
|
|
if (connector_id == -1) {
|
|
//Set Default connector
|
|
connector_id = dev.resources->connectors[0].connector->connector_id;
|
|
}
|
|
|
|
if (crtc_id == -1) {
|
|
crtc_id = dev.resources->crtcs[0].crtc->crtc_id;
|
|
}
|
|
mode = connector_find_mode(&dev, connector_id, display_mode, display_refresh);
|
|
{//Dump crtc/connector info
|
|
int i,j;
|
|
for (j = 0; j < dev.resources->res->count_crtcs; j++) {
|
|
struct crtc *crtc = &dev.resources->crtcs[j];
|
|
fprintf(stderr, "Dump crtc[%d]\n", crtc->crtc->crtc_id);
|
|
}
|
|
for (j = 0; j < dev.resources->res->count_connectors; j++) {
|
|
struct connector *conn = &dev.resources->connectors[j];
|
|
fprintf(stderr, "Dump connector[%d] info:%s [have %d modes]\n", conn->connector->connector_id, conn->name, conn->connector->count_modes);
|
|
if (conn->connector->count_modes) {
|
|
for (i = 0; i < conn->connector->count_modes; i++) {
|
|
fprintf(stderr, "mode %d: %s@%d\n", i, conn->connector->modes[i].name, conn->connector->modes[i].vrefresh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mode == NULL) {
|
|
fprintf(stderr, "failed to find display-mode for connector %d\n", connector_id);
|
|
need_wait_connection = true;
|
|
wait_connection();
|
|
}
|
|
} while (need_wait_connection && !user_stop);
|
|
|
|
if (user_stop) {
|
|
fprintf(stderr, "User stop\n");
|
|
goto out;
|
|
}
|
|
|
|
fprintf(stdout, "Set crtc[%d] with conn[%u] use displaymode:%s@%u\n", crtc_id,connector_id, mode->name, mode->vrefresh);
|
|
/*ATOMIC to set crtc-connector, but no plane&fb specified.*/
|
|
dev.req = drmModeAtomicAlloc();
|
|
|
|
add_property(&dev, connector_id, "CRTC_ID", crtc_id);
|
|
|
|
uint32_t mode_blobid = 0;
|
|
drmModeCreatePropertyBlob(dev.fd, mode, sizeof(*mode), &mode_blobid);
|
|
add_property(&dev, crtc_id, "MODE_ID", mode_blobid);
|
|
add_property(&dev, crtc_id, "ACTIVE", 1);
|
|
|
|
ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
|
if (ret) {
|
|
fprintf(stderr, "failed to set mode: %d-%s\n", ret, strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
drmModeAtomicFree(dev.req);
|
|
dev.req = NULL;
|
|
|
|
/* not run as service */
|
|
if (!run_as_service) {
|
|
fprintf(stderr, "exit after set mode.\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Set up our event handler */
|
|
drmEventContext evctx;
|
|
memset(&evctx, 0, sizeof evctx);
|
|
evctx.version = DRM_EVENT_CONTEXT_VERSION;
|
|
evctx.vblank_handler = NULL;
|
|
evctx.page_flip_handler = NULL;
|
|
|
|
user_stop = true;
|
|
/* Poll for events */
|
|
while (!user_stop) {
|
|
#if 1
|
|
sleep(1);
|
|
pthread_mutex_lock(&main_mutex);
|
|
pthread_cond_wait(&main_cond, &main_mutex);
|
|
pthread_mutex_unlock(&main_mutex);
|
|
#else
|
|
fd_set fds;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(0, &fds);
|
|
FD_SET(dev.fd, &fds);
|
|
ret = select(dev.fd + 1, &fds, NULL, NULL, NULL);
|
|
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "select timed out or error (ret %d)\n",
|
|
ret);
|
|
continue;
|
|
} else if (FD_ISSET(0, &fds)) {
|
|
break;
|
|
}
|
|
|
|
ret = drmHandleEvent(dev.fd, &evctx);
|
|
if (ret != 0) {
|
|
printf("drmHandleEvent failed: %i\n", ret);
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
out:
|
|
if (dev.resources)
|
|
free_resources(dev.resources);
|
|
drmClose(dev.fd);
|
|
fprintf(stderr, "Exit\n");
|
|
return ret;
|
|
}
|
|
|