/* * Copyright 2008 Alex Deucher * * 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. * * * Based on radeon_exa_render.c and kdrive ati_video.c by Eric Anholt, et al. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "radeon.h" #include "radeon_reg.h" #include "r600_reg.h" #include "radeon_macros.h" #include "radeon_probe.h" #include "radeon_video.h" #include #include "fourcc.h" extern void R600DisplayTexturedVideo(ScrnInfoPtr pScrn, RADEONPortPrivPtr pPriv); extern Bool R600CopyToVRAM(ScrnInfoPtr pScrn, char *src, int src_pitch, uint32_t dst_pitch, uint32_t dst_mc_addr, uint32_t dst_height, int bpp, int x, int y, int w, int h); #define IMAGE_MAX_WIDTH 2048 #define IMAGE_MAX_HEIGHT 2048 #define IMAGE_MAX_WIDTH_R500 4096 #define IMAGE_MAX_HEIGHT_R500 4096 #define IMAGE_MAX_WIDTH_R600 8192 #define IMAGE_MAX_HEIGHT_R600 8192 static Bool RADEONTilingEnabled(ScrnInfoPtr pScrn, PixmapPtr pPix) { RADEONInfoPtr info = RADEONPTR(pScrn); #ifdef USE_EXA if (info->useEXA) { if (info->tilingEnabled && exaGetPixmapOffset(pPix) == 0) return TRUE; else return FALSE; } else #endif { if (info->tilingEnabled && ((pPix->devPrivate.ptr - info->FB) == 0)) return TRUE; else return FALSE; } } static __inline__ uint32_t F_TO_DW(float val) { union { float f; uint32_t l; } tmp; tmp.f = val; return tmp.l; } /* Borrowed from Mesa */ static __inline__ uint32_t F_TO_24(float val) { float mantissa; int exponent; uint32_t float24 = 0; if (val == 0.0) return 0; mantissa = frexpf(val, &exponent); /* Handle -ve */ if (mantissa < 0) { float24 |= (1 << 23); mantissa = mantissa * -1.0; } /* Handle exponent, bias of 63 */ exponent += 62; float24 |= (exponent << 16); /* Kill 7 LSB of mantissa */ float24 |= (F_TO_DW(mantissa) & 0x7FFFFF) >> 7; return float24; } #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 OUT_ACCEL_REG_F(reg, val) OUTREG(reg, F_TO_DW(val)) #define FINISH_ACCEL() #include "radeon_textured_videofuncs.c" #undef ACCEL_MMIO #undef ACCEL_PREAMBLE #undef BEGIN_ACCEL #undef OUT_ACCEL_REG #undef OUT_ACCEL_REG_F #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 OUT_ACCEL_REG_F(reg, val) OUT_ACCEL_REG(reg, F_TO_DW(val)) #define FINISH_ACCEL() ADVANCE_RING() #define OUT_RING_F(x) OUT_RING(F_TO_DW(x)) #include "radeon_textured_videofuncs.c" #undef ACCEL_CP #undef ACCEL_PREAMBLE #undef BEGIN_ACCEL #undef OUT_ACCEL_REG #undef OUT_ACCEL_REG_F #undef FINISH_ACCEL #undef OUT_RING_F #endif /* XF86DRI */ static void R600CopyPlanarHW(ScrnInfoPtr pScrn, unsigned char *y_src, unsigned char *u_src, unsigned char *v_src, uint32_t dst_mc_addr, int srcPitch, int srcPitch2, int dstPitch, int w, int h) { int dstPitch2 = dstPitch >> 1; int h2 = h >> 1; int w2 = w >> 1; int v_offset, u_offset; v_offset = dstPitch * h; v_offset = (v_offset + 255) & ~255; u_offset = v_offset + (dstPitch2 * h2); u_offset = (u_offset + 255) & ~255; /* Y */ R600CopyToVRAM(pScrn, (char *)y_src, srcPitch, dstPitch, dst_mc_addr, h, 8, 0, 0, w, h); /* V */ R600CopyToVRAM(pScrn, (char *)v_src, srcPitch2, dstPitch2, dst_mc_addr + v_offset, h2, 8, 0, 0, w2, h2); /* U */ R600CopyToVRAM(pScrn, (char *)u_src, srcPitch2, dstPitch2, dst_mc_addr + u_offset, h2, 8, 0, 0, w2, h2); } static void R600CopyPackedHW(ScrnInfoPtr pScrn, unsigned char *src, uint32_t dst_mc_addr, int srcPitch, int dstPitch, int w, int h) { /* YUV */ R600CopyToVRAM(pScrn, (char *)src, srcPitch, dstPitch >> 2, dst_mc_addr, h, 32, 0, 0, w >> 1, h); } static void R600CopyPlanarSW(ScrnInfoPtr pScrn, unsigned char *y_src, unsigned char *u_src, unsigned char *v_src, unsigned char *dst, int srcPitch, int srcPitch2, int dstPitch, int w, int h) { int i; int dstPitch2 = dstPitch >> 1; int h2 = h >> 1; /* Y */ if (srcPitch == dstPitch) { memcpy(dst, y_src, srcPitch * h); dst += (dstPitch * h); } else { for (i = 0; i < h; i++) { memcpy(dst, y_src, srcPitch); y_src += srcPitch; dst += dstPitch; } } /* tex base need 256B alignment */ if (h & 1) dst += dstPitch; /* V */ if (srcPitch2 == dstPitch2) { memcpy(dst, v_src, srcPitch2 * h2); dst += (dstPitch2 * h2); } else { for (i = 0; i < h2; i++) { memcpy(dst, v_src, srcPitch2); v_src += srcPitch2; dst += dstPitch2; } } /* tex base need 256B alignment */ if (h2 & 1) dst += dstPitch2; /* U */ if (srcPitch2 == dstPitch2) { memcpy(dst, u_src, srcPitch2 * h2); dst += (dstPitch2 * h2); } else { for (i = 0; i < h2; i++) { memcpy(dst, u_src, srcPitch2); u_src += srcPitch2; dst += dstPitch2; } } } static void R600CopyPackedSW(ScrnInfoPtr pScrn, unsigned char *src, unsigned char *dst, int srcPitch, int dstPitch, int w, int h) { int i; if (srcPitch == dstPitch) { memcpy(dst, src, srcPitch * h); dst += (dstPitch * h); } else { for (i = 0; i < h; i++) { memcpy(dst, src, srcPitch); src += srcPitch; dst += dstPitch; } } } static int RADEONPutImageTextured(ScrnInfoPtr pScrn, short src_x, short src_y, short drw_x, short drw_y, short src_w, short src_h, short drw_w, short drw_h, int id, unsigned char *buf, short width, short height, Bool sync, RegionPtr clipBoxes, pointer data, DrawablePtr pDraw) { ScreenPtr pScreen = pScrn->pScreen; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPortPrivPtr pPriv = (RADEONPortPrivPtr)data; INT32 x1, x2, y1, y2; int srcPitch, srcPitch2, dstPitch; int s2offset, s3offset, tmp; int top, left, npixels, nlines, size; BoxRec dstBox; int dst_width = width, dst_height = height; /* make the compiler happy */ s2offset = s3offset = srcPitch2 = 0; /* Clip */ x1 = src_x; x2 = src_x + src_w; y1 = src_y; y2 = src_y + src_h; dstBox.x1 = drw_x; dstBox.x2 = drw_x + drw_w; dstBox.y1 = drw_y; dstBox.y2 = drw_y + drw_h; if (!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes, width, height)) return Success; src_w = (x2 - x1) >> 16; src_h = (y2 - y1) >> 16; drw_w = dstBox.x2 - dstBox.x1; drw_h = dstBox.y2 - dstBox.y1; if ((x1 >= x2) || (y1 >= y2)) return Success; switch(id) { case FOURCC_YV12: case FOURCC_I420: dstPitch = ((dst_width << 1) + 15) & ~15; srcPitch = (width + 3) & ~3; srcPitch2 = ((width >> 1) + 3) & ~3; size = dstPitch * dst_height; break; case FOURCC_UYVY: case FOURCC_YUY2: default: dstPitch = ((dst_width << 1) + 15) & ~15; srcPitch = (width << 1); srcPitch2 = 0; size = dstPitch * dst_height; break; } if (info->ChipFamily >= CHIP_FAMILY_R600) dstPitch = (dstPitch + 255) & ~255; else dstPitch = (dstPitch + 63) & ~63; if (pPriv->video_memory != NULL && size != pPriv->size) { radeon_legacy_free_memory(pScrn, pPriv->video_memory); pPriv->video_memory = NULL; } if (pPriv->video_memory == NULL) { if (info->ChipFamily >= CHIP_FAMILY_R600) pPriv->video_offset = radeon_legacy_allocate_memory(pScrn, &pPriv->video_memory, size * 2, 256); else pPriv->video_offset = radeon_legacy_allocate_memory(pScrn, &pPriv->video_memory, size * 2, 64); if (pPriv->video_offset == 0) return BadAlloc; } /* Bicubic filter setup */ pPriv->bicubic_enabled = (pPriv->bicubic_state != BICUBIC_OFF); if (!(IS_R300_3D || IS_R500_3D || IS_R600_3D)) pPriv->bicubic_enabled = FALSE; if (pPriv->bicubic_enabled && (pPriv->bicubic_state == BICUBIC_AUTO)) { /* * Applying the bicubic filter with a scale of less than 200% * results in a blurred picture, so disable the filter. */ if ((src_w > drw_w / 2) || (src_h > drw_h / 2)) pPriv->bicubic_enabled = FALSE; } /* Bicubic filter loading */ if (pPriv->bicubic_memory == NULL && pPriv->bicubic_enabled) { pPriv->bicubic_offset = radeon_legacy_allocate_memory(pScrn, &pPriv->bicubic_memory, sizeof(bicubic_tex_512), 64); pPriv->bicubic_src_offset = pPriv->bicubic_offset + info->fbLocation + pScrn->fbOffset; if (pPriv->bicubic_offset == 0) pPriv->bicubic_enabled = FALSE; } if (pDraw->type == DRAWABLE_WINDOW) pPriv->pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr)pDraw); else pPriv->pPixmap = (PixmapPtr)pDraw; #ifdef USE_EXA if (info->useEXA) { /* Force the pixmap into framebuffer so we can draw to it. */ exaMoveInPixmap(pPriv->pPixmap); } #endif if (!info->useEXA && (((char *)pPriv->pPixmap->devPrivate.ptr < (char *)info->FB) || ((char *)pPriv->pPixmap->devPrivate.ptr >= (char *)info->FB + info->FbMapSize))) { /* If the pixmap wasn't in framebuffer, then we have no way in XAA to * force it there. So, we simply refuse to draw and fail. */ return BadAlloc; } /* copy data */ top = y1 >> 16; left = (x1 >> 16) & ~1; npixels = ((((x2 + 0xffff) >> 16) + 1) & ~1) - left; pPriv->src_offset = pPriv->video_offset + info->fbLocation + pScrn->fbOffset; if (info->ChipFamily >= CHIP_FAMILY_R600) pPriv->src_addr = (uint8_t *)(info->FB + pPriv->video_offset); else pPriv->src_addr = (uint8_t *)(info->FB + pPriv->video_offset + (top * dstPitch)); pPriv->src_pitch = dstPitch; pPriv->size = size; pPriv->pDraw = pDraw; #if 0 ErrorF("src_offset: 0x%x\n", pPriv->src_offset); ErrorF("src_addr: 0x%x\n", pPriv->src_addr); ErrorF("src_pitch: 0x%x\n", pPriv->src_pitch); #endif switch(id) { case FOURCC_YV12: case FOURCC_I420: if (info->ChipFamily >= CHIP_FAMILY_R600) { s2offset = srcPitch * height; s3offset = (srcPitch2 * (height >> 1)) + s2offset; if (info->DMAForXv) { if (id == FOURCC_YV12) R600CopyPlanarHW(pScrn, buf, buf + s3offset, buf + s2offset, pPriv->src_offset, srcPitch, srcPitch2, pPriv->src_pitch, width, height); else R600CopyPlanarHW(pScrn, buf, buf + s2offset, buf + s3offset, pPriv->src_offset, srcPitch, srcPitch2, pPriv->src_pitch, width, height); } else { if (id == FOURCC_YV12) R600CopyPlanarSW(pScrn, buf, buf + s3offset, buf + s2offset, pPriv->src_addr, srcPitch, srcPitch2, pPriv->src_pitch, width, height); else R600CopyPlanarSW(pScrn, buf, buf + s2offset, buf + s3offset, pPriv->src_addr, srcPitch, srcPitch2, pPriv->src_pitch, width, height); } } else { top &= ~1; nlines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top; s2offset = srcPitch * height; s3offset = (srcPitch2 * (height >> 1)) + s2offset; top &= ~1; pPriv->src_addr += left << 1; tmp = ((top >> 1) * srcPitch2) + (left >> 1); s2offset += tmp; s3offset += tmp; if (id == FOURCC_I420) { tmp = s2offset; s2offset = s3offset; s3offset = tmp; } RADEONCopyMungedData(pScrn, buf + (top * srcPitch) + left, buf + s2offset, buf + s3offset, pPriv->src_addr, srcPitch, srcPitch2, dstPitch, nlines, npixels); } break; case FOURCC_UYVY: case FOURCC_YUY2: default: if (info->ChipFamily >= CHIP_FAMILY_R600) { if (info->DMAForXv) R600CopyPackedHW(pScrn, buf, pPriv->src_offset, 2 * width, pPriv->src_pitch, width, height); else R600CopyPackedSW(pScrn, buf, pPriv->src_addr, 2 * width, pPriv->src_pitch, width, height); } else { nlines = ((y2 + 0xffff) >> 16) - top; RADEONCopyData(pScrn, buf, pPriv->src_addr, srcPitch, dstPitch, nlines, npixels, 2); } break; } /* Upload bicubic filter tex */ if (pPriv->bicubic_enabled) { if (info->ChipFamily < CHIP_FAMILY_R600) RADEONCopyData(pScrn, (uint8_t *)bicubic_tex_512, (uint8_t *)(info->FB + pPriv->bicubic_offset), 1024, 1024, 1, 512, 2); } /* update cliplist */ if (!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) { REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes); } pPriv->id = id; pPriv->src_w = src_w; pPriv->src_h = src_h; pPriv->drw_x = drw_x; pPriv->drw_y = drw_y; pPriv->dst_w = drw_w; pPriv->dst_h = drw_h; pPriv->w = width; pPriv->h = height; #ifdef XF86DRI if (IS_R600_3D) R600DisplayTexturedVideo(pScrn, pPriv); else if (info->directRenderingEnabled) RADEONDisplayTexturedVideoCP(pScrn, pPriv); else #endif RADEONDisplayTexturedVideoMMIO(pScrn, pPriv); return Success; } /* client libraries expect an encoding */ static XF86VideoEncodingRec DummyEncoding[1] = { { 0, "XV_IMAGE", IMAGE_MAX_WIDTH, IMAGE_MAX_HEIGHT, {1, 1} } }; static XF86VideoEncodingRec DummyEncodingR500[1] = { { 0, "XV_IMAGE", IMAGE_MAX_WIDTH_R500, IMAGE_MAX_HEIGHT_R500, {1, 1} } }; static XF86VideoEncodingRec DummyEncodingR600[1] = { { 0, "XV_IMAGE", IMAGE_MAX_WIDTH_R600, IMAGE_MAX_HEIGHT_R600, {1, 1} } }; #define NUM_FORMATS 3 static XF86VideoFormatRec Formats[NUM_FORMATS] = { {15, TrueColor}, {16, TrueColor}, {24, TrueColor} }; #define NUM_ATTRIBUTES 1 static XF86AttributeRec Attributes[NUM_ATTRIBUTES+1] = { {XvSettable | XvGettable, 0, 1, "XV_VSYNC"}, {0, 0, 0, NULL} }; #define NUM_ATTRIBUTES_R300 2 static XF86AttributeRec Attributes_r300[NUM_ATTRIBUTES_R300+1] = { {XvSettable | XvGettable, 0, 2, "XV_BICUBIC"}, {XvSettable | XvGettable, 0, 1, "XV_VSYNC"}, {0, 0, 0, NULL} }; static Atom xvBicubic; static Atom xvVSync; #define NUM_IMAGES 4 static XF86ImageRec Images[NUM_IMAGES] = { XVIMAGE_YUY2, XVIMAGE_YV12, XVIMAGE_I420, XVIMAGE_UYVY }; int RADEONGetTexPortAttribute(ScrnInfoPtr pScrn, Atom attribute, INT32 *value, pointer data) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPortPrivPtr pPriv = (RADEONPortPrivPtr)data; if (info->accelOn) RADEON_SYNC(info, pScrn); if (attribute == xvBicubic) *value = pPriv->bicubic_state; else if (attribute == xvVSync) *value = pPriv->vsync; else return BadMatch; return Success; } int RADEONSetTexPortAttribute(ScrnInfoPtr pScrn, Atom attribute, INT32 value, pointer data) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPortPrivPtr pPriv = (RADEONPortPrivPtr)data; RADEON_SYNC(info, pScrn); if (attribute == xvBicubic) pPriv->bicubic_state = ClipValue (value, 0, 2); else if (attribute == xvVSync) pPriv->vsync = ClipValue (value, 0, 1); else return BadMatch; return Success; } XF86VideoAdaptorPtr RADEONSetupImageTexturedVideo(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPortPrivPtr pPortPriv; XF86VideoAdaptorPtr adapt; int i; int num_texture_ports = 16; adapt = xcalloc(1, sizeof(XF86VideoAdaptorRec) + num_texture_ports * (sizeof(RADEONPortPrivRec) + sizeof(DevUnion))); if (adapt == NULL) return NULL; xvBicubic = MAKE_ATOM("XV_BICUBIC"); xvVSync = MAKE_ATOM("XV_VSYNC"); adapt->type = XvWindowMask | XvInputMask | XvImageMask; adapt->flags = 0; adapt->name = "Radeon Textured Video"; adapt->nEncodings = 1; if (IS_R600_3D) adapt->pEncodings = DummyEncodingR600; else if (IS_R500_3D) adapt->pEncodings = DummyEncodingR500; else adapt->pEncodings = DummyEncoding; adapt->nFormats = NUM_FORMATS; adapt->pFormats = Formats; adapt->nPorts = num_texture_ports; adapt->pPortPrivates = (DevUnion*)(&adapt[1]); pPortPriv = (RADEONPortPrivPtr)(&adapt->pPortPrivates[num_texture_ports]); if (IS_R300_3D || IS_R500_3D) { adapt->pAttributes = Attributes_r300; adapt->nAttributes = NUM_ATTRIBUTES_R300; } else { adapt->pAttributes = Attributes; adapt->nAttributes = NUM_ATTRIBUTES; } adapt->pImages = Images; adapt->nImages = NUM_IMAGES; adapt->PutVideo = NULL; adapt->PutStill = NULL; adapt->GetVideo = NULL; adapt->GetStill = NULL; adapt->StopVideo = RADEONStopVideo; adapt->SetPortAttribute = RADEONSetTexPortAttribute; adapt->GetPortAttribute = RADEONGetTexPortAttribute; adapt->QueryBestSize = RADEONQueryBestSize; adapt->PutImage = RADEONPutImageTextured; adapt->ReputImage = NULL; adapt->QueryImageAttributes = RADEONQueryImageAttributes; for (i = 0; i < num_texture_ports; i++) { RADEONPortPrivPtr pPriv = &pPortPriv[i]; pPriv->textured = TRUE; pPriv->videoStatus = 0; pPriv->currentBuffer = 0; pPriv->doubleBuffer = 0; pPriv->bicubic_state = BICUBIC_AUTO; pPriv->vsync = TRUE; /* gotta uninit this someplace, XXX: shouldn't be necessary for textured */ REGION_NULL(pScreen, &pPriv->clip); adapt->pPortPrivates[i].ptr = (pointer) (pPriv); } return adapt; }