diff --git a/src/egl/drivers/dri/Makefile b/src/egl/drivers/dri/Makefile new file mode 100644 index 00000000000..6b50959a5ab --- /dev/null +++ b/src/egl/drivers/dri/Makefile @@ -0,0 +1,64 @@ +# src/egl/drivers/dri/Makefile + +TOP = ../../../.. +include $(TOP)/configs/current + + +### Include directories +INCLUDE_DIRS = \ + -I. \ + -I$(DRM_SOURCE_PATH)/shared-core \ + -I$(DRM_SOURCE_PATH)/libdrm \ + -I$(TOP)/include \ + -I$(TOP)/include/GL/internal \ + -I$(TOP)/src/mesa \ + -I$(TOP)/src/mesa/main \ + -I$(TOP)/src/mesa/glapi \ + -I$(TOP)/src/mesa/math \ + -I$(TOP)/src/mesa/transform \ + -I$(TOP)/src/mesa/shader \ + -I$(TOP)/src/mesa/swrast \ + -I$(TOP)/src/mesa/swrast_setup \ + -I$(TOP)/src/egl/main \ + -I$(TOP)/src/mesa/drivers/dri/common + + +HEADERS = egldri.h + +SOURCES = egldri.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drm.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drmHash.c \ + $(DRM_SOURCE_PATH)/libdrm/xf86drmRandom.c + +OBJECTS = $(SOURCES:.c=.o) + + +.c.o: + $(CC) -c $(INCLUDE_DIRS) $(CFLAGS) $< -o $@ + + + +default: depend library Makefile + + +# EGLdri Library +library: $(LIB_DIR)/libEGLdri.so + +$(LIB_DIR)/libEGLdri.so: $(OBJECTS) + $(TOP)/bin/mklib -o EGLdri -major 1 -minor 0 \ + -install $(LIB_DIR) -ldl $(OBJECTS) + + +clean: + rm -f *.o + rm -f *.so + +depend: $(SOURCES) $(HEADERS) + @ echo "running $(MKDEP)" + @ touch depend + $(MKDEP) $(MKDEP_OPTIONS) $(DEFINES) $(INCLUDE_DIRS) \ + $(SOURCES) $(HEADERS) > /dev/null + +include depend +# DO NOT DELETE + diff --git a/src/egl/drivers/dri/egldri.c b/src/egl/drivers/dri/egldri.c new file mode 100644 index 00000000000..2d451277690 --- /dev/null +++ b/src/egl/drivers/dri/egldri.c @@ -0,0 +1,1001 @@ +/* + * Generic EGL driver for DRI + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "egldriver.h" +#include "egldisplay.h" +#include "eglcontext.h" +#include "eglconfig.h" +#include "eglsurface.h" +#include "eglscreen.h" +#include "eglglobals.h" +#include "eglmode.h" + +#include "egldri.h" + +const char *sysfs = "/sys/class"; +#define None 0 +static const int empty_attribute_list[1] = { None }; + +/** + * The bootstrap function. Return a new driDriver object and + * plug in API functions. + */ +_EGLDriver * +_eglMain(_EGLDisplay *dpy) +{ + int length; + char path[NAME_MAX]; + struct dirent *dirent; + FILE *file; + DIR *dir; + _EGLDriver *driver = NULL;; + + snprintf(path, sizeof(path), "%s/drm", sysfs); + if (!(dir = opendir(path))) { + printf("EGL - %s DRM devices not found.", path); + return EGL_FALSE; + } + while ((dirent = readdir(dir))) { + + if (strncmp(&dirent->d_name[0], "card", 4) != 0) + continue; + if (strcmp(&dirent->d_name[4], &dpy->Name[1]) != 0) + continue; + + snprintf(path, sizeof(path), "%s/drm/card%s/dri_library_name", sysfs, &dpy->Name[1]); + file = fopen(path, "r"); + fgets(path, sizeof(path), file); + fclose(file); + + if ((length = strlen(path)) > 0) + path[length - 1] = '\0'; /* remove the trailing newline from sysfs */ + strncat(path, "_dri", sizeof(path)); + + driver = _eglOpenDriver(dpy, path); + + break; + } + closedir(dir); + + return driver; +} + + +static EGLContext +_eglDRICreateContext(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list) +{ + _EGLConfig *conf; + driContext *c; + driDisplay *disp = Lookup_driDisplay(dpy); + driContext *share = Lookup_driContext(share_list); + void *sharePriv; + __GLcontextModes mode; + int i; + + conf = _eglLookupConfig(drv, dpy, config); + if (!conf) { + _eglError(EGL_BAD_CONFIG, "eglCreateContext"); + return EGL_NO_CONTEXT; + } + + for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) { + switch (attrib_list[i]) { + /* no attribs defined for now */ + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglCreateContext"); + return EGL_NO_CONTEXT; + } + } + + c = (driContext *) calloc(1, sizeof(*c)); + if (!c) + return EGL_NO_CONTEXT; + + _eglInitContext(&c->Base); + c->Base.Display = &disp->Base; + c->Base.Config = conf; + c->Base.DrawSurface = EGL_NO_SURFACE; + c->Base.ReadSurface = EGL_NO_SURFACE; + + _eglConfigToContextModesRec(conf, &mode); + + if (share) + sharePriv = share->driContext.private; + else + sharePriv = NULL; + + c->driContext.private = disp->driScreen.createNewContext(disp, &mode, + GLX_WINDOW_BIT, sharePriv, &c->driContext); + + if (!c->driContext.private) { + free(c); + return EGL_FALSE; + } + + /* generate handle and insert into hash table */ + _eglSaveContext(&c->Base); + assert(c->Base.Handle); + + return c->Base.Handle; +} + + +static EGLBoolean +_eglDRIMakeCurrent(_EGLDriver *drv, EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context) +{ + driDisplay *disp = Lookup_driDisplay(dpy); + driContext *ctx = Lookup_driContext(context); + EGLBoolean b; + + b = _eglMakeCurrent(drv, dpy, draw, read, context); + if (!b) + return EGL_FALSE; + + if (ctx) { + ctx->driContext.bindContext(disp, 0, read, draw, &ctx->driContext); + } else { +// _mesa_make_current( NULL, NULL, NULL ); + } + return EGL_TRUE; +} + + +static EGLSurface +_eglDRICreateWindowSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list) +{ + int i; + for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) { + switch (attrib_list[i]) { + /* no attribs at this time */ + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglCreateWindowSurface"); + return EGL_NO_SURFACE; + } + } + printf("eglCreateWindowSurface()\n"); + /* XXX unfinished */ + + return EGL_NO_SURFACE; +} + + +static EGLSurface +_eglDRICreatePixmapSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, const EGLint *attrib_list) +{ + _EGLConfig *conf; + EGLint i; + + conf = _eglLookupConfig(drv, dpy, config); + if (!conf) { + _eglError(EGL_BAD_CONFIG, "eglCreatePixmapSurface"); + return EGL_NO_SURFACE; + } + + for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) { + switch (attrib_list[i]) { + /* no attribs at this time */ + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglCreatePixmapSurface"); + return EGL_NO_SURFACE; + } + } + + if (conf->Attrib[EGL_SURFACE_TYPE - FIRST_ATTRIB] == 0) { + _eglError(EGL_BAD_MATCH, "eglCreatePixmapSurface"); + return EGL_NO_SURFACE; + } + + printf("eglCreatePixmapSurface()\n"); + return EGL_NO_SURFACE; +} + + +static EGLSurface +_eglDRICreatePbufferSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) +{ + driSurface *surf; + + surf = (driSurface *) calloc(1, sizeof(*surf)); + if (!surf) { + return EGL_NO_SURFACE; + } + + if (_eglInitPbufferSurface(&surf->Base, drv, dpy, config, attrib_list) == EGL_NO_SURFACE) { + free(surf); + return EGL_NO_SURFACE; + } + + /* create software-based pbuffer */ + { +// GLcontext *ctx = NULL; /* this _should_ be OK */ + GLvisual vis; + _EGLConfig *conf = _eglLookupConfig(drv, dpy, config); + assert(conf); /* bad config should be caught earlier */ + _eglConfigToContextModesRec(conf, &vis); + +#if 0 + surf->mesa_framebuffer = _mesa_create_framebuffer(&vis); + _mesa_add_soft_renderbuffers(surf->mesa_framebuffer, + GL_TRUE, /* color bufs */ + vis.haveDepthBuffer, + vis.haveStencilBuffer, + vis.haveAccumBuffer, + GL_FALSE, /* alpha */ + GL_FALSE /* aux */ ); + + /* set pbuffer/framebuffer size */ + _mesa_resize_framebuffer(ctx, surf->mesa_framebuffer, + surf->Base.Width, surf->Base.Height); +#endif + } + + return surf->Base.Handle; +} + + +static EGLBoolean +_eglDRIDestroySurface(_EGLDriver *drv, EGLDisplay dpy, EGLSurface surface) +{ + driDisplay *disp = Lookup_driDisplay(dpy); + driSurface *fs = Lookup_driSurface(surface); + _eglRemoveSurface(&fs->Base); + + fs->drawable.destroyDrawable(disp, fs->drawable.private); + + if (fs->Base.IsBound) { + fs->Base.DeletePending = EGL_TRUE; + } + else { + free(fs); + } + return EGL_TRUE; +} + + +static EGLBoolean +_eglDRIDestroyContext(_EGLDriver *drv, EGLDisplay dpy, EGLContext context) +{ + driDisplay *disp = Lookup_driDisplay(dpy); + driContext *fc = Lookup_driContext(context); + + _eglRemoveContext(&fc->Base); + + fc->driContext.destroyContext(disp, 0, fc->driContext.private); + + if (fc->Base.IsBound) { + fc->Base.DeletePending = EGL_TRUE; + } else { + free(fc); + } + return EGL_TRUE; +} + + +/** + * Create a drawing surface which can be directly displayed on a screen. + */ +static EGLSurface +_eglDRICreateScreenSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLConfig cfg, + const EGLint *attrib_list) +{ + _EGLConfig *config = _eglLookupConfig(drv, dpy, cfg); + driDisplay *disp = Lookup_driDisplay(dpy); + driSurface *surface; + EGLSurface surf; + GLvisual vis; + + surface = (driSurface *) malloc(sizeof(*surface)); + if (!surface) { + return EGL_NO_SURFACE; + } + + /* init base class, error check, etc. */ + surf = _eglInitScreenSurface(&surface->Base, drv, dpy, cfg, attrib_list); + if (surf == EGL_NO_SURFACE) { + free(surface); + return EGL_NO_SURFACE; + } + + /* convert EGLConfig to GLvisual */ + _eglConfigToContextModesRec(config, &vis); + + /* Create a new drawable */ + if (!disp->driScreen.createNewDrawable(disp, &vis, surf, &surface->drawable, + GLX_WINDOW_BIT, empty_attribute_list)) { + free(surface); + _eglRemoveSurface(&surface->Base); + return EGL_NO_SURFACE; + } + return surf; +} + + +/** + * Show the given surface on the named screen. + * If surface is EGL_NO_SURFACE, disable the screen's output. + */ +EGLBoolean +_eglDRIShowSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen, + EGLSurface surface, EGLModeMESA m) +{ + driDisplay *display = Lookup_driDisplay(dpy); + driScreen *scrn = Lookup_driScreen(dpy, screen); + driSurface *surf = Lookup_driSurface(surface); + FILE *file; + char buffer[NAME_MAX]; + _EGLMode *mode = _eglLookupMode(dpy, m); + int width, height, temp; + + _eglQuerySurface(drv, dpy, surface, EGL_WIDTH, &width); + _eglQuerySurface(drv, dpy, surface, EGL_HEIGHT, &height); + + if (!_eglShowSurfaceMESA(drv, dpy, screen, surface, m)) + return EGL_FALSE; + + snprintf(buffer, sizeof(buffer), "%s/graphics/%s/blank", sysfs, scrn->fb); + + file = fopen(buffer, "r+"); + if (!file) { +err: + printf("kernel patch?? chown all fb sysfs attrib to allow write - %s\n", buffer); + _eglError(EGL_BAD_SURFACE, "eglShowSurfaceMESA"); + return EGL_FALSE; + } + snprintf(buffer, sizeof(buffer), "%d", (m == EGL_NO_MODE_MESA ? VESA_POWERDOWN : VESA_VSYNC_SUSPEND)); + fputs(buffer, file); + fclose(file); + + if (m == EGL_NO_MODE_MESA) + return EGL_TRUE; + + snprintf(buffer, sizeof(buffer), "%s/graphics/%s/mode", sysfs, scrn->fb); + + file = fopen(buffer, "r+"); + if (!file) + goto err; + fputs(mode->Name, file); + fclose(file); + + snprintf(buffer, sizeof(buffer), "%s/graphics/%s/bits_per_pixel", sysfs, scrn->fb); + + file = fopen(buffer, "r+"); + if (!file) + goto err; + display->bpp = GET_CONFIG_ATTRIB(surf->Base.Config, EGL_BUFFER_SIZE); + display->cpp = display->bpp / 8; + snprintf(buffer, sizeof(buffer), "%d", display->bpp); + fputs(buffer, file); + fclose(file); + + snprintf(buffer, sizeof(buffer), "%s/graphics/%s/blank", sysfs, scrn->fb); + + file = fopen(buffer, "r+"); + if (!file) + goto err; + + snprintf(buffer, sizeof(buffer), "%d", VESA_NO_BLANKING); + fputs(buffer, file); + fclose(file); + + snprintf(buffer, sizeof(buffer), "%s/graphics/%s/virtual_size", sysfs, scrn->fb); + file = fopen(buffer, "r+"); + snprintf(buffer, sizeof(buffer), "%d,%d", width, height); + fputs(buffer, file); + rewind(file); + fgets(buffer, sizeof(buffer), file); + sscanf(buffer, "%d,%d", &display->virtualWidth, &display->virtualHeight); + fclose(file); + + temp = display->virtualWidth; + switch (display->bpp / 8) { + case 1: temp = (display->virtualWidth + 127) & ~127; break; + case 2: temp = (display->virtualWidth + 31) & ~31; break; + case 3: + case 4: temp = (display->virtualWidth + 15) & ~15; break; + } + display->virtualWidth = temp; + + if ((width != display->virtualWidth) || (height != display->virtualHeight)) + goto err; + + return EGL_TRUE; +} + + +/* If the backbuffer is on a videocard, this is extraordinarily slow! + */ +static EGLBoolean +_eglDRISwapBuffers(_EGLDriver *drv, EGLDisplay dpy, EGLSurface draw) +{ + driSurface *drawable = Lookup_driSurface(draw); + + if (!_eglSwapBuffers(drv, dpy, draw)) + return EGL_FALSE; + + drawable->drawable.swapBuffers(NULL, drawable->drawable.private); + + return EGL_TRUE; +} + + +EGLBoolean +_eglDRIGetDisplayInfo( driDisplay *dpy) { + char path[ NAME_MAX ]; + FILE *file; + int rc, mtrr; + unsigned int i; + drmMapType type; + drmMapFlags flags; + drm_handle_t handle, offset; + drmSize size; + drmSetVersion sv; + drm_magic_t magic; + + snprintf( path, sizeof( path ), "%s/graphics/fb%d/device/device", sysfs, dpy->minor ); + file = fopen( path, "r" ); + fgets( path, sizeof( path ), file ); + sscanf( path, "%x", &dpy->chipset ); + fclose( file ); + + sprintf(path, DRM_DEV_NAME, DRM_DIR_NAME, dpy->minor); + if ( ( dpy->drmFD = open(path, O_RDWR, 0) ) < 0 ) { + fprintf( stderr, "[drm] drmOpen failed\n" ); + return EGL_FALSE; + } + + /* Set the interface version, asking for 1.2 */ + sv.drm_di_major = 1; + sv.drm_di_minor = 2; + sv.drm_dd_major = -1; + if ((rc = drmSetInterfaceVersion(dpy->drmFD, &sv))) + return EGL_FALSE; + + /* self authorize */ + if (drmGetMagic(dpy->drmFD, &magic)) + return EGL_FALSE; + if (drmAuthMagic(dpy->drmFD, magic)) + return EGL_FALSE; + + for ( i = 0;; i++ ) { + if ( ( rc = drmGetMap( dpy->drmFD, i, &offset, &size, &type, &flags, &handle, &mtrr ) ) != 0 ) + break; + if ( type == DRM_FRAME_BUFFER ) { + if ( ( rc = drmMap( dpy->drmFD, offset, size, ( drmAddressPtr ) & dpy->pFB ) ) < 0 ) + return EGL_FALSE; + dpy->fbSize = size; + break; + } + } + if ( !dpy->pFB ) + return EGL_FALSE; + + dpy->SAREASize = SAREA_MAX; + + for ( i = 0;; i++ ) { + if ( drmGetMap( dpy->drmFD, i, &offset, &size, &type, &flags, &handle, &mtrr ) != 0 ) + break; + if ( type == DRM_SHM ) { + if ( drmMap( dpy->drmFD, offset, size, ( drmAddressPtr ) ( &dpy->pSAREA ) ) < 0 ) { + fprintf( stderr, "[drm] drmMap failed\n" ); + return 0; + } + break; + } + } + if ( !dpy->pSAREA ) + return 0; + + memset( dpy->pSAREA, 0, dpy->SAREASize ); + fprintf( stderr, "[drm] mapped SAREA 0x%08lx to %p, size %d\n", + offset, dpy->pSAREA, dpy->SAREASize ); + return EGL_TRUE; +} + + + /* Return the DRI per screen structure */ +static __DRIscreen *__eglFindDRIScreen(__DRInativeDisplay *ndpy, int scrn) +{ + driDisplay *disp = (driDisplay *)ndpy; + return &disp->driScreen; +} + +static GLboolean __eglCreateContextWithConfig(__DRInativeDisplay* ndpy, int screen, int configID, void* context, drm_context_t * hHWContext) +{ + __DRIscreen *pDRIScreen; + __DRIscreenPrivate *psp; + + pDRIScreen = __eglFindDRIScreen(ndpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + psp = (__DRIscreenPrivate *) pDRIScreen->private; + if (psp->fd) { + if (drmCreateContext(psp->fd, hHWContext)) { + fprintf(stderr, ">>> drmCreateContext failed\n"); + return GL_FALSE; + } + *(void**)context = (void*) *hHWContext; + } +#if 0 + __DRIscreen *pDRIScreen; + __DRIscreenPrivate *psp; + + pDRIScreen = __glXFindDRIScreen(dpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + + psp = (__DRIscreenPrivate *) pDRIScreen->private; + + if (psp->fd) { + if (drmCreateContext(psp->fd, hHWContext)) { + fprintf(stderr, ">>> drmCreateContext failed\n"); + return GL_FALSE; + } + *(void**)contextID = (void*) *hHWContext; + } +#endif + return GL_TRUE; +} + +static GLboolean __eglDestroyContext( __DRInativeDisplay * ndpy, int screen, __DRIid context ) +{ + __DRIscreen *pDRIScreen; + __DRIscreenPrivate *psp; + + pDRIScreen = __eglFindDRIScreen(ndpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + psp = (__DRIscreenPrivate *) pDRIScreen->private; + if (psp->fd) + drmDestroyContext(psp->fd, context); + + return GL_TRUE; +} + +static GLboolean __eglCreateDrawable( __DRInativeDisplay * ndpy, int screen, __DRIid drawable, drm_drawable_t * hHWDrawable ) +{ + __DRIscreen *pDRIScreen; + __DRIscreenPrivate *psp; + + pDRIScreen = __eglFindDRIScreen(ndpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + psp = (__DRIscreenPrivate *) pDRIScreen->private; + if (psp->fd) { + if (drmCreateDrawable(psp->fd, hHWDrawable)) { + fprintf(stderr, ">>> drmCreateDrawable failed\n"); + return GL_FALSE; + } + } + return GL_TRUE; +} + +static GLboolean __eglDestroyDrawable( __DRInativeDisplay * ndpy, int screen, __DRIid drawable ) +{ + __DRIscreen *pDRIScreen; + __DRIscreenPrivate *psp; + + pDRIScreen = __eglFindDRIScreen(ndpy, screen); + if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) { + return GL_FALSE; + } + psp = (__DRIscreenPrivate *) pDRIScreen->private; + if (psp->fd) + drmDestroyDrawable(psp->fd, drawable); + + return GL_TRUE; +} + +static GLboolean __eglGetDrawableInfo(__DRInativeDisplay * ndpy, int screen, __DRIid drawable, + unsigned int* index, unsigned int* stamp, + int* X, int* Y, int* W, int* H, + int* numClipRects, drm_clip_rect_t ** pClipRects, + int* backX, int* backY, + int* numBackClipRects, drm_clip_rect_t ** pBackClipRects ) +{ + driSurface *surf = Lookup_driSurface(drawable); + + *X = 0; + *Y = 0; + *W = surf->Base.Width; + *H = surf->Base.Height; + + *numClipRects = 1; + *pClipRects = malloc(sizeof(**pClipRects)); + **pClipRects = (drm_clip_rect_t){0, 0, surf->Base.Width, surf->Base.Height}; + +#if 0 + GLXDrawable drawable = (GLXDrawable) draw; + drm_clip_rect_t * cliprect; + Display* display = (Display*)dpy; + __DRIcontextPrivate *pcp = (__DRIcontextPrivate *)CurrentContext->driContext.private; + if (drawable == 0) { + return GL_FALSE; + } + + cliprect = (drm_clip_rect_t*) _mesa_malloc(sizeof(drm_clip_rect_t)); + cliprect->x1 = drawable->x; + cliprect->y1 = drawable->y; + cliprect->x2 = drawable->x + drawable->w; + cliprect->y2 = drawable->y + drawable->h; + + /* the drawable index is by client id */ + *index = display->clientID; + + *stamp = pcp->driScreenPriv->pSAREA->drawableTable[display->clientID].stamp; + *x = drawable->x; + *y = drawable->y; + *width = drawable->w; + *height = drawable->h; + *numClipRects = 1; + *pClipRects = cliprect; + + *backX = drawable->x; + *backY = drawable->y; + *numBackClipRects = 0; + *pBackClipRects = 0; +#endif + return GL_TRUE; +} + +/** + * Implement \c __DRIinterfaceMethods::getProcAddress. + */ +static __DRIfuncPtr get_proc_address( const char * proc_name ) +{ +#if 0 + if (strcmp( proc_name, "glxEnableExtension" ) == 0) { + return (__DRIfuncPtr) __glXScrEnableExtension; + } +#endif + return NULL; +} + + +/** + * Destroy a linked list of \c __GLcontextModes structures created by + * \c _gl_context_modes_create. + * + * \param modes Linked list of structures to be destroyed. All structres + * in the list will be freed. + */ +void +__egl_context_modes_destroy( __GLcontextModes * modes ) +{ + while ( modes != NULL ) { + __GLcontextModes * const next = modes->next; + + free( modes ); + modes = next; + } +} + + +/** + * Allocate a linked list of \c __GLcontextModes structures. The fields of + * each structure will be initialized to "reasonable" default values. In + * most cases this is the default value defined by table 3.4 of the GLX + * 1.3 specification. This means that most values are either initialized to + * zero or \c GLX_DONT_CARE (which is -1). As support for additional + * extensions is added, the new values will be initialized to appropriate + * values from the extension specification. + * + * \param count Number of structures to allocate. + * \param minimum_size Minimum size of a structure to allocate. This allows + * for differences in the version of the + * \c __GLcontextModes stucture used in libGL and in a + * DRI-based driver. + * \returns A pointer to the first element in a linked list of \c count + * stuctures on success, or \c NULL on failure. + * + * \warning Use of \c minimum_size does \b not guarantee binary compatibility. + * The fundamental assumption is that if the \c minimum_size + * specified by the driver and the size of the \c __GLcontextModes + * structure in libGL is the same, then the meaning of each byte in + * the structure is the same in both places. \b Be \b careful! + * Basically this means that fields have to be added in libGL and + * then propagated to drivers. Drivers should \b never arbitrarilly + * extend the \c __GLcontextModes data-structure. + */ +__GLcontextModes * +__egl_context_modes_create( unsigned count, size_t minimum_size ) +{ + const size_t size = (minimum_size > sizeof( __GLcontextModes )) + ? minimum_size : sizeof( __GLcontextModes ); + __GLcontextModes * base = NULL; + __GLcontextModes ** next; + unsigned i; + + next = & base; + for ( i = 0 ; i < count ; i++ ) { + *next = (__GLcontextModes *) malloc( size ); + if ( *next == NULL ) { + __egl_context_modes_destroy( base ); + base = NULL; + break; + } + + (void) memset( *next, 0, size ); + (*next)->visualID = GLX_DONT_CARE; + (*next)->visualType = GLX_DONT_CARE; + (*next)->visualRating = GLX_NONE; + (*next)->transparentPixel = GLX_NONE; + (*next)->transparentRed = GLX_DONT_CARE; + (*next)->transparentGreen = GLX_DONT_CARE; + (*next)->transparentBlue = GLX_DONT_CARE; + (*next)->transparentAlpha = GLX_DONT_CARE; + (*next)->transparentIndex = GLX_DONT_CARE; + (*next)->xRenderable = GLX_DONT_CARE; + (*next)->fbconfigID = GLX_DONT_CARE; + (*next)->swapMethod = GLX_SWAP_UNDEFINED_OML; + + next = & ((*next)->next); + } + + return base; +} + + +GLboolean __eglWindowExists(__DRInativeDisplay *dpy, __DRIid draw) +{ + return EGL_TRUE; +} + + +/** + * Get the unadjusted system time (UST). Currently, the UST is measured in + * microseconds since Epoc. The actual resolution of the UST may vary from + * system to system, and the units may vary from release to release. + * Drivers should not call this function directly. They should instead use + * \c glXGetProcAddress to obtain a pointer to the function. + * + * \param ust Location to store the 64-bit UST + * \returns Zero on success or a negative errno value on failure. + * + * \sa glXGetProcAddress, PFNGLXGETUSTPROC + * + * \since Internal API version 20030317. + */ +int __eglGetUST( int64_t * ust ) +{ + struct timeval tv; + + if ( ust == NULL ) { + return -EFAULT; + } + + if ( gettimeofday( & tv, NULL ) == 0 ) { + ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec; + return 0; + } else { + return -errno; + } +} + +/** + * Determine the refresh rate of the specified drawable and display. + * + * \param dpy Display whose refresh rate is to be determined. + * \param drawable Drawable whose refresh rate is to be determined. + * \param numerator Numerator of the refresh rate. + * \param demoninator Denominator of the refresh rate. + * \return If the refresh rate for the specified display and drawable could + * be calculated, True is returned. Otherwise False is returned. + * + * \note This function is implemented entirely client-side. A lot of other + * functionality is required to export GLX_OML_sync_control, so on + * XFree86 this function can be called for direct-rendering contexts + * when GLX_OML_sync_control appears in the client extension string. + */ +GLboolean __eglGetMSCRate(__DRInativeDisplay * dpy, __DRIid drawable, int32_t * numerator, int32_t * denominator) +{ + return EGL_TRUE; +} + +/** + * Table of functions exported by the loader to the driver. + */ +static const __DRIinterfaceMethods interface_methods = { + get_proc_address, + + __egl_context_modes_create, + __egl_context_modes_destroy, + + __eglFindDRIScreen, + __eglWindowExists, + + __eglCreateContextWithConfig, + __eglDestroyContext, + + __eglCreateDrawable, + __eglDestroyDrawable, + __eglGetDrawableInfo, + + __eglGetUST, + __eglGetMSCRate, +}; + + +int __glXGetInternalVersion(void) +{ + return 20050725; +} + +static const char createNewScreenName[] = "__driCreateNewScreen_20050727"; + +EGLBoolean +_eglDRICreateDisplay( driDisplay *dpy, __DRIframebuffer *framebuffer) { + PFNCREATENEWSCREENFUNC createNewScreen; + int api_ver = __glXGetInternalVersion(); + __DRIversion ddx_version; + __DRIversion dri_version; + __DRIversion drm_version; + drmVersionPtr version; + + version = drmGetVersion( dpy->drmFD ); + if ( version ) { + drm_version.major = version->version_major; + drm_version.minor = version->version_minor; + drm_version.patch = version->version_patchlevel; + drmFreeVersion( version ); + } else { + drm_version.major = -1; + drm_version.minor = -1; + drm_version.patch = -1; + } + + /* + * Get device name (like "tdfx") and the ddx version numbers. + * We'll check the version in each DRI driver's "createScreen" + * function. + */ + ddx_version.major = 4; + ddx_version.minor = 0; + ddx_version.patch = 0; + + /* + * Get the DRI X extension version. + */ + dri_version.major = 4; + dri_version.minor = 0; + dri_version.patch = 0; + + createNewScreen = ( PFNCREATENEWSCREENFUNC ) dlsym( dpy->Base.Driver->LibHandle, createNewScreenName ); + if ( !createNewScreen ) { + fprintf( stderr, "Couldn't find %s in CallCreateNewScreen\n", createNewScreenName ); + return EGL_FALSE; + } + + dpy->driScreen.private = createNewScreen( dpy, 0, &dpy->driScreen, NULL, + &ddx_version, &dri_version, + &drm_version, framebuffer, + dpy->pSAREA, dpy->drmFD, + api_ver, + & interface_methods, + ( __GLcontextModes ** ) & dpy->driver_modes ); + if (!dpy->driScreen.private) + return EGL_FALSE; + + DRM_UNLOCK( dpy->drmFD, dpy->pSAREA, dpy->serverContext ); + + return EGL_TRUE; +} + + +EGLBoolean +_eglDRICreateScreen( driDisplay *dpy) { + char c, *buffer, path[ NAME_MAX ]; + unsigned int i, x, y, r; + int fd; + FILE *file; + driScreen *s; + _EGLScreen *scrn; + + /* Create a screen */ + if ( !( s = ( driScreen * ) calloc( 1, sizeof( *s ) ) ) ) + return EGL_FALSE; + + snprintf( s->fb, NAME_MAX, "fb%d", dpy->minor ); + scrn = &s->Base; + _eglInitScreen( scrn ); + _eglAddScreen( &dpy->Base, scrn ); + + snprintf( path, sizeof( path ), "%s/graphics/%s/modes", sysfs, s->fb ); + file = fopen( path, "r" ); + while ( fgets( path, sizeof( path ), file ) ) { + path[ strlen( path ) - 1 ] = '\0'; /* strip off \n from sysfs */ + sscanf( path, "%c:%ux%u-%u", &c, &x, &y, &r ); + _eglAddMode( scrn, x, y, r * 1000, path ); + } + fclose( file ); + + /* cmap attribute uses 256 lines of 16 bytes */ + if ( !( buffer = malloc( 256 * 16 ) ) ) + return EGL_FALSE; + + /* cmap attribute uses 256 lines of 16 bytes */ + for ( i = 0; i < 256; i++ ) + sprintf( &buffer[ i * 16 ], "%02x%c%4x%4x%4x\n", + i, ' ', 256 * i, 256 * i, 256 * i ); + + snprintf( path, sizeof( path ), "%s/graphics/%s/color_map", sysfs, s->fb ); + if ( !( fd = open( path, O_RDWR ) ) ) + return EGL_FALSE; + write( fd, buffer, 256 * 16 ); + close( fd ); + + free( buffer ); + + return EGL_TRUE; +} + +EGLBoolean +_eglDRIInitialize(_EGLDriver *drv, EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + _EGLDisplay *disp = _eglLookupDisplay(dpy); + driDisplay *display; + + /* Switch display structure to one with our private fields */ + display = calloc(1, sizeof(*display)); + display->Base = *disp; + _eglHashInsert(_eglGlobal.Displays, disp->Handle, display); + free(disp); + + *major = 1; + *minor = 0; + + sscanf(&disp->Name[1], "%d", &display->minor); + + drv->Initialized = EGL_TRUE; + return EGL_TRUE; +} + + +static EGLBoolean +_eglDRITerminate(_EGLDriver *drv, EGLDisplay dpy) +{ + driDisplay *display = Lookup_driDisplay(dpy); + _eglCleanupDisplay(&display->Base); + free(display); + free(drv); + return EGL_TRUE; +} + + +void +_eglDRIInitDriverFallbacks(_EGLDriver *drv) +{ + _eglInitDriverFallbacks(drv); + + drv->Initialize = _eglDRIInitialize; + drv->Terminate = _eglDRITerminate; + drv->CreateContext = _eglDRICreateContext; + drv->MakeCurrent = _eglDRIMakeCurrent; + drv->CreateWindowSurface = _eglDRICreateWindowSurface; + drv->CreatePixmapSurface = _eglDRICreatePixmapSurface; + drv->CreatePbufferSurface = _eglDRICreatePbufferSurface; + drv->DestroySurface = _eglDRIDestroySurface; + drv->DestroyContext = _eglDRIDestroyContext; + drv->CreateScreenSurfaceMESA = _eglDRICreateScreenSurfaceMESA; + drv->ShowSurfaceMESA = _eglDRIShowSurfaceMESA; + drv->SwapBuffers = _eglDRISwapBuffers; + + /* enable supported extensions */ + drv->MESA_screen_surface = EGL_TRUE; + drv->MESA_copy_context = EGL_TRUE; + +} diff --git a/src/mesa/drivers/dri/radeon/server/radeon_egl.c b/src/mesa/drivers/dri/radeon/server/radeon_egl.c new file mode 100644 index 00000000000..e491bba079a --- /dev/null +++ b/src/mesa/drivers/dri/radeon/server/radeon_egl.c @@ -0,0 +1,980 @@ +/* + * EGL driver for radeon_dri.so + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eglconfig.h" +#include "eglcontext.h" +#include "egldisplay.h" +#include "egldriver.h" +#include "eglglobals.h" +#include "eglmode.h" +#include "eglscreen.h" +#include "eglsurface.h" +#include "egldri.h" + +#include "mtypes.h" +#include "memops.h" +#include "drm.h" +#include "drm_sarea.h" +#include "radeon_drm.h" +#include "radeon_dri.h" +#include "radeon.h" + +static size_t radeon_drm_page_size; +const char *sysfs = "/sys/class/graphics"; + +/** + * radeon driver-specific driver class derived from _EGLDriver + */ +typedef struct radeon_driver +{ + _EGLDriver Base; /* base class/object */ + GLuint radeonStuff; +} radeonDriver; + +static int RADEONCheckDRMVersion( driDisplay *disp, + RADEONInfoPtr info ) +{ + drmVersionPtr version; + + version = drmGetVersion(disp->drmFD); + if (version) { + int req_minor, req_patch; + + /* Need 1.8.x for proper cleanup-on-client-exit behaviour. + */ + req_minor = 8; + req_patch = 0; + + if (version->version_major != 1 || + version->version_minor < req_minor || + (version->version_minor == req_minor && + version->version_patchlevel < req_patch)) { + /* Incompatible drm version */ + fprintf(stderr, + "[dri] RADEONDRIScreenInit failed because of a version " + "mismatch.\n" + "[dri] radeon.o kernel module version is %d.%d.%d " + "but version 1.%d.%d or newer is needed.\n" + "[dri] Disabling DRI.\n", + version->version_major, + version->version_minor, + version->version_patchlevel, + req_minor, + req_patch); + drmFreeVersion(version); + return 0; + } + + info->drmMinor = version->version_minor; + drmFreeVersion(version); + } + + return 1; +} + + +/** + * \brief Compute base 2 logarithm. + * + * \param val value. + * + * \return base 2 logarithm of \p val. + */ +static int RADEONMinBits(int val) +{ + int bits; + + if (!val) return 1; + for (bits = 0; val; val >>= 1, ++bits); + return bits; +} + + +/* Initialize the PCI GART state. Request memory for use in PCI space, + * and initialize the Radeon registers to point to that memory. + */ +static int RADEONDRIPciInit(driDisplay *disp, RADEONInfoPtr info) +{ + int ret; + int flags = DRM_READ_ONLY | DRM_LOCKED | DRM_KERNEL; + int s, l; + + ret = drmScatterGatherAlloc(disp->drmFD, info->gartSize*1024*1024, + &info->gartMemHandle); + if (ret < 0) { + fprintf(stderr, "[pci] Out of memory (%d)\n", ret); + return 0; + } + fprintf(stderr, + "[pci] %d kB allocated with handle 0x%04lx\n", + info->gartSize*1024, info->gartMemHandle); + + info->gartOffset = 0; + + /* Initialize the CP ring buffer data */ + info->ringStart = info->gartOffset; + info->ringMapSize = info->ringSize*1024*1024 + radeon_drm_page_size; + + info->ringReadOffset = info->ringStart + info->ringMapSize; + info->ringReadMapSize = radeon_drm_page_size; + + /* Reserve space for vertex/indirect buffers */ + info->bufStart = info->ringReadOffset + info->ringReadMapSize; + info->bufMapSize = info->bufSize*1024*1024; + + /* Reserve the rest for AGP textures */ + info->gartTexStart = info->bufStart + info->bufMapSize; + s = (info->gartSize*1024*1024 - info->gartTexStart); + l = RADEONMinBits((s-1) / RADEON_NR_TEX_REGIONS); + if (l < RADEON_LOG_TEX_GRANULARITY) l = RADEON_LOG_TEX_GRANULARITY; + info->gartTexMapSize = (s >> l) << l; + info->log2GARTTexGran = l; + + if (drmAddMap(disp->drmFD, info->ringStart, info->ringMapSize, + DRM_SCATTER_GATHER, flags, &info->ringHandle) < 0) { + fprintf(stderr, + "[pci] Could not add ring mapping\n"); + return 0; + } + fprintf(stderr, + "[pci] ring handle = 0x%08lx\n", info->ringHandle); + + if (drmAddMap(disp->drmFD, info->ringReadOffset, info->ringReadMapSize, + DRM_SCATTER_GATHER, flags, &info->ringReadPtrHandle) < 0) { + fprintf(stderr, + "[pci] Could not add ring read ptr mapping\n"); + return 0; + } + fprintf(stderr, + "[pci] ring read ptr handle = 0x%08lx\n", + info->ringReadPtrHandle); + + if (drmAddMap(disp->drmFD, info->bufStart, info->bufMapSize, + DRM_SCATTER_GATHER, 0, &info->bufHandle) < 0) { + fprintf(stderr, + "[pci] Could not add vertex/indirect buffers mapping\n"); + return 0; + } + fprintf(stderr, + "[pci] vertex/indirect buffers handle = 0x%08lx\n", + info->bufHandle); + + if (drmAddMap(disp->drmFD, info->gartTexStart, info->gartTexMapSize, + DRM_SCATTER_GATHER, 0, &info->gartTexHandle) < 0) { + fprintf(stderr, + "[pci] Could not add GART texture map mapping\n"); + return 0; + } + fprintf(stderr, + "[pci] GART texture map handle = 0x%08lx\n", + info->gartTexHandle); + + return 1; +} + + +/** + * \brief Initialize the AGP state + * + * \param ctx display handle. + * \param info driver private data. + * + * \return one on success, or zero on failure. + * + * Acquires and enables the AGP device. Reserves memory in the AGP space for + * the ring buffer, vertex buffers and textures. Initialize the Radeon + * registers to point to that memory and add client mappings. + */ +static int RADEONDRIAgpInit( driDisplay *disp, RADEONInfoPtr info) +{ + int mode, ret; + int s, l; + int agpmode = 1; + + if (drmAgpAcquire(disp->drmFD) < 0) { + fprintf(stderr, "[gart] AGP not available\n"); + return 0; + } + + mode = drmAgpGetMode(disp->drmFD); /* Default mode */ + /* Disable fast write entirely - too many lockups. + */ + mode &= ~RADEON_AGP_MODE_MASK; + switch (agpmode) { + case 4: mode |= RADEON_AGP_4X_MODE; + case 2: mode |= RADEON_AGP_2X_MODE; + case 1: default: mode |= RADEON_AGP_1X_MODE; + } + + if (drmAgpEnable(disp->drmFD, mode) < 0) { + fprintf(stderr, "[gart] AGP not enabled\n"); + drmAgpRelease(disp->drmFD); + return 0; + } + +#if 0 + /* Workaround for some hardware bugs */ + if (info->ChipFamily < CHIP_FAMILY_R200) + OUTREG(RADEON_AGP_CNTL, INREG(RADEON_AGP_CNTL) | 0x000e0000); +#endif + info->gartOffset = 0; + + if ((ret = drmAgpAlloc(disp->drmFD, info->gartSize*1024*1024, 0, NULL, + &info->gartMemHandle)) < 0) { + fprintf(stderr, "[gart] Out of memory (%d)\n", ret); + drmAgpRelease(disp->drmFD); + return 0; + } + fprintf(stderr, + "[gart] %d kB allocated with handle 0x%08x\n", + info->gartSize*1024, (unsigned)info->gartMemHandle); + + if (drmAgpBind(disp->drmFD, + info->gartMemHandle, info->gartOffset) < 0) { + fprintf(stderr, "[gart] Could not bind\n"); + drmAgpFree(disp->drmFD, info->gartMemHandle); + drmAgpRelease(disp->drmFD); + return 0; + } + + /* Initialize the CP ring buffer data */ + info->ringStart = info->gartOffset; + info->ringMapSize = info->ringSize*1024*1024 + radeon_drm_page_size; + + info->ringReadOffset = info->ringStart + info->ringMapSize; + info->ringReadMapSize = radeon_drm_page_size; + + /* Reserve space for vertex/indirect buffers */ + info->bufStart = info->ringReadOffset + info->ringReadMapSize; + info->bufMapSize = info->bufSize*1024*1024; + + /* Reserve the rest for AGP textures */ + info->gartTexStart = info->bufStart + info->bufMapSize; + s = (info->gartSize*1024*1024 - info->gartTexStart); + l = RADEONMinBits((s-1) / RADEON_NR_TEX_REGIONS); + if (l < RADEON_LOG_TEX_GRANULARITY) l = RADEON_LOG_TEX_GRANULARITY; + info->gartTexMapSize = (s >> l) << l; + info->log2GARTTexGran = l; + + if (drmAddMap(disp->drmFD, info->ringStart, info->ringMapSize, + DRM_AGP, DRM_READ_ONLY, &info->ringHandle) < 0) { + fprintf(stderr, "[gart] Could not add ring mapping\n"); + return 0; + } + fprintf(stderr, "[gart] ring handle = 0x%08lx\n", info->ringHandle); + + + if (drmAddMap(disp->drmFD, info->ringReadOffset, info->ringReadMapSize, + DRM_AGP, DRM_READ_ONLY, &info->ringReadPtrHandle) < 0) { + fprintf(stderr, + "[gart] Could not add ring read ptr mapping\n"); + return 0; + } + + fprintf(stderr, + "[gart] ring read ptr handle = 0x%08lx\n", + info->ringReadPtrHandle); + + if (drmAddMap(disp->drmFD, info->bufStart, info->bufMapSize, + DRM_AGP, 0, &info->bufHandle) < 0) { + fprintf(stderr, + "[gart] Could not add vertex/indirect buffers mapping\n"); + return 0; + } + fprintf(stderr, + "[gart] vertex/indirect buffers handle = 0x%08lx\n", + info->bufHandle); + + if (drmAddMap(disp->drmFD, info->gartTexStart, info->gartTexMapSize, + DRM_AGP, 0, &info->gartTexHandle) < 0) { + fprintf(stderr, + "[gart] Could not add AGP texture map mapping\n"); + return 0; + } + fprintf(stderr, + "[gart] AGP texture map handle = 0x%08lx\n", + info->gartTexHandle); + + return 1; +} + + +static int RADEONMemoryInit( driDisplay *disp, RADEONInfoPtr info ) +{ + int width_bytes = disp->virtualWidth * disp->cpp; + int cpp = disp->cpp; + int bufferSize = ((disp->virtualHeight * width_bytes + + RADEON_BUFFER_ALIGN) + & ~RADEON_BUFFER_ALIGN); + int depthSize = ((((disp->virtualHeight+15) & ~15) * width_bytes + + RADEON_BUFFER_ALIGN) + & ~RADEON_BUFFER_ALIGN); + int l; + + info->frontOffset = 0; + info->frontPitch = disp->virtualWidth; + + fprintf(stderr, + "Using %d MB AGP aperture\n", info->gartSize); + fprintf(stderr, + "Using %d MB for the ring buffer\n", info->ringSize); + fprintf(stderr, + "Using %d MB for vertex/indirect buffers\n", info->bufSize); + fprintf(stderr, + "Using %d MB for AGP textures\n", info->gartTexSize); + + /* Front, back and depth buffers - everything else texture?? + */ + info->textureSize = disp->fbSize - 2 * bufferSize - depthSize; + + if (info->textureSize < 0) + return 0; + + l = RADEONMinBits((info->textureSize-1) / RADEON_NR_TEX_REGIONS); + if (l < RADEON_LOG_TEX_GRANULARITY) l = RADEON_LOG_TEX_GRANULARITY; + + /* Round the texture size up to the nearest whole number of + * texture regions. Again, be greedy about this, don't + * round down. + */ + info->log2TexGran = l; + info->textureSize = (info->textureSize >> l) << l; + + /* Set a minimum usable local texture heap size. This will fit + * two 256x256x32bpp textures. + */ + if (info->textureSize < 512 * 1024) { + info->textureOffset = 0; + info->textureSize = 0; + } + + /* Reserve space for textures */ + info->textureOffset = ((disp->fbSize - info->textureSize + + RADEON_BUFFER_ALIGN) & + ~RADEON_BUFFER_ALIGN); + + /* Reserve space for the shared depth + * buffer. + */ + info->depthOffset = ((info->textureOffset - depthSize + + RADEON_BUFFER_ALIGN) & + ~RADEON_BUFFER_ALIGN); + info->depthPitch = disp->virtualWidth; + + info->backOffset = ((info->depthOffset - bufferSize + + RADEON_BUFFER_ALIGN) & + ~RADEON_BUFFER_ALIGN); + info->backPitch = disp->virtualWidth; + + + fprintf(stderr, + "Will use back buffer at offset 0x%x\n", + info->backOffset); + fprintf(stderr, + "Will use depth buffer at offset 0x%x\n", + info->depthOffset); + fprintf(stderr, + "Will use %d kb for textures at offset 0x%x\n", + info->textureSize/1024, info->textureOffset); + + info->frontPitchOffset = (((info->frontPitch * cpp / 64) << 22) | + (info->frontOffset >> 10)); + + info->backPitchOffset = (((info->backPitch * cpp / 64) << 22) | + (info->backOffset >> 10)); + + info->depthPitchOffset = (((info->depthPitch * cpp / 64) << 22) | + (info->depthOffset >> 10)); + + return 1; +} + + +/** + * \brief Initialize the kernel data structures and enable the CP engine. + * + * \param ctx display handle. + * \param info driver private data. + * + * \return non-zero on success, or zero on failure. + * + * This function is a wrapper around the DRM_RADEON_CP_INIT command, passing + * all the parameters in a drm_radeon_init_t structure. + */ +static int RADEONDRIKernelInit( driDisplay *disp, + RADEONInfoPtr info) +{ + int cpp = disp->bpp / 8; + drm_radeon_init_t drmInfo; + int ret; + + memset(&drmInfo, 0, sizeof(drmInfo)); + + if ( (info->ChipFamily == CHIP_FAMILY_R200) || + (info->ChipFamily == CHIP_FAMILY_RV250) || + (info->ChipFamily == CHIP_FAMILY_M9) || + (info->ChipFamily == CHIP_FAMILY_RV280) ) + drmInfo.func = RADEON_INIT_R200_CP; + else + drmInfo.func = RADEON_INIT_CP; + + /* This is the struct passed to the kernel module for its initialization */ + drmInfo.sarea_priv_offset = sizeof(drm_sarea_t); + drmInfo.cp_mode = RADEON_DEFAULT_CP_BM_MODE; + drmInfo.gart_size = info->gartSize*1024*1024; + drmInfo.ring_size = info->ringSize*1024*1024; + drmInfo.usec_timeout = 1000; + drmInfo.fb_bpp = disp->bpp; + drmInfo.depth_bpp = disp->bpp; + drmInfo.front_offset = info->frontOffset; + drmInfo.front_pitch = info->frontPitch * cpp; + drmInfo.back_offset = info->backOffset; + drmInfo.back_pitch = info->backPitch * cpp; + drmInfo.depth_offset = info->depthOffset; + drmInfo.depth_pitch = info->depthPitch * cpp; + drmInfo.ring_offset = info->ringHandle; + drmInfo.ring_rptr_offset = info->ringReadPtrHandle; + drmInfo.buffers_offset = info->bufHandle; + drmInfo.gart_textures_offset = info->gartTexHandle; + + ret = drmCommandWrite(disp->drmFD, DRM_RADEON_CP_INIT, &drmInfo, + sizeof(drm_radeon_init_t)); + + return ret >= 0; +} + + +/** + * \brief Add a map for the vertex buffers that will be accessed by any + * DRI-based clients. + * + * \param ctx display handle. + * \param info driver private data. + * + * \return one on success, or zero on failure. + * + * Calls drmAddBufs() with the previously allocated vertex buffers. + */ +static int RADEONDRIBufInit( driDisplay *disp, RADEONInfoPtr info ) +{ + /* Initialize vertex buffers */ + info->bufNumBufs = drmAddBufs(disp->drmFD, + info->bufMapSize / RADEON_BUFFER_SIZE, + RADEON_BUFFER_SIZE, + disp->isPCI ? DRM_SG_BUFFER : DRM_AGP_BUFFER, + info->bufStart); + + if (info->bufNumBufs <= 0) { + fprintf(stderr, + "[drm] Could not create vertex/indirect buffers list\n"); + return 0; + } + fprintf(stderr, + "[drm] Added %d %d byte vertex/indirect buffers\n", + info->bufNumBufs, RADEON_BUFFER_SIZE); + + return 1; +} + + +/** + * \brief Install an IRQ handler. + * + * \param disp display handle. + * \param info driver private data. + * + * Attempts to install an IRQ handler via drmCtlInstHandler(), falling back to + * IRQ-free operation on failure. + */ +static void RADEONDRIIrqInit(driDisplay *disp, RADEONInfoPtr info) +{ + if ((drmCtlInstHandler(disp->drmFD, 0)) != 0) + fprintf(stderr, "[drm] failure adding irq handler, " + "there is a device already using that irq\n" + "[drm] falling back to irq-free operation\n"); +} + + +/** + * \brief Initialize the AGP heap. + * + * \param disp display handle. + * \param info driver private data. + * + * This function is a wrapper around the DRM_RADEON_INIT_HEAP command, passing + * all the parameters in a drm_radeon_mem_init_heap structure. + */ +static void RADEONDRIAgpHeapInit(driDisplay *disp, + RADEONInfoPtr info) +{ + drm_radeon_mem_init_heap_t drmHeap; + + /* Start up the simple memory manager for gart space */ + drmHeap.region = RADEON_MEM_REGION_GART; + drmHeap.start = 0; + drmHeap.size = info->gartTexMapSize; + + if (drmCommandWrite(disp->drmFD, DRM_RADEON_INIT_HEAP, + &drmHeap, sizeof(drmHeap))) { + fprintf(stderr, + "[drm] Failed to initialized gart heap manager\n"); + } else { + fprintf(stderr, + "[drm] Initialized kernel gart heap manager, %d\n", + info->gartTexMapSize); + } +} + + +/** + * Called at the start of each server generation. + * + * \param disp display handle. + * \param info driver private data. + * + * \return non-zero on success, or zero on failure. + * + * Performs static frame buffer allocation. Opens the DRM device and add maps + * to the SAREA, framebuffer and MMIO regions. Fills in \p info with more + * information. Creates a \e server context to grab the lock for the + * initialization ioctls and calls the other initilization functions in this + * file. Starts the CP engine via the DRM_RADEON_CP_START command. + * + * Setups a RADEONDRIRec structure to be passed to radeon_dri.so for its + * initialization. + */ +static int RADEONScreenInit( driDisplay *disp, RADEONInfoPtr info, RADEONDRIPtr pRADEONDRI) +{ + int i, err; + + { + int width_bytes = (disp->virtualWidth * disp->cpp); + int maxy = disp->fbSize / width_bytes; + + + if (maxy <= disp->virtualHeight * 3) { + fprintf(stderr, + "Static buffer allocation failed -- " + "need at least %d kB video memory (have %d kB)\n", + (disp->virtualWidth * disp->virtualHeight * + disp->cpp * 3 + 1023) / 1024, + disp->fbSize / 1024); + return 0; + } + } + if (info->ChipFamily >= CHIP_FAMILY_R300) { + fprintf(stderr, + "Direct rendering not yet supported on " + "Radeon 9700 and newer cards\n"); + return 0; + } + + radeon_drm_page_size = getpagesize(); + + /* Check the radeon DRM version */ + if (!RADEONCheckDRMVersion(disp, info)) { + return 0; + } + + if (disp->isPCI) { + /* Initialize PCI */ + if (!RADEONDRIPciInit(disp, info)) + return 0; + } + else { + /* Initialize AGP */ + if (!RADEONDRIAgpInit(disp, info)) + return 0; + } + + /* Memory manager setup */ + if (!RADEONMemoryInit(disp, info)) { + return 0; + } + + /* Create a 'server' context so we can grab the lock for + * initialization ioctls. + */ + if ((err = drmCreateContext(disp->drmFD, &disp->serverContext)) != 0) { + fprintf(stderr, "%s: drmCreateContext failed %d\n", __FUNCTION__, err); + return 0; + } + + DRM_LOCK(disp->drmFD, disp->pSAREA, disp->serverContext, 0); + + /* Initialize the kernel data structures */ + if (!RADEONDRIKernelInit(disp, info)) { + fprintf(stderr, "RADEONDRIKernelInit failed\n"); + DRM_UNLOCK(disp->drmFD, disp->pSAREA, disp->serverContext); + return 0; + } + + /* Initialize the vertex buffers list */ + if (!RADEONDRIBufInit(disp, info)) { + fprintf(stderr, "RADEONDRIBufInit failed\n"); + DRM_UNLOCK(disp->drmFD, disp->pSAREA, disp->serverContext); + return 0; + } + + /* Initialize IRQ */ + RADEONDRIIrqInit(disp, info); + + /* Initialize kernel gart memory manager */ + RADEONDRIAgpHeapInit(disp, info); + + fprintf(stderr,"page flipping %sabled\n", info->page_flip_enable?"en":"dis"); + /* Initialize the SAREA private data structure */ + { + drm_radeon_sarea_t *pSAREAPriv; + pSAREAPriv = (drm_radeon_sarea_t *)(((char*)disp->pSAREA) + + sizeof(drm_sarea_t)); + memset(pSAREAPriv, 0, sizeof(*pSAREAPriv)); + pSAREAPriv->pfState = info->page_flip_enable; + } + + for ( i = 0;; i++ ) { + drmMapType type; + drmMapFlags flags; + drm_handle_t handle, offset; + drmSize size; + int rc, mtrr; + + if ( ( rc = drmGetMap( disp->drmFD, i, &offset, &size, &type, &flags, &handle, &mtrr ) ) != 0 ) + break; + if ( type == DRM_REGISTERS ) { + pRADEONDRI->registerHandle = offset; + pRADEONDRI->registerSize = size; + break; + } + } + /* Quick hack to clear the front & back buffers. Could also use + * the clear ioctl to do this, but would need to setup hw state + * first. + */ + drimemsetio((char *)disp->pFB + info->frontOffset, + 0xEE, + info->frontPitch * disp->cpp * disp->virtualHeight ); + + drimemsetio((char *)disp->pFB + info->backOffset, + 0x30, + info->backPitch * disp->cpp * disp->virtualHeight ); + + + /* This is the struct passed to radeon_dri.so for its initialization */ + pRADEONDRI->deviceID = info->Chipset; + pRADEONDRI->width = disp->virtualWidth; + pRADEONDRI->height = disp->virtualHeight; + pRADEONDRI->depth = disp->bpp; /* XXX: depth */ + pRADEONDRI->bpp = disp->bpp; + pRADEONDRI->IsPCI = disp->isPCI; + pRADEONDRI->frontOffset = info->frontOffset; + pRADEONDRI->frontPitch = info->frontPitch; + pRADEONDRI->backOffset = info->backOffset; + pRADEONDRI->backPitch = info->backPitch; + pRADEONDRI->depthOffset = info->depthOffset; + pRADEONDRI->depthPitch = info->depthPitch; + pRADEONDRI->textureOffset = info->textureOffset; + pRADEONDRI->textureSize = info->textureSize; + pRADEONDRI->log2TexGran = info->log2TexGran; + pRADEONDRI->statusHandle = info->ringReadPtrHandle; + pRADEONDRI->statusSize = info->ringReadMapSize; + pRADEONDRI->gartTexHandle = info->gartTexHandle; + pRADEONDRI->gartTexMapSize = info->gartTexMapSize; + pRADEONDRI->log2GARTTexGran = info->log2GARTTexGran; + pRADEONDRI->gartTexOffset = info->gartTexStart; + pRADEONDRI->sarea_priv_offset = sizeof(drm_sarea_t); + + /* Don't release the lock now - let the VT switch handler do it. */ + + return 1; +} + + +/** + * \brief Get Radeon chip family from chipset number. + * + * \param info driver private data. + * + * \return non-zero on success, or zero on failure. + * + * Called by radeonInitFBDev() to set RADEONInfoRec::ChipFamily + * according to the value of RADEONInfoRec::Chipset. Fails if the + * chipset is unrecognized or not appropriate for this driver (i.e., not + * an r100 style radeon) + */ +static int get_chipfamily_from_chipset( RADEONInfoPtr info ) +{ + switch (info->Chipset) { + case PCI_CHIP_RADEON_LY: + case PCI_CHIP_RADEON_LZ: + info->ChipFamily = CHIP_FAMILY_M6; + break; + + case PCI_CHIP_RADEON_QY: + case PCI_CHIP_RADEON_QZ: + info->ChipFamily = CHIP_FAMILY_VE; + break; + + case PCI_CHIP_R200_QL: + case PCI_CHIP_R200_QN: + case PCI_CHIP_R200_QO: + case PCI_CHIP_R200_Ql: + case PCI_CHIP_R200_BB: + info->ChipFamily = CHIP_FAMILY_R200; + break; + + case PCI_CHIP_RV200_QW: /* RV200 desktop */ + case PCI_CHIP_RV200_QX: + info->ChipFamily = CHIP_FAMILY_RV200; + break; + + case PCI_CHIP_RADEON_LW: + case PCI_CHIP_RADEON_LX: + info->ChipFamily = CHIP_FAMILY_M7; + break; + + case PCI_CHIP_RV250_Id: + case PCI_CHIP_RV250_Ie: + case PCI_CHIP_RV250_If: + case PCI_CHIP_RV250_Ig: + info->ChipFamily = CHIP_FAMILY_RV250; + break; + + case PCI_CHIP_RV250_Ld: + case PCI_CHIP_RV250_Le: + case PCI_CHIP_RV250_Lf: + case PCI_CHIP_RV250_Lg: + info->ChipFamily = CHIP_FAMILY_M9; + break; + + case PCI_CHIP_RV280_Y_: + case PCI_CHIP_RV280_Ya: + case PCI_CHIP_RV280_Yb: + case PCI_CHIP_RV280_Yc: + info->ChipFamily = CHIP_FAMILY_RV280; + break; + + case PCI_CHIP_R300_ND: + case PCI_CHIP_R300_NE: + case PCI_CHIP_R300_NF: + case PCI_CHIP_R300_NG: + info->ChipFamily = CHIP_FAMILY_R300; + break; + + default: + /* Original Radeon/7200 */ + info->ChipFamily = CHIP_FAMILY_RADEON; + } + + return 1; +} + + +/** + * \brief Initialize the framebuffer device mode + * + * \param disp display handle. + * + * \return one on success, or zero on failure. + * + * Fills in \p info with some default values and some information from \p disp + * and then calls RADEONScreenInit() for the screen initialization. + * + * Before exiting clears the framebuffer memory accessing it directly. + */ +static int radeonInitFBDev( driDisplay *disp, RADEONDRIPtr pRADEONDRI ) +{ + int err; + RADEONInfoPtr info = calloc(1, sizeof(*info)); + + disp->driverPrivate = (void *)info; + + info->gartFastWrite = RADEON_DEFAULT_AGP_FAST_WRITE; + info->gartSize = RADEON_DEFAULT_AGP_SIZE; + info->gartTexSize = RADEON_DEFAULT_AGP_TEX_SIZE; + info->bufSize = RADEON_DEFAULT_BUFFER_SIZE; + info->ringSize = RADEON_DEFAULT_RING_SIZE; + info->page_flip_enable = RADEON_DEFAULT_PAGE_FLIP; + + info->Chipset = disp->chipset; + + if (!get_chipfamily_from_chipset( info )) { + fprintf(stderr, "Unknown or non-radeon chipset -- cannot continue\n"); + fprintf(stderr, "==> Verify PCI BusID is correct in miniglx.conf\n"); + return 0; + } + + info->frontPitch = disp->virtualWidth; + + if (!RADEONScreenInit( disp, info, pRADEONDRI)) + return 0; + + /* Initialize and start the CP if required */ + if ((err = drmCommandNone(disp->drmFD, DRM_RADEON_CP_START)) != 0) { + fprintf(stderr, "%s: CP start %d\n", __FUNCTION__, err); + return 0; + } + + return 1; +} + +static EGLBoolean +radeonFillInConfigs(_EGLDisplay *disp, unsigned pixel_bits, unsigned depth_bits, + unsigned stencil_bits, GLboolean have_back_buffer) { + _EGLConfig *configs; + _EGLConfig *c; + unsigned int i, num_configs; + unsigned int depth_buffer_factor; + unsigned int back_buffer_factor; + GLenum fb_format; + GLenum fb_type; + + /* Right now GLX_SWAP_COPY_OML isn't supported, but it would be easy + * enough to add support. Basically, if a context is created with an + * fbconfig where the swap method is GLX_SWAP_COPY_OML, pageflipping + * will never be used. + */ + static const GLenum back_buffer_modes[] = { + GLX_NONE, GLX_SWAP_UNDEFINED_OML /*, GLX_SWAP_COPY_OML */ + }; + + u_int8_t depth_bits_array[2]; + u_int8_t stencil_bits_array[2]; + + depth_bits_array[0] = depth_bits; + depth_bits_array[1] = depth_bits; + + /* Just like with the accumulation buffer, always provide some modes + * with a stencil buffer. It will be a sw fallback, but some apps won't + * care about that. + */ + stencil_bits_array[0] = 0; + stencil_bits_array[1] = (stencil_bits == 0) ? 8 : stencil_bits; + + depth_buffer_factor = ((depth_bits != 0) || (stencil_bits != 0)) ? 2 : 1; + back_buffer_factor = (have_back_buffer) ? 2 : 1; + + num_configs = depth_buffer_factor * back_buffer_factor * 2; + + if (pixel_bits == 16) { + fb_format = GL_RGB; + fb_type = GL_UNSIGNED_SHORT_5_6_5; + } else { + fb_format = GL_RGBA; + fb_type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + + configs = calloc(sizeof(*configs), num_configs); + c = configs; + if (!_eglFillInConfigs(c, fb_format, fb_type, + depth_bits_array, stencil_bits_array, depth_buffer_factor, + back_buffer_modes, back_buffer_factor, + GLX_TRUE_COLOR)) { + fprintf(stderr, "[%s:%u] Error creating FBConfig!\n", + __func__, __LINE__); + return EGL_FALSE; + } + + /* Mark the visual as slow if there are "fake" stencil bits. + */ + for (i = 0, c = configs; i < num_configs; i++, c++) { + int stencil = GET_CONFIG_ATTRIB(c, EGL_STENCIL_SIZE); + if ((stencil != 0) && (stencil != stencil_bits)) { + SET_CONFIG_ATTRIB(c, EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG); + } + } + + for (i = 0, c = configs; i < num_configs; i++, c++) + _eglAddConfig(disp, c); + + free(configs); + + return EGL_TRUE; +} + +/** + * Show the given surface on the named screen. + * If surface is EGL_NO_SURFACE, disable the screen's output. + */ +static EGLBoolean +radeonShowSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen, + EGLSurface surface, EGLModeMESA m) +{ + _eglDRIShowSurfaceMESA(drv, dpy, screen, surface, m); + return EGL_FALSE; +} + +static EGLBoolean +radeonInitialize(_EGLDriver *drv, EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + __DRIframebuffer framebuffer; + driDisplay *display; + + if (!_eglDRIInitialize(drv, dpy, major, minor)) + return EGL_FALSE; + + display = Lookup_driDisplay(dpy); + + framebuffer.dev_priv_size = sizeof(RADEONDRIRec); + framebuffer.dev_priv = malloc(sizeof(RADEONDRIRec)); + + display->virtualWidth = 1024; + display->virtualHeight = 768; + display->bpp = 32; + display->cpp = 4; + + if (!_eglDRIGetDisplayInfo(display)) + return EGL_FALSE; + + framebuffer.base = display->pFB; + radeonInitFBDev( display, framebuffer.dev_priv ); + + if (!_eglDRICreateDisplay(display, &framebuffer)) + return EGL_FALSE; + + if (!_eglDRICreateScreen(display)) + return EGL_FALSE; + + radeonFillInConfigs(&display->Base, 32, 24, 8, 1); + radeonFillInConfigs(&display->Base, 16, 16, 0, 1); + + drv->Initialized = EGL_TRUE; + return EGL_TRUE; +} + + +/** + * The bootstrap function. Return a new radeonDriver object and + * plug in API functions. + */ +_EGLDriver * +_eglMain(_EGLDisplay *dpy) +{ + radeonDriver *radeon; + + radeon = (radeonDriver *) calloc(1, sizeof(*radeon)); + if (!radeon) { + return NULL; + } + + /* First fill in the dispatch table with defaults */ + _eglDRIInitDriverFallbacks(&radeon->Base); + + /* then plug in our radeon-specific functions */ + radeon->Base.Initialize = radeonInitialize; + radeon->Base.ShowSurfaceMESA = radeonShowSurfaceMESA; + + return &radeon->Base; +}