/* * Copyright 2010 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license. * * Authors: * Gerald Zajac */ #include "accelerant.h" #include "3dfx.h" #include uint32 OverlayCount(const display_mode *mode) { (void)mode; // avoid compiler warning for unused arg return 1; } const uint32* OverlaySupportedSpaces(const display_mode* mode) { (void)mode; // avoid compiler warning for unused arg static const uint32 kSupportedSpaces[] = {B_RGB16, B_YCbCr422, 0}; return kSupportedSpaces; } uint32 OverlaySupportedFeatures(uint32 colorSpace) { (void)colorSpace; // avoid compiler warning for unused arg return B_OVERLAY_COLOR_KEY | B_OVERLAY_HORIZONTAL_FILTERING | B_OVERLAY_VERTICAL_FILTERING; } const overlay_buffer* AllocateOverlayBuffer(color_space colorSpace, uint16 width, uint16 height) { SharedInfo& si = *gInfo.sharedInfo; TRACE("AllocateOverlayBuffer() width %u, height %u, colorSpace 0x%lx\n", width, height, colorSpace); si.overlayLock.Acquire(); // Note: When allocating buffers, buffer allocation starts at the end of // video memory, and works backward to the end of the video frame buffer. // The allocated buffers are recorded in a linked list of OverlayBuffer // objects which are ordered by the buffer address with the first object // in the list having the highest buffer address. uint32 bytesPerPixel; switch (colorSpace) { case B_YCbCr422: case B_RGB16: bytesPerPixel = 2; break; default: si.overlayLock.Release(); TRACE("AllocateOverlayBuffer() unsupported color space 0x%x\n", colorSpace); return NULL; } // Calculate required buffer size as a multiple of 1K. uint32 buffSize = (width * bytesPerPixel * height + 0x3ff) & ~0x3ff; // If the buffer area starts at the end of the video memory, the Voodoo5 // chip has the following display problems: displays a pink/red band at // the bottom of the overlay display, leaves some artifacts at the top of // screen, and messes up the displayed cursor when a hardware cursor is // used (cursor image is at beginning of video memory). I don't know // whether the Voodoo5 goes beyond the buffer area or whether this is some // sort of memory mapping problem; nevertheless, adding 4k or 8K to the // buffer size solves the problem. Thus, 16K is added to the buffer size. if (si.chipType == VOODOO_5) buffSize += 16 * 1024; OverlayBuffer* ovBuff = si.overlayBuffer; OverlayBuffer* prevOvBuff = NULL; // If no buffers have been allocated, prevBuffAddr calculated here will be // the address where the buffer area will start. addr_t prevBuffAddr = si.videoMemAddr + si.frameBufferOffset + si.maxFrameBufferSize; while (ovBuff != NULL) { // Test if there is sufficient space between the end of the current // buffer and the start of the previous buffer to allocate the new // buffer. addr_t currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size; if ((prevBuffAddr - currentBuffEndAddr) >= buffSize) break; // sufficient space for the new buffer prevBuffAddr = (addr_t)ovBuff->buffer; prevOvBuff = ovBuff; ovBuff = ovBuff->nextBuffer; } OverlayBuffer* nextOvBuff = ovBuff; if (ovBuff == NULL) { // No space between any current buffers of the required size was found; // thus space must be allocated between the last buffer and the end of // the video frame buffer. Compute where current video frame buffer // ends so that it can be determined if there is sufficient space for // the new buffer to be created. addr_t fbEndAddr = si.videoMemAddr + si.frameBufferOffset + (si.displayMode.virtual_width * si.displayMode.bytesPerPixel * si.displayMode.virtual_height); if (buffSize > prevBuffAddr - fbEndAddr) { si.overlayLock.Release(); TRACE("AllocateOverlayBuffer() insuffcient space for %ld (0x%lx) " "byte buffer\n", buffSize, buffSize); return NULL; // insufficient space for buffer } nextOvBuff = NULL; } ovBuff = (OverlayBuffer*)malloc(sizeof(OverlayBuffer)); if (ovBuff == NULL) { si.overlayLock.Release(); return NULL; // memory not available for OverlayBuffer struct } ovBuff->nextBuffer = nextOvBuff; ovBuff->size = buffSize; ovBuff->space = colorSpace; ovBuff->width = width; ovBuff->height = height; ovBuff->bytes_per_row = width * bytesPerPixel; ovBuff->buffer = (void*)(prevBuffAddr - buffSize); ovBuff->buffer_dma = (void*)(si.videoMemPCI + ((addr_t)ovBuff->buffer - si.videoMemAddr)); if (prevOvBuff == NULL) si.overlayBuffer = ovBuff; else prevOvBuff->nextBuffer = ovBuff; si.overlayLock.Release(); TRACE("AllocateOverlayBuffer() allocated %ld (0x%lx) byte buffer at 0x%lx\n", buffSize, buffSize, ovBuff->buffer); return ovBuff; } status_t ReleaseOverlayBuffer(const overlay_buffer* buffer) { SharedInfo& si = *gInfo.sharedInfo; TRACE("ReleaseOverlayBuffer() called\n"); if (buffer == NULL) return B_BAD_VALUE; // Find the buffer to be released. OverlayBuffer* ovBuff = si.overlayBuffer; OverlayBuffer* prevOvBuff = NULL; while (ovBuff != NULL) { if (ovBuff->buffer == buffer->buffer) { // Buffer to be released has been found. Remove the OverlayBuffer // object from the chain of overlay buffers. if (prevOvBuff == NULL) si.overlayBuffer = ovBuff->nextBuffer; else prevOvBuff->nextBuffer = ovBuff->nextBuffer; free(ovBuff); TRACE("ReleaseOverlayBuffer() return OK\n"); return B_OK; } prevOvBuff = ovBuff; ovBuff = ovBuff->nextBuffer; } return B_ERROR; // buffer to be released not found in chain of buffers } status_t GetOverlayConstraints(const display_mode* mode, const overlay_buffer* buffer, overlay_constraints* constraints) { if ((mode == NULL) || (buffer == NULL) || (constraints == NULL)) return B_ERROR; // Position (values are in pixels) constraints->view.h_alignment = 0; constraints->view.v_alignment = 0; switch (buffer->space) { case B_YCbCr422: case B_RGB16: constraints->view.width_alignment = 7; break; default: TRACE("GetOverlayConstraints() color space 0x%x out of range\n", buffer->space); return B_BAD_VALUE; } constraints->view.height_alignment = 0; //Size constraints->view.width.min = 4; constraints->view.height.min = 4; constraints->view.width.max = buffer->width; constraints->view.height.max = buffer->height; // Scaler output restrictions constraints->window.h_alignment = 0; constraints->window.v_alignment = 0; constraints->window.width_alignment = 0; constraints->window.height_alignment = 0; constraints->window.width.min = 2; constraints->window.width.max = mode->virtual_width; constraints->window.height.min = 2; constraints->window.height.max = mode->virtual_height; constraints->h_scale.min = 1.0; constraints->h_scale.max = 8.0; constraints->v_scale.min = 1.0; constraints->v_scale.max = 8.0; return B_OK; } overlay_token AllocateOverlay(void) { SharedInfo& si = *gInfo.sharedInfo; TRACE("AllocateOverlay() called\n"); // There is only a single overlay channel; thus, check if it is already // allocated. if (atomic_or(&si.overlayAllocated, 1) != 0) { TRACE("AllocateOverlay() overlay channel already in use\n"); return NULL; } TRACE("AllocateOverlay() Overlay allocated, overlayToken: %d\n", si.overlayToken); return (overlay_token)++si.overlayToken; } status_t ReleaseOverlay(overlay_token overlayToken) { SharedInfo& si = *gInfo.sharedInfo; TRACE("ReleaseOverlay() called\n"); if (overlayToken != (overlay_token)si.overlayToken) return B_BAD_VALUE; TDFX_StopOverlay(); atomic_and(&si.overlayAllocated, 0); // mark overlay as unallocated TRACE("ReleaseOverlay() return OK\n"); return B_OK; } status_t ConfigureOverlay (overlay_token overlayToken, const overlay_buffer* buffer, const overlay_window* window, const overlay_view* view) { SharedInfo& si = *gInfo.sharedInfo; if (overlayToken != (overlay_token)si.overlayToken) return B_BAD_VALUE; if (buffer == NULL) return B_BAD_VALUE; if (window == NULL || view == NULL) { TDFX_StopOverlay(); TRACE("ConfigureOverlay() hide only\n"); return B_OK; } // Program the overlay hardware. if (!TDFX_DisplayOverlay(window, buffer, view)) { TRACE("ConfigureOverlay(), call to TDFX_DisplayOverlay() returned error\n"); return B_ERROR; } return B_OK; }