| /* |
| * EGL driver for radeon_dri.so |
| */ |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| |
| #include "eglconfig.h" |
| #include "eglcontext.h" |
| #include "egldisplay.h" |
| #include "egldriver.h" |
| #include "eglglobals.h" |
| #include "egllog.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; |
| |
| /** |
| * radeon driver-specific driver class derived from _EGLDriver |
| */ |
| typedef struct radeon_driver |
| { |
| _EGLDriver Base; /* base class/object */ |
| GLuint radeonStuff; |
| } radeonDriver; |
| |
| static int |
| RADEONSetParam(driDisplay *disp, int param, int value) |
| { |
| drm_radeon_setparam_t sp; |
| int ret; |
| |
| memset(&sp, 0, sizeof(sp)); |
| sp.param = param; |
| sp.value = value; |
| |
| if ((ret=drmCommandWrite(disp->drmFD, DRM_RADEON_SETPARAM, &sp, sizeof(sp)))) { |
| fprintf(stderr,"Set param failed\n", ret); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| RADEONCheckDRMVersion(driDisplay *disp, RADEONInfoPtr info) |
| { |
| drmVersionPtr version; |
| |
| version = drmGetVersion(disp->drmFD); |
| if (version) { |
| int req_minor, req_patch; |
| |
| /* Need 1.21.x for card type detection getparam |
| */ |
| req_minor = 21; |
| 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, (long) 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; |
| } |
| |
| |
| /** |
| * Initialize all the memory-related fields of the RADEONInfo object. |
| * This includes the various 'offset' and 'size' fields. |
| */ |
| 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; |
| int pcie_gart_table_size = 0; |
| |
| info->frontOffset = 0; |
| info->frontPitch = disp->virtualWidth; |
| |
| if (disp->card_type==RADEON_CARD_PCIE) |
| pcie_gart_table_size = RADEON_PCIGART_TABLE_SIZE; |
| |
| /* Front, back and depth buffers - everything else texture?? |
| */ |
| info->textureSize = disp->fbSize - pcie_gart_table_size - 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 - pcie_gart_table_size - 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; |
| |
| if (pcie_gart_table_size) |
| info->pcieGartTableOffset = disp->fbSize - pcie_gart_table_size; |
| |
| fprintf(stderr, |
| "Will use back buffer at offset 0x%x, pitch %d\n", |
| info->backOffset, info->backPitch); |
| fprintf(stderr, |
| "Will use depth buffer at offset 0x%x, pitch %d\n", |
| info->depthOffset, info->depthPitch); |
| fprintf(stderr, |
| "Will use %d kb for textures at offset 0x%x\n", |
| info->textureSize/1024, info->textureOffset); |
| if (pcie_gart_table_size) |
| { |
| fprintf(stderr, |
| "Will use %d kb for PCIE GART Table at offset 0x%x\n", |
| pcie_gart_table_size/1024, info->pcieGartTableOffset); |
| } |
| |
| /* XXX I don't think these are needed. */ |
| #if 0 |
| 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)); |
| #endif |
| |
| if (pcie_gart_table_size) |
| RADEONSetParam(disp, RADEON_SETPARAM_PCIGART_LOCATION, info->pcieGartTableOffset); |
| |
| 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_R300) ) |
| drmInfo.func = RADEON_INIT_R300_CP; |
| else 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 */ |
| /* XXX problem here: |
| * The front/back/depth_offset/pitch fields may change depending upon |
| * which drawing surface we're using!!! They can't be set just once |
| * during initialization. |
| * Looks like we'll need a new ioctl to update these fields for drawing |
| * to other surfaces... |
| */ |
| 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->card_type!=RADEON_CARD_AGP) ? 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); |
| } |
| } |
| |
| static int RADEONGetCardType(driDisplay *disp, RADEONInfoPtr info) |
| { |
| drm_radeon_getparam_t gp; |
| int ret; |
| |
| gp.param = RADEON_PARAM_CARD_TYPE; |
| gp.value = &disp->card_type; |
| |
| ret=drmCommandWriteRead(disp->drmFD, DRM_RADEON_GETPARAM, &gp, sizeof(gp)); |
| if (ret) { |
| fprintf(stderr, "drm_radeon_getparam_t (RADEON_PARAM_CARD_TYPE) : %d\n", ret); |
| return -1; |
| } |
| |
| return disp->card_type; |
| } |
| |
| /** |
| * 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; |
| |
| /* XXX this probably isn't needed here */ |
| { |
| int width_bytes = (disp->virtualWidth * disp->cpp); |
| int maxy = disp->fbSize / width_bytes; |
| |
| if (maxy <= disp->virtualHeight * 3) { |
| _eglLog(_EGL_WARNING, |
| "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; |
| } |
| } |
| |
| /* 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) { |
| _eglLog(_EGL_WARNING, "%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)) { |
| _eglLog(_EGL_WARNING, "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); |
| |
| /* 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->card_type != RADEON_CARD_AGP);; |
| 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; |
| |
| case PCI_CHIP_RV370_5460: |
| info->ChipFamily = CHIP_FAMILY_RV380; |
| 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; |
| |
| 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); |
| fprintf(stderr, |
| "page flipping %sabled\n", info->page_flip_enable?"en":"dis"); |
| |
| 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; |
| } |
| #if 0 |
| if (info->ChipFamily >= CHIP_FAMILY_R300) { |
| fprintf(stderr, |
| "Direct rendering not yet supported on " |
| "Radeon 9700 and newer cards\n"); |
| return 0; |
| } |
| #endif |
| |
| #if 00 |
| /* don't seem to need this here */ |
| info->frontPitch = disp->virtualWidth; |
| #endif |
| |
| /* Check the radeon DRM version */ |
| if (!RADEONCheckDRMVersion(disp, info)) { |
| return 0; |
| } |
| |
| if (RADEONGetCardType(disp, info)<0) |
| return 0; |
| |
| if (disp->card_type!=RADEON_CARD_AGP) { |
| /* Initialize PCI */ |
| if (!RADEONDRIPciInit(disp, info)) |
| return 0; |
| } |
| else { |
| /* Initialize AGP */ |
| if (!RADEONDRIAgpInit(disp, info)) |
| return 0; |
| } |
| |
| 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; |
| } |
| |
| |
| /** |
| * Create list of all supported surface configs, attach list to the display. |
| */ |
| 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 |
| radeonShowScreenSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen, |
| EGLSurface surface, EGLModeMESA m) |
| { |
| EGLBoolean b = _eglDRIShowScreenSurfaceMESA(drv, dpy, screen, surface, m); |
| return b; |
| } |
| |
| |
| /** |
| * Called via eglInitialize() by user. |
| */ |
| static EGLBoolean |
| radeonInitialize(_EGLDriver *drv, EGLDisplay dpy, EGLint *major, EGLint *minor) |
| { |
| __DRIframebuffer framebuffer; |
| driDisplay *display; |
| |
| /* one-time init */ |
| radeon_drm_page_size = getpagesize(); |
| |
| 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)); |
| |
| /* XXX we shouldn't hard-code values here! */ |
| /* we won't know the screen surface size until the user calls |
| * eglCreateScreenSurfaceMESA(). |
| */ |
| #if 0 |
| display->virtualWidth = 1024; |
| display->virtualHeight = 768; |
| #else |
| display->virtualWidth = 1280; |
| display->virtualHeight = 1024; |
| #endif |
| display->bpp = 32; |
| display->cpp = 4; |
| |
| if (!_eglDRIGetDisplayInfo(display)) |
| return EGL_FALSE; |
| |
| framebuffer.base = display->pFB; |
| framebuffer.width = display->virtualWidth; |
| framebuffer.height = display->virtualHeight; |
| framebuffer.stride = display->virtualWidth; |
| framebuffer.size = display->fbSize; |
| radeonInitFBDev( display, framebuffer.dev_priv ); |
| |
| if (!_eglDRICreateDisplay(display, &framebuffer)) |
| return EGL_FALSE; |
| |
| if (!_eglDRICreateScreens(display)) |
| return EGL_FALSE; |
| |
| /* create a variety of both 32 and 16-bit configurations */ |
| radeonFillInConfigs(&display->Base, 32, 24, 8, GL_TRUE); |
| radeonFillInConfigs(&display->Base, 16, 16, 0, GL_TRUE); |
| |
| 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.API.Initialize = radeonInitialize; |
| radeon->Base.API.ShowScreenSurfaceMESA = radeonShowScreenSurfaceMESA; |
| |
| return &radeon->Base; |
| } |