You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
7.7 KiB
C
252 lines
7.7 KiB
C
/* DRM (Direct Rendering Manager, _not_ Digital Restrictions or Rights
|
|
Management!) functions. Modified from code provided at
|
|
|
|
https://embear.ch/blog/drm-framebuffer (describes the code)
|
|
https://github.com/embear-engineering/drm-framebuffer */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <xf86drm.h>
|
|
#include <xf86drmMode.h>
|
|
|
|
struct type_name {
|
|
unsigned int type;
|
|
const char *name;
|
|
};
|
|
|
|
struct framebuffer {
|
|
int fd;
|
|
uint32_t buffer_id;
|
|
uint16_t res_x;
|
|
uint16_t res_y;
|
|
uint8_t *data;
|
|
uint32_t size;
|
|
struct drm_mode_create_dumb dumb_framebuffer;
|
|
drmModeCrtcPtr crtc;
|
|
drmModeConnectorPtr connector;
|
|
drmModeModeInfoPtr resolution;
|
|
};
|
|
|
|
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" },
|
|
};
|
|
|
|
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
|
|
|
const char *connector_type_name(unsigned int type)
|
|
{
|
|
if (type < ARRAY_SIZE(connector_type_names)) {
|
|
return connector_type_names[type].name;
|
|
}
|
|
|
|
return "INVALID";
|
|
}
|
|
|
|
void release_framebuffer(struct framebuffer *fb)
|
|
{
|
|
if (fb->fd) {
|
|
/* Try to become master again, else we can't set CRTC. Then the current master needs to reset everything. */
|
|
drmSetMaster(fb->fd);
|
|
if (fb->crtc) {
|
|
/* Set back to orignal frame buffer */
|
|
drmModeSetCrtc(fb->fd, fb->crtc->crtc_id, fb->crtc->buffer_id, 0, 0, &fb->connector->connector_id, 1, fb->resolution);
|
|
drmModeFreeCrtc(fb->crtc);
|
|
}
|
|
if (fb->buffer_id)
|
|
drmModeFreeFB(drmModeGetFB(fb->fd, fb->buffer_id));
|
|
/* This will also release resolution */
|
|
if (fb->connector) {
|
|
drmModeFreeConnector(fb->connector);
|
|
fb->resolution = 0;
|
|
}
|
|
/* if (fb->dumb_framebuffer.handle)
|
|
ioctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, fb->dumb_framebuffer);
|
|
*/ close(fb->fd);
|
|
}
|
|
}
|
|
|
|
#define FRAMEBUFFER_COULD_NOT_OPEN_DEVICE -1
|
|
#define FRAMEBUFFER_COULD_NOT_GET_RESOURCES -2
|
|
#define FRAMEBUFFER_COULD_NOT_FIND_CONNECTOR -3
|
|
#define FRAMEBUFFER_COULD_NOT_FIND_RESOLUTION -4
|
|
#define FRAMEBUFFER_COULD_NOT_CREATE_FRAMEBUFFER -5
|
|
#define FRAMEBUFFER_COULD_NOT_ADD_FRAMEBUFFER_TO_DRM -6
|
|
#define FRAMEBUFFER_COULD_NOT_GET_ENCODER -7
|
|
#define FRAMEBUFFER_MODE_MAP_FRAMEBUFFER_FAILED -8
|
|
#define FRAMEBUFFER_MODE_MAP_FAILED -9
|
|
|
|
int get_framebuffer(const char *dri_device, const char *connector_name, struct framebuffer *fb)
|
|
{
|
|
int err;
|
|
int fd;
|
|
drmModeResPtr res;
|
|
drmModeEncoderPtr encoder = 0;
|
|
|
|
if( !connector_name || !*connector_name)
|
|
connector_name = "def";
|
|
|
|
/* Open the dri device /dev/dri/cardX */
|
|
fd = open(dri_device, O_RDWR);
|
|
if (fd < 0)
|
|
return FRAMEBUFFER_COULD_NOT_OPEN_DEVICE;
|
|
|
|
/* Get the resources of the DRM device (connectors, encoders, etc.)*/
|
|
res = drmModeGetResources(fd);
|
|
if (!res) {
|
|
close( fd);
|
|
return FRAMEBUFFER_COULD_NOT_GET_RESOURCES;
|
|
}
|
|
|
|
/* Search the connector provided as argument */
|
|
drmModeConnectorPtr connector = 0;
|
|
for (int i = 0; i < res->count_connectors; i++) {
|
|
char name[32];
|
|
|
|
connector = drmModeGetConnectorCurrent(fd, res->connectors[i]);
|
|
if (!connector)
|
|
continue;
|
|
|
|
snprintf(name, sizeof(name), "%s-%u", connector_type_name(connector->connector_type),
|
|
connector->connector_type_id);
|
|
|
|
if( !strncmp(name, connector_name, strlen( connector_name)))
|
|
break;
|
|
|
|
if (strcmp( "def", connector_name) == 0 && connector->count_modes)
|
|
break;
|
|
|
|
drmModeFreeConnector(connector);
|
|
}
|
|
drmModeFreeResources( res);
|
|
|
|
if (!connector)
|
|
return FRAMEBUFFER_COULD_NOT_FIND_CONNECTOR;
|
|
|
|
/* Get the preferred resolution */
|
|
drmModeModeInfoPtr resolution = 0;
|
|
for (int i = 0; i < connector->count_modes; i++) {
|
|
resolution = &connector->modes[i];
|
|
if (resolution->type & DRM_MODE_TYPE_PREFERRED)
|
|
break;
|
|
}
|
|
|
|
if (!resolution) {
|
|
err = FRAMEBUFFER_COULD_NOT_FIND_RESOLUTION;
|
|
goto cleanup;
|
|
}
|
|
|
|
fb->dumb_framebuffer.height = resolution->vdisplay;
|
|
fb->dumb_framebuffer.width = resolution->hdisplay;
|
|
fb->dumb_framebuffer.bpp = 32;
|
|
|
|
err = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &fb->dumb_framebuffer);
|
|
if (err) {
|
|
err = FRAMEBUFFER_COULD_NOT_CREATE_FRAMEBUFFER;
|
|
goto cleanup;
|
|
}
|
|
|
|
err = drmModeAddFB(fd, resolution->hdisplay, resolution->vdisplay, 24, 32,
|
|
fb->dumb_framebuffer.pitch, fb->dumb_framebuffer.handle, &fb->buffer_id);
|
|
if (err) {
|
|
err = FRAMEBUFFER_COULD_NOT_ADD_FRAMEBUFFER_TO_DRM;
|
|
goto cleanup;
|
|
}
|
|
|
|
encoder = drmModeGetEncoder(fd, connector->encoder_id);
|
|
if (!encoder) {
|
|
err = FRAMEBUFFER_COULD_NOT_GET_ENCODER;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Get the crtc settings */
|
|
fb->crtc = drmModeGetCrtc(fd, encoder->crtc_id);
|
|
|
|
struct drm_mode_map_dumb mreq;
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.handle = fb->dumb_framebuffer.handle;
|
|
|
|
err = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
|
|
if (err) {
|
|
err = FRAMEBUFFER_MODE_MAP_FRAMEBUFFER_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
fb->data = mmap(0, fb->dumb_framebuffer.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);
|
|
if (fb->data == MAP_FAILED) {
|
|
err = FRAMEBUFFER_MODE_MAP_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Make sure we are not master anymore so that other processes can add new framebuffers as well */
|
|
drmDropMaster(fd);
|
|
|
|
fb->fd = fd;
|
|
fb->connector = connector;
|
|
fb->resolution = resolution;
|
|
|
|
cleanup:
|
|
/* We don't need the encoder and connector anymore so let's free them */
|
|
if (encoder)
|
|
drmModeFreeEncoder(encoder);
|
|
|
|
if (err)
|
|
release_framebuffer(fb);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Interface between DRM functions above and PDCursesMod */
|
|
|
|
static struct framebuffer _drm_framebuffer;
|
|
|
|
static int init_drm( const char *dri_device, const char *connector_name)
|
|
{
|
|
int rval = get_framebuffer( dri_device, connector_name, &_drm_framebuffer);
|
|
|
|
if( !rval)
|
|
{
|
|
PDC_fb.framebuf = _drm_framebuffer.data;
|
|
PDC_fb.xres = _drm_framebuffer.dumb_framebuffer.width;
|
|
PDC_fb.yres = _drm_framebuffer.dumb_framebuffer.height;
|
|
PDC_fb.bits_per_pixel = 32;
|
|
PDC_fb.line_length = _drm_framebuffer.dumb_framebuffer.pitch;
|
|
PDC_fb.smem_len = 0; /* unused */
|
|
drmSetMaster(_drm_framebuffer.fd);
|
|
drmModeSetCrtc( _drm_framebuffer.fd, _drm_framebuffer.crtc->crtc_id, 0, 0, 0, NULL, 0, NULL);
|
|
drmModeSetCrtc( _drm_framebuffer.fd, _drm_framebuffer.crtc->crtc_id, _drm_framebuffer.buffer_id, 0, 0, &_drm_framebuffer.connector->connector_id, 1, _drm_framebuffer.resolution);
|
|
drmDropMaster(_drm_framebuffer.fd);
|
|
}
|
|
return( rval);
|
|
}
|
|
|
|
static void close_drm( void)
|
|
{
|
|
release_framebuffer( &_drm_framebuffer);
|
|
}
|