From 06066b47b5f318f82a11020a81c268d71d9082be Mon Sep 17 00:00:00 2001 From: Vincent Wei Date: Mon, 10 Jun 2019 18:25:58 +0800 Subject: [PATCH] helpers for DRM mode setting --- src/newgal/drm/drmvideo.c | 494 ++++++++++++++++++++++++++++++++++---- 1 file changed, 446 insertions(+), 48 deletions(-) diff --git a/src/newgal/drm/drmvideo.c b/src/newgal/drm/drmvideo.c index 30894cbf..af6a6bb6 100644 --- a/src/newgal/drm/drmvideo.c +++ b/src/newgal/drm/drmvideo.c @@ -40,6 +40,7 @@ #ifdef _MGGAL_DRM +#include #include #include #include @@ -57,19 +58,33 @@ #define DRM_DRIVER_NAME "drm" -/* Initialization/Query functions */ -static int DRM_VideoInit(_THIS, GAL_PixelFormat *vformat); -static GAL_Rect **DRM_ListModes(_THIS, GAL_PixelFormat *format, Uint32 flags); -static GAL_Surface *DRM_SetVideoMode(_THIS, GAL_Surface *current, int width, int height, int bpp, Uint32 flags); -static int DRM_SetColors(_THIS, int firstcolor, int ncolors, GAL_Color *colors); -static void DRM_VideoQuit(_THIS); +/* DRM engine operators for dumb buffer */ +static int DRM_VideoInit_Dumb(_THIS, GAL_PixelFormat *vformat); +static GAL_Rect **DRM_ListModes_Dumb(_THIS, GAL_PixelFormat *format, Uint32 flags); +static GAL_Surface *DRM_SetVideoMode_Dumb(_THIS, GAL_Surface *current, + int width, int height, int bpp, Uint32 flags); +static void DRM_VideoQuit_Dumb(_THIS); +static int DRM_AllocHWSurface_Dumb(_THIS, GAL_Surface *surface); +static void DRM_FreeHWSurface_Dumb(_THIS, GAL_Surface *surface); -/* Hardware surface functions */ -static int DRM_AllocHWSurface(_THIS, GAL_Surface *surface); -static void DRM_FreeHWSurface(_THIS, GAL_Surface *surface); +/* DRM engine operators accelerated */ +static int DRM_VideoInit_Accl(_THIS, GAL_PixelFormat *vformat); +static GAL_Rect **DRM_ListModes_Accl(_THIS, GAL_PixelFormat *format, Uint32 flags); +static GAL_Surface *DRM_SetVideoMode_Accl(_THIS, GAL_Surface *current, + int width, int height, int bpp, Uint32 flags); +static void DRM_VideoQuit_Accl(_THIS); +static int DRM_AllocHWSurface_Accl(_THIS, GAL_Surface *surface); +static void DRM_FreeHWSurface_Accl(_THIS, GAL_Surface *surface); +static int DRM_CheckHWBlit_Accl(_THIS, GAL_Surface *src, GAL_Surface *dst); +static int DRM_FillHWRect_Accl(_THIS, GAL_Surface *dst, GAL_Rect *rect, + Uint32 color); +static int DRM_SetHWColorKey_Accl(_THIS, GAL_Surface *surface, Uint32 key); +static int DRM_SetHWAlpha_Accl(_THIS, GAL_Surface *surface, Uint8 value); + +/* DRM engine methods for both dumb buffer and acclerated buffers */ +static int DRM_SetColors(_THIS, int firstcolor, int ncolors, GAL_Color *colors); /* DRM driver bootstrap functions */ - static int DRM_Available(void) { return drmAvailable(); @@ -142,7 +157,7 @@ static int open_drm_device(GAL_VideoDevice *device) if (device_fd < 0) { _ERR_PRINTF("drmOpen failed"); free(driver_name); - return -1; + return -errno; } if (strcmp(driver_name, "i915") == 0) { @@ -156,17 +171,28 @@ static int open_drm_device(GAL_VideoDevice *device) free (driver_name); if (device->hidden->driver_ops == NULL) { - close (device_fd); - return -1; - } + uint64_t has_dumb; - device->hidden->driver = device->hidden->driver_ops->create_driver(device_fd); - if (device->hidden->driver == NULL) { - close(device_fd); - return -1; + /* check whether supports dumb buffer */ + if (drmGetCap(device_fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || + !has_dumb) { + _WRN_PRINTF("the DRM device '%s' does not support dumb buffers\n", + device->hidden->dev_name); + close(device_fd); + return -EOPNOTSUPP; + } + + device->hidden->dev_fd = device_fd; + return 0; } device->hidden->dev_fd = device_fd; + device->hidden->driver = device->hidden->driver_ops->create_driver(device_fd); + if (device->hidden->driver == NULL) { + _WRN_PRINTF("failed to create DRM driver"); + device->hidden->driver_ops = NULL; + } + return 0; } @@ -202,24 +228,42 @@ static GAL_VideoDevice *DRM_CreateDevice(int devindex) return NULL; } - /* Set the function pointers */ - device->VideoInit = DRM_VideoInit; - device->ListModes = DRM_ListModes; - device->SetVideoMode = DRM_SetVideoMode; - device->SetColors = DRM_SetColors; - device->VideoQuit = DRM_VideoQuit; + if (device->hidden->driver) { + /* Use accelerated driver */ + device->VideoInit = DRM_VideoInit_Accl; + device->ListModes = DRM_ListModes_Accl; + device->SetVideoMode = DRM_SetVideoMode_Accl; + device->SetColors = DRM_SetColors; + device->VideoQuit = DRM_VideoQuit_Accl; #ifndef _MGRM_THREADS - device->RequestHWSurface = NULL; + device->RequestHWSurface = NULL; #endif - device->AllocHWSurface = DRM_AllocHWSurface; - device->CheckHWBlit = NULL; - device->FillHWRect = NULL; - device->SetHWColorKey = NULL; - device->SetHWAlpha = NULL; - device->FreeHWSurface = DRM_FreeHWSurface; + device->AllocHWSurface = DRM_AllocHWSurface_Accl; + device->CheckHWBlit = DRM_CheckHWBlit_Accl; + device->FillHWRect = DRM_FillHWRect_Accl; + device->SetHWColorKey = DRM_SetHWColorKey_Accl; + device->SetHWAlpha = DRM_SetHWAlpha_Accl; + device->FreeHWSurface = DRM_FreeHWSurface_Accl; + } + else { + /* Use DUMB buffer */ + device->VideoInit = DRM_VideoInit_Dumb; + device->ListModes = DRM_ListModes_Dumb; + device->SetVideoMode = DRM_SetVideoMode_Dumb; + device->SetColors = DRM_SetColors; + device->VideoQuit = DRM_VideoQuit_Dumb; +#ifndef _MGRM_THREADS + device->RequestHWSurface = NULL; +#endif + device->AllocHWSurface = DRM_AllocHWSurface_Dumb; + device->CheckHWBlit = NULL; + device->FillHWRect = NULL; + device->SetHWColorKey = NULL; + device->SetHWAlpha = NULL; + device->FreeHWSurface = DRM_FreeHWSurface_Dumb; + } device->free = DRM_DeleteDevice; - return device; } @@ -228,13 +272,317 @@ VideoBootStrap DRM_bootstrap = { DRM_Available, DRM_CreateDevice }; +/* + * The following helpers derived from DRM HOWTO by David Herrmann. + * + * Copyright 2012-2017 David Herrmann + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. -static int DRM_VideoInit(_THIS, GAL_PixelFormat *vformat) + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, + * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct drm_dev { + struct drm_dev *next; + + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t size; + uint32_t handle; + uint8_t *map; + + drmModeModeInfo mode; + uint32_t fb; + uint32_t conn; + uint32_t crtc; + drmModeCrtc *saved_crtc; +}; + +static struct drm_dev *drm_list = NULL; + +/* + * modeset_find_crtc(fd, res, conn, dev): + * This small helper tries to find a suitable CRTC for the given connector. + */ +static int drm_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, + struct drm_dev *dev) { - _DBG_PRINTF("NEWGAL>DRM: Calling init method!\n"); + drmModeEncoder *enc; + unsigned int i, j; + int32_t crtc; + struct drm_dev *iter; + + /* first try the currently conected encoder+crtc */ + if (conn->encoder_id) + enc = drmModeGetEncoder(fd, conn->encoder_id); + else + enc = NULL; + + if (enc) { + if (enc->crtc_id) { + crtc = enc->crtc_id; + for (iter = drm_list; iter; iter = iter->next) { + if (iter->crtc == crtc) { + crtc = -1; + break; + } + } + + if (crtc >= 0) { + drmModeFreeEncoder(enc); + dev->crtc = crtc; + return 0; + } + } + + drmModeFreeEncoder(enc); + } + + /* If the connector is not currently bound to an encoder or if the + * encoder+crtc is already used by another connector (actually unlikely + * but lets be safe), iterate all other available encoders to find a + * matching CRTC. */ + for (i = 0; i < conn->count_encoders; ++i) { + enc = drmModeGetEncoder(fd, conn->encoders[i]); + if (!enc) { + _ERR_PRINTF("NEWGAL>DRM: cannot retrieve encoder %u:%u (%d): %m\n", + i, conn->encoders[i], errno); + continue; + } + + /* iterate all global CRTCs */ + for (j = 0; j < res->count_crtcs; ++j) { + /* check whether this CRTC works with the encoder */ + if (!(enc->possible_crtcs & (1 << j))) + continue; + + /* check that no other device already uses this CRTC */ + crtc = res->crtcs[j]; + for (iter = drm_list; iter; iter = iter->next) { + if (iter->crtc == crtc) { + crtc = -1; + break; + } + } + + /* we have found a CRTC, so save it and return */ + if (crtc >= 0) { + drmModeFreeEncoder(enc); + dev->crtc = crtc; + return 0; + } + } + + drmModeFreeEncoder(enc); + } + + _ERR_PRINTF("NEWGAL>DRM: cannot find suitable CRTC for connector %u\n", + conn->connector_id); + return -ENOENT; +} + +/* + * drm_create_dumb_fb(fd, dev): + * Call this function to create a so called "dumb buffer". + * We can use it for unaccelerated software rendering on the CPU. + */ +static int drm_create_dumb_fb(int fd, struct drm_dev *dev) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + /* create dumb buffer */ + memset(&creq, 0, sizeof(creq)); + creq.width = dev->width; + creq.height = dev->height; + creq.bpp = 32; + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + _ERR_PRINTF("NEWGAL>DRM: cannot create dumb buffer (%d): %m\n", + errno); + return -errno; + } + dev->stride = creq.pitch; + dev->size = creq.size; + dev->handle = creq.handle; + + /* create framebuffer object for the dumb-buffer */ + ret = drmModeAddFB(fd, dev->width, dev->height, 24, 32, dev->stride, + dev->handle, &dev->fb); + if (ret) { + _ERR_PRINTF("NEWGAL>DRM: cannot create framebuffer (%d): %m\n", + errno); + ret = -errno; + goto err_destroy; + } + + /* prepare buffer for memory mapping */ + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = dev->handle; + ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + _ERR_PRINTF("NEWGAL>DRM: cannot map dumb buffer (%d): %m\n", + errno); + ret = -errno; + goto err_fb; + } + + /* perform actual memory mapping */ + dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, mreq.offset); + if (dev->map == MAP_FAILED) { + _ERR_PRINTF("NEWGAL>DRM: cannot mmap dumb buffer (%d): %m\n", + errno); + ret = -errno; + goto err_fb; + } + + /* clear the framebuffer to 0 */ + memset(dev->map, 0, dev->size); + return 0; + +err_fb: + drmModeRmFB(fd, dev->fb); + +err_destroy: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dev->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +/* + * drm_setup_dev: + * Set up a single connector. + */ +static int drm_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn, + struct drm_dev *dev) +{ + int ret; + + /* check if a monitor is connected */ + if (conn->connection != DRM_MODE_CONNECTED) { + _DBG_PRINTF("NEWGAL>DRM: ignoring unused connector %u\n", + conn->connector_id); + return -ENOENT; + } + + /* check if there is at least one valid mode */ + if (conn->count_modes == 0) { + _DBG_PRINTF("NEWGAL>DRM: no valid mode for connector %u\n", + conn->connector_id); + return -EFAULT; + } + + /* copy the mode information into our device structure */ + memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); + dev->width = conn->modes[0].hdisplay; + dev->height = conn->modes[0].vdisplay; + _DBG_PRINTF("NEWGAL>DRM: mode for connector %u is %ux%u\n", + conn->connector_id, dev->width, dev->height); + + /* find a crtc for this connector */ + ret = drm_find_crtc(fd, res, conn, dev); + if (ret) { + _DBG_PRINTF("NEWGAL>DRM: no valid crtc for connector %u\n", + conn->connector_id); + return ret; + } + + /* create a framebuffer for this CRTC */ + ret = drm_create_dumb_fb(fd, dev); + if (ret) { + _DBG_PRINTF("NEWGAL>DRM: cannot create framebuffer for connector %u\n", + conn->connector_id); + return ret; + } + + return 0; +} + +static int drm_prepare(int fd) +{ + drmModeRes *res; + drmModeConnector *conn; + unsigned int i; + struct drm_dev *dev; + int ret; + + /* retrieve resources */ + res = drmModeGetResources(fd); + if (!res) { + _ERR_PRINTF("NEWGAL>DRM: cannot retrieve DRM resources (%d): %m\n", + errno); + return -errno; + } + + /* iterate all connectors */ + for (i = 0; i < res->count_connectors; ++i) { + /* get information for each connector */ + conn = drmModeGetConnector(fd, res->connectors[i]); + if (!conn) { + _ERR_PRINTF("NEWGAL>DRM: cannot retrieve DRM connector %u:%u(%d): %m\n", + i, res->connectors[i], errno); + continue; + } + + /* create a device structure */ + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->conn = conn->connector_id; + + /* call helper function to prepare this connector */ + ret = drm_setup_dev(fd, res, conn, dev); + if (ret) { + if (ret != -ENOENT) { + errno = -ret; + _ERR_PRINTF("NEWGAL>DRM: cannot setup device for connector" + " %u:%u (%d): %m\n", + i, res->connectors[i], errno); + } + free(dev); + drmModeFreeConnector(conn); + continue; + } + + /* free connector data and link device into global list */ + drmModeFreeConnector(conn); + dev->next = drm_list; + drm_list = dev; + } + + /* free resources again */ + drmModeFreeResources(res); + return 0; +} + +static int DRM_VideoInit_Dumb(_THIS, GAL_PixelFormat *vformat) +{ + _DBG_PRINTF("NEWGAL>DRM: Calling %s\n", __FUNCTION__); + + drm_prepare(this->hidden->dev_fd); + + struct drm_dev *iter; + int i = 0; + for (iter = drm_list; iter; iter = iter->next) { + _MG_PRINTF("mode #%d: %ux%u(%u), fb: %u, conn: %u, crtc: %u\n", i, + iter->width, iter->height, iter->stride, + iter->fb, iter->conn, iter->crtc); + i++; + } - /* Determine the screen depth (use default 8-bit depth) */ - /* we change this during the GAL_SetVideoMode implementation... */ vformat->BitsPerPixel = 8; vformat->BytesPerPixel = 1; @@ -242,7 +590,7 @@ static int DRM_VideoInit(_THIS, GAL_PixelFormat *vformat) return(0); } -static GAL_Rect **DRM_ListModes(_THIS, GAL_PixelFormat *format, Uint32 flags) +static GAL_Rect **DRM_ListModes_Dumb(_THIS, GAL_PixelFormat *format, Uint32 flags) { if (format->BitsPerPixel < 8) { return NULL; @@ -251,7 +599,7 @@ static GAL_Rect **DRM_ListModes(_THIS, GAL_PixelFormat *format, Uint32 flags) return (GAL_Rect**) -1; } -static GAL_Surface *DRM_SetVideoMode(_THIS, GAL_Surface *current, +static GAL_Surface *DRM_SetVideoMode_Dumb(_THIS, GAL_Surface *current, int width, int height, int bpp, Uint32 flags) { int pitch; @@ -292,31 +640,81 @@ static GAL_Surface *DRM_SetVideoMode(_THIS, GAL_Surface *current, return(current); } -static int DRM_AllocHWSurface(_THIS, GAL_Surface *surface) +static int DRM_AllocHWSurface_Dumb(_THIS, GAL_Surface *surface) { return(-1); } -static void DRM_FreeHWSurface(_THIS, GAL_Surface *surface) +static void DRM_FreeHWSurface_Dumb(_THIS, GAL_Surface *surface) { surface->pixels = NULL; } +/* Note: If we are terminated, this could be called in the middle of + another video routine -- notably UpdateRects. */ +static void DRM_VideoQuit_Dumb(_THIS) +{ + if (this->screen->pixels != NULL) { + free(this->screen->pixels); + this->screen->pixels = NULL; + } +} + static int DRM_SetColors(_THIS, int firstcolor, int ncolors, GAL_Color *colors) { /* do nothing of note. */ return(1); } -/* Note: If we are terminated, this could be called in the middle of - another video routine -- notably UpdateRects. -*/ -static void DRM_VideoQuit(_THIS) +/* DRM engine operators accelerated */ +static int DRM_VideoInit_Accl(_THIS, GAL_PixelFormat *vformat) { - if (this->screen->pixels != NULL) { - free(this->screen->pixels); - this->screen->pixels = NULL; - } + return 0; +} + +static GAL_Rect **DRM_ListModes_Accl(_THIS, GAL_PixelFormat *format, Uint32 flags) +{ + return NULL; +} + +static GAL_Surface *DRM_SetVideoMode_Accl(_THIS, GAL_Surface *current, + int width, int height, int bpp, Uint32 flags) +{ + return NULL; +} + +static void DRM_VideoQuit_Accl(_THIS) +{ +} + +static int DRM_AllocHWSurface_Accl(_THIS, GAL_Surface *surface) +{ + return 0; +} + +static void DRM_FreeHWSurface_Accl(_THIS, GAL_Surface *surface) +{ +} + +static int DRM_CheckHWBlit_Accl(_THIS, GAL_Surface *src, GAL_Surface *dst) +{ + return 0; +} + +static int DRM_FillHWRect_Accl(_THIS, GAL_Surface *dst, GAL_Rect *rect, + Uint32 color) +{ + return 0; +} + +static int DRM_SetHWColorKey_Accl(_THIS, GAL_Surface *surface, Uint32 key) +{ + return 0; +} + +static int DRM_SetHWAlpha_Accl(_THIS, GAL_Surface *surface, Uint8 value) +{ + return 0; } #endif /* _MGGAL_DRM */