#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "r128.h" #include "r128_reg.h" #ifdef R128DRI #include "r128_common.h" #include "r128_sarea.h" #endif #include "xf86.h" #include "dixstruct.h" #include #include "fourcc.h" #define OFF_DELAY 250 /* milliseconds */ #define FREE_DELAY 15000 #define OFF_TIMER 0x01 #define FREE_TIMER 0x02 #define CLIENT_VIDEO_ON 0x04 #define TIMER_MASK (OFF_TIMER | FREE_TIMER) static XF86VideoAdaptorPtr R128SetupImageVideo(ScreenPtr); static int R128SetPortAttribute(ScrnInfoPtr, Atom, INT32, pointer); static int R128GetPortAttribute(ScrnInfoPtr, Atom ,INT32 *, pointer); static void R128StopVideo(ScrnInfoPtr, pointer, Bool); static void R128QueryBestSize(ScrnInfoPtr, Bool, short, short, short, short, unsigned int *, unsigned int *, pointer); static int R128PutImage(ScrnInfoPtr, short, short, short, short, short, short, short, short, int, unsigned char*, short, short, Bool, RegionPtr, pointer, DrawablePtr); static int R128QueryImageAttributes(ScrnInfoPtr, int, unsigned short *, unsigned short *, int *, int *); static void R128ResetVideo(ScrnInfoPtr); static void R128VideoTimerCallback(ScrnInfoPtr pScrn, Time now); #define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE) static Atom xvBrightness, xvColorKey, xvSaturation, xvDoubleBuffer; typedef struct { int brightness; int saturation; Bool doubleBuffer; unsigned char currentBuffer; void* BufferHandle; int videoOffset; RegionRec clip; uint32_t colorKey; uint32_t videoStatus; Time offTime; Time freeTime; int ecp_div; } R128PortPrivRec, *R128PortPrivPtr; static void R128ECP(ScrnInfoPtr pScrn, R128PortPrivPtr pPriv) { R128InfoPtr info = R128PTR(pScrn); unsigned char *R128MMIO = info->MMIO; int dot_clock = info->ModeReg.dot_clock_freq; if (dot_clock < 12500) pPriv->ecp_div = 0; else if (dot_clock < 25000) pPriv->ecp_div = 1; else pPriv->ecp_div = 2; OUTPLLP(pScrn, R128_VCLK_ECP_CNTL, pPriv->ecp_div<<8, ~R128_ECP_DIV_MASK); } void R128InitVideo(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL; XF86VideoAdaptorPtr newAdaptor = NULL; int num_adaptors; newAdaptor = R128SetupImageVideo(pScreen); num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors); if(newAdaptor) { if(!num_adaptors) { num_adaptors = 1; adaptors = &newAdaptor; } else { newAdaptors = /* need to free this someplace */ malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*)); if(newAdaptors) { memcpy(newAdaptors, adaptors, num_adaptors * sizeof(XF86VideoAdaptorPtr)); newAdaptors[num_adaptors] = newAdaptor; adaptors = newAdaptors; num_adaptors++; } } } if(num_adaptors) xf86XVScreenInit(pScreen, adaptors, num_adaptors); if(newAdaptors) free(newAdaptors); } #define MAXWIDTH 2048 #define MAXHEIGHT 2048 /* client libraries expect an encoding */ static XF86VideoEncodingRec DummyEncoding = { 0, "XV_IMAGE", MAXWIDTH, MAXHEIGHT, {1, 1} }; #define NUM_FORMATS 12 static XF86VideoFormatRec Formats[NUM_FORMATS] = { {8, TrueColor}, {8, DirectColor}, {8, PseudoColor}, {8, GrayScale}, {8, StaticGray}, {8, StaticColor}, {15, TrueColor}, {16, TrueColor}, {24, TrueColor}, {15, DirectColor}, {16, DirectColor}, {24, DirectColor} }; #define NUM_ATTRIBUTES 4 static XF86AttributeRec Attributes[NUM_ATTRIBUTES] = { {XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"}, {XvSettable | XvGettable, -64, 63, "XV_BRIGHTNESS"}, {XvSettable | XvGettable, 0, 31, "XV_SATURATION"}, {XvSettable | XvGettable, 0, 1, "XV_DOUBLE_BUFFER"} }; #define NUM_IMAGES 4 static XF86ImageRec Images[NUM_IMAGES] = { XVIMAGE_YUY2, XVIMAGE_UYVY, XVIMAGE_YV12, XVIMAGE_I420 }; static void R128ResetVideo(ScrnInfoPtr pScrn) { R128InfoPtr info = R128PTR(pScrn); unsigned char *R128MMIO = info->MMIO; R128PortPrivPtr pPriv = info->adaptor->pPortPrivates[0].ptr; OUTREG(R128_OV0_SCALE_CNTL, 0x80000000); OUTREG(R128_OV0_EXCLUSIVE_HORZ, 0); OUTREG(R128_OV0_AUTO_FLIP_CNTL, 0); /* maybe */ OUTREG(R128_OV0_FILTER_CNTL, 0x0000000f); OUTREG(R128_OV0_COLOUR_CNTL, (pPriv->brightness & 0x7f) | (pPriv->saturation << 8) | (pPriv->saturation << 16)); OUTREG(R128_OV0_GRAPHICS_KEY_MSK, (1 << pScrn->depth) - 1); OUTREG(R128_OV0_GRAPHICS_KEY_CLR, pPriv->colorKey); OUTREG(R128_OV0_KEY_CNTL, R128_GRAPHIC_KEY_FN_NE); OUTREG(R128_OV0_TEST, 0); } static XF86VideoAdaptorPtr R128AllocAdaptor(ScrnInfoPtr pScrn) { XF86VideoAdaptorPtr adapt; R128InfoPtr info = R128PTR(pScrn); R128PortPrivPtr pPriv; if(!(adapt = xf86XVAllocateVideoAdaptorRec(pScrn))) return NULL; if(!(pPriv = calloc(1, sizeof(R128PortPrivRec) + sizeof(DevUnion)))) { free(adapt); return NULL; } adapt->pPortPrivates = (DevUnion*)(&pPriv[1]); adapt->pPortPrivates[0].ptr = (pointer)pPriv; xvBrightness = MAKE_ATOM("XV_BRIGHTNESS"); xvSaturation = MAKE_ATOM("XV_SATURATION"); xvColorKey = MAKE_ATOM("XV_COLORKEY"); xvDoubleBuffer = MAKE_ATOM("XV_DOUBLE_BUFFER"); pPriv->colorKey = info->videoKey; pPriv->doubleBuffer = TRUE; pPriv->videoStatus = 0; pPriv->brightness = 0; pPriv->saturation = 16; pPriv->currentBuffer = 0; R128ECP(pScrn, pPriv); return adapt; } static XF86VideoAdaptorPtr R128SetupImageVideo(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); R128InfoPtr info = R128PTR(pScrn); R128PortPrivPtr pPriv; XF86VideoAdaptorPtr adapt; if(!(adapt = R128AllocAdaptor(pScrn))) return NULL; adapt->type = XvWindowMask | XvInputMask | XvImageMask; adapt->flags = VIDEO_OVERLAID_IMAGES /*| VIDEO_CLIP_TO_VIEWPORT*/; adapt->name = "ATI Rage128 Video Overlay"; adapt->nEncodings = 1; adapt->pEncodings = &DummyEncoding; adapt->nFormats = NUM_FORMATS; adapt->pFormats = Formats; adapt->nPorts = 1; adapt->nAttributes = NUM_ATTRIBUTES; adapt->pAttributes = Attributes; adapt->nImages = NUM_IMAGES; adapt->pImages = Images; adapt->PutVideo = NULL; adapt->PutStill = NULL; adapt->GetVideo = NULL; adapt->GetStill = NULL; adapt->StopVideo = R128StopVideo; adapt->SetPortAttribute = R128SetPortAttribute; adapt->GetPortAttribute = R128GetPortAttribute; adapt->QueryBestSize = R128QueryBestSize; adapt->PutImage = R128PutImage; adapt->QueryImageAttributes = R128QueryImageAttributes; info->adaptor = adapt; pPriv = (R128PortPrivPtr)(adapt->pPortPrivates[0].ptr); REGION_NULL(pScreen, &(pPriv->clip)); R128ResetVideo(pScrn); return adapt; } static void R128StopVideo(ScrnInfoPtr pScrn, pointer data, Bool cleanup) { R128InfoPtr info = R128PTR(pScrn); unsigned char *R128MMIO = info->MMIO; R128PortPrivPtr pPriv = (R128PortPrivPtr)data; REGION_EMPTY(pScrn->pScreen, &pPriv->clip); if(cleanup) { if(pPriv->videoStatus & CLIENT_VIDEO_ON) { OUTREG(R128_OV0_SCALE_CNTL, 0); } if(pPriv->BufferHandle) { if (!info->useEXA) { xf86FreeOffscreenLinear((FBLinearPtr) pPriv->BufferHandle); } #ifdef USE_EXA else { exaOffscreenFree(pScrn->pScreen, (ExaOffscreenArea *) pPriv->BufferHandle); } #endif pPriv->BufferHandle = NULL; } pPriv->videoStatus = 0; } else { if(pPriv->videoStatus & CLIENT_VIDEO_ON) { pPriv->videoStatus |= OFF_TIMER; pPriv->offTime = currentTime.milliseconds + OFF_DELAY; } } } static int R128SetPortAttribute( ScrnInfoPtr pScrn, Atom attribute, INT32 value, pointer data ){ R128InfoPtr info = R128PTR(pScrn); unsigned char *R128MMIO = info->MMIO; R128PortPrivPtr pPriv = (R128PortPrivPtr)data; if(attribute == xvBrightness) { if((value < -64) || (value > 63)) return BadValue; pPriv->brightness = value; OUTREG(R128_OV0_COLOUR_CNTL, (pPriv->brightness & 0x7f) | (pPriv->saturation << 8) | (pPriv->saturation << 16)); } else if(attribute == xvSaturation) { if((value < 0) || (value > 31)) return BadValue; pPriv->saturation = value; OUTREG(R128_OV0_COLOUR_CNTL, (pPriv->brightness & 0x7f) | (pPriv->saturation << 8) | (pPriv->saturation << 16)); } else if(attribute == xvDoubleBuffer) { if((value < 0) || (value > 1)) return BadValue; pPriv->doubleBuffer = value; } else if(attribute == xvColorKey) { pPriv->colorKey = value; OUTREG(R128_OV0_GRAPHICS_KEY_CLR, pPriv->colorKey); REGION_EMPTY(pScrn->pScreen, &pPriv->clip); } else return BadMatch; return Success; } static int R128GetPortAttribute( ScrnInfoPtr pScrn, Atom attribute, INT32 *value, pointer data ){ R128PortPrivPtr pPriv = (R128PortPrivPtr)data; if(attribute == xvBrightness) { *value = pPriv->brightness; } else if(attribute == xvSaturation) { *value = pPriv->saturation; } else if(attribute == xvDoubleBuffer) { *value = pPriv->doubleBuffer ? 1 : 0; } else if(attribute == xvColorKey) { *value = pPriv->colorKey; } else return BadMatch; return Success; } static void R128QueryBestSize( ScrnInfoPtr pScrn, Bool motion, short vid_w, short vid_h, short drw_w, short drw_h, unsigned int *p_w, unsigned int *p_h, pointer data ){ if(vid_w > (drw_w << 4)) drw_w = vid_w >> 4; if(vid_h > (drw_h << 4)) drw_h = vid_h >> 4; *p_w = drw_w; *p_h = drw_h; } /* * * R128DMA - abuse the texture blit ioctl to transfer rectangular blocks * * The block is split into 'passes' pieces of 'hpass' lines which fit entirely * into an indirect buffer * */ Bool R128DMA( R128InfoPtr info, unsigned char *src, unsigned char *dst, int srcPitch, int dstPitch, int h, int w ){ #ifdef R128DRI #define BUFSIZE (R128_BUFFER_SIZE - R128_HOSTDATA_BLIT_OFFSET) #define MAXPASSES (MAXHEIGHT/(BUFSIZE/(MAXWIDTH*2))+1) unsigned char *fb = (uint8_t*)info->FB; unsigned char *buf; int err=-1, i, idx, offset, hpass, passes, srcpassbytes, dstpassbytes; int sizes[MAXPASSES], list[MAXPASSES]; drmDMAReq req; drmR128Blit blit; /* Verify conditions and bail out as early as possible */ if (!info->directRenderingEnabled || !info->DMAForXv) return FALSE; if ((hpass = min(h,(BUFSIZE/w))) == 0) return FALSE; if ((passes = (h+hpass-1)/hpass) > MAXPASSES) return FALSE; /* Request indirect buffers */ srcpassbytes = w*hpass; req.context = info->drmCtx; req.send_count = 0; req.send_list = NULL; req.send_sizes = NULL; req.flags = DRM_DMA_LARGER_OK; req.request_count = passes; req.request_size = srcpassbytes + R128_HOSTDATA_BLIT_OFFSET; req.request_list = &list[0]; req.request_sizes = &sizes[0]; req.granted_count = 0; if (drmDMA(info->drmFD, &req)) return FALSE; if (req.granted_count < passes) { drmFreeBufs(info->drmFD, req.granted_count, req.request_list); return FALSE; } /* Copy parts of the block into buffers and fire them */ dstpassbytes = hpass*dstPitch; dstPitch /= 8; for (i=0, offset=dst-fb; ibuffers->list[idx].address + R128_HOSTDATA_BLIT_OFFSET; if (srcPitch == w) { memcpy(buf, src, srcpassbytes); src += srcpassbytes; } else { int count = hpass; while(count--) { memcpy(buf, src, w); src += srcPitch; buf += w; } } blit.idx = idx; blit.offset = offset; blit.pitch = dstPitch; blit.format = (R128_DATATYPE_CI8 >> 16); blit.x = (offset % 32); blit.y = 0; blit.width = w; blit.height = hpass; if ((err = drmCommandWrite(info->drmFD, DRM_R128_BLIT, &blit, sizeof(drmR128Blit))) < 0) break; } drmFreeBufs(info->drmFD, req.granted_count, req.request_list); return (err==0) ? TRUE : FALSE; #else /* This is to avoid cluttering the rest of the code with '#ifdef R128DRI' */ return FALSE; #endif /* R128DRI */ } static void R128CopyData422( R128InfoPtr info, unsigned char *src, unsigned char *dst, int srcPitch, int dstPitch, int h, int w ){ w <<= 1; /* Attempt data transfer with DMA and fall back to memcpy */ if (!R128DMA(info, src, dst, srcPitch, dstPitch, h, w)) { while(h--) { memcpy(dst, src, w); src += srcPitch; dst += dstPitch; } } } static void R128CopyData420( R128InfoPtr info, unsigned char *src1, unsigned char *src2, unsigned char *src3, unsigned char *dst1, unsigned char *dst2, unsigned char *dst3, int srcPitch, int srcPitch2, int dstPitch, int h, int w ){ int count; /* Attempt data transfer with DMA and fall back to memcpy */ if (!R128DMA(info, src1, dst1, srcPitch, dstPitch, h, w)) { count = h; while(count--) { memcpy(dst1, src1, w); src1 += srcPitch; dst1 += dstPitch; } } w >>= 1; h >>= 1; dstPitch >>= 1; if (!R128DMA(info, src2, dst2, srcPitch2, dstPitch, h, w)) { count = h; while(count--) { memcpy(dst2, src2, w); src2 += srcPitch2; dst2 += dstPitch; } } if (!R128DMA(info, src3, dst3, srcPitch2, dstPitch, h, w)) { count = h; while(count--) { memcpy(dst3, src3, w); src3 += srcPitch2; dst3 += dstPitch; } } } uint32_t R128AllocateMemory( ScrnInfoPtr pScrn, void **mem_struct, int size, int align, Bool need_accel ){ R128InfoPtr info = R128PTR(pScrn); ScreenPtr pScreen = xf86ScrnToScreen(pScrn); Bool do_linear = !need_accel; uint32_t offset = 0; #ifdef HAVE_XAA_H if (!info->accel && need_accel) do_linear = FALSE; else do_linear = TRUE; #endif #ifdef USE_EXA if (info->ExaDriver) { ExaOffscreenArea *area = *mem_struct; if (area != NULL) { if (area->size >= size) return area->offset; exaOffscreenFree(pScreen, area); } area = exaOffscreenAlloc(pScreen, size, align, TRUE, NULL, NULL); *mem_struct = area; if (area == NULL) return 0; offset = area->offset; } #endif if (!info->useEXA && do_linear) { FBLinearPtr linear = *mem_struct; int cpp = info->CurrentLayout.pixel_bytes; /* XAA allocates in units of pixels at the screen bpp, so adjust size appropriately. */ size = (size + cpp - 1) / cpp; align = (align + cpp - 1) / cpp; if(linear) { if(linear->size >= size) return linear->offset * cpp; if(xf86ResizeOffscreenLinear(linear, size)) return linear->offset * cpp; xf86FreeOffscreenLinear(linear); } linear = xf86AllocateOffscreenLinear(pScreen, size, align, NULL, NULL, NULL); *mem_struct = linear; if(!linear) { int max_size; xf86QueryLargestOffscreenLinear(pScreen, &max_size, align, PRIORITY_EXTREME); if(max_size < size) return 0; xf86PurgeUnlockedOffscreenAreas(pScreen); linear = xf86AllocateOffscreenLinear(pScreen, size, align, NULL, NULL, NULL); *mem_struct = linear; if(!linear) return 0; } offset = linear->offset * cpp; } return offset; } static void R128DisplayVideo422( ScrnInfoPtr pScrn, int id, int offset, short width, short height, int pitch, int left, int right, int top, BoxPtr dstBox, short src_w, short src_h, short drw_w, short drw_h ){ R128InfoPtr info = R128PTR(pScrn); R128EntPtr pR128Ent = R128EntPriv(pScrn); xf86OutputPtr output = R128FirstOutput(pR128Ent->pCrtc[0]); R128OutputPrivatePtr r128_output = output->driver_private; unsigned char *R128MMIO = info->MMIO; R128PortPrivPtr pPriv = info->adaptor->pPortPrivates[0].ptr; int v_inc, h_inc, step_by, tmp, v_inc_shift; int p1_h_accum_init, p23_h_accum_init; int p1_v_accum_init; Bool rmx_active = FALSE; R128ECP(pScrn, pPriv); v_inc_shift = 20; if (pScrn->currentMode->Flags & V_INTERLACE) v_inc_shift++; if (pScrn->currentMode->Flags & V_DBLSCAN) v_inc_shift--; if (r128_output->PanelYRes > 0) rmx_active = INREG(R128_FP_VERT_STRETCH) & R128_VERT_STRETCH_ENABLE; if (rmx_active) { v_inc = ((src_h * pScrn->currentMode->CrtcVDisplay / r128_output->PanelYRes) << v_inc_shift) / drw_h; } else { v_inc = (src_h << v_inc_shift) / drw_h; } h_inc = (src_w << (12 + pPriv->ecp_div)) / drw_w; step_by = 1; while(h_inc >= (2 << 12)) { step_by++; h_inc >>= 1; } /* keep everything in 16.16 */ offset += ((left >> 16) & ~7) << 1; tmp = (left & 0x0003ffff) + 0x00028000 + (h_inc << 3); p1_h_accum_init = ((tmp << 4) & 0x000f8000) | ((tmp << 12) & 0xf0000000); tmp = ((left >> 1) & 0x0001ffff) + 0x00028000 + (h_inc << 2); p23_h_accum_init = ((tmp << 4) & 0x000f8000) | ((tmp << 12) & 0x70000000); tmp = (top & 0x0000ffff) + 0x00018000; p1_v_accum_init = ((tmp << 4) & 0x03ff8000) | 0x00000001; left = (left >> 16) & 7; OUTREG(R128_OV0_REG_LOAD_CNTL, 1); while(!(INREG(R128_OV0_REG_LOAD_CNTL) & (1 << 3))); OUTREG(R128_OV0_H_INC, h_inc | ((h_inc >> 1) << 16)); OUTREG(R128_OV0_STEP_BY, step_by | (step_by << 8)); OUTREG(R128_OV0_Y_X_START, dstBox->x1 | (dstBox->y1 << 16)); OUTREG(R128_OV0_Y_X_END, dstBox->x2 | (dstBox->y2 << 16)); OUTREG(R128_OV0_V_INC, v_inc); OUTREG(R128_OV0_P1_BLANK_LINES_AT_TOP, 0x00000fff | ((src_h - 1) << 16)); OUTREG(R128_OV0_VID_BUF_PITCH0_VALUE, pitch); OUTREG(R128_OV0_P1_X_START_END, (width - 1) | (left << 16)); left >>= 1; width >>= 1; OUTREG(R128_OV0_P2_X_START_END, (width - 1) | (left << 16)); OUTREG(R128_OV0_P3_X_START_END, (width - 1) | (left << 16)); OUTREG(R128_OV0_VID_BUF0_BASE_ADRS, offset & 0xfffffff0); OUTREG(R128_OV0_P1_V_ACCUM_INIT, p1_v_accum_init); OUTREG(R128_OV0_P23_V_ACCUM_INIT, 0); OUTREG(R128_OV0_P1_H_ACCUM_INIT, p1_h_accum_init); OUTREG(R128_OV0_P23_H_ACCUM_INIT, p23_h_accum_init); if(id == FOURCC_UYVY) OUTREG(R128_OV0_SCALE_CNTL, 0x41FF8C03); else OUTREG(R128_OV0_SCALE_CNTL, 0x41FF8B03); OUTREG(R128_OV0_REG_LOAD_CNTL, 0); } static void R128DisplayVideo420( ScrnInfoPtr pScrn, short width, short height, int pitch, int offset1, int offset2, int offset3, int left, int right, int top, BoxPtr dstBox, short src_w, short src_h, short drw_w, short drw_h ){ R128InfoPtr info = R128PTR(pScrn); R128EntPtr pR128Ent = R128EntPriv(pScrn); xf86OutputPtr output = R128FirstOutput(pR128Ent->pCrtc[0]); R128OutputPrivatePtr r128_output = output->driver_private; unsigned char *R128MMIO = info->MMIO; R128PortPrivPtr pPriv = info->adaptor->pPortPrivates[0].ptr; int v_inc, h_inc, step_by, tmp, leftUV, v_inc_shift; int p1_h_accum_init, p23_h_accum_init; int p1_v_accum_init, p23_v_accum_init; Bool rmx_active = FALSE; v_inc_shift = 20; if (pScrn->currentMode->Flags & V_INTERLACE) v_inc_shift++; if (pScrn->currentMode->Flags & V_DBLSCAN) v_inc_shift--; if (r128_output->PanelYRes > 0) rmx_active = INREG(R128_FP_VERT_STRETCH) & R128_VERT_STRETCH_ENABLE; if (rmx_active) { v_inc = ((src_h * pScrn->currentMode->CrtcVDisplay / r128_output->PanelYRes) << v_inc_shift) / drw_h; } else { v_inc = (src_h << v_inc_shift) / drw_h; } h_inc = (src_w << (12 + pPriv->ecp_div)) / drw_w; step_by = 1; while(h_inc >= (2 << 12)) { step_by++; h_inc >>= 1; } /* keep everything in 16.16 */ offset1 += (left >> 16) & ~15; offset2 += (left >> 17) & ~15; offset3 += (left >> 17) & ~15; tmp = (left & 0x0003ffff) + 0x00028000 + (h_inc << 3); p1_h_accum_init = ((tmp << 4) & 0x000f8000) | ((tmp << 12) & 0xf0000000); tmp = ((left >> 1) & 0x0001ffff) + 0x00028000 + (h_inc << 2); p23_h_accum_init = ((tmp << 4) & 0x000f8000) | ((tmp << 12) & 0x70000000); tmp = (top & 0x0000ffff) + 0x00018000; p1_v_accum_init = ((tmp << 4) & 0x03ff8000) | 0x00000001; tmp = ((top >> 1) & 0x0000ffff) + 0x00018000; p23_v_accum_init = ((tmp << 4) & 0x01ff8000) | 0x00000001; leftUV = (left >> 17) & 15; left = (left >> 16) & 15; OUTREG(R128_OV0_REG_LOAD_CNTL, 1); while(!(INREG(R128_OV0_REG_LOAD_CNTL) & (1 << 3))); OUTREG(R128_OV0_H_INC, h_inc | ((h_inc >> 1) << 16)); OUTREG(R128_OV0_STEP_BY, step_by | (step_by << 8)); OUTREG(R128_OV0_Y_X_START, dstBox->x1 | (dstBox->y1 << 16)); OUTREG(R128_OV0_Y_X_END, dstBox->x2 | (dstBox->y2 << 16)); OUTREG(R128_OV0_V_INC, v_inc); OUTREG(R128_OV0_P1_BLANK_LINES_AT_TOP, 0x00000fff | ((src_h - 1) << 16)); src_h = (src_h + 1) >> 1; OUTREG(R128_OV0_P23_BLANK_LINES_AT_TOP, 0x000007ff | ((src_h - 1) << 16)); OUTREG(R128_OV0_VID_BUF_PITCH0_VALUE, pitch); OUTREG(R128_OV0_VID_BUF_PITCH1_VALUE, pitch >> 1); OUTREG(R128_OV0_P1_X_START_END, (width - 1) | (left << 16)); width >>= 1; OUTREG(R128_OV0_P2_X_START_END, (width - 1) | (leftUV << 16)); OUTREG(R128_OV0_P3_X_START_END, (width - 1) | (leftUV << 16)); OUTREG(R128_OV0_VID_BUF0_BASE_ADRS, offset1 & 0xfffffff0); OUTREG(R128_OV0_VID_BUF1_BASE_ADRS, (offset2 & 0xfffffff0) | 0x00000001); OUTREG(R128_OV0_VID_BUF2_BASE_ADRS, (offset3 & 0xfffffff0) | 0x00000001); OUTREG(R128_OV0_P1_V_ACCUM_INIT, p1_v_accum_init); OUTREG(R128_OV0_P23_V_ACCUM_INIT, p23_v_accum_init); OUTREG(R128_OV0_P1_H_ACCUM_INIT, p1_h_accum_init); OUTREG(R128_OV0_P23_H_ACCUM_INIT, p23_h_accum_init); OUTREG(R128_OV0_SCALE_CNTL, 0x41FF8A03); OUTREG(R128_OV0_REG_LOAD_CNTL, 0); } static int R128PutImage( 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 ){ R128InfoPtr info = R128PTR(pScrn); R128PortPrivPtr pPriv = (R128PortPrivPtr)data; unsigned char *fb = (uint8_t*)info->FB; INT32 xa, xb, ya, yb; int new_size, offset, s1offset, s2offset, s3offset; int srcPitch, srcPitch2, dstPitch; int d1line, d2line, d3line, d1offset, d2offset, d3offset; int top, left, npixels, nlines; BoxRec dstBox; uint32_t tmp; /* Currently, the video is only visible on the first monitor. * In the future we could try to make this smarter, or just implement * textured video. */ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); xf86CrtcPtr crtc = xf86_config->crtc[0]; #if X_BYTE_ORDER == X_BIG_ENDIAN unsigned char *R128MMIO = info->MMIO; uint32_t config_cntl = INREG(R128_CONFIG_CNTL); /* We need to disable byte swapping, or the data gets mangled */ OUTREG(R128_CONFIG_CNTL, config_cntl & ~(APER_0_BIG_ENDIAN_16BPP_SWAP | APER_0_BIG_ENDIAN_32BPP_SWAP)); #endif /* * s1offset, s2offset, s3offset - byte offsets to the Y, U and V planes * of the source. * * d1offset, d2offset, d3offset - byte offsets to the Y, U and V planes * of the destination. * * offset - byte offset within the framebuffer to where the destination * is stored. * * d1line, d2line, d3line - byte offsets within the destination to the * first displayed scanline in each plane. * */ if(src_w > (drw_w << 4)) drw_w = src_w >> 4; if(src_h > (drw_h << 4)) drw_h = src_h >> 4; /* Clip */ xa = src_x; xb = src_x + src_w; ya = src_y; yb = 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, &xa, &xb, &ya, &yb, clipBoxes, width, height)) return Success; dstBox.x1 -= crtc->x; dstBox.x2 -= crtc->x; dstBox.y1 -= crtc->y; dstBox.y2 -= crtc->y; switch(id) { case FOURCC_YV12: case FOURCC_I420: srcPitch = (width + 3) & ~3; srcPitch2 = ((width >> 1) + 3) & ~3; dstPitch = (width + 31) & ~31; /* of luma */ new_size = dstPitch * (height + (height >> 1)); s1offset = 0; s2offset = srcPitch * height; s3offset = (srcPitch2 * (height >> 1)) + s2offset; break; case FOURCC_UYVY: case FOURCC_YUY2: default: srcPitch = width << 1; srcPitch2 = 0; dstPitch = ((width << 1) + 15) & ~15; new_size = dstPitch * height; s1offset = 0; s2offset = 0; s3offset = 0; break; } pPriv->videoOffset = R128AllocateMemory(pScrn, &(pPriv->BufferHandle), pPriv->doubleBuffer ? (new_size << 1) : new_size, 64, FALSE); if (pPriv->videoOffset == 0) return BadAlloc; pPriv->currentBuffer ^= 1; /* copy data */ top = ya >> 16; left = (xa >> 16) & ~1; npixels = ((((xb + 0xffff) >> 16) + 1) & ~1) - left; offset = pPriv->videoOffset; if(pPriv->doubleBuffer) offset += pPriv->currentBuffer * new_size; switch(id) { case FOURCC_YV12: case FOURCC_I420: d1line = top * dstPitch; d2line = (height * dstPitch) + ((top >> 1) * (dstPitch >> 1)); d3line = d2line + ((height >> 1) * (dstPitch >> 1)); top &= ~1; d1offset = (top * dstPitch) + left + offset; d2offset = d2line + (left >> 1) + offset; d3offset = d3line + (left >> 1) + offset; s1offset += (top * srcPitch) + left; tmp = ((top >> 1) * srcPitch2) + (left >> 1); s2offset += tmp; s3offset += tmp; if(id == FOURCC_YV12) { tmp = s2offset; s2offset = s3offset; s3offset = tmp; } nlines = ((((yb + 0xffff) >> 16) + 1) & ~1) - top; R128CopyData420(info, buf + s1offset, buf + s2offset, buf + s3offset, fb + d1offset, fb + d2offset, fb + d3offset, srcPitch, srcPitch2, dstPitch, nlines, npixels); break; case FOURCC_UYVY: case FOURCC_YUY2: default: left <<= 1; d1line = top * dstPitch; d2line = 0; d3line = 0; d1offset = d1line + left + offset; d2offset = 0; d3offset = 0; s1offset += (top * srcPitch) + left; nlines = ((yb + 0xffff) >> 16) - top; R128CopyData422(info, buf + s1offset, fb + d1offset, srcPitch, dstPitch, nlines, npixels); break; } #if X_BYTE_ORDER == X_BIG_ENDIAN /* restore byte swapping */ OUTREG(R128_CONFIG_CNTL, config_cntl); #endif /* update cliplist */ if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) { REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes); /* draw these */ xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey, clipBoxes); } switch(id) { case FOURCC_YV12: case FOURCC_I420: R128DisplayVideo420(pScrn, width, height, dstPitch, offset + d1line, offset + d2line, offset + d3line, xa, xb, ya, &dstBox, src_w, src_h, drw_w, drw_h); break; case FOURCC_UYVY: case FOURCC_YUY2: default: R128DisplayVideo422(pScrn, id, offset + d1line, width, height, dstPitch, xa, xb, ya, &dstBox, src_w, src_h, drw_w, drw_h); break; } pPriv->videoStatus = CLIENT_VIDEO_ON; info->VideoTimerCallback = R128VideoTimerCallback; return Success; } static int R128QueryImageAttributes( ScrnInfoPtr pScrn, int id, unsigned short *w, unsigned short *h, int *pitches, int *offsets ){ int size, tmp; if(*w > MAXWIDTH) *w = MAXWIDTH; if(*h > MAXHEIGHT) *h = MAXHEIGHT; *w = (*w + 1) & ~1; if(offsets) offsets[0] = 0; switch(id) { case FOURCC_YV12: case FOURCC_I420: *h = (*h + 1) & ~1; size = (*w + 3) & ~3; if(pitches) pitches[0] = size; size *= *h; if(offsets) offsets[1] = size; tmp = ((*w >> 1) + 3) & ~3; if(pitches) pitches[1] = pitches[2] = tmp; tmp *= (*h >> 1); size += tmp; if(offsets) offsets[2] = size; size += tmp; break; case FOURCC_UYVY: case FOURCC_YUY2: default: size = *w << 1; if(pitches) pitches[0] = size; size *= *h; break; } return size; } static void R128VideoTimerCallback(ScrnInfoPtr pScrn, Time now) { R128InfoPtr info = R128PTR(pScrn); R128PortPrivPtr pPriv = info->adaptor->pPortPrivates[0].ptr; if(pPriv->videoStatus & TIMER_MASK) { if(pPriv->videoStatus & OFF_TIMER) { if(pPriv->offTime < now) { unsigned char *R128MMIO = info->MMIO; OUTREG(R128_OV0_SCALE_CNTL, 0); pPriv->videoStatus = FREE_TIMER; pPriv->freeTime = now + FREE_DELAY; } } else { /* FREE_TIMER */ if(pPriv->freeTime < now) { if(pPriv->BufferHandle) { if (!info->useEXA) { xf86FreeOffscreenLinear((FBLinearPtr) pPriv->BufferHandle); } #ifdef USE_EXA else { exaOffscreenFree(pScrn->pScreen, (ExaOffscreenArea *) pPriv->BufferHandle); } #endif pPriv->BufferHandle = NULL; } pPriv->videoStatus = 0; info->VideoTimerCallback = NULL; } } } else /* shouldn't get here */ info->VideoTimerCallback = NULL; }