summaryrefslogtreecommitdiff
path: root/src/radeon_exa.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/radeon_exa.c')
-rw-r--r--src/radeon_exa.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/src/radeon_exa.c b/src/radeon_exa.c
new file mode 100644
index 0000000..632faaf
--- /dev/null
+++ b/src/radeon_exa.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2005 Eric Anholt
+ * Copyright 2005 Benjamin Herrenschmidt
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <anholt@FreeBSD.org>
+ * Zack Rusin <zrusin@trolltech.com>
+ * Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *
+ */
+
+#include "radeon.h"
+#include "radeon_reg.h"
+#include "radeon_dri.h"
+#include "radeon_macros.h"
+#include "radeon_probe.h"
+#include "radeon_version.h"
+#include "radeon_sarea.h"
+
+#include "xf86.h"
+
+
+/***********************************************************************/
+#define RINFO_FROM_SCREEN(pScr) ScrnInfoPtr pScrn = xf86Screens[pScr->myNum]; \
+ RADEONInfoPtr info = RADEONPTR(pScrn)
+
+#define RADEON_TRACE_FALL 0
+#define RADEON_TRACE_DRAW 0
+
+#if RADEON_TRACE_FALL
+#define RADEON_FALLBACK(x) \
+do { \
+ ErrorF("%s: ", __FUNCTION__); \
+ ErrorF x; \
+ return FALSE; \
+} while (0)
+#else
+#define RADEON_FALLBACK(x) return FALSE
+#endif
+
+#if RADEON_TRACE_DRAW
+#define TRACE do { ErrorF("TRACE: %s\n", __FUNCTION__); } while(0)
+#else
+#define TRACE
+#endif
+
+static struct {
+ int rop;
+ int pattern;
+} RADEON_ROP[] = {
+ { RADEON_ROP3_ZERO, RADEON_ROP3_ZERO }, /* GXclear */
+ { RADEON_ROP3_DSa, RADEON_ROP3_DPa }, /* Gxand */
+ { RADEON_ROP3_SDna, RADEON_ROP3_PDna }, /* GXandReverse */
+ { RADEON_ROP3_S, RADEON_ROP3_P }, /* GXcopy */
+ { RADEON_ROP3_DSna, RADEON_ROP3_DPna }, /* GXandInverted */
+ { RADEON_ROP3_D, RADEON_ROP3_D }, /* GXnoop */
+ { RADEON_ROP3_DSx, RADEON_ROP3_DPx }, /* GXxor */
+ { RADEON_ROP3_DSo, RADEON_ROP3_DPo }, /* GXor */
+ { RADEON_ROP3_DSon, RADEON_ROP3_DPon }, /* GXnor */
+ { RADEON_ROP3_DSxn, RADEON_ROP3_PDxn }, /* GXequiv */
+ { RADEON_ROP3_Dn, RADEON_ROP3_Dn }, /* GXinvert */
+ { RADEON_ROP3_SDno, RADEON_ROP3_PDno }, /* GXorReverse */
+ { RADEON_ROP3_Sn, RADEON_ROP3_Pn }, /* GXcopyInverted */
+ { RADEON_ROP3_DSno, RADEON_ROP3_DPno }, /* GXorInverted */
+ { RADEON_ROP3_DSan, RADEON_ROP3_DPan }, /* GXnand */
+ { RADEON_ROP3_ONE, RADEON_ROP3_ONE } /* GXset */
+};
+
+/* Compute log base 2 of val. */
+static __inline__ int
+RADEONLog2(int val)
+{
+ int bits;
+
+ for (bits = 0; val != 0; val >>= 1, ++bits)
+ ;
+ return bits - 1;
+}
+
+static __inline__ CARD32 F_TO_DW(float val)
+{
+ union {
+ float f;
+ CARD32 l;
+ } tmp;
+ tmp.f = val;
+ return tmp.l;
+}
+
+/* Assumes that depth 15 and 16 can be used as depth 16, which is okay since we
+ * require src and dest datatypes to be equal.
+ */
+static Bool RADEONGetDatatypeBpp(int bpp, CARD32 *type)
+{
+ switch (bpp) {
+ case 8:
+ *type = ATI_DATATYPE_CI8;
+ return TRUE;
+ case 16:
+ *type = ATI_DATATYPE_RGB565;
+ return TRUE;
+ case 24:
+ *type = ATI_DATATYPE_CI8;
+ return TRUE;
+ case 32:
+ *type = ATI_DATATYPE_ARGB8888;
+ return TRUE;
+ default:
+ RADEON_FALLBACK(("Unsupported bpp: %d\n", bpp));
+ return FALSE;
+ }
+}
+
+static Bool RADEONPixmapIsColortiled(PixmapPtr pPix)
+{
+ RINFO_FROM_SCREEN(pPix->drawable.pScreen);
+
+ /* This doesn't account for the back buffer, which we may want to wrap in
+ * a pixmap at some point for the purposes of DRI buffer moves.
+ */
+ if (info->tilingEnabled && exaGetPixmapOffset(pPix) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static Bool RADEONGetOffsetPitch(PixmapPtr pPix, int bpp, CARD32 *pitch_offset,
+ unsigned int offset, unsigned int pitch)
+{
+ RINFO_FROM_SCREEN(pPix->drawable.pScreen);
+
+ if (pitch % info->exa.card.pixmapPitchAlign != 0)
+ RADEON_FALLBACK(("Bad pitch 0x%08x\n", pitch));
+
+ if (offset % info->exa.card.pixmapOffsetAlign != 0)
+ RADEON_FALLBACK(("Bad offset 0x%08x\n", offset));
+
+ pitch = pitch >> 6;
+ *pitch_offset = (pitch << 22) | (offset >> 10);
+
+ /* If it's the front buffer, we've got to note that it's tiled? */
+ if (RADEONPixmapIsColortiled(pPix))
+ *pitch_offset |= RADEON_DST_TILE_MACRO;
+ return TRUE;
+}
+
+static Bool RADEONGetPixmapOffsetPitch(PixmapPtr pPix, CARD32 *pitch_offset)
+{
+ RINFO_FROM_SCREEN(pPix->drawable.pScreen);
+ CARD32 pitch, offset;
+ int bpp;
+
+ bpp = pPix->drawable.bitsPerPixel;
+ if (bpp == 24)
+ bpp = 8;
+
+ offset = exaGetPixmapOffset(pPix) + info->fbLocation;
+ pitch = exaGetPixmapPitch(pPix);
+
+ return RADEONGetOffsetPitch(pPix, bpp, pitch_offset, offset, pitch);
+}
+
+#if X_BYTE_ORDER == X_BIG_ENDIAN
+
+static unsigned long swapper_surfaces[3];
+
+static Bool RADEONPrepareAccess(PixmapPtr pPix, int index)
+{
+ RINFO_FROM_SCREEN(pPix->drawable.pScreen);
+ unsigned char *RADEONMMIO = info->MMIO;
+ CARD32 offset = exaGetPixmapOffset(pPix);
+ int bpp, rc, soff;
+ CARD32 size, flags;
+
+ /* Front buffer is always set with proper swappers */
+ if (offset == 0)
+ return TRUE;
+
+ /* If same bpp as front buffer, just do nothing as the main
+ * swappers will apply
+ */
+ bpp = pPix->drawable.bitsPerPixel;
+ if (bpp == pScrn->bitsPerPixel)
+ return TRUE;
+
+ /* We need to setup a separate swapper, let's request a
+ * surface. We need to align the size first
+ */
+ size = exaGetPixmapSize(pPix);
+ size = (size + RADEON_BUFFER_ALIGN) & ~(RADEON_BUFFER_ALIGN);
+
+ /* Set surface to tiling disabled with appropriate swapper */
+ switch (bpp) {
+ case 16:
+ flags = RADEON_SURF_AP0_SWP_16BPP | RADEON_SURF_AP1_SWP_16BPP;
+ break;
+ case 32:
+ flags = RADEON_SURF_AP0_SWP_32BPP | RADEON_SURF_AP1_SWP_32BPP;
+ break;
+ default:
+ flags = 0;
+ }
+#if defined(XF86DRI)
+ if (info->directRenderingEnabled && info->allowColorTiling) {
+ drmRadeonSurfaceAlloc drmsurfalloc;
+
+ drmsurfalloc.address = offset;
+ drmsurfalloc.size = size;
+ drmsurfalloc.flags = flags | 1; /* bogus pitch to please DRM */
+
+ rc = drmCommandWrite(info->drmFD, DRM_RADEON_SURF_ALLOC,
+ &drmsurfalloc, sizeof(drmsurfalloc));
+ if (rc < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "drm: could not allocate surface for access"
+ " swapper, err: %d!\n", rc);
+ return FALSE;
+ }
+ swapper_surfaces[index] = offset;
+
+ return TRUE;
+ }
+#endif
+ soff = (index + 1) * 0x10;
+ OUTREG(RADEON_SURFACE0_INFO + soff, flags);
+ OUTREG(RADEON_SURFACE0_LOWER_BOUND + soff, offset);
+ OUTREG(RADEON_SURFACE0_UPPER_BOUND + soff, offset + size - 1);
+ swapper_surfaces[index] = offset;
+ return TRUE;
+}
+
+static void RADEONFinishAccess(PixmapPtr pPix, int index)
+{
+ RINFO_FROM_SCREEN(pPix->drawable.pScreen);
+ unsigned char *RADEONMMIO = info->MMIO;
+ CARD32 offset = exaGetPixmapOffset(pPix);
+ int bpp, soff;
+
+ /* Front buffer is always set with proper swappers */
+ if (offset == 0)
+ return;
+
+ if (swapper_surfaces[index] == 0)
+ return;
+#if defined(XF86DRI)
+ if (info->directRenderingEnabled && info->allowColorTiling) {
+ drmRadeonSurfaceFree drmsurffree;
+
+ drmsurffree.address = offset;
+ drmCommandWrite(info->drmFD, DRM_RADEON_SURF_FREE,
+ &drmsurffree, sizeof(drmsurffree));
+ swapper_surfaces[index] = 0;
+ return;
+ }
+#endif
+ soff = (index + 1) * 0x10;
+ OUTREG(RADEON_SURFACE0_INFO + soff, 0);
+ OUTREG(RADEON_SURFACE0_LOWER_BOUND + soff, 0);
+ OUTREG(RADEON_SURFACE0_UPPER_BOUND + soff, 0);
+ swapper_surfaces[index] = 0;
+}
+
+#endif /* X_BYTE_ORDER == X_BIG_ENDIAN */
+
+#define RADEON_SWITCH_TO_2D() \
+do { \
+ if (info->engineMode == EXA_ENGINEMODE_2D) \
+ break; \
+ BEGIN_ACCEL(2); \
+ OUT_ACCEL_REG(RADEON_RB2D_DSTCACHE_CTLSTAT, RADEON_RB2D_DC_FLUSH); \
+ OUT_ACCEL_REG(RADEON_WAIT_UNTIL, \
+ RADEON_WAIT_HOST_IDLECLEAN | \
+ RADEON_WAIT_3D_IDLECLEAN); \
+ FINISH_ACCEL(); \
+ info->engineMode = EXA_ENGINEMODE_2D; \
+} while (0);
+
+#define RADEON_SWITCH_TO_3D() \
+do { \
+ if (info->engineMode == EXA_ENGINEMODE_3D) \
+ break; \
+ BEGIN_ACCEL(2); \
+ OUT_ACCEL_REG(RADEON_RB2D_DSTCACHE_CTLSTAT, RADEON_RB2D_DC_FLUSH); \
+ OUT_ACCEL_REG(RADEON_WAIT_UNTIL, \
+ RADEON_WAIT_HOST_IDLECLEAN | \
+ RADEON_WAIT_2D_IDLECLEAN | \
+ RADEON_WAIT_3D_IDLECLEAN); \
+ FINISH_ACCEL(); \
+ info->engineMode = EXA_ENGINEMODE_3D; \
+} while (0);
+
+#define ENTER_DRAW(x) TRACE
+#define LEAVE_DRAW(x) TRACE
+/***********************************************************************/
+
+#define ACCEL_MMIO
+#define ACCEL_PREAMBLE() unsigned char *RADEONMMIO = info->MMIO
+#define BEGIN_ACCEL(n) RADEONWaitForFifo(pScrn, (n))
+#define OUT_ACCEL_REG(reg, val) OUTREG(reg, val)
+#define FINISH_ACCEL()
+
+#ifdef RENDER
+#include "radeon_exa_render.c"
+#endif
+#include "radeon_exa_funcs.c"
+
+#undef ACCEL_MMIO
+#undef ACCEL_PREAMBLE
+#undef BEGIN_ACCEL
+#undef OUT_ACCEL_REG
+#undef FINISH_ACCEL
+
+#ifdef XF86DRI
+
+#define ACCEL_CP
+#define ACCEL_PREAMBLE() \
+ RING_LOCALS; \
+ RADEONCP_REFRESH(pScrn, info)
+#define BEGIN_ACCEL(n) BEGIN_RING(2*(n))
+#define OUT_ACCEL_REG(reg, val) OUT_RING_REG(reg, val)
+#define FINISH_ACCEL() ADVANCE_RING()
+
+#define OUT_RING_F(x) OUT_RING(F_TO_DW(x))
+
+#ifdef RENDER
+#include "radeon_exa_render.c"
+#endif
+#include "radeon_exa_funcs.c"
+
+#endif /* XF86DRI */
+
+/*
+ * Once screen->off_screen_base is set, this function
+ * allocates the remaining memory appropriately
+ */
+Bool RADEONSetupMemEXA (ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ int cpp = info->CurrentLayout.pixel_bytes;
+ int l;
+ int next, screen_size;
+ int byteStride = pScrn->displayWidth * cpp;
+
+ /* Need to adjust screen size for 16 line tiles, and then make it align to.
+ * the buffer alignment requirement.
+ */
+ if (info->allowColorTiling)
+ screen_size = RADEON_ALIGN(pScrn->virtualY, 16) * byteStride;
+ else
+ screen_size = pScrn->virtualY * byteStride;
+
+ info->exa.card.memoryBase = info->FB + pScrn->fbOffset;
+ info->exa.card.memorySize = info->FbMapSize;
+ info->exa.card.offScreenBase = screen_size;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Allocating from a screen of %ld kb\n",
+ info->exa.card.memorySize / 1024);
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %d kb for front buffer at offset 0x%08x\n",
+ screen_size / 1024, 0);
+
+#if defined(XF86DRI)
+ if (info->directRenderingEnabled) {
+ int depth_size;
+
+ info->frontOffset = 0;
+ info->frontPitch = pScrn->displayWidth;
+
+ if (info->IsPCI && info->drmMinor >= 19) {
+ info->pciGartSize = RADEON_PCIGART_TABLE_SIZE;
+ info->pciGartOffset = RADEON_ALIGN(info->exa.card.offScreenBase,
+ 256);
+ info->exa.card.offScreenBase = info->pciGartOffset +
+ info->pciGartSize;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %d kb for PCI GART at offset 0x%08x\n",
+ RADEON_PCIGART_TABLE_SIZE / 1024,
+ (int)info->pciGartOffset);
+ }
+ /* Reserve a static area for the back buffer the same size as the
+ * visible screen. XXX: This would be better initialized in ati_dri.c
+ * when GLX is set up, but the offscreen memory manager's allocations
+ * don't last through VT switches, while the kernel's understanding of
+ * offscreen locations does.
+ */
+ info->backPitch = pScrn->displayWidth;
+ next = RADEON_ALIGN(info->exa.card.offScreenBase, RADEON_BUFFER_ALIGN);
+ if (!info->noBackBuffer &&
+ next + screen_size <= info->exa.card.memorySize)
+ {
+ info->backOffset = next;
+ info->exa.card.offScreenBase = next + screen_size;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %d kb for back buffer at offset 0x%08x\n",
+ screen_size / 1024, info->backOffset);
+ }
+
+ /* Reserve the static depth buffer, and adjust pitch and height to
+ * handle tiling.
+ */
+ info->depthPitch = RADEON_ALIGN(pScrn->displayWidth, 32);
+ depth_size = RADEON_ALIGN(pScrn->virtualY, 16) * info->depthPitch * cpp;
+ next = RADEON_ALIGN(info->exa.card.offScreenBase, RADEON_BUFFER_ALIGN);
+ if (next + depth_size <= info->exa.card.memorySize)
+ {
+ info->depthOffset = next;
+ info->exa.card.offScreenBase = next + depth_size;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %d kb for depth buffer at offset 0x%08x\n",
+ depth_size / 1024, info->depthOffset);
+ }
+
+ /* Reserve approx. half of remaining offscreen memory for local
+ * textures. Round down to a whole number of texture regions.
+ */
+ info->textureSize = (info->exa.card.memorySize -
+ info->exa.card.offScreenBase) / 2;
+ l = RADEONLog2(info->textureSize / RADEON_NR_TEX_REGIONS);
+ if (l < RADEON_LOG_TEX_GRANULARITY)
+ l = RADEON_LOG_TEX_GRANULARITY;
+ info->textureSize = (info->textureSize >> l) << l;
+ if (info->textureSize >= 512 * 1024) {
+ info->textureOffset = info->exa.card.offScreenBase;
+ info->exa.card.offScreenBase += info->textureSize;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %d kb for textures at offset 0x%08x\n",
+ info->textureSize / 1024, info->textureOffset);
+ } else {
+ /* Minimum texture size is for 2 256x256x32bpp textures */
+ info->textureSize = 0;
+ }
+ }
+#endif /* XF86DRI */
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Will use %ld kb for X Server offscreen at offset 0x%08lx\n",
+ (info->exa.card.memorySize - info->exa.card.offScreenBase) /
+ 1024, info->exa.card.offScreenBase);
+
+ return TRUE;
+}