diff --git a/src/add-ons/accelerants/ati/mach64_overlay.cpp b/src/add-ons/accelerants/ati/mach64_overlay.cpp new file mode 100644 index 0000000000..dedbaacf01 --- /dev/null +++ b/src/add-ons/accelerants/ati/mach64_overlay.cpp @@ -0,0 +1,145 @@ +/* + Haiku ATI video driver adapted from the X.org ATI driver which has the + following copyright: + + Copyright 2003 through 2004 by Marc Aurele La France, tsi@xfree86.org + + Copyright 2011 Haiku, Inc. All rights reserved. + Distributed under the terms of the MIT license. + + Authors: + Gerald Zajac +*/ + +#include "accelerant.h" +#include "mach64.h" + + + +bool +Mach64_DisplayOverlay(const overlay_window* window, + const overlay_buffer* buffer) +{ + // Return true if setup is successful. + + SharedInfo& si = *gInfo.sharedInfo; + + if (window == NULL || buffer == NULL) + return false; + + uint32 videoFormat; + + if (buffer->space == B_YCbCr422) + videoFormat = SCALE_IN_VYUY422; + else + return false; // color space not supported + + int32 x1 = (window->h_start < 0) ? 0 : window->h_start; + int32 y1 = (window->v_start < 0) ? 0 : window->v_start; + + int32 x2 = window->h_start + window->width - 1; + int32 y2 = window->v_start + window->height - 1; + + if (x2 > si.displayMode.timing.h_display) + x2 = si.displayMode.timing.h_display; + + if (y2 > si.displayMode.timing.v_display) + y2 = si.displayMode.timing.v_display; + + // If window is moved beyond edge of screen, do not allow width < 4 or + // height < 4; otherwise there is a possibilty of divide by zero when + // computing the scale factors.. + if (x2 < x1 + 4) + x2 = x1 + 4; + + if (y2 < y1 + 4) + y2 = y1 + 4; + + // Calculate overlay scale factors. + uint32 horzScale = (buffer->width << 12) / (x2 - x1 + 1); + uint32 vertScale = (buffer->height << 12) / (y2 - y1 + 1); + + if (horzScale > 0xffff) // only 16 bits are used for scale factors + horzScale = 0xffff; + if (vertScale > 0xffff) + vertScale = 0xffff; + + gInfo.WaitForFifo(2); + OUTREG(BUS_CNTL, INREG(BUS_CNTL) | BUS_EXT_REG_EN); // enable reg block 1 + OUTREG(OVERLAY_SCALE_CNTL, SCALE_EN); // reset the video + + if (si.chipType >= MACH64_264GTPRO) { + const uint32 brightness = 0; + const uint32 saturation = 12; + + gInfo.WaitForFifo(6); + OUTREG(SCALER_COLOUR_CNTL, brightness | saturation << 8 + | saturation << 16); + OUTREG(SCALER_H_COEFF0, 0x0002000); + OUTREG(SCALER_H_COEFF1, 0xd06200d); + OUTREG(SCALER_H_COEFF2, 0xd0a1c0d); + OUTREG(SCALER_H_COEFF3, 0xc0e1a0c); + OUTREG(SCALER_H_COEFF4, 0xc14140c); + } + + uint32 keyColor = 0; + uint32 keyMask = 0; + + switch (si.displayMode.bitsPerPixel) { + case 15: + keyMask = 0x7fff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 5 + | (window->red.value & window->red.mask) << 10; + // 15 bit color has no alpha bits + break; + case 16: + keyMask = 0xffff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 5 + | (window->red.value & window->red.mask) << 11; + // 16 bit color has no alpha bits + break; + default: + keyMask = 0xffffffff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 8 + | (window->red.value & window->red.mask) << 16 + | (window->alpha.value & window->alpha.mask) << 24; + break; + } + + gInfo.WaitForFifo(3); + OUTREG(OVERLAY_GRAPHICS_KEY_MSK, keyMask); + OUTREG(OVERLAY_GRAPHICS_KEY_CLR, keyColor); + OUTREG(OVERLAY_KEY_CNTL, OVERLAY_MIX_FALSE | OVERLAY_MIX_EQUAL); + + gInfo.WaitForFifo(8); + OUTREG(OVERLAY_Y_X_START, OVERLAY_LOCK_START | (x1 << 16) | y1); + OUTREG(OVERLAY_Y_X_END, (x2 << 16) | y2); + OUTREG(OVERLAY_SCALE_INC, (horzScale << 16) | vertScale); + OUTREG(SCALER_HEIGHT_WIDTH, (buffer->width << 16) | buffer->height); + OUTREG(VIDEO_FORMAT, videoFormat); + + // Compute offset of overlay buffer in the video memory. + uint32 offset = uint32(buffer->buffer) - si.videoMemAddr; + + if (si.chipType < MACH64_264VTB) { + OUTREG(BUF0_OFFSET, offset); + OUTREG(BUF0_PITCH, buffer->width); + } else { + OUTREG(SCALER_BUF0_OFFSET, offset); + OUTREG(SCALER_BUF0_PITCH, buffer->width); + } + + OUTREG(OVERLAY_SCALE_CNTL, SCALE_PIX_EXPAND | OVERLAY_EN | SCALE_EN); + + return true; +} + + +void +Mach64_StopOverlay(void) +{ + OUTREG(OVERLAY_SCALE_CNTL, SCALE_EN); // reset the video +} diff --git a/src/add-ons/accelerants/ati/overlay.cpp b/src/add-ons/accelerants/ati/overlay.cpp new file mode 100644 index 0000000000..53479f8c67 --- /dev/null +++ b/src/add-ons/accelerants/ati/overlay.cpp @@ -0,0 +1,319 @@ +/* + Copyright 2011 Haiku, Inc. All rights reserved. + Distributed under the terms of the MIT license. + + Authors: + Gerald Zajac + */ + +#include "accelerant.h" +#include "rage128.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_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); + + // If Mach 64 chip, check hardware limits. + + if (MACH64_FAMILY(si.chipType)) { + if (height > 2048 || width > 768 + || (width > 384 && si.chipType < MACH64_264VTB) + || (width > 720 && (si.chipType < MACH64_264GTPRO + || si.chipType > MACH64_264LTPRO))) + return NULL; + } + + 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. + + if (colorSpace != B_YCbCr422) { + si.overlayLock.Release(); + TRACE("AllocateOverlayBuffer() unsupported color space 0x%x\n", + colorSpace); + return NULL; + } + + uint32 bytesPerPixel = 2; // B_YCbCr422 uses 2 bytes per pixel + + // Calculate required buffer size as a multiple of 1K. + uint32 buffSize = (width * bytesPerPixel * height + 0x3ff) & ~0x3ff; + + 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. Leave a gap of about 4K + // between where the overlay buffers will start and the cursor image; + // thus, if the overlay buffer overflows it will be less apt to affect the + // cursor. + + uint32 prevBuffAddr = (si.videoMemAddr + si.cursorOffset - 0xfff) & ~0xfff; + + 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. + + uint32 currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size; + if ((prevBuffAddr - currentBuffEndAddr) >= buffSize) + break; // sufficient space for the new buffer + + prevBuffAddr = (uint32)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. + + uint32 fbEndAddr = si.videoMemAddr + si.frameBufferOffset + + (si.displayMode.virtual_width + * ((si.displayMode.bitsPerPixel + 7) / 8) // bytes per pixel + * 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; + + 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); + return B_OK; + } + + prevOvBuff = ovBuff; + ovBuff = ovBuff->nextBuffer; + } + + TRACE("ReleaseOverlayBuffer() buffer to release at 0x%lx not found\n", + buffer->buffer); + 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; + + if (buffer->space == B_YCbCr422) + constraints->view.width_alignment = 7; + else { + 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; + + // 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; + } + + return (overlay_token)++si.overlayToken; +} + + +status_t +ReleaseOverlay(overlay_token overlayToken) +{ + SharedInfo& si = *gInfo.sharedInfo; + + if (overlayToken != (overlay_token)si.overlayToken) { + TRACE("ReleaseOverlay() error - no overlay previously allocated\n"); + return B_BAD_VALUE; + } + + if (MACH64_FAMILY(si.chipType)) + Mach64_StopOverlay(); + else + Rage128_StopOverlay(); + + atomic_and(&si.overlayAllocated, 0); // mark overlay as unallocated + 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) { + if (MACH64_FAMILY(si.chipType)) + Mach64_StopOverlay(); + else + Rage128_StopOverlay(); + + return B_OK; + } + + // Program the overlay hardware. + if (MACH64_FAMILY(si.chipType)) { + if (!Mach64_DisplayOverlay(window, buffer)) { + TRACE("ConfigureOverlay(), call to Mach64_DisplayOverlay() " + "returned error\n"); + return B_ERROR; + } + } else { + if (!Rage128_DisplayOverlay(window, buffer)) { + TRACE("ConfigureOverlay(), call to Rage128_DisplayOverlay() " + "returned error\n"); + return B_ERROR; + } + } + + return B_OK; +} diff --git a/src/add-ons/accelerants/ati/rage128_overlay.cpp b/src/add-ons/accelerants/ati/rage128_overlay.cpp new file mode 100644 index 0000000000..727fa961fb --- /dev/null +++ b/src/add-ons/accelerants/ati/rage128_overlay.cpp @@ -0,0 +1,178 @@ +/* + Haiku ATI video driver adapted from the X.org ATI driver which has the + following copyright: + + Copyright 1999, 2000 ATI Technologies Inc., Markham, Ontario, + Precision Insight, Inc., Cedar Park, Texas, and + VA Linux Systems Inc., Fremont, California. + + Copyright 2011 Haiku, Inc. All rights reserved. + Distributed under the terms of the MIT license. + + Authors: + Gerald Zajac + */ + +#include "accelerant.h" +#include "rage128.h" + + +static uint32 sCurrentKeyColor = 0; +static uint32 sCurrentKeyMask = 0; + + + +bool +Rage128_DisplayOverlay(const overlay_window* window, + const overlay_buffer* buffer) +{ + // Return true if setup is successful. + + SharedInfo& si = *gInfo.sharedInfo; + + if (window == NULL || buffer == NULL) + return false; + + if (buffer->space != B_YCbCr422) + return false; // color space not supported + + uint32 keyColor = 0; + uint32 keyMask = 0; + + switch (si.displayMode.bitsPerPixel) { + case 15: + keyMask = 0x7fff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 5 + | (window->red.value & window->red.mask) << 10; + // 15 bit color has no alpha bits + break; + case 16: + keyMask = 0xffff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 5 + | (window->red.value & window->red.mask) << 11; + // 16 bit color has no alpha bits + break; + default: + keyMask = 0xffffffff; + keyColor = (window->blue.value & window->blue.mask) << 0 + | (window->green.value & window->green.mask) << 8 + | (window->red.value & window->red.mask) << 16 + | (window->alpha.value & window->alpha.mask) << 24; + break; + } + + // If the key color or key mask has changed since the overlay was + // previously initialized, initialize it again. This is to avoid + // initializing the overlay video everytime the overlay buffer is + // switched which causes artifacts in the overlay display. + + if (keyColor != sCurrentKeyColor || keyMask != sCurrentKeyMask) + { + TRACE("Rage128_DisplayOverlay() initializing overlay video\n"); + + // Initialize the overlay video by first resetting the video. + + OUTREG(R128_OV0_SCALE_CNTL, 0); + OUTREG(R128_OV0_EXCLUSIVE_HORZ, 0); + OUTREG(R128_OV0_AUTO_FLIP_CNTL, 0); + OUTREG(R128_OV0_FILTER_CNTL, 0x0000000f); + + const uint32 brightness = 0; + const uint32 saturation = 16; + OUTREG(R128_OV0_COLOUR_CNTL, brightness | saturation << 8 + | saturation << 16); + + OUTREG(R128_OV0_GRAPHICS_KEY_MSK, keyMask); + OUTREG(R128_OV0_GRAPHICS_KEY_CLR, keyColor); + OUTREG(R128_OV0_KEY_CNTL, R128_GRAPHIC_KEY_FN_NE); + OUTREG(R128_OV0_TEST, 0); + + sCurrentKeyColor = keyColor; + sCurrentKeyMask = keyMask; + } + + uint32 ecpDiv; + if (si.displayMode.timing.pixel_clock < 125000) + ecpDiv = 0; + else if (si.displayMode.timing.pixel_clock < 250000) + ecpDiv = 1; + else + ecpDiv = 2; + + SetPLLReg(R128_VCLK_ECP_CNTL, ecpDiv << 8, R128_ECP_DIV_MASK); + + int32 vertInc = (buffer->height << 20) / window->height; + int32 horzInc = (buffer->width << (12 + ecpDiv)) / window->width; + int32 stepBy = 1; + + while (horzInc >= (2 << 12)) { + stepBy++; + horzInc >>= 1; + } + + int32 x1 = window->h_start; + int32 y1 = window->v_start; + + int32 x2 = window->h_start + window->width; + int32 y2 = window->v_start + window->height; + + int32 left = x1; + int32 tmp = (left & 0x0003ffff) + 0x00028000 + (horzInc << 3); + int32 p1_h_accum_init = ((tmp << 4) & 0x000f8000) | + ((tmp << 12) & 0xf0000000); + + tmp = ((left >> 1) & 0x0001ffff) + 0x00028000 + (horzInc << 2); + int32 p23_h_accum_init = ((tmp << 4) & 0x000f8000) | + ((tmp << 12) & 0x70000000); + + tmp = (y1 & 0x0000ffff) + 0x00018000; + int32 p1_v_accum_init = ((tmp << 4) & 0x03ff8000) | 0x00000001; + + // Compute offset of overlay buffer in the video memory. + uint32 offset = uint32(buffer->buffer) - si.videoMemAddr; + + OUTREG(R128_OV0_REG_LOAD_CNTL, 1); + while (!(INREG(R128_OV0_REG_LOAD_CNTL) & (1 << 3))) + ; + + OUTREG(R128_OV0_H_INC, horzInc | ((horzInc >> 1) << 16)); + OUTREG(R128_OV0_STEP_BY, stepBy | (stepBy << 8)); + OUTREG(R128_OV0_Y_X_START, x1 | y1 << 16); + OUTREG(R128_OV0_Y_X_END, x2 | y2 << 16); + OUTREG(R128_OV0_V_INC, vertInc); + OUTREG(R128_OV0_P1_BLANK_LINES_AT_TOP, + 0x00000fff | ((buffer->height - 1) << 16)); + OUTREG(R128_OV0_VID_BUF_PITCH0_VALUE, buffer->bytes_per_row); + + int32 width = window->width; + left = 0; + OUTREG(R128_OV0_P1_X_START_END, (width - 1) | (left << 16)); + 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); + 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); + + OUTREG(R128_OV0_SCALE_CNTL, 0x41FF8B03); + OUTREG(R128_OV0_REG_LOAD_CNTL, 0); + + return true; +} + + +void +Rage128_StopOverlay(void) +{ + OUTREG(R128_OV0_SCALE_CNTL, 0); // reset the video + + // Reset the key color and mask so that when the overlay is started again + // it will be initialized. + + sCurrentKeyColor = 0; + sCurrentKeyMask = 0; +}