From 409f1731e2fc4ec30f97cb9723d5a1ae2bb28d33 Mon Sep 17 00:00:00 2001 From: shadow303 Date: Mon, 15 Mar 2004 00:58:55 +0000 Subject: [PATCH] Convert Thomas's radeon driver to out build structure. Still needs work in order to properly enable logging and extra settings. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6985 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/accelerants/radeon/Acceleration.c | 293 +++++ src/add-ons/accelerants/radeon/CP.c | 296 +++++ src/add-ons/accelerants/radeon/CPMicroCode.h | 786 ++++++++++++ src/add-ons/accelerants/radeon/Cursor.c | 250 ++++ .../accelerants/radeon/EngineManagment.c | 217 ++++ .../accelerants/radeon/GetAccelerantHook.c | 108 ++ src/add-ons/accelerants/radeon/GetModeInfo.c | 90 ++ src/add-ons/accelerants/radeon/GlobalData.c | 20 + src/add-ons/accelerants/radeon/GlobalData.h | 16 + .../accelerants/radeon/InitAccelerant.c | 261 ++++ src/add-ons/accelerants/radeon/Jamfile | 40 + .../accelerants/radeon/ProposeDisplayMode.c | 628 ++++++++++ .../accelerants/radeon/SetDisplayMode.c | 493 ++++++++ src/add-ons/accelerants/radeon/crtc.c | 179 +++ src/add-ons/accelerants/radeon/ddc.c | 160 +++ src/add-ons/accelerants/radeon/ddc.h | 21 + src/add-ons/accelerants/radeon/ddc_int.h | 23 + src/add-ons/accelerants/radeon/dpms.c | 242 ++++ src/add-ons/accelerants/radeon/dump_edid.c | 152 +++ src/add-ons/accelerants/radeon/edid.c | 220 ++++ src/add-ons/accelerants/radeon/edid.h | 146 +++ src/add-ons/accelerants/radeon/edid_raw.h | 278 +++++ src/add-ons/accelerants/radeon/engine_sync.c | 247 ++++ src/add-ons/accelerants/radeon/flat_panel.c | 273 +++++ src/add-ons/accelerants/radeon/generic.h | 63 + src/add-ons/accelerants/radeon/i2c.c | 422 +++++++ src/add-ons/accelerants/radeon/i2c.h | 69 ++ src/add-ons/accelerants/radeon/log_coll.c | 129 ++ src/add-ons/accelerants/radeon/log_coll.h | 76 ++ src/add-ons/accelerants/radeon/log_dump.c | 75 ++ src/add-ons/accelerants/radeon/log_dump.h | 17 + src/add-ons/accelerants/radeon/log_enum.h | 18 + src/add-ons/accelerants/radeon/log_names.h | 18 + src/add-ons/accelerants/radeon/multimon.c | 242 ++++ src/add-ons/accelerants/radeon/overlay.c | 1052 +++++++++++++++++ .../accelerants/radeon/overlay_management.c | 375 ++++++ src/add-ons/accelerants/radeon/pll.c | 213 ++++ .../accelerants/radeon/radeon_accelerant.h | 113 ++ .../accelerants/radeon/radeonlog_dump.c | 73 ++ src/add-ons/accelerants/radeon/settings.cpp | 119 ++ src/add-ons/accelerants/radeon/test_ddc.c | 127 ++ src/add-ons/accelerants/radeon/utils.c | 26 + src/add-ons/accelerants/radeon/utils.h | 25 + 43 files changed, 8691 insertions(+) create mode 100644 src/add-ons/accelerants/radeon/Acceleration.c create mode 100644 src/add-ons/accelerants/radeon/CP.c create mode 100644 src/add-ons/accelerants/radeon/CPMicroCode.h create mode 100644 src/add-ons/accelerants/radeon/Cursor.c create mode 100644 src/add-ons/accelerants/radeon/EngineManagment.c create mode 100644 src/add-ons/accelerants/radeon/GetAccelerantHook.c create mode 100644 src/add-ons/accelerants/radeon/GetModeInfo.c create mode 100644 src/add-ons/accelerants/radeon/GlobalData.c create mode 100644 src/add-ons/accelerants/radeon/GlobalData.h create mode 100644 src/add-ons/accelerants/radeon/InitAccelerant.c create mode 100644 src/add-ons/accelerants/radeon/Jamfile create mode 100644 src/add-ons/accelerants/radeon/ProposeDisplayMode.c create mode 100644 src/add-ons/accelerants/radeon/SetDisplayMode.c create mode 100644 src/add-ons/accelerants/radeon/crtc.c create mode 100644 src/add-ons/accelerants/radeon/ddc.c create mode 100644 src/add-ons/accelerants/radeon/ddc.h create mode 100644 src/add-ons/accelerants/radeon/ddc_int.h create mode 100644 src/add-ons/accelerants/radeon/dpms.c create mode 100644 src/add-ons/accelerants/radeon/dump_edid.c create mode 100644 src/add-ons/accelerants/radeon/edid.c create mode 100644 src/add-ons/accelerants/radeon/edid.h create mode 100644 src/add-ons/accelerants/radeon/edid_raw.h create mode 100644 src/add-ons/accelerants/radeon/engine_sync.c create mode 100644 src/add-ons/accelerants/radeon/flat_panel.c create mode 100644 src/add-ons/accelerants/radeon/generic.h create mode 100644 src/add-ons/accelerants/radeon/i2c.c create mode 100644 src/add-ons/accelerants/radeon/i2c.h create mode 100644 src/add-ons/accelerants/radeon/log_coll.c create mode 100644 src/add-ons/accelerants/radeon/log_coll.h create mode 100644 src/add-ons/accelerants/radeon/log_dump.c create mode 100644 src/add-ons/accelerants/radeon/log_dump.h create mode 100644 src/add-ons/accelerants/radeon/log_enum.h create mode 100644 src/add-ons/accelerants/radeon/log_names.h create mode 100644 src/add-ons/accelerants/radeon/multimon.c create mode 100644 src/add-ons/accelerants/radeon/overlay.c create mode 100644 src/add-ons/accelerants/radeon/overlay_management.c create mode 100644 src/add-ons/accelerants/radeon/pll.c create mode 100644 src/add-ons/accelerants/radeon/radeon_accelerant.h create mode 100644 src/add-ons/accelerants/radeon/radeonlog_dump.c create mode 100644 src/add-ons/accelerants/radeon/settings.cpp create mode 100644 src/add-ons/accelerants/radeon/test_ddc.c create mode 100644 src/add-ons/accelerants/radeon/utils.c create mode 100644 src/add-ons/accelerants/radeon/utils.h diff --git a/src/add-ons/accelerants/radeon/Acceleration.c b/src/add-ons/accelerants/radeon/Acceleration.c new file mode 100644 index 0000000000..c04d1c8f28 --- /dev/null +++ b/src/add-ons/accelerants/radeon/Acceleration.c @@ -0,0 +1,293 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Public functions to provide 2D hardware acceleration +*/ + + +#include "radeon_accelerant.h" +#include "GlobalData.h" +#include "generic.h" +#include "cp_regs.h" +#include "3d_regs.h" +#include "2d_regs.h" +#include "mmio.h" + + +// currently, an CP instruction stream is written to +// a buffer on stack and then copied into the official +// CP buffer + +#define PACKET_BUFFER_LEN 0x100 + +// copy screen to screen +// et - ignored +// list - list of rectangles +// count - number of rectangles +void SCREEN_TO_SCREEN_BLIT(engine_token *et, blit_params *list, uint32 count) +{ + virtual_card *vc = ai->vc; + int offset = 0; + uint32 buffer[PACKET_BUFFER_LEN]; + + SHOW_FLOW0( 4, "" ); + + for( ; count > 0; --count, ++list ) { + if( offset == 0 ) { + buffer[offset++] = RADEON_CP_PACKET3_CNTL_BITBLT_MULTI; + buffer[offset++] = RADEON_GMC_BRUSH_NONE + | (vc->datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_SRC_DATATYPE_COLOR + | RADEON_ROP3_S + | RADEON_DP_SRC_SOURCE_MEMORY; + } + + buffer[offset++] = (list->src_left << 16) | list->src_top; + buffer[offset++] = (list->dest_left << 16) | list->dest_top; + buffer[offset++] = ((list->width + 1) << 16) | (list->height + 1); + + if( offset + 3 > PACKET_BUFFER_LEN ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + + offset = 0; + } + } + + if( offset > 0 ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + } + + ++ai->si->engine.count; +} + + +// fill rectangles on screen +// et - ignored +// colorIndex - fill colour +// list - list of rectangles +// count - number of rectangles +void FILL_RECTANGLE(engine_token *et, uint32 colorIndex, + fill_rect_params *list, uint32 count) +{ + virtual_card *vc = ai->vc; + int offset = 0; + uint32 buffer[PACKET_BUFFER_LEN]; + + SHOW_FLOW0( 4, "" ); + + for( ; count > 0; --count, ++list ) { + if( offset == 0 ) { + buffer[offset++] = RADEON_CP_PACKET3_CNTL_PAINT_MULTI; + buffer[offset++] = RADEON_GMC_BRUSH_SOLID_COLOR + | (vc->datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_SRC_DATATYPE_COLOR + | RADEON_ROP3_P; + buffer[offset++] = colorIndex; + } + + buffer[offset++] = (list->left << 16) | list->top; + buffer[offset++] = + ((list->right - list->left + 1) << 16) | + (list->bottom - list->top + 1); + + if( offset + 2 > PACKET_BUFFER_LEN ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + + offset = 0; + } + } + + if( offset > 0 ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + } + + ++ai->si->engine.count; +} + + +// invert rectangle on screen +// et - ignored +// list - list of rectangles +// count - number of rectangles +void INVERT_RECTANGLE(engine_token *et, fill_rect_params *list, uint32 count) +{ + virtual_card *vc = ai->vc; + int offset = 0; + uint32 buffer[PACKET_BUFFER_LEN]; + + SHOW_FLOW0( 4, "" ); + + for( ; count > 0; --count, ++list ) { + if( offset == 0 ) { + buffer[offset++] = RADEON_CP_PACKET3_CNTL_PAINT_MULTI; + buffer[offset++] = RADEON_GMC_BRUSH_NONE + | (vc->datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_SRC_DATATYPE_COLOR + | RADEON_ROP3_Dn; + } + + buffer[offset++] = (list->left << 16) | list->top; + buffer[offset++] = + ((list->right - list->left + 1) << 16) | + (list->bottom - list->top + 1); + + // always leave 2 extra bytes for fix (see below) + if( offset + 2 > PACKET_BUFFER_LEN - 2 ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + + offset = 0; + } + } + + buffer[0] |= (offset - 2) << 16; + + // we have to reset ROP, else we get garbage during next + // CPU access; it looks like some cache coherency/forwarding + // problem as it goes away later on; things like flushing the + // destination cache or waiting for 2D engine or HDP to become + // idle and clean didn't change a thing + // (I dont't really understand what exactly happens, + // but this code fixes it) + buffer[offset++] = CP_PACKET0( RADEON_DP_GUI_MASTER_CNTL, 0 ); + buffer[offset++] = RADEON_GMC_BRUSH_NONE + | (vc->datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_SRC_DATATYPE_COLOR + | RADEON_ROP3_S + | RADEON_DP_SRC_SOURCE_MEMORY; + + if( offset > 0 ) + Radeon_SendCP( ai, buffer, offset ); + + ++ai->si->engine.count; +} + +// fill horizontal spans on screen +// et - ignored +// colorIndex - fill colour +// list - list of spans +// count - number of spans +void FILL_SPAN(engine_token *et, uint32 colorIndex, uint16 *list, uint32 count) +{ + virtual_card *vc = ai->vc; + int offset = 0; + uint32 buffer[PACKET_BUFFER_LEN]; + + SHOW_FLOW0( 4, "" ); + + for( ; count > 0; --count ) { + uint16 y, x, width; + + if( offset == 0 ) { + buffer[offset++] = RADEON_CP_PACKET3_CNTL_PAINT_MULTI; + buffer[offset++] = RADEON_GMC_BRUSH_SOLID_COLOR + | (vc->datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_SRC_DATATYPE_COLOR + | RADEON_ROP3_P; + buffer[offset++] = colorIndex; + } + + y = *list++; + x = *list++; + width = *list++ - x + 1; + + buffer[offset++] = (x << 16) | y; + buffer[offset++] = (width << 16) | 1; + + if( offset + 2 > PACKET_BUFFER_LEN ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + + offset = 0; + } + } + + if( offset > 0 ) { + buffer[0] |= (offset - 2) << 16; + + Radeon_SendCP( ai, buffer, offset ); + } + + ++ai->si->engine.count; +} + + +// prepare 2D acceleration +void Radeon_Init2D( accelerator_info *ai, uint32 datatype ) +{ + SHOW_FLOW0( 3, "" ); + + // forget about 3D + OUTREG( ai->regs, RADEON_RB3D_CNTL, 0 ); + + //Radeon_ResetEngine( ai ); + + // no siccors + Radeon_WaitForFifo( ai, 1 ); + OUTREG( ai->regs, RADEON_DEFAULT_SC_BOTTOM_RIGHT, (RADEON_DEFAULT_SC_RIGHT_MAX + | RADEON_DEFAULT_SC_BOTTOM_MAX)); + + // setup general flags - perhaps this is not needed as all + // 2D commands contain this register + Radeon_WaitForFifo( ai, 1 ); + OUTREG( ai->regs, RADEON_DP_GUI_MASTER_CNTL, + (datatype << RADEON_GMC_DST_DATATYPE_SHIFT) + | RADEON_GMC_CLR_CMP_CNTL_DIS + + | RADEON_GMC_BRUSH_SOLID_COLOR + | RADEON_GMC_SRC_DATATYPE_COLOR + + | RADEON_ROP3_P + | RADEON_DP_SRC_SOURCE_MEMORY + | RADEON_GMC_WR_MSK_DIS ); + + + // most of this init is probably not nessacary + // as we neither draw lines nor use brushes + Radeon_WaitForFifo( ai, 7 ); + OUTREG( ai->regs, RADEON_DST_LINE_START, 0); + OUTREG( ai->regs, RADEON_DST_LINE_END, 0); + OUTREG( ai->regs, RADEON_DP_BRUSH_FRGD_CLR, 0xffffffff); + OUTREG( ai->regs, RADEON_DP_BRUSH_BKGD_CLR, 0x00000000); + OUTREG( ai->regs, RADEON_DP_SRC_FRGD_CLR, 0xffffffff); + OUTREG( ai->regs, RADEON_DP_SRC_BKGD_CLR, 0x00000000); + OUTREG( ai->regs, RADEON_DP_WRITE_MASK, 0xffffffff); + + Radeon_WaitForIdle( ai ); +} + +// switch to virtual card, i.e. setup all specific engine registers +void Radeon_ActivateVirtualCard( accelerator_info *ai ) +{ + virtual_card *vc = ai->vc; + uint32 buffer[3*2]; + uint32 pitch_offset; + int idx = 0; + + SHOW_FLOW0( 4, "" ); + + pitch_offset = (vc->fb_offset >> 10) | ((vc->pitch >> 6) << 22); + buffer[idx++] = CP_PACKET0( RADEON_DEFAULT_OFFSET, 0 ); + buffer[idx++] = pitch_offset; + buffer[idx++] = CP_PACKET0( RADEON_DST_PITCH_OFFSET, 0 ); + buffer[idx++] = pitch_offset; + buffer[idx++] = CP_PACKET0( RADEON_SRC_PITCH_OFFSET, 0 ); + buffer[idx++] = pitch_offset; + + Radeon_SendCP( ai, buffer, idx ); + + ai->si->active_vc = vc->id; +} diff --git a/src/add-ons/accelerants/radeon/CP.c b/src/add-ons/accelerants/radeon/CP.c new file mode 100644 index 0000000000..3207691adf --- /dev/null +++ b/src/add-ons/accelerants/radeon/CP.c @@ -0,0 +1,296 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Command Processor handling +*/ + +#include "radeon_accelerant.h" +#include "mmio.h" +#include "CPMicroCode.h" +#include "cp_regs.h" +#include "buscntrl_regs.h" +#include "utils.h" +#include + +#include "log_coll.h" +#include "log_enum.h" + +#include + +uint getAvailRingBuffer( accelerator_info *ai ); + + +// non-local memory is used as following: +// - 0x10000 dwords for ring buffer +// - 8 dwords for returned data (i.e. current read ptr) +// - 6 dwords for "scratch registers" +// +// usage of scratch registers: +// - reg 0 = reached engine.count +// +// the ring buffer stuff must be at a constant offset as +// clones cannot be informed if it were changed + + +// upload Micro-Code of CP +static void loadMicroEngineRAMData( accelerator_info *ai ) +{ + int i; + const uint32 (*microcode)[2]; + + SHOW_FLOW0( 3, "" ); + + switch( ai->si->asic ) { + case rt_r300: + case rt_r300_4p: + case rt_rv350: + case rt_rv360: + case rt_r350: + case rt_r360: + microcode = r300_cp_microcode; + break; + case rt_r200: + //case rt_rv250: + //case rt_m9: + microcode = r200_cp_microcode; + break; + default: + microcode = radeon_cp_microcode; + } + + Radeon_WaitForIdle( ai ); + + OUTREG( ai->regs, RADEON_CP_ME_RAM_ADDR, 0 ); + + for ( i = 0 ; i < 256 ; i++ ) { + OUTREG( ai->regs, RADEON_CP_ME_RAM_DATAH, microcode[i][1] ); + OUTREG( ai->regs, RADEON_CP_ME_RAM_DATAL, microcode[i][0] ); + } +} + +// convert CPU's to graphics card's virtual address +#define CPU2GC( addr ) (((uint32)(addr) - (uint32)si->nonlocal_mem) + si->nonlocal_vm_start) + +// initialize bus mastering +static status_t setupCPRegisters( accelerator_info *ai, int aring_size ) +{ + vuint8 *regs = ai->regs; + shared_info *si = ai->si; + uint32 tmp; + +#if 0 + { + // allocate ring buffer etc. from local memory instead of PCI memory + radeon_alloc_local_mem am; + + am.magic = RADEON_PRIVATE_DATA_MAGIC; + am.size = (aring_size + 14) * 4; + + if( ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ) != B_OK ) + SHOW_ERROR0( 0, "Cannot allocate ring buffer from local memory" ); + else { + si->nonlocal_vm_start = am.fb_offset; + si->nonlocal_mem = (uint32 *)(si->framebuffer + am.fb_offset); + } + } +#endif + + memset( &si->ring, 0, sizeof( si->ring )); + + // set write pointer delay to zero; + // we assume that memory synchronization is done correctly my MoBo + // and Radeon_SendCP contains a hack that hopefully fixes such problems + OUTREG( regs, RADEON_CP_RB_WPTR_DELAY, 0 ); + + // setup CP buffer + si->ring.start = si->nonlocal_mem; + si->ring.size = aring_size; + OUTREG( regs, RADEON_CP_RB_BASE, CPU2GC( si->ring.start )); + SHOW_INFO( 3, "CP buffer address=%lx", CPU2GC( si->ring.start )); + + // setup CP read pointer buffer + si->ring.head = si->ring.start + si->ring.size; + OUTREG( regs, RADEON_CP_RB_RPTR_ADDR, CPU2GC( si->ring.head )); + SHOW_INFO( 3, "CP read pointer buffer==%lx", CPU2GC( si->ring.head )); + + // set ring buffer size + // (it's log2 of qwords) + OUTREG( regs, RADEON_CP_RB_CNTL, log2( si->ring.size / 2 )); + SHOW_INFO( 3, "CP buffer size mask=%ld", log2( si->ring.size / 2 ) ); + + // set CP buffer pointers + OUTREG( regs, RADEON_CP_RB_RPTR, 0 ); + OUTREG( regs, RADEON_CP_RB_WPTR, 0 ); + *si->ring.head = 0; + si->ring.tail = 0; + + // setup scratch register buffer + si->scratch_ptr = si->ring.head + RADEON_SCRATCH_REG_OFFSET / sizeof( uint32 ); + OUTREG( regs, RADEON_SCRATCH_ADDR, CPU2GC( si->scratch_ptr )); + OUTREG( regs, RADEON_SCRATCH_UMSK, 0x3f ); + + Radeon_WaitForIdle( ai ); + + // enable bus mastering +#if 1 + tmp = INREG( ai->regs, RADEON_BUS_CNTL ) & ~RADEON_BUS_MASTER_DIS; + OUTREG( regs, RADEON_BUS_CNTL, tmp ); +#endif + + // sync units + OUTREG( regs, RADEON_ISYNC_CNTL, + (RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI) ); + + return B_OK; +} + + +// get number of free entries in CP's ring buffer +uint getAvailRingBuffer( accelerator_info *ai ) +{ + shared_info *si = ai->si; + int space; + +// space = *si->ring.head - si->ring.tail; + space = INREG( ai->regs, RADEON_CP_RB_RPTR ) - si->ring.tail; + + if( space <= 0 ) + space += si->ring.size; + + // don't fill up the entire buffer as we cannot + // distinguish between a full and an empty ring + --space; + + SHOW_FLOW( 4, "head=%ld, tail=%ld, space=%ld", *si->ring.head, si->ring.tail, space ); + + LOG1( si->log, _GetAvailRingBufferQueue, space ); + + return space; +} + +// initialize CP so it's ready for BM +status_t Radeon_InitCP( accelerator_info *ai ) +{ +// shared_info *si = ai->si; + status_t result; + + SHOW_FLOW0( 3, "" ); + + // init raw CP + loadMicroEngineRAMData( ai ); + + // do soft-reset + Radeon_ResetEngine( ai ); + + // after warm-reset, the CP may still be active and thus react to + // register writes during initialization unpredictably, so we better + // stop it first + OUTREG( ai->regs, RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS ); + INREG( ai->regs, RADEON_CP_CSQ_CNTL ); + + // reset CP to make disabling active + Radeon_ResetEngine( ai ); + + // setup CP memory ranges + result = setupCPRegisters( ai, 0x10000 ); + if( result < 0 ) + return result; + + // tell CP to use BM + Radeon_WaitForIdle( ai ); + OUTREG( ai->regs, RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIBM_INDBM ); + + // this may be a bit too much + Radeon_SendPurgeCache( ai ); + Radeon_SendWaitUntilIdle( ai ); + + return B_OK; +} + + +// write to register via CP +void Radeon_WriteRegCP( accelerator_info *ai, uint32 reg, uint32 value ) +{ + uint32 buffer[2]; + + SHOW_FLOW0( 4, "" ); + + LOG2( ai->si->log, _Radeon_WriteRegFifo, reg, value ); + + buffer[0] = CP_PACKET0( reg, 0 ); + buffer[1] = value; + + Radeon_SendCP( ai, buffer, 2 ); +} + + +// send packets to CP +void Radeon_SendCP( accelerator_info *ai, uint32 *buffer, uint32 num_dwords ) +{ + shared_info *si = ai->si; + + SHOW_FLOW( 4, "num_dwords=%d", num_dwords ); + + while( num_dwords > 0 ) { + uint32 space; + uint32 max_copy; +// uint i; + + space = getAvailRingBuffer( ai ); + + if( space == 0 ) + continue; + + max_copy = min( space, num_dwords ); + +#ifdef ENABLE_LOGGING + for( i = 0; i < max_copy; ++i ) + LOG1( si->log, _Radeon_SendCP, buffer[i] ); +#endif + + if( si->ring.tail + max_copy >= si->ring.size ) { + uint32 sub_len; + + sub_len = si->ring.size - si->ring.tail; + memcpy( si->ring.start + si->ring.tail, buffer, sub_len * sizeof( uint32 )); + buffer += sub_len; + num_dwords -= sub_len; + max_copy -= sub_len; + si->ring.tail = 0; + } + + memcpy( si->ring.start + si->ring.tail, buffer, max_copy * sizeof( uint32 ) ); + buffer += max_copy; + num_dwords -= max_copy; + if( si->ring.tail + max_copy < si->ring.size ) + si->ring.tail += max_copy; + else + si->ring.tail = 0; + } + + // some chipsets have problems with write buffers; effectively, the command + // list we've just created gets delayed in some queue and the graphics chip + // reads out-dated commands, which don't make sense and thus crash the + // graphics card + + // flush writes to ring + // (this code is a bit of a overkill - currently, only some WinChip/Cyrix + // CPU's support out-of-order writes, but we are prepared) + __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory"); + // make sure the chipset has flushed its write buffer by + // reading some uncached memory + (void)*si->ring.head; + + // now, the command list should really be written to memory, + // so it's safe to instruct the graphics card to read it + OUTREG( ai->regs, RADEON_CP_RB_WPTR, si->ring.tail ); + + // read from PCI bus to ensure correct posting + INREG( ai->regs, RADEON_CP_RB_RPTR ); +} diff --git a/src/add-ons/accelerants/radeon/CPMicroCode.h b/src/add-ons/accelerants/radeon/CPMicroCode.h new file mode 100644 index 0000000000..f486ea096b --- /dev/null +++ b/src/add-ons/accelerants/radeon/CPMicroCode.h @@ -0,0 +1,786 @@ +#ifndef _CPMICROCODE_H +#define _CPMICROCODE_H + +// CP microcode (from ATI) +// if you take a look at the hex-dump +// you find some hidden message ;) +static const uint32 radeon_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000b4, 0x00000004 }, + { 0x000000b8, 0x00000004 }, + { 0x6f5b4d4c, 0000000000 }, + { 0x4c4c427f, 0000000000 }, + { 0x5b568a92, 0000000000 }, + { 0x4ca09c6d, 0000000000 }, + { 0xad4c4c4c, 0000000000 }, + { 0x4ce1af3d, 0000000000 }, + { 0xd8afafaf, 0000000000 }, + { 0xd64c4cdc, 0000000000 }, + { 0x4cd10d10, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x362f242d, 0000000000 }, + { 0x00000012, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x362f282d, 0000000000 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x000f0001, 0x00000016 }, + { 0x333a3730, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000021, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000017, 0x00000004 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000017, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x0003802b, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802c, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x00000030, 0x00000018 }, + { 0x00000030, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c0603e, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x00000049, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011055, 0x00000014 }, + { 0x00000055, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000504f, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000058, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0e4, 0x00000002 }, + { 0x01c110e4, 0x00000002 }, + { 0x26667066, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x00000066, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x0000005d, 0x00000004 }, + { 0x00401069, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x0080006c, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x0000008f, 0x00000018 }, + { 0x0000005b, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x0060007e, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x20007073, 0x00000004 }, + { 0x00005073, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00600083, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x00000083, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x00600086, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000095, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x00000097, 0x00000004 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x00600096, 0x00000004 }, + { 0x400070e5, 0000000000 }, + { 0x000380e6, 0x00000002 }, + { 0x040025c5, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000a8, 0x0000001c }, + { 0x000650aa, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00007832, 0x00000002 }, + { 0x00005000, 0x00000002 }, + { 0x000380e7, 0x00000002 }, + { 0x04002c97, 0x00000002 }, + { 0x00007820, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380e5, 0x00000002 }, + { 0x000000c6, 0x0000001c }, + { 0x000610ab, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x000610aa, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x000610ab, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000c9, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000cd, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000cf, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0000000000, 0000000000 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x6464614d, 0000000000 }, + { 0x69687420, 0000000000 }, + { 0x00000073, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x000380d0, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380e0, 0x00000002 }, + { 0x04002394, 0x00000002 }, + { 0x00005000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0x00000008, 0000000000 }, + { 0x00000004, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +// special r200 microcode +static const uint32 r200_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000ab, 0x00000004 }, + { 0x000000af, 0x00000004 }, + { 0x66544a49, 0000000000 }, + { 0x49494174, 0000000000 }, + { 0x54517d83, 0000000000 }, + { 0x498d8b64, 0000000000 }, + { 0x49494949, 0000000000 }, + { 0x49da493c, 0000000000 }, + { 0x49989898, 0000000000 }, + { 0xd34949d5, 0000000000 }, + { 0x9dc90e11, 0000000000 }, + { 0xce9b9b9b, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x352e232c, 0000000000 }, + { 0x00000013, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x352e272c, 0000000000 }, + { 0x000f0001, 0x00000016 }, + { 0x3239362f, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000016, 0x00000004 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000016, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802b, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002e, 0x00000018 }, + { 0x0000002e, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c0603d, 0x00000004 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x00000046, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011055, 0x00000014 }, + { 0x00000055, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000504a, 0x00000004 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000051, 0x00000004 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0dc, 0x00000002 }, + { 0x01c110dc, 0x00000002 }, + { 0x2666705d, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x0000005d, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000054, 0x00000004 }, + { 0x00401060, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x00800063, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x00000080, 0x00000018 }, + { 0x00000054, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x00600073, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x20007068, 0x00000004 }, + { 0x00005068, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00600076, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x00000076, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x00600079, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000084, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x00000086, 0x00000004 }, + { 0x00600085, 0x00000004 }, + { 0x400070dd, 0000000000 }, + { 0x000380dd, 0x00000002 }, + { 0x00000093, 0x0000001c }, + { 0x00065095, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x00061096, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01665000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x000671cc, 0x00000002 }, + { 0x0286f1cd, 0x00000002 }, + { 0x000000a3, 0x00000010 }, + { 0x21007000, 0000000000 }, + { 0x000000aa, 0x0000001c }, + { 0x00065000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x000b0000, 0x00000002 }, + { 0x38067000, 0x00000002 }, + { 0x000a00a6, 0x00000004 }, + { 0x20007000, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380dd, 0x00000002 }, + { 0x000000bd, 0x0000001c }, + { 0x00061096, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x00061095, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x00061096, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000c0, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000c4, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000c6, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0x000025bb, 0x00000002 }, + { 0x000040c0, 0x00000004 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x00007999, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460299b, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x00038042, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380d9, 0x00000002 }, + { 0x04007394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +// r300 microcode +static const uint32 r300_cp_microcode[][2] = { + { 0x4200e000, 0000000000 }, + { 0x4000e000, 0000000000 }, + { 0x000000af, 0x00000008 }, + { 0x000000b3, 0x00000008 }, + { 0x6c5a504f, 0000000000 }, + { 0x4f4f497a, 0000000000 }, + { 0x5a578288, 0000000000 }, + { 0x4f91906a, 0000000000 }, + { 0x4f4f4f4f, 0000000000 }, + { 0x4fe24f44, 0000000000 }, + { 0x4f9c9c9c, 0000000000 }, + { 0xdc4f4fde, 0000000000 }, + { 0xa1cd4f4f, 0000000000 }, + { 0xd29d9d9d, 0000000000 }, + { 0x4f0f9fd7, 0000000000 }, + { 0x000ca000, 0x00000004 }, + { 0x000d0012, 0x00000038 }, + { 0x0000e8b4, 0x00000004 }, + { 0x000d0014, 0x00000038 }, + { 0x0000e8b6, 0x00000004 }, + { 0x000d0016, 0x00000038 }, + { 0x0000e854, 0x00000004 }, + { 0x000d0018, 0x00000038 }, + { 0x0000e855, 0x00000004 }, + { 0x000d001a, 0x00000038 }, + { 0x0000e856, 0x00000004 }, + { 0x000d001c, 0x00000038 }, + { 0x0000e857, 0x00000004 }, + { 0x000d001e, 0x00000038 }, + { 0x0000e824, 0x00000004 }, + { 0x000d0020, 0x00000038 }, + { 0x0000e825, 0x00000004 }, + { 0x000d0022, 0x00000038 }, + { 0x0000e830, 0x00000004 }, + { 0x000d0024, 0x00000038 }, + { 0x0000f0c0, 0x00000004 }, + { 0x000d0026, 0x00000038 }, + { 0x0000f0c1, 0x00000004 }, + { 0x000d0028, 0x00000038 }, + { 0x0000f041, 0x00000004 }, + { 0x000d002a, 0x00000038 }, + { 0x0000f184, 0x00000004 }, + { 0x000d002c, 0x00000038 }, + { 0x0000f185, 0x00000004 }, + { 0x000d002e, 0x00000038 }, + { 0x0000f186, 0x00000004 }, + { 0x000d0030, 0x00000038 }, + { 0x0000f187, 0x00000004 }, + { 0x000d0032, 0x00000038 }, + { 0x0000f180, 0x00000004 }, + { 0x000d0034, 0x00000038 }, + { 0x0000f393, 0x00000004 }, + { 0x000d0036, 0x00000038 }, + { 0x0000f38a, 0x00000004 }, + { 0x000d0038, 0x00000038 }, + { 0x0000f38e, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00000043, 0x00000018 }, + { 0x00cce800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x001b0001, 0x00000004 }, + { 0x08004800, 0x00000004 }, + { 0x0000003a, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x02c0a000, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x00130000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0xc980c045, 0x00000008 }, + { 0x2000451d, 0x00000004 }, + { 0x0000e580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x08004580, 0x00000004 }, + { 0x000ce581, 0x00000004 }, + { 0x0000004c, 0x00000008 }, + { 0x0000a000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x00032000, 0x00000004 }, + { 0x00022056, 0x00000028 }, + { 0x00000056, 0x00000024 }, + { 0x0800450f, 0x00000004 }, + { 0x0000a050, 0x00000008 }, + { 0x0000e565, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x00000057, 0x00000008 }, + { 0x03cca5b4, 0x00000004 }, + { 0x05432000, 0x00000004 }, + { 0x00022000, 0x00000004 }, + { 0x4ccce063, 0x00000030 }, + { 0x08274565, 0x00000004 }, + { 0x00000063, 0x00000030 }, + { 0x08004564, 0x00000004 }, + { 0x0000e566, 0x00000004 }, + { 0x0000005a, 0x00000008 }, + { 0x00802066, 0x00000010 }, + { 0x00202000, 0x00000004 }, + { 0x001b00ff, 0x00000004 }, + { 0x01000069, 0x00000010 }, + { 0x001f2000, 0x00000004 }, + { 0x001c00ff, 0x00000004 }, + { 0000000000, 0x0000000c }, + { 0x00000085, 0x00000030 }, + { 0x0000005a, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x000ca000, 0x00000004 }, + { 0x00012000, 0x00000004 }, + { 0x00082000, 0x00000004 }, + { 0x1800650e, 0x00000004 }, + { 0x00092000, 0x00000004 }, + { 0x000a2000, 0x00000004 }, + { 0x000f0000, 0x00000004 }, + { 0x00400000, 0x00000004 }, + { 0x00000079, 0x00000018 }, + { 0x0000e563, 0x00000004 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x0000006e, 0x00000008 }, + { 0x0000a06e, 0x00000008 }, + { 0x0000e576, 0x00000004 }, + { 0x0000e577, 0x00000004 }, + { 0x0000e50e, 0x00000004 }, + { 0x0000e50f, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000007c, 0x00000018 }, + { 0x00c0e5f9, 0x000000c2 }, + { 0x0000007c, 0x00000008 }, + { 0x0014e50e, 0x00000004 }, + { 0x0040e50f, 0x00000004 }, + { 0x00c0007f, 0x00000008 }, + { 0x0000e570, 0x00000004 }, + { 0x0000e571, 0x00000004 }, + { 0x0000e572, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x0000e568, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00000089, 0x00000018 }, + { 0x000b0000, 0x00000004 }, + { 0x18c0e562, 0x00000004 }, + { 0x0000008b, 0x00000008 }, + { 0x00c0008a, 0x00000008 }, + { 0x000700e4, 0x00000004 }, + { 0x00000097, 0x00000038 }, + { 0x000ca099, 0x00000030 }, + { 0x080045bb, 0x00000004 }, + { 0x000c209a, 0x00000030 }, + { 0x0800e5bc, 0000000000 }, + { 0x0000e5bb, 0x00000004 }, + { 0x0000e5bc, 0000000000 }, + { 0x00120000, 0x0000000c }, + { 0x00120000, 0x00000004 }, + { 0x001b0002, 0x0000000c }, + { 0x0000a000, 0x00000004 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e800, 0000000000 }, + { 0x0000e821, 0x00000004 }, + { 0x0000e82e, 0000000000 }, + { 0x02cca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000ce1cc, 0x00000004 }, + { 0x050de1cd, 0x00000004 }, + { 0x000000a7, 0x00000020 }, + { 0x4200e000, 0000000000 }, + { 0x000000ae, 0x00000038 }, + { 0x000ca000, 0x00000004 }, + { 0x00140000, 0x00000004 }, + { 0x000c2000, 0x00000004 }, + { 0x00160000, 0x00000004 }, + { 0x700ce000, 0x00000004 }, + { 0x001400aa, 0x00000008 }, + { 0x4000e000, 0000000000 }, + { 0x02400000, 0x00000004 }, + { 0x400ee000, 0x00000004 }, + { 0x02400000, 0x00000004 }, + { 0x4000e000, 0000000000 }, + { 0x000c2000, 0x00000004 }, + { 0x0240e51b, 0x00000004 }, + { 0x0080e50a, 0x00000005 }, + { 0x0080e50b, 0x00000005 }, + { 0x00220000, 0x00000004 }, + { 0x000700e4, 0x00000004 }, + { 0x000000c1, 0x00000038 }, + { 0x000c209a, 0x00000030 }, + { 0x0880e5bd, 0x00000005 }, + { 0x000c2099, 0x00000030 }, + { 0x0800e5bb, 0x00000005 }, + { 0x000c209a, 0x00000030 }, + { 0x0880e5bc, 0x00000005 }, + { 0x000000c4, 0x00000008 }, + { 0x0080e5bd, 0x00000005 }, + { 0x0000e5bb, 0x00000005 }, + { 0x0080e5bc, 0x00000005 }, + { 0x00210000, 0x00000004 }, + { 0x02800000, 0x00000004 }, + { 0x00c000c8, 0x00000018 }, + { 0x4180e000, 0x00000040 }, + { 0x000000ca, 0x00000024 }, + { 0x01000000, 0x0000000c }, + { 0x0100e51d, 0x0000000c }, + { 0x000045bb, 0x00000004 }, + { 0x000080c4, 0x00000008 }, + { 0x0000f3ce, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053cf, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f3d2, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c053d3, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x0000f39d, 0x00000004 }, + { 0x0140a000, 0x00000004 }, + { 0x00cc2000, 0x00000004 }, + { 0x08c0539e, 0x00000040 }, + { 0x00008000, 0000000000 }, + { 0x03c00830, 0x00000004 }, + { 0x4200e000, 0000000000 }, + { 0x0000a000, 0x00000004 }, + { 0x200045e0, 0x00000004 }, + { 0x0000e5e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000700e1, 0x00000004 }, + { 0x0800e394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + +#endif diff --git a/src/add-ons/accelerants/radeon/Cursor.c b/src/add-ons/accelerants/radeon/Cursor.c new file mode 100644 index 0000000000..dad8d093a5 --- /dev/null +++ b/src/add-ons/accelerants/radeon/Cursor.c @@ -0,0 +1,250 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Hardware cursor support +*/ + + +#include "radeon_accelerant.h" +#include "GlobalData.h" +#include "generic.h" +#include "mmio.h" +#include "crtc_regs.h" + +static void doShowCursor( accelerator_info *ai, virtual_port *port ); +static void moveOneCursor( accelerator_info *ai, virtual_port *port, int x, int y ); + +// set standard foreground/background colours +void Radeon_SetCursorColors( accelerator_info *ai, virtual_port *port ) +{ + SHOW_FLOW0( 3, "" ); + + if( port->is_crtc2 ) { + Radeon_WriteRegCP( ai, RADEON_CUR2_CLR0, 0xffffff ); + Radeon_WriteRegCP( ai, RADEON_CUR2_CLR1, 0 ); + } else { + Radeon_WriteRegCP( ai, RADEON_CUR_CLR0, 0xffffff ); + Radeon_WriteRegCP( ai, RADEON_CUR_CLR1, 0 ); + } +} + +// public function to set shape of cursor +status_t SET_CURSOR_SHAPE( uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, + uint8 *andMask, uint8 *xorMask) +{ + virtual_card *vc = ai->vc; + uint8 *fb_cursor = vc->cursor.data; + int row, col_byte; + + /* NOTE: Currently, for BeOS, cursor width and height must be equal to 16. */ +/* if( width != 16 || height != 16 ) + return B_ERROR;*/ + + if( hot_x >= width || hot_y >= height ) + return B_ERROR; + + // TBD: should we sync here? I'd say so, but if I fail, we deadlock + + vc->cursor.hot_x = hot_x; + vc->cursor.hot_y = hot_y; + + for( row = 0; row < 64; ++row ) { + for( col_byte = 0; col_byte < 64 / 8; ++col_byte ) { + if( row < height && col_byte < (width + 7) / 8 ) { + fb_cursor[row * 64/8 * 2 + col_byte] = *andMask++; + fb_cursor[row * 64/8 * 2 + col_byte + 64/8] = *xorMask++; + } else { + fb_cursor[row * 64/8 * 2 + col_byte] = 0xff; + fb_cursor[row * 64/8 * 2 + col_byte + 64/8] = 0; + } + } + } + + return B_OK; +} + + +// public function to move cursor +void MOVE_CURSOR(uint16 x, uint16 y) +{ + virtual_card *vc = ai->vc; + bool move_screen = false; + uint16 hds, vds; +// int xorigin, yorigin, x1, y1; + + // alignment mask for horizontal position + uint16 h_adjust = 7; + + ACQUIRE_BEN( ai->si->engine.lock ); + + hds = vc->mode.h_display_start; + vds = vc->mode.v_display_start; + + // clamp cursor (negative positions are impossible due to uint16) + if (x >= vc->mode.virtual_width) + x = vc->mode.virtual_width - 1; + if (y >= vc->mode.virtual_height) + y = vc->mode.virtual_height - 1; + + // if scrolling enabled, i.e. we have a larger virtual screen, + // pan display accordingly + if( vc->scroll ) { + if( x >= (vc->mode.timing.h_display + hds) ) { + hds = ((x - vc->mode.timing.h_display) + 1 + h_adjust) & ~h_adjust; + move_screen = true; + } else if( x < hds ) { + hds = x & ~h_adjust; + move_screen = true; + } + if( y >= (vc->mode.timing.v_display + vds) ) { + vds = y - vc->mode.timing.v_display + 1; + move_screen = true; + } else if( y < vds ) { + vds = y; + move_screen = true; + } + + if( move_screen ) + Radeon_MoveDisplay( ai, hds, vds ); + } + + // adjust according to virtual screen position + x -= hds; + y -= vds; + + // go + moveOneCursor( ai, &vc->ports[0], x, y ); + if( vc->independant_ports > 1 ) + moveOneCursor( ai, &vc->ports[1], x, y ); + + RELEASE_BEN( ai->si->engine.lock ); +} + + +// public function to show cursor +void SHOW_CURSOR( bool is_visible ) +{ + virtual_card *vc = ai->vc; + + SHOW_FLOW0( 4, "" ); + +// ACQUIRE_BEN( si->engine.lock ); + + // this is the public statement + vc->cursor.is_visible = is_visible; + + // the following functions take also care to not + // show the cursor if it's on the other port + doShowCursor( ai, &vc->ports[0] ); + if( vc->independant_ports > 1 ) + doShowCursor( ai, &vc->ports[1] ); + +// RELEASE_BEN( si->engine.lock ); +} + + +// move cursor on one port +// main_port - common data is stored here +void moveOneCursor( accelerator_info *ai, virtual_port *port, int x, int y ) +{ + virtual_card *vc = ai->vc; + int xorigin, yorigin; + bool prev_state; + + // adjust according to relative screen position + x -= port->rel_x; + y -= port->rel_y; + + // and to hot spot + x -= vc->cursor.hot_x; + y -= vc->cursor.hot_y; + + // check whether the cursor is (partially) visible on this screen + prev_state = port->cursor_on_screen; + port->cursor_on_screen = true; + + // in theory, cursor can be up to 64 pixels off screen, + // but there were display errors + if( y > port->mode.timing.v_display || + x > port->mode.timing.h_display || + x <= -16 || y <= -16 ) + { + port->cursor_on_screen = false; + } + + if( prev_state != port->cursor_on_screen ) + doShowCursor( ai, port ); + + if( !port->cursor_on_screen ) + return; + + // if upper-left corner of cursor is outside of + // screen, we have to use special registers to clip it + xorigin = 0; + yorigin = 0; + + if( x < 0 ) + xorigin = -x; + + if( y < 0 ) + yorigin = -y; + + Radeon_WaitForFifo( ai, 3 ); + + if( port->is_crtc2 ) { + OUTREG( ai->regs, RADEON_CUR2_HORZ_VERT_OFF, RADEON_CUR2_LOCK + | (xorigin << 16) + | yorigin ); + OUTREG( ai->regs, RADEON_CUR2_HORZ_VERT_POSN, RADEON_CUR2_LOCK + | ((xorigin ? 0 : x) << 16) + | (yorigin ? 0 : y) ); + OUTREG( ai->regs, RADEON_CUR2_OFFSET, + vc->cursor.fb_offset + xorigin + yorigin * 16 ); + + } else { + OUTREG( ai->regs, RADEON_CUR_HORZ_VERT_OFF, RADEON_CUR_LOCK + | (xorigin << 16) + | yorigin ); + OUTREG( ai->regs, RADEON_CUR_HORZ_VERT_POSN, RADEON_CUR_LOCK + | ((xorigin ? 0 : x) << 16) + | (yorigin ? 0 : y) ); + OUTREG( ai->regs, RADEON_CUR_OFFSET, + vc->cursor.fb_offset + xorigin + yorigin * 16 ); + } +} + + +// show cursor on one port, depending on official whishes and whether +// cursor is located on this subscreen +void doShowCursor( accelerator_info *ai, virtual_port *port ) +{ + virtual_card *vc = ai->vc; + uint32 tmp; + + if( port->is_crtc2 ) { + tmp = INREG( ai->regs, RADEON_CRTC2_GEN_CNTL ); + + if( vc->cursor.is_visible && port->cursor_on_screen ) + tmp |= RADEON_CRTC2_CUR_EN; + else + tmp &= ~RADEON_CRTC2_CUR_EN; + + Radeon_WaitForFifo( ai, 1 ); + OUTREG( ai->regs, RADEON_CRTC2_GEN_CNTL, tmp ); + + } else { + tmp = INREG( ai->regs, RADEON_CRTC_GEN_CNTL ); + + if( vc->cursor.is_visible && port->cursor_on_screen ) { + tmp |= RADEON_CRTC_CUR_EN; + } else { + tmp &= ~RADEON_CRTC_CUR_EN; + } + + Radeon_WaitForFifo( ai, 1 ); + OUTREG( ai->regs, RADEON_CRTC_GEN_CNTL, tmp ); + } +} diff --git a/src/add-ons/accelerants/radeon/EngineManagment.c b/src/add-ons/accelerants/radeon/EngineManagment.c new file mode 100644 index 0000000000..53cd994c42 --- /dev/null +++ b/src/add-ons/accelerants/radeon/EngineManagment.c @@ -0,0 +1,217 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Hardware accelerator management + + All accelerator commands go through the following steps: + - accelerant adds command to CP buffer and updates CP write pointer + - CP fetches command and sends it to MicroController + - MicroController instructs 2D unit to execute command + - 2D unit draws into 2D Destination Cache (DC) + - 2D Destination Cache is drained to frame buffer + + Whenever a token is required by BeOS, a command is queued to write + the timestamp into Scratch Register 0. I haven't fully understand + when and how coherancy is assured by Radeon, so I assume the following: + - when the timestamp is written, all previous commands have been issued, + i.e. they are read and executed by the microcontroller + - to make sure previously issued 2D commands have been finished, + a WAIT_2D_IDLECLEAN command is inserted before the scratch register + write + - to flush the destination cache, a RB2D_DC_FLUSH_ALL command is + issued before the wait; I hope that the wait command also waits for + the flush command, but I'm not sure about that + + Remains the cache coherency problem. It you can set various bits in + DSTCACHE_MODE register to assure that, but first I don't really understand + them, and second I'm not sure which other caches/FIFO may make trouble. + Especially, Be wants to use CPU and CP accesses in parallel. Hopefully, + they don't interfere. + + I know that the PAINT_MULTI commands makes trouble if you change the + ROP to something else: CPU writes produce garbage in frame buffer for the + next couple of accesses. Resetting the ROP to a simply copy helps, but + I'm not sure what happens with concurrent CPU accesses to other areas + of the frame buffer. +*/ + + +#include "radeon_accelerant.h" +#include "generic.h" +#include "cp_regs.h" +#include "rbbm_regs.h" +#include "GlobalData.h" +#include "mmio.h" + +static engine_token radeon_engine_token = { 1, B_2D_ACCELERATION, NULL }; + +// public function: return number of hardware engine +uint32 ACCELERANT_ENGINE_COUNT(void) +{ + // hm, is there *any* card sporting more then + // one hardware accelerator??? + return 1; +} + +// write current token into CP stream +static void writeSyncToken( accelerator_info *ai ) +{ + uint32 buffer[6]; + uint idx = 0; + + // don't write token if it hasn't changed since last write + if( ai->si->engine.count == ai->si->engine.written ) + return; + + // flush pending data + buffer[idx++] = CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ); + buffer[idx++] = RADEON_RB2D_DC_FLUSH_ALL; + + // make sure commands are finished + buffer[idx++] = CP_PACKET0( RADEON_WAIT_UNTIL, 0 ); + buffer[idx++] = RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_3D_IDLECLEAN | RADEON_WAIT_HOST_IDLECLEAN; + + // write scratch register + buffer[idx++] = CP_PACKET0( RADEON_SCRATCH_REG0, 0 ); + buffer[idx++] = ai->si->engine.count; + + ai->si->engine.written = ai->si->engine.count; + + Radeon_SendCP( ai, buffer, idx ); +} + +// public function: acquire engine for future use +// capabilites - required 2D/3D capabilities of engine, ignored +// max_wait - maximum time we want to wait (in ms?), ignored +// st - when engine has been acquired, wait for this sync token +// et - (out) specifier of the engine acquired +status_t ACQUIRE_ENGINE( uint32 capabilities, uint32 max_wait, + sync_token *st, engine_token **et ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + + SHOW_FLOW0( 4, "" ); + + ACQUIRE_BEN( si->engine.lock) + + if( si->active_vc != vc->id ) + Radeon_ActivateVirtualCard( ai ); + + // wait for sync + if (st) + SYNC_TO_TOKEN( st ); + + *et = &radeon_engine_token; + return B_OK; +} + +// public function: release accelerator +// et - engine to release +// st - (out) sync token to be filled out +status_t RELEASE_ENGINE( engine_token *et, sync_token *st ) +{ + shared_info *si = ai->si; + + SHOW_FLOW0( 4, "" ); + + // fill out sync token + if (st) { + writeSyncToken( ai ); + + st->engine_id = et->engine_id; + st->counter = si->engine.count; + } + + RELEASE_BEN( ai->si->engine.lock ) + + return B_OK; +} + +// public function: wait until engine is idle +// ??? which engine to wait for? Is there anyone using this function? +void WAIT_ENGINE_IDLE(void) +{ + SHOW_FLOW0( 4, "" ); + + Radeon_Finish( ai ); +} + +// public function: get sync token +// et - engine to wait for +// st - (out) sync token to be filled out +status_t GET_SYNC_TOKEN( engine_token *et, sync_token *st ) +{ + shared_info *si = ai->si; + + SHOW_FLOW0( 4, "" ); + + writeSyncToken( ai ); + + st->engine_id = et->engine_id; + st->counter = si->engine.count; + + SHOW_FLOW( 4, "got counter=%d", si->engine.count ); + + return B_OK; +} + +// this is the same as the corresponding kernel function +static void spin( uint32 delay ) +{ + bigtime_t start_time; + + start_time = system_time(); + + while( system_time() - start_time < delay ) + ; +} + +// public: sync to token +// st - token to wait for +status_t SYNC_TO_TOKEN( sync_token *st ) +{ + shared_info *si = ai->si; + bigtime_t start_time, sample_time; +// status_t result; + + SHOW_FLOW0( 4, "" ); + + start_time = system_time(); + + while( 1 ) { + SHOW_FLOW( 4, "passed counter=%d", *si->scratch_ptr ); + + // a bit nasty: counter is 64 bit, but we have 32 bit only, + // this is a tricky calculation to handle wrap-arounds correctly + /*if( (int32)(*si->scratch_ptr - st->counter) >= 0 ) + return B_OK;*/ + if( (int32)(INREG( ai->regs, RADEON_SCRATCH_REG0 ) - st->counter) >= 0 ) + return B_OK; + + sample_time = system_time(); + + if( sample_time - start_time > 100000 ) + break; + + // use exponential fall-off + // in the beginning do busy-waiting, later on we let thread sleep + // the micro-spin is used to reduce PCI load + if( sample_time - start_time > 5000 ) + snooze( (sample_time - start_time) / 10 ); + else + spin( 1 ); + } + + // we could reset engine now, but caller doesn't need to acquire + // engine before calling this function, so we either reset it + // without sync (ouch!) or acquire engine first and risk deadlocking + SHOW_ERROR( 0, "Failed waiting for token %d (active token: %d)", + st->counter, INREG( ai->regs, RADEON_SCRATCH_REG0 )/**si->scratch_ptr*/ ); + + return B_ERROR; +} diff --git a/src/add-ons/accelerants/radeon/GetAccelerantHook.c b/src/add-ons/accelerants/radeon/GetAccelerantHook.c new file mode 100644 index 0000000000..903eeea9c6 --- /dev/null +++ b/src/add-ons/accelerants/radeon/GetAccelerantHook.c @@ -0,0 +1,108 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Contains entry point to get public functions. + (directly copied from sample driver) +*/ + + +#include "generic.h" + +/* + +The standard entry point. Given a uint32 feature identifier, this routine +returns a pointer to the function that implements the feature. Some features +require more information than just the identifier to select the proper +function. The extra information (which is specific to the feature) is +pointed at by the void *data parameter. By default, no extra information +is available. Any extra information available to choose the function will be +noted on a case by case below. + +*/ +void * get_accelerant_hook(uint32 feature, void *data) { + switch (feature) { +/* +These definitions are out of pure lazyness. +*/ +#define HOOK(x) case B_##x: return (void *)x +#define ZERO(x) case B_##x: return (void *)0 + +/* +One of either B_INIT_ACCELERANT or B_CLONE_ACCELERANT will be requested and +subsequently called before any other hook is requested. All other feature +hook selections can be predicated on variables assigned during the accelerant +initialization process. +*/ + /* initialization */ + HOOK(INIT_ACCELERANT); + HOOK(CLONE_ACCELERANT); + + HOOK(ACCELERANT_CLONE_INFO_SIZE); + HOOK(GET_ACCELERANT_CLONE_INFO); + HOOK(UNINIT_ACCELERANT); + HOOK(GET_ACCELERANT_DEVICE_INFO); + HOOK(ACCELERANT_RETRACE_SEMAPHORE); + + /* mode configuration */ + HOOK(ACCELERANT_MODE_COUNT); + HOOK(GET_MODE_LIST); + HOOK(PROPOSE_DISPLAY_MODE); + HOOK(SET_DISPLAY_MODE); + HOOK(GET_DISPLAY_MODE); + HOOK(GET_FRAME_BUFFER_CONFIG); + HOOK(GET_PIXEL_CLOCK_LIMITS); + HOOK(MOVE_DISPLAY); + HOOK(SET_INDEXED_COLORS); + //HOOK(GET_TIMING_CONSTRAINTS); + + HOOK(DPMS_CAPABILITIES); + HOOK(DPMS_MODE); + HOOK(SET_DPMS_MODE); + + /* cursor managment */ + HOOK(SET_CURSOR_SHAPE); + HOOK(MOVE_CURSOR); + HOOK(SHOW_CURSOR); + + /* synchronization */ + HOOK(ACCELERANT_ENGINE_COUNT); + HOOK(ACQUIRE_ENGINE); + HOOK(RELEASE_ENGINE); + HOOK(WAIT_ENGINE_IDLE); + HOOK(GET_SYNC_TOKEN); + HOOK(SYNC_TO_TOKEN); + +/* +When requesting an acceleration hook, the calling application provides a +pointer to the display_mode for which the acceleration function will be used. +Depending on the engine architecture, you may choose to provide a different +function to be used with each bit-depth. In the sample driver we return +the same function all the time. +*/ + /* 2D acceleration */ + HOOK(SCREEN_TO_SCREEN_BLIT); + HOOK(FILL_RECTANGLE); + HOOK(INVERT_RECTANGLE); + HOOK(FILL_SPAN); + + // overlay + HOOK(OVERLAY_COUNT); + HOOK(OVERLAY_SUPPORTED_SPACES); + HOOK(OVERLAY_SUPPORTED_FEATURES); + HOOK(ALLOCATE_OVERLAY_BUFFER); + HOOK(RELEASE_OVERLAY_BUFFER); + HOOK(GET_OVERLAY_CONSTRAINTS); + HOOK(ALLOCATE_OVERLAY); + HOOK(RELEASE_OVERLAY); + HOOK(CONFIGURE_OVERLAY); +#undef HOOK +#undef ZERO + } +/* +Return a null pointer for any feature we don't understand. +*/ + return 0; +} diff --git a/src/add-ons/accelerants/radeon/GetModeInfo.c b/src/add-ons/accelerants/radeon/GetModeInfo.c new file mode 100644 index 0000000000..fe3e5aba96 --- /dev/null +++ b/src/add-ons/accelerants/radeon/GetModeInfo.c @@ -0,0 +1,90 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Public mode-specific info functions +*/ + + +#include "radeon_accelerant.h" +#include "GlobalData.h" +#include "generic.h" +#include +#include + + +// public function: return current display mode +status_t GET_DISPLAY_MODE( display_mode *current_mode ) +{ + virtual_card *vc = ai->vc; + + // TBD: there is a race condition if someone else is just setting it + // we won't lock up but return non-sense + + *current_mode = vc->mode; + + // we hide multi-monitor-mode because :- + // - we want to look like an ordinary single-screen driver + // - the multi-mode is already adapted to current screen configuration, + // and the mode should be configuration-independant + Radeon_HideMultiMode( vc, current_mode ); + + return B_OK; +} + +// public function: return configuration of frame buffer +status_t GET_FRAME_BUFFER_CONFIG( frame_buffer_config *afb ) +{ + virtual_card *vc = ai->vc; + + // TBD: race condition again + + // easy again, as the last mode set stored the info in a convienient form + *afb = vc->fbc; + return B_OK; +} + +// public function: return clock limits for given display mode +status_t GET_PIXEL_CLOCK_LIMITS(display_mode *dm, uint32 *low, uint32 *high) +{ + // we ignore stuff like DVI/LCD restrictions - + // they are handled automatically on set_display_mode + uint32 total_pix = (uint32)dm->timing.h_total * (uint32)dm->timing.v_total; + uint32 clock_limit = ai->si->pll.max_pll_freq * 10; + + /* lower limit of about 48Hz vertical refresh */ + *low = (total_pix * 48L) / 1000L; + if (*low > clock_limit) + return B_ERROR; + + *high = clock_limit; + return B_OK; +} + +/* + Return the semaphore id that will be used to signal a vertical retrace + occured. +*/ +sem_id ACCELERANT_RETRACE_SEMAPHORE(void) +{ +// virtual_card *vc = ai->vc; + + /* + NOTE: + The kernel driver created this for us. We don't know if the system is + using real interrupts, or if we're faking it, and we don't care. + If we choose not to support this at all, we'd just return B_ERROR here, + and the user wouldn't get any kind of vertical retrace support. + */ + // with multi-monitor mode, we have two vertical blanks! + // until we find a better solution, we always return virtual port 0, + // which may be either physical port 0 or 1 +// int physical_port = vc->ports[0].physical_port; + + //SHOW_INFO( 3, "semaphore: %x", ai->si->ports[physical_port].vblank ); + + //return ai->si->ports[physical_port].vblank; + return 0; +} diff --git a/src/add-ons/accelerants/radeon/GlobalData.c b/src/add-ons/accelerants/radeon/GlobalData.c new file mode 100644 index 0000000000..68edb82cb8 --- /dev/null +++ b/src/add-ons/accelerants/radeon/GlobalData.c @@ -0,0 +1,20 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Global data +*/ + + +#include "GlobalData.h" + +// the sample driver stores everything in global variables; +// I dislike this idea as this makes supporting multiple graphics +// card impossible; to be prepared, only the following variable is used +accelerator_info *ai; + +int debug_level_flow = 2; +int debug_level_info = 4; +int debug_level_error = 4; diff --git a/src/add-ons/accelerants/radeon/GlobalData.h b/src/add-ons/accelerants/radeon/GlobalData.h new file mode 100644 index 0000000000..b754b1b8a0 --- /dev/null +++ b/src/add-ons/accelerants/radeon/GlobalData.h @@ -0,0 +1,16 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(GLOBALDATA_H) +#define GLOBALDATA_H + +#ifndef _RADEON_ACCELERANT_H +#include "radeon_accelerant.h" +#endif + +extern accelerator_info *ai; + + +#endif diff --git a/src/add-ons/accelerants/radeon/InitAccelerant.c b/src/add-ons/accelerants/radeon/InitAccelerant.c new file mode 100644 index 0000000000..b92fdca32f --- /dev/null +++ b/src/add-ons/accelerants/radeon/InitAccelerant.c @@ -0,0 +1,261 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Main init/uninit functions +*/ + + +#include "GlobalData.h" +#include "generic.h" + +#include "string.h" +#include "unistd.h" +#include "sys/types.h" +#include "sys/stat.h" +#include "fcntl.h" +#include +#include + + +// init data used by both primary and cloned accelerant +// the_fd - file descriptor of kernel driver +// accelerant_is_clone - if true, this is a cloned accelerant +static status_t init_common( int the_fd, bool accelerant_is_clone ) +{ + status_t result; + radeon_get_private_data gpd; + + SHOW_FLOW0( 3, "" ); + + ai = malloc( sizeof( *ai )); + if( ai == NULL ) + return B_NO_MEMORY; + + memset( ai, 0, sizeof( *ai )); + + ai->accelerant_is_clone = accelerant_is_clone; + ai->fd = the_fd; + + // get basic info from driver + gpd.magic = RADEON_PRIVATE_DATA_MAGIC; + + result = ioctl( ai->fd, RADEON_GET_PRIVATE_DATA, &gpd, sizeof(gpd) ); + if (result != B_OK) goto err; + + ai->virtual_card_area = clone_area( "Radeon virtual card", (void **)&ai->vc, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, gpd.virtual_card_area ); + if( ai->virtual_card_area < 0 ) { + result = ai->virtual_card_area; + goto err; + } + ai->shared_info_area = clone_area("Radeon shared info", (void **)&ai->si, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, gpd.shared_info_area); + if( ai->shared_info_area < 0 ) { + result = ai->shared_info_area; + goto err2; + } + + ai->regs_area = clone_area("Radeon regs area", (void **)&ai->regs, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, ai->si->regs_area); + if( ai->regs_area < 0 ) { + result = ai->regs_area; + goto err3; + } + + return B_OK; + +err3: + delete_area( ai->shared_info_area ); +err2: + delete_area( ai->virtual_card_area ); +err: + free( ai ); + return result; +} + +// clean up data common to both primary and cloned accelerant +static void uninit_common( void ) +{ + delete_area( ai->regs_area ); + delete_area( ai->shared_info_area ); + delete_area( ai->virtual_card_area ); + + ai->regs_area = ai->shared_info_area = ai->virtual_card_area = 0; + + ai->regs = 0; + ai->si = 0; + ai->vc = 0; + + // close the file handle ONLY if we're the clone + // (this is what Be tells us ;) + if( ai->accelerant_is_clone ) + close( ai->fd ); + + free( ai ); +} + +// public function: init primary accelerant +// the_fd - file handle of kernel driver +status_t INIT_ACCELERANT( int the_fd ) +{ + shared_info *si; + virtual_card *vc; + status_t result; + + SHOW_FLOW0( 3, "" ); + + result = init_common( the_fd, 0 ); + if (result != B_OK) + goto err; + + si = ai->si; + vc = ai->vc; + + // init Command Processor + result = Radeon_InitCP( ai ); + if( result != B_OK ) + goto err2; + + // this isn't the best place, but has to be done sometime + Radeon_ReadSettings( vc ); + + // read FP info via DDC + // (ignore result - if it fails we fall back to BIOS detection) + if( si->fp_port.disp_type == dt_dvi_1 ) + Radeon_ReadFPEDID( ai, si ); + + // create list of supported modes + result = Radeon_CreateModeList( si ); + if (result != B_OK) + goto err3; + + /* init the shared semaphore */ + INIT_BEN( "Radeon engine", si->engine.lock ); + + // init engine sync token + // (count of issued parameters or commands) + si->engine.last_idle = si->engine.count = 0; + // set last written count to be very old, so it must be written on first use + // (see writeSyncToken) + si->engine.written = -1; + + // init overlay + si->overlay_mgr.token = 0; + si->overlay_mgr.inuse = 0; + + // mark overlay as inactive + si->active_overlay.port = -1; + si->pending_overlay.port = -1; + + // reset list of allocated overlays + vc->overlay_buffers = NULL; + + // everything else is initialized upon set_display_mode + return B_OK; + +err3: +err2: + uninit_common(); +err: + return result; +} + + +// public function: return size of clone info +ssize_t ACCELERANT_CLONE_INFO_SIZE( void ) +{ + // clone info is device name, so return its maximum size + return MAX_RADEON_DEVICE_NAME_LENGTH; +} + + +// public function: return clone info +// data - buffer to contain info (allocated by caller) +void GET_ACCELERANT_CLONE_INFO( void *data ) +{ + radeon_device_name dn; + status_t result; + + // clone info is device name - ask device driver + dn.magic = RADEON_PRIVATE_DATA_MAGIC; + dn.name = (char *)data; + + result = ioctl( ai->fd, RADEON_DEVICE_NAME, &dn, sizeof(dn) ); +} + +// public function: init cloned accelerant +// data - clone info from get_accelerant_clone_info +status_t CLONE_ACCELERANT( void *data ) +{ + status_t result; + char path[MAXPATHLEN]; + int fd; + + // create full device name + strcpy(path, "/dev"); + strcat(path, (const char *)data); + + // open device; according to Be, permissions aren't important + // this will probably change once access right are checked properly + fd = open(path, B_READ_WRITE); + if( fd < 0 ) + return fd; + + result = init_common( fd, 1 ); + if( result != B_OK ) + goto err1; + + // get (cloned) copy of supported display modes + result = ai->mode_list_area = clone_area( + "Radeon cloned display_modes", (void **)&ai->mode_list, + B_ANY_ADDRESS, B_READ_AREA, ai->si->mode_list_area ); + if (result < B_OK) + goto err2; + + return B_OK; + +err2: + uninit_common(); +err1: + close( fd ); + return result; +} + +// public function: uninit primary or cloned accelerant +void UNINIT_ACCELERANT( void ) +{ + // TBD: + // we should put accelerator into stable state first - + // on my Laptop, you never can boot Windows/Linux after shutting + // down BeOS; if both ports have been used, even the BIOS screen + // is completely messed up + + // cloned accelerants have mode_list cloned, so deleting is OK + // primary accelerant owns mode list, so deleting is OK as well + delete_area( ai->mode_list_area ); + ai->mode_list = 0; + + uninit_common(); +} + +// public function: get some info about graphics card +status_t GET_ACCELERANT_DEVICE_INFO( accelerant_device_info *di ) +{ + // is there anyone using it? + + // TBD: everything apart from memsize + di->version = B_ACCELERANT_VERSION; + strcpy( di->name, "Radeon" ); + strcpy( di->chipset, "Radeon" ); + strcpy( di->serial_no, "None" ); + + di->memory = ai->si->local_mem_size; + + // TBD: is max PLL speed really equal to max DAC speed? + di->dac_speed = ai->si->pll.max_pll_freq; + + return B_OK; +} diff --git a/src/add-ons/accelerants/radeon/Jamfile b/src/add-ons/accelerants/radeon/Jamfile new file mode 100644 index 0000000000..0a7ee155bc --- /dev/null +++ b/src/add-ons/accelerants/radeon/Jamfile @@ -0,0 +1,40 @@ +SubDir OBOS_TOP src add-ons accelerants radeon ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics radeon ] ; + + +Addon radeon.accelerant : accelerants : + Acceleration.c + CP.c + Cursor.c + EngineManagment.c + GetAccelerantHook.c + GetModeInfo.c + GlobalData.c + InitAccelerant.c + ProposeDisplayMode.c + SetDisplayMode.c + crtc.c + dpms.c + engine_sync.c + flat_panel.c + multimon.c + overlay.c + overlay_management.c + pll.c + settings.cpp + utils.c + log_coll.c + log_dump.c + ddc.c + dump_edid.c + edid.c + i2c.c +; + +Package openbeos-radeon-cvs : + radeon.accelerant : + boot home config add-ons accelerants ; + +Depends radeon.accelerant : radeon.driver ; diff --git a/src/add-ons/accelerants/radeon/ProposeDisplayMode.c b/src/add-ons/accelerants/radeon/ProposeDisplayMode.c new file mode 100644 index 0000000000..961c386c02 --- /dev/null +++ b/src/add-ons/accelerants/radeon/ProposeDisplayMode.c @@ -0,0 +1,628 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Everything concerning getting/testing display modes +*/ + +#include "radeon_accelerant.h" +#include "generic.h" +#include +#include +#include "GlobalData.h" + +#include "crtc_regs.h" +#include "utils.h" + +// standard mode list +// all drivers contain this list - this should really be moved to +// something like the screen preferences panel + +#define T_POSITIVE_SYNC (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC) +#define MODE_FLAGS (B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS) +//#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode)) + +static const display_mode base_mode_list[] = { +{ { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */ +{ { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */ +{ { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */ +{ { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */ +{ { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */ +{ { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */ +{ { 25175, 640, 656, 752, 800, 400, 412, 414, 449, B_POSITIVE_VSYNC}, B_CMAP8, 640, 400, 0, 0, MODE_FLAGS}, /* 640x400 - www.epanorama.net/documents/pc/vga_timing.html) */ +{ { 25175, 640, 656, 752, 800, 350, 387, 389, 449, B_POSITIVE_HSYNC}, B_CMAP8, 640, 350, 0, 0, MODE_FLAGS}, /* 640x350 - www.epanorama.net/documents/pc/vga_timing.html) */ +{ { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */ +{ { 40000, 800, 840, 968, 1056, 600, 601, 605, 628, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */ +{ { 49500, 800, 816, 896, 1056, 600, 601, 604, 625, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */ +{ { 50000, 800, 856, 976, 1040, 600, 637, 643, 666, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(800X600X8.Z1) */ +{ { 56250, 800, 832, 896, 1048, 600, 601, 604, 631, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */ +{ { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */ +{ { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */ +{ { 78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */ +{ { 94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */ +{ { 94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */ +{ { 108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */ +{ { 121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */ +{ { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */ +{ { 135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */ +{ { 157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */ +{ { 162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */ +{ { 175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */ +{ { 189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */ +{ { 202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */ +{ { 216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */ +{ { 229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS} /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */ +}; + +status_t Radeon_ProposeDisplayMode( shared_info *si, physical_port *port, + pll_info *pll, display_mode *target, + const display_mode *low, const display_mode *high ); +void Radeon_DisposeModeList( shared_info *si ); + + +// convert Be colour space in Radeon data type +// returns true, if supported colour space +// space - Be colour space +// format - (out) Radeon data type +// bpp - (out) bytes per pixel +bool Radeon_GetFormat( int space, int *format, int *bpp ) +{ + switch( space ) { + /*case 4: format = 1; bytpp = 0; break;*/ + case B_CMAP8: *format = 2; *bpp = 1; break; + case B_RGB15_LITTLE: *format = 3; *bpp = 2; break; /* 555 */ + case B_RGB16_LITTLE: *format = 4; *bpp = 2; break; /* 565 */ + case B_RGB24_LITTLE: *format = 5; *bpp = 3; break; /* RGB */ + case B_RGB32_LITTLE: *format = 6; *bpp = 4; break; /* xRGB */ + default: + SHOW_ERROR( 1, "Unsupported color space (%d)", space ); + return false; + } + + return true; +} + + +// macros to convert between register values and pixels +#define H_DISPLAY_2REG( a ) ((a) / 8 - 1) +#define H_DISPLAY_2PIX( a ) (((a) + 1) * 8) +#define H_TOTAL_2REG( a ) ((a) / 8 - 1) +#define H_TOTAL_2PIX( a ) (((a) + 1) * 8) +#define H_SSTART_2REG( a ) ((a) - 8 + h_sync_fudge) +#define H_SSTART_2PIX( a ) ((a) + 8 - h_sync_fudge) +#define H_SWID_2REG( a ) ((a) / 8) +#define H_SWID_2PIX( a ) ((a) * 8) + +#define V_2REG( a ) ((a) - 1) +#define V_2PIX( a ) ((a) + 1) + +/* + Validate a target display mode is both + a) a valid display mode for this device and + b) falls between the contraints imposed by "low" and "high" + + If the mode is not (or cannot) be made valid for this device, return B_ERROR. + If a valid mode can be constructed, but it does not fall within the limits, + return B_BAD_VALUE. + If the mode is both valid AND falls within the limits, return B_OK. +*/ +status_t Radeon_ProposeDisplayMode( shared_info *si, physical_port *port, + pll_info *pll, display_mode *target, + const display_mode *low, const display_mode *high ) +{ + status_t result = B_OK; + + uint64 target_refresh; + bool want_same_width, want_same_height; + int format, bpp; + uint32 row_bytes; + int eff_virtual_width; +// display_type_e disp_type; + + // save refresh rate - we want to leave this (artifical) value untouched + // don't use floating point, we are in kernel mode + target_refresh = + (((uint64)target->timing.pixel_clock * 1000) << FIX_SHIFT) / + ((uint64)target->timing.h_total * target->timing.v_total); + + want_same_width = target->timing.h_display == target->virtual_width; + want_same_height = target->timing.v_display == target->virtual_height; + + if( !Radeon_GetFormat( target->space, &format, &bpp )) + return B_ERROR; + + // for flat panels, check maximum resolution; + // all the other tricks (like fixed resolution and resulting scaling) + // are done automagically by set_display_mode + if( port->disp_type == dt_dvi_1 || port->disp_type == dt_lvds ) { + if( target->timing.h_display > si->fp_port.panel_xres ) + target->timing.h_display = si->fp_port.panel_xres; + + if( target->timing.v_display > si->fp_port.panel_yres ) + target->timing.v_display = si->fp_port.panel_yres; + } + + // validate horizontal timings + { + int h_sync_fudge, h_display, h_sync_start, h_sync_wid, h_total; + + h_display = target->timing.h_display; + h_sync_fudge = Radeon_GetHSyncFudge( si, port, format ); + h_sync_start = target->timing.h_sync_start; + h_sync_wid = target->timing.h_sync_end - target->timing.h_sync_start; + h_total = target->timing.h_total; + + // make sure, display is not too small + // (I reckon Radeon doesn't care, but your monitor probably does) + if( h_display < 320 ) + h_display = 320; + // apply hardware restrictions + // as h_display is the smallest register, it's always possible + // to adjust other values to keep them in supported range + if( h_display > H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ) ) + h_display = H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ); + // round properly + h_display = H_DISPLAY_2PIX( H_DISPLAY_2REG( h_display )); + + // ensure minimum time before sync + if( h_sync_start < h_display + 2*8 ) + h_sync_start = h_display + 2*8; + // sync has wider range than display are, so we won't collide there, + // but total width has same range as sync start, so leave some space + if( h_sync_start > H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8 ) + h_sync_start = H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8; + + // ensure minimum sync length + if( h_sync_wid < H_SWID_2PIX( 3 )) + h_sync_wid = H_SWID_2PIX( 3 ); + // allowed range is quite small, so make sure sync isn't too long + if( h_sync_wid > H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ) ) + h_sync_wid = H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ); + // round properly + h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid )); + + // last but not least adapt total width + // "+7" is needed for rounding up: sync_start isn't rounded, but h_total is + if( h_total < h_sync_start + h_sync_wid + 1*8 + 7 ) + h_total = h_sync_start + h_sync_wid + 1*8 + 7; + // we may get a too long total width; this can only happen + // because sync is too long, so truncate sync accordingly + if( h_total > H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ) ) { + h_total = H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ); + h_sync_wid = min( h_sync_wid, h_total - h_sync_start ); + h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid )); + } + // round properly + h_total = H_TOTAL_2PIX( H_TOTAL_2REG( h_total )); + + target->timing.h_display = h_display; + target->timing.h_sync_start = h_sync_start; + target->timing.h_sync_end = h_sync_start + h_sync_wid; + target->timing.h_total = h_total; + } + + // did we fall out of one of the limits? + if( target->timing.h_display < low->timing.h_display || + target->timing.h_display > high->timing.h_display || + target->timing.h_sync_start < low->timing.h_sync_start || + target->timing.h_sync_start > high->timing.h_sync_start || + target->timing.h_sync_end < low->timing.h_sync_end || + target->timing.h_sync_end > high->timing.h_sync_end || + target->timing.h_total < low->timing.h_total || + target->timing.h_total > high->timing.h_total) + { + SHOW_FLOW0( 4, "out of horizontal limits" ); + result = B_BAD_VALUE; + } + + // validate vertical timings + { + int v_display, v_sync_start, v_sync_wid, v_total; + + v_display = target->timing.v_display; + v_sync_start = target->timing.v_sync_start; + v_sync_wid = target->timing.v_sync_end - target->timing.v_sync_start; + v_total = target->timing.v_total; + + // apply a reasonable minimal height to make monitor happy + if( v_display < 200 ) + v_display = 200; + // apply limits but make sure we have enough lines left for blank and sync + if( v_display > V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5) + v_display = V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5; + + // leave at least one line before sync + // (some flat panel have zero gap here; probably, this leads to + // the infamous bright line at top of screen) + if( v_sync_start < v_display + 1 ) + v_sync_start = v_display + 1; + // apply hardware limit and leave some lines for sync + if( v_sync_start > V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4) + v_sync_start = V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4; + + // don't make sync too short + if( v_sync_wid < 2 ) + v_sync_wid = 2; + // sync width is quite restricted + if( v_sync_wid > (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT)) + v_sync_wid = (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT); + + // leave a gap of at least 1 line + if( v_total < v_sync_start + v_sync_wid + 1 ) + v_total = v_sync_start + v_sync_wid + 1; + // if too long, truncate it and adapt sync len + if( v_total > V_2PIX( RADEON_CRTC_V_TOTAL ) ) { + v_total = V_2PIX( RADEON_CRTC_V_TOTAL ); + v_sync_wid = min( v_sync_wid, v_total - v_sync_start - 4 ); + } + + target->timing.v_display = v_display; + target->timing.v_sync_start = v_sync_start; + target->timing.v_sync_end = v_sync_start + v_sync_wid; + target->timing.v_total = v_total; + } + + // did we fall out of one of the limits? + if( target->timing.v_display < low->timing.v_display || + target->timing.v_display > high->timing.v_display || + target->timing.v_sync_start < low->timing.v_sync_start || + target->timing.v_sync_start > high->timing.h_sync_start || + target->timing.v_sync_end < low->timing.v_sync_end || + target->timing.v_sync_end > high->timing.v_sync_end || + target->timing.v_total < low->timing.v_total || + target->timing.v_total > high->timing.v_total ) + { + SHOW_FLOW0( 4, "out of vertical limits" ); + result = B_BAD_VALUE; + } + + // restore whished refresh rate + target->timing.pixel_clock = + ((uint64)target_refresh / 1000 * target->timing.h_total * target->timing.v_total + FIX_SCALE / 2) + >> FIX_SHIFT; + + // apply PLL restrictions + if( target->timing.pixel_clock / 10 > pll->max_pll_freq || + target->timing.pixel_clock / 10 * 12 < pll->min_pll_freq ) + { + SHOW_ERROR( 2, "pixel_clock (%ld) out of range (%d, %d)", target->timing.pixel_clock, + pll->max_pll_freq * 10, pll->min_pll_freq / 12 ); + return B_ERROR; + } + + // make sure virtual_size > visible_size + // additionally, restore virtual_size == visible_size if it was so on entry + if ((target->timing.h_display > target->virtual_width) || want_same_width) + target->virtual_width = target->timing.h_display; + if ((target->timing.v_display > target->virtual_height) || want_same_height) + target->virtual_height = target->timing.v_display; + + // TBD: limit is taken from XFree86 + // this is probably a CRTC limit; don't know about the accelerator limit (if any) + // h_display can be at most 512*8, so we don't risk h_virtual < h_display + // after applying this restriction + if (target->virtual_width > 1024*8) + target->virtual_width = 1024*8; + + if (target->virtual_width < low->virtual_width || + target->virtual_width > high->virtual_width ) + { + SHOW_FLOW0( 4, "out of virtual horizontal limits" ); + result = B_BAD_VALUE; + } + + // we may have to use a larger virtual width - + // take care of that when calculating memory consumption + eff_virtual_width = Radeon_RoundVWidth( target->virtual_height, bpp ); + + // calculate rowbytes after we've nailed the virtual width + row_bytes = eff_virtual_width * bpp; + + // if we haven't enough memory, reduce virtual height + // (some programs create back buffers by asking for a huge + // virtual screen; they actually want to know what is possible + // to adjust the number of back buffers according to amount + // of graphics memory) + + // careful about additionally required memory: + // 1024 bytes are needed for hardware cursor + if ((row_bytes * target->virtual_height) > si->local_mem_size - 1024 ) + target->virtual_height = (si->local_mem_size - 1024) / row_bytes; + + // make sure we haven't shrunk virtual height too much + if (target->virtual_height < target->timing.v_display) { + SHOW_ERROR( 2, "not enough memory for this mode (could show only %d of %d lines)", + target->virtual_height, target->timing.v_display ); + return B_ERROR; + } + + if (target->virtual_height < low->virtual_height || + target->virtual_height > high->virtual_height ) + { + SHOW_FLOW0( 4, "out of virtual vertical limits" ); + result = B_BAD_VALUE; + } + + // we ignore flags - in the sample driver, they did the same, + // so why bother? + return result; +} + +// public function: return number of display modes returned by get_mode_list +uint32 ACCELERANT_MODE_COUNT( void ) +{ + return ai->si->mode_count; +} + +// public function: get list of standard display modes +// dm - modes are copied to here (to be allocated by caller) +status_t GET_MODE_LIST( display_mode *dm ) +{ + memcpy( dm, ai->mode_list, ai->si->mode_count * sizeof(display_mode) ); + + return B_OK; +} + + +static const color_space spaces[4] = { + B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE +}; + +// if given mode is possible on this card, add it to standard mode list +// mode - mode to add (colourspace is ignored but replaced +// by each officially supported colour space in turn) +// ignore_timing - don't care if timing has to be modified to make mode valid +// (used for fp modes - we just want their resolution) +static void checkAndAddMode( accelerator_info *ai, const display_mode *mode, bool ignore_timing ) +{ + shared_info *si = ai->si; + uint i; + display_mode low, high; + uint32 pix_clk_range; + display_mode *dst; + + if( ignore_timing ) { + // for fp modes: don't add mode if its resolution is already in official mode list + for( i = 0; i < si->mode_count; ++i ) { + if( ai->mode_list[i].timing.h_display == mode->timing.h_display && + ai->mode_list[i].timing.v_display == mode->timing.v_display && + ai->mode_list[i].virtual_width == mode->virtual_width && + ai->mode_list[i].virtual_height == mode->virtual_height ) + return; + } + } + + // set ranges for acceptable values + low = high = *mode; + + // range is 6.25% of default clock: arbitrarily picked + pix_clk_range = low.timing.pixel_clock >> 5; + low.timing.pixel_clock -= pix_clk_range; + high.timing.pixel_clock += pix_clk_range; + + if( ignore_timing ) { + low.timing.h_total = 0; + low.timing.h_sync_start = 0; + low.timing.h_sync_end = 0; + low.timing.v_total = 0; + low.timing.v_sync_start = 0; + low.timing.v_sync_end = 0; + high.timing.h_total = 0xffff; + high.timing.h_sync_start = 0xffff; + high.timing.h_sync_end = 0xffff; + high.timing.v_total = 0xffff; + high.timing.v_sync_start = 0xffff; + high.timing.v_sync_end = 0xffff; + } + + dst = &ai->mode_list[si->mode_count]; + + // iterator through all colour spaces + for( i = 0; i < (sizeof(spaces) / sizeof(color_space)); i++ ) { + // check whether first port can handle it + *dst = *mode; + dst->space = low.space = high.space = spaces[i]; + + if( Radeon_ProposeDisplayMode( si, &si->ports[0], + &si->pll, dst, &low, &high ) == B_OK ) + { + si->mode_count++; + ++dst; + + } else { + // it can't, so try second port + *dst = *mode; + dst->space = spaces[i]; + + if( Radeon_ProposeDisplayMode( si, &si->ports[1], + &si->pll, dst, &low, &high ) == B_OK ) + { + si->mode_count++; + ++dst; + + } else + SHOW_FLOW( 4, "%ld, %ld not supported", dst->virtual_width, dst->virtual_height ); + } + } +} + + +// add display mode including span mode variations to offical list +static void checkAndAddMultiMode( accelerator_info *ai, const display_mode *mode, + bool ignore_timing ) +{ + display_mode wide_mode; + + SHOW_FLOW( 4, "%ld, %ld", mode->virtual_width, mode->virtual_height ); + + // plain mode + checkAndAddMode( ai, mode, ignore_timing ); + + // double width mode + wide_mode = *mode; + wide_mode.virtual_width *= 2; + wide_mode.flags |= B_SCROLL; + checkAndAddMode( ai, &wide_mode, ignore_timing ); + + // double height mode + wide_mode = *mode; + wide_mode.virtual_height *= 2; + wide_mode.flags |= B_SCROLL; + checkAndAddMode( ai, &wide_mode, ignore_timing ); +} + +// add display mode of flat panel to official list +static void addFPMode( accelerator_info *ai, fp_info *fp_info ) +{ + if( fp_info->disp_type == dt_dvi_1 || fp_info->disp_type == dt_lvds ) { + display_mode mode; + + mode.virtual_width = mode.timing.h_display = fp_info->panel_xres; + mode.virtual_height = mode.timing.v_display = fp_info->panel_yres; + + mode.timing.h_total = mode.timing.h_display + fp_info->h_blank; + mode.timing.h_sync_start = mode.timing.h_display + fp_info->h_over_plus; + mode.timing.h_sync_end = mode.timing.h_sync_start + fp_info->h_sync_width; + mode.timing.v_total = mode.timing.v_display + fp_info->v_blank; + mode.timing.v_sync_start = mode.timing.v_display + fp_info->v_over_plus; + mode.timing.v_sync_end = mode.timing.v_sync_start + fp_info->v_sync_width; + + mode.timing.pixel_clock = fp_info->dot_clock; + + // if we have no pixel clock, assume 60 Hz + // (as we don't program PLL in this case, it doesn't matter + // if it's wrong, we just want this resolution in the mode list) + if( mode.timing.pixel_clock == 0 ) { + // devide by 1000 as clock is in kHz + mode.timing.pixel_clock = + ((uint32)mode.timing.h_total * mode.timing.v_total * 60) / 1000; + } + + mode.flags = MODE_FLAGS; + mode.h_display_start = 0; + mode.v_display_start = 0; + + SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)", + mode.timing.h_display, mode.timing.h_sync_start, + mode.timing.h_sync_end, mode.timing.h_total, mode.virtual_width ); + SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)", + mode.timing.v_display, mode.timing.v_sync_start, + mode.timing.v_sync_end, mode.timing.v_total, mode.virtual_height ); + SHOW_FLOW( 2, "clk: %ld", mode.timing.pixel_clock ); + + // flat panels seem to have strange timings; + // as we ignore user-supplied timing for FPs anyway, + // the mode can (and usually has to) be modified to be + // used for normal CRTs + checkAndAddMultiMode( ai, &mode, true ); + } +} + +// create list of officially supported modes +status_t Radeon_CreateModeList( shared_info *si ) +{ + size_t max_size; + uint i; + uint max_num_modes; + + // maximum number of official modes: + // (predefined-modes + fp-modes) * number-of-colour-spaces * number-of-(non)-span-modes + max_num_modes = ((sizeof( base_mode_list ) / sizeof( base_mode_list[0] ) + 1) * 4 * 3); + + max_size = (max_num_modes * sizeof(display_mode) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); + + si->mode_list_area = create_area("Radeon accelerant mode info", + (void **)&ai->mode_list, B_ANY_ADDRESS, + max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + + if( si->mode_list_area < B_OK ) + return si->mode_list_area; + + si->mode_count = 0; + + // check standard modes + for( i = 0; i < sizeof( base_mode_list ) / sizeof( base_mode_list[0] ); i++ ) + checkAndAddMultiMode( ai, &base_mode_list[i], false ); + + // plus fp mode + addFPMode( ai, &si->fp_port ); + + // as we've created the list ourself, we don't clone it + ai->mode_list_area = si->mode_list_area; + + return B_OK; +} + + +// cleanup official display mode list +void Radeon_DisposeModeList( shared_info *si ) +{ + delete_area( si->mode_list_area ); +} + + +// public function: wraps for internal propose_display_mode +status_t PROPOSE_DISPLAY_MODE( display_mode *target, const display_mode *low, + const display_mode *high ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + status_t result1, result2; + bool isTunneled; + status_t result; + + // check whether we got a tunneled settings command + result = Radeon_CheckMultiMonTunnel( vc, target, low, high, &isTunneled ); + if( isTunneled ) + return result; + + // transform to multi-screen mode first + Radeon_DetectMultiMode( vc, target ); + Radeon_VerifyMultiMode( vc, si, target ); + + SHOW_FLOW0( 2, "wished:" ); + SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)", + target->timing.h_display, target->timing.h_sync_start, + target->timing.h_sync_end, target->timing.h_total, target->virtual_width ); + SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)", + target->timing.v_display, target->timing.v_sync_start, + target->timing.v_sync_end, target->timing.v_total, target->virtual_height ); + SHOW_FLOW( 2, "clk: %ld", target->timing.pixel_clock ); + + // we must assure that each ProposeMode call doesn't tweak the mode in + // a way that it cannot be handled by the other port anymore + result1 = Radeon_ProposeDisplayMode( si, &si->ports[vc->ports[0].physical_port], + &si->pll, target, low, high ); + + if( result1 == B_ERROR ) + return B_ERROR; + + if( Radeon_NeedsSecondPort( target )) { + // if both ports are used, make sure both can handle mode + result2 = Radeon_ProposeDisplayMode( si, &si->ports[vc->ports[1].physical_port], + &si->pll, target, low, high ); + + if( result2 == B_ERROR ) + return B_ERROR; + } else { + result2 = B_OK; + } + + SHOW_INFO0( 2, "got:" ); + SHOW_INFO( 2, "H: %4d %4d %4d %4d (v=%4d)", + target->timing.h_display, target->timing.h_sync_start, + target->timing.h_sync_end, target->timing.h_total, target->virtual_width ); + SHOW_INFO( 2, "V: %4d %4d %4d %4d (h=%4d)", + target->timing.v_display, target->timing.v_sync_start, + target->timing.v_sync_end, target->timing.v_total, target->virtual_height ); + SHOW_INFO( 2, "clk: %ld", target->timing.pixel_clock ); + + Radeon_HideMultiMode( vc, target ); + + if( result1 == B_OK && result2 == B_OK ) + return B_OK; + else + return B_BAD_VALUE; +} diff --git a/src/add-ons/accelerants/radeon/SetDisplayMode.c b/src/add-ons/accelerants/radeon/SetDisplayMode.c new file mode 100644 index 0000000000..902caccc37 --- /dev/null +++ b/src/add-ons/accelerants/radeon/SetDisplayMode.c @@ -0,0 +1,493 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Sets display modes, colour palette and handles DPMS +*/ + + +#include "GlobalData.h" +#include "generic.h" +#include +#include "radeon_regs.h" +#include "mmio.h" +#include "crtc_regs.h" +#include + +#include "overlay_regs.h" +#include "capture_regs.h" +#include "rbbm_regs.h" +#include "dac_regs.h" + +#include + +void Radeon_SetMode( accelerator_info *ai, virtual_port *port, display_mode *mode ); +void Radeon_EnableIRQ( accelerator_info *ai, bool enable ); + + +// Radeon's DACs share same public registers, this function +// selects the DAC you'll talk to +static void selectDAC( accelerator_info *ai, virtual_port *port ) +{ + Radeon_WriteRegCP( ai, RADEON_DAC_CNTL2, + (port->is_crtc2 ? RADEON_DAC2_PALETTE_ACC_CTL : 0) | + (ai->si->dac_cntl2 & ~RADEON_DAC2_PALETTE_ACC_CTL) ); +} + + +// set standard colour palette (needed for non-palette modes) +static void initDAC( accelerator_info *ai, virtual_port *port ) +{ + int i; + + selectDAC( ai, port ); + + Radeon_WriteRegCP( ai, RADEON_PALETTE_INDEX, 0 ); + + for( i = 0; i < 256; ++i ) + Radeon_WriteRegCP( ai, RADEON_PALETTE_DATA, (i << 16) | (i << 8) | i ); +} + + + +// round virtual width up to next valid size +uint32 Radeon_RoundVWidth( int virtual_width, int bpp ) +{ + // we have to make both the CRTC and the accelerator happy: + // - the CRTC wants virtual width in pixels to be a multiple of 8 + // - the accelerator expects width in bytes to be a multiple of 64 + + // to put that together, width (in bytes) must be a multiple of the least + // common nominator of bytes-per-pixel*8 (CRTC) and 64 (accelerator); + + // if bytes-per-pixel is a power of two and less than 8, the LCM is 64; + // almost all colour depth satisfy that apart from 24 bit; in this case, + // the LCM is 64*3=192 + + // after dividing by bytes-per-pixel we get pixels: in first case, + // width must be multiple of 64/bytes-per-pixel; in second case, + // width must be multiple of 64*3/3=64 + + if( bpp != 3 ) + return (virtual_width + 64/bpp - 1) & ~(64/bpp - 1); + else + return (virtual_width + 63) & ~63; +} + + +// list of registers that must be reset before display mode switch +// to avoid interferences +static struct { + uint16 reg; + uint32 val; +} common_regs[] = { + { RADEON_OVR_CLR, 0 }, + { RADEON_OVR_WID_LEFT_RIGHT, 0 }, + { RADEON_OVR_WID_TOP_BOTTOM, 0 }, + { RADEON_OV0_SCALE_CNTL, 0 }, + { RADEON_SUBPIC_CNTL, 0 }, + { RADEON_VIPH_CONTROL, 0 }, + { RADEON_I2C_CNTL_1, 0 }, + { RADEON_GEN_INT_CNTL, 0 }, + { RADEON_CAP0_TRIG_CNTL, 0 }, +}; + +static void Radeon_InitCommonRegs( accelerator_info *ai ) +{ + vuint8 *regs = ai->regs; + uint i; + + for( i = 0; i < sizeof( common_regs) / sizeof( common_regs[0] ); ++i ) + OUTREG( regs, common_regs[i].reg, common_regs[i].val ); +} + +// set display mode of one port; +// port restrictions, like fixed-sync TFTs connected to it, are taken care of +void Radeon_SetMode( accelerator_info *ai, virtual_port *port, display_mode *mode ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + vuint8 *regs = ai->regs; + int format; + int bpp; + display_type_e disp_type; + port_regs values; + + port->mode = *mode; + + // don't destroy passed values, use our copy instead + mode = &port->mode; + + disp_type = si->ports[port->physical_port].disp_type; + + // if using an flat panel or LCD, maximum resolution + // is determined by the physical resolution; + // also, all timing is fixed + if( disp_type == dt_dvi_1 || disp_type == dt_lvds ) { + fp_info *fp_info = &si->fp_port; + + if( mode->timing.h_display > fp_info->panel_xres ) + mode->timing.h_display = fp_info->panel_xres; + if( mode->timing.v_display > fp_info->panel_yres ) + mode->timing.v_display = fp_info->panel_yres; + + mode->timing.h_total = mode->timing.h_display + fp_info->h_blank; + mode->timing.h_sync_start = mode->timing.h_display + fp_info->h_over_plus; + mode->timing.h_sync_end = mode->timing.h_sync_start + fp_info->h_sync_width; + mode->timing.v_total = mode->timing.v_display + fp_info->v_blank; + mode->timing.v_sync_start = mode->timing.v_display + fp_info->v_over_plus; + mode->timing.v_sync_end = mode->timing.v_sync_start + fp_info->v_sync_width; + + mode->timing.pixel_clock = fp_info->dot_clock; + } + + Radeon_GetFormat( mode->space, &format, &bpp ); + + vc->bpp = bpp; + vc->datatype = format; + + // calculate all hardware register values + Radeon_CalcCRTCRegisters( ai, port, mode, &values ); + + values.surface_cntl = RADEON_SURF_TRANSLATION_DIS; + + // for flat panels, we may not have pixel clock if DDC data is missing; + // as we don't change effective resolution we can leave it as set by BIOS + if( mode->timing.pixel_clock ) + Radeon_CalcPLLDividers( &si->pll, mode->timing.pixel_clock / 10, &values ); + + if( disp_type == dt_dvi_1 || disp_type == dt_lvds ) + Radeon_CalcFPRegisters( ai, port, &si->fp_port, mode, &values ); + + + // write values to registers + Radeon_SetDPMS( ai, port, B_DPMS_SUSPEND ); + + Radeon_InitCommonRegs( ai ); + + Radeon_ProgramCRTCRegisters( ai, port, &values ); + + OUTREG( regs, RADEON_SURFACE_CNTL, values.surface_cntl ); + + if( disp_type == dt_dvi_1 || disp_type == dt_lvds ) + Radeon_ProgramFPRegisters( ai, &si->fp_port, &values ); + + + if( mode->timing.pixel_clock ) + Radeon_ProgramPLL( ai, port, &values ); + + Radeon_SetDPMS( ai, port, B_DPMS_ON ); + + // overlay must be setup again after modeswitch (whoever was using it) + // TBD: this won't work if another virtual card was using it, + // but currently, virtual cards don't work anyway... + si->active_overlay.port = -1; +} + + +// enable or disable VBlank interrupts +void Radeon_EnableIRQ( accelerator_info *ai, bool enable ) +{ + shared_info *si = ai->si; + uint32 int_cntl, int_mask; + + int_cntl = INREG( ai->regs, RADEON_GEN_INT_CNTL ); + int_mask = + RADEON_CRTC_VBLANK_MASK + | (si->has_crtc2 ? RADEON_CRTC2_VBLANK_MASK : 0); + + if( enable ) + int_cntl |= int_mask; + else + int_cntl &= ~int_mask; + + OUTREG( ai->regs, RADEON_GEN_INT_CNTL, int_cntl ); + + if( enable ) { + // on enable, we have to acknowledge all IRQs as the graphics card + // waits for that before it issues further IRQs + OUTREG( ai->regs, RADEON_GEN_INT_STATUS, int_cntl ); + } + + si->enable_virtual_irq = enable; +} + + +// public function: set display mode +status_t SET_DISPLAY_MODE( display_mode *mode_in ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + display_mode bounds, mode; + + mode = bounds = *mode_in; + + ACQUIRE_BEN( si->engine.lock ); + + SHOW_FLOW( 2, "width=%d, height=%d", mode.timing.h_display, mode.timing.v_display ); + + // check mode and tweak parameters so we can program hardware + // without any further checks + if( PROPOSE_DISPLAY_MODE( &mode, &bounds, &bounds ) == B_ERROR ) { + SHOW_ERROR0( 2, "invalid mode" ); + + RELEASE_BEN( si->engine.lock ); + return B_ERROR; + } + + // already done by propose_display_mode, but it was undone on return; + // do this before equality check to recognize changed to multi-monitor mode + Radeon_DetectMultiMode( vc, &mode ); + + // mode switches can take quite long and are visible, + // so avoid them if possible + if( memcmp( &mode, &vc->mode, sizeof( display_mode )) == 0 ) { + RELEASE_BEN( si->engine.lock ); + return B_OK; + } + + // make sure, we don't get disturbed + Radeon_Finish( ai ); + Radeon_EnableIRQ( ai, false ); + + // free cursor and framebuffer memory + { + radeon_free_local_mem fm; + + fm.magic = RADEON_PRIVATE_DATA_MAGIC; + + if( vc->cursor.mem_handle ) { + fm.handle = vc->cursor.mem_handle; + ioctl( ai->fd, RADEON_FREE_LOCAL_MEM, &fm ); + } + + if( vc->fb_mem_handle ) { + fm.handle = vc->fb_mem_handle; + ioctl( ai->fd, RADEON_FREE_LOCAL_MEM, &fm ); + } + } + + memcpy( &vc->mode, &mode, sizeof( display_mode )); + + // verify hardware restrictions *after* saving mode + // e.g. if you want a span mode but have one monitor disconnected, + // configuration shouldn't be touched, so you can continue working + // with two monitors later on just like nothing has happened + Radeon_VerifyMultiMode( vc, si, &mode ); + + // set main flags + vc->independant_ports = Radeon_NeedsSecondPort( &mode ) ? 2 : 1; + vc->different_ports = Radeon_DifferentPorts( &mode ); + SHOW_FLOW( 2, "independant ports: %d", vc->independant_ports ); + vc->scroll = mode.flags & B_SCROLL; + SHOW_FLOW( 2, "scrolling %s", vc->scroll ? "enabled" : "disabled" ); + + // allocate frame buffer and cursor image memory + { + radeon_alloc_local_mem am; + int format, bpp; + + // alloc cursor memory + am.magic = RADEON_PRIVATE_DATA_MAGIC; + am.size = 1024; + + if( ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ) == B_OK ) { + vc->cursor.mem_handle = am.handle; + vc->cursor.fb_offset = am.fb_offset; + } else { + // too bad that we are out of mem -> set reasonable values as + // it's too late to give up (ouch!) + SHOW_ERROR0( 2, "no memory for cursor image!" ); + vc->cursor.mem_handle = 0; + vc->cursor.fb_offset = 0; + } + + vc->cursor.data = si->framebuffer + vc->cursor.fb_offset; + + // alloc frame buffer + Radeon_GetFormat( mode.space, &format, &bpp ); + vc->pitch = Radeon_RoundVWidth( mode.virtual_width, bpp ) * bpp; + am.size = vc->pitch * mode.virtual_height; + + if( ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ) == B_OK ) { + vc->fb_mem_handle = am.handle; + vc->fb_offset = am.fb_offset; + } else { + // ouch again - set reasonable values + SHOW_ERROR0( 2, "no memory for frame buffer!" ); + vc->fb_mem_handle = 0; + vc->fb_offset = 1024; + } + + vc->fbc.frame_buffer = si->framebuffer + vc->fb_offset; + vc->fbc.frame_buffer_dma = (void *)((uint8 *)si->framebuffer_pci + vc->fb_offset); + vc->fbc.bytes_per_row = vc->pitch; + } + + // multi-screen stuff + Radeon_InitMultiModeVars( vc, &mode ); + + // GO! + Radeon_SetMode( ai, &vc->ports[0], &mode ); + + if( vc->independant_ports > 1 ) + Radeon_SetMode( ai, &vc->ports[1], &mode ); + + SHOW_FLOW( 3, "pitch=%ld", vc->pitch ); + + // we'll modify bits of this reg, so save it for async access + si->dac_cntl2 = INREG( ai->regs, RADEON_DAC_CNTL2 ); + + // init accelerator + Radeon_Init2D( ai, vc->datatype ); + + // remember that 2D accelerator is not prepared for any virtual card + si->active_vc = -1; + + Radeon_ActivateVirtualCard( ai ); + + // first move to well-defined position (to setup CRTC offset) + Radeon_MoveDisplay( ai, 0, 0 ); + // then to (probably faulty) user-defined pos + Radeon_MoveDisplay( ai, mode.h_display_start, mode.v_display_start ); + + // set standard palette in direct-colour modes + initDAC( ai, &vc->ports[0] ); + if( vc->independant_ports > 1 ) + initDAC( ai, &vc->ports[1] ); + + // initialize cursor data + Radeon_SetCursorColors( ai, &vc->ports[0] ); + if( vc->independant_ports > 1 ) + Radeon_SetCursorColors( ai, &vc->ports[1] ); + + // sync should be settled now, so we can reenable IRQs + // TBD: IRQ handling doesn't work correctly and doesn't make sense with two + // displays connected, so let's leave them disabled for now + //Radeon_EnableIRQ( ai, true ); + + RELEASE_BEN( si->engine.lock ); + + // !! all this must be done after lock has been + // released to avoid dead-lock !! + // TBD: any invalid intermediate states? + + // move_cursor sets all cursor-related variables and registers + vc->cursor.is_visible = false; + MOVE_CURSOR( 0, 0 ); + + return B_OK; +} + +// update shown are of one port +static void moveOneDisplay( accelerator_info *ai, virtual_port *port ) +{ + virtual_card *vc = ai->vc; + uint32 offset; + + offset = (vc->mode.v_display_start + port->rel_y) * vc->pitch + + (vc->mode.h_display_start + port->rel_x) * vc->bpp + + vc->fb_offset; + + SHOW_FLOW( 3, "Setting address %x on port %d", + offset, port->is_crtc2 ); + + Radeon_WaitForFifo( ai, 1 ); + + OUTREG( ai->regs, port->is_crtc2 ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET, offset ); +/* Radeon_WriteRegCP( ai, port->is_crtc2 ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET, + offset );*/ +} + +status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start ) +{ + virtual_card *vc = ai->vc; + + SHOW_FLOW( 4, "h_display_start=%ld, v_display_start=%ld", + h_display_start, v_display_start ); + + if( h_display_start + vc->eff_width > vc->mode.virtual_width || + v_display_start + vc->eff_height > vc->mode.virtual_height ) + return B_ERROR; + + // this is needed both for get_mode_info and for scrolling of virtual screens + vc->mode.h_display_start = h_display_start & ~7; + vc->mode.v_display_start = v_display_start; + + // do it + moveOneDisplay( ai, &vc->ports[0] ); + + if( vc->independant_ports > 1 ) + moveOneDisplay( ai, &vc->ports[1] ); + + // overlay position must be adjusted + Radeon_UpdateOverlay( ai ); + + return B_OK; +} + +// public function: pan display +status_t MOVE_DISPLAY( uint16 h_display_start, uint16 v_display_start ) +{ + shared_info *si = ai->si; + status_t result; + + ACQUIRE_BEN( si->engine.lock ); + + // TBD: we should probably lock card first; in this case, we must + // split this function into locking and worker part, as this + // function is used internally as well + result = Radeon_MoveDisplay( ai, h_display_start, v_display_start ); + + RELEASE_BEN( si->engine.lock ); + + return result; +} + +static void setPalette( accelerator_info *ai, virtual_port *port, + uint count, uint8 first, uint8 *color_data ); + +// public function: set colour palette +void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; +// uint i; + + SHOW_FLOW( 3, "first=%d, count=%d", first, flags ); + + if( vc->mode.space != B_CMAP8 ) { + SHOW_ERROR0( 2, "Tried to set palette in non-palette mode" ); + return; + } + + // we need to lock card, though this isn't done in sample driver + ACQUIRE_BEN( si->engine.lock ); + + setPalette( ai, &vc->ports[0], count, first, color_data ); + + if( vc->independant_ports > 1 ) + setPalette( ai, &vc->ports[1], count, first, color_data ); + + RELEASE_BEN( si->engine.lock ); +} + + +// set palette of one DAC +static void setPalette( accelerator_info *ai, virtual_port *port, + uint count, uint8 first, uint8 *color_data ) +{ + uint i; + + selectDAC( ai, port ); + + Radeon_WriteRegCP( ai, RADEON_PALETTE_INDEX, first ); + + for( i = 0; i < count; ++i, color_data += 3 ) + Radeon_WriteRegCP( ai, RADEON_PALETTE_DATA, + ((uint32)color_data[0] << 16) | + ((uint32)color_data[1] << 8) | + color_data[2] ); +} diff --git a/src/add-ons/accelerants/radeon/crtc.c b/src/add-ons/accelerants/radeon/crtc.c new file mode 100644 index 0000000000..ff0a03d6f3 --- /dev/null +++ b/src/add-ons/accelerants/radeon/crtc.c @@ -0,0 +1,179 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + CRTC programming +*/ + +#include "radeon_accelerant.h" +//#include "../include/radeon_regs.h" +#include "mmio.h" +#include "crtc_regs.h" +#include "dac_regs.h" + +// hammer CRTC registers +void Radeon_ProgramCRTCRegisters( accelerator_info *ai, virtual_port *port, + port_regs *values ) +{ + vuint8 *regs = ai->regs; + + SHOW_FLOW0( 2, "" ); + + if( port->is_crtc2 ) { + OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, values->crtc_gen_cntl, + RADEON_CRTC2_VSYNC_DIS | + RADEON_CRTC2_HSYNC_DIS | + RADEON_CRTC2_DISP_DIS ); + + switch( ai->si->asic ) { + case rt_r200: + case rt_r300: + OUTREG( regs, RADEON_DISP_OUTPUT_CNTL, values->disp_output_cntl ); + break; + default: + OUTREG( regs, RADEON_DAC_CNTL2, values->dac_cntl ); + } + + OUTREG( regs, RADEON_CRTC2_H_TOTAL_DISP, values->crtc_h_total_disp ); + OUTREG( regs, RADEON_CRTC2_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid ); + OUTREG( regs, RADEON_CRTC2_V_TOTAL_DISP, values->crtc_v_total_disp ); + OUTREG( regs, RADEON_CRTC2_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid ); + OUTREG( regs, RADEON_CRTC2_OFFSET_CNTL, values->crtc_offset_cntl ); + OUTREG( regs, RADEON_CRTC2_PITCH, values->crtc_pitch ); + + } else { + OUTREG( regs, RADEON_CRTC_GEN_CNTL, values->crtc_gen_cntl ); + + OUTREGP( regs, RADEON_CRTC_EXT_CNTL, values->crtc_ext_cntl, + RADEON_CRTC_VSYNC_DIS | + RADEON_CRTC_HSYNC_DIS | + RADEON_CRTC_DISPLAY_DIS ); + + OUTREGP( regs, RADEON_DAC_CNTL, values->dac_cntl, + RADEON_DAC_RANGE_CNTL | RADEON_DAC_BLANKING ); + + OUTREG( regs, RADEON_CRTC_H_TOTAL_DISP, values->crtc_h_total_disp ); + OUTREG( regs, RADEON_CRTC_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid ); + OUTREG( regs, RADEON_CRTC_V_TOTAL_DISP, values->crtc_v_total_disp ); + OUTREG( regs, RADEON_CRTC_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid ); + OUTREG( regs, RADEON_CRTC_OFFSET_CNTL, values->crtc_offset_cntl ); + OUTREG( regs, RADEON_CRTC_PITCH, values->crtc_pitch ); + } +} + + +// get required hsync delay depending on bit depth and output device +uint16 Radeon_GetHSyncFudge( shared_info *si, physical_port *port, int datatype ) +{ + static int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 }; + static int hsync_fudge_fp[] = { 0x02, 0x02, 0x00, 0x00, 0x05, 0x05 }; + + // there is an sync delay which depends on colour-depth and output device + if( port->disp_type == dt_dvi_1 || port->disp_type == dt_dvi_2 || + port->disp_type == dt_lvds ) + return hsync_fudge_fp[datatype - 1]; + else + return hsync_fudge_default[datatype - 1]; +} + + +// calculate CRTC register content +void Radeon_CalcCRTCRegisters( accelerator_info *ai, virtual_port *port, + display_mode *mode, port_regs *values ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + int hsync_start; + int hsync_wid; + int hsync_fudge; + int vsync_wid; + display_type_e disp_type; + physical_port *phys_port = &si->ports[port->physical_port]; + + disp_type = si->ports[port->physical_port].disp_type; + hsync_fudge = Radeon_GetHSyncFudge( si, phys_port, vc->datatype ); + + if( port->is_crtc2 ) { + values->crtc_gen_cntl = (RADEON_CRTC2_EN + | RADEON_CRTC2_CRT2_ON + | (vc->datatype << 8) + | (0/*doublescan*/ ? RADEON_CRTC2_DBL_SCAN_EN : 0) + | ((mode->timing.flags & B_TIMING_INTERLACED) + ? RADEON_CRTC2_INTERLACE_EN : 0)); + + //values->crtc_gen_cntl &= ~RADEON_CRTC2_CRT2_ON; + + // make ports independant of each other + switch( si->asic ) { + case rt_r200: + case rt_r300: + values->disp_output_cntl = INREG( ai->regs, RADEON_DISP_OUTPUT_CNTL ); + values->disp_output_cntl = + (values->disp_output_cntl & ~RADEON_DISP_DAC_SOURCE_MASK) + | RADEON_DISP_DAC_SOURCE_CRTC2; + break; + default: + // we always use CRTC1 for FP and CRTC2 for CRT + // a better way were to take output device into consideration too + values->dac_cntl = INREG( ai->regs, RADEON_DAC_CNTL2 ) & + ~RADEON_DAC_CLK_SEL; + values->dac_cntl |= RADEON_DAC_CLK_SEL_CRTC2; + } + + } else { + // here, we should set interlace/double scan mode + // but we don't support them (anyone missing them?) + values->crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN + | RADEON_CRTC_EN + | (vc->datatype << 8)); + + // we shouldn't set CRT_ON if a flat panel is connected, + // but this flag seems to be independant of CRTC the + // CRT is connected to + values->crtc_ext_cntl = + RADEON_VGA_ATI_LINEAR | + RADEON_XCRT_CNT_EN | + RADEON_CRTC_CRT_ON; + + values->dac_cntl = RADEON_DAC_MASK_ALL + | RADEON_DAC_VGA_ADR_EN + | RADEON_DAC_8BIT_EN; + } + + values->crtc_h_total_disp = + ((mode->timing.h_total / 8 - 1) & RADEON_CRTC_H_TOTAL) + | (((mode->timing.h_display / 8 - 1) << RADEON_CRTC_H_DISP_SHIFT) & RADEON_CRTC_H_DISP); + + hsync_wid = (mode->timing.h_sync_end - mode->timing.h_sync_start) / 8; + + hsync_start = mode->timing.h_sync_start - 8 + hsync_fudge; + + // TBD: the sync may be the other way around + values->crtc_h_sync_strt_wid = + (hsync_start & (RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX)) + | (hsync_wid << RADEON_CRTC_H_SYNC_WID_SHIFT) + | ((mode->flags & B_POSITIVE_HSYNC) == 0 ? RADEON_CRTC_H_SYNC_POL : 0); + + values->crtc_v_total_disp = + ((mode->timing.v_total - 1) & RADEON_CRTC_V_TOTAL) + | (((mode->timing.v_display - 1) << RADEON_CRTC_V_DISP_SHIFT) & RADEON_CRTC_V_DISP); + + vsync_wid = mode->timing.v_sync_end - mode->timing.v_sync_start; + + // TBD: vertial sync may be the other way around + values->crtc_v_sync_strt_wid = + ((mode->timing.v_sync_start - 1) & RADEON_CRTC_V_SYNC_STRT) + | (vsync_wid << RADEON_CRTC_V_SYNC_WID_SHIFT) + | ((mode->flags & B_POSITIVE_VSYNC) == 0 + ? RADEON_CRTC_V_SYNC_POL : 0); + + values->crtc_offset_cntl = 0; + + values->crtc_pitch = Radeon_RoundVWidth( mode->virtual_width, vc->bpp ) / 8; + + SHOW_FLOW( 2, "crtc_pitch=%ld", values->crtc_pitch ); + + values->crtc_pitch |= values->crtc_pitch << 16; +} diff --git a/src/add-ons/accelerants/radeon/ddc.c b/src/add-ons/accelerants/radeon/ddc.c new file mode 100644 index 0000000000..4b486c3caa --- /dev/null +++ b/src/add-ons/accelerants/radeon/ddc.c @@ -0,0 +1,160 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Main DDC communication +*/ + +#include +#include +#include +#include "ddc_int.h" +#include "edid.h" + +#include "i2c.h" + +#define READ_RETRIES 4 + +status_t ddc2_read_edid1( const i2c_bus *bus, edid1_info *edid, void **vdif, size_t *vdif_len ); + +// verify checksum of ddc data +// (some monitors have a broken checksum - bad luck for them) +static status_t verify_checksum( const uint8 *data, size_t len ) +{ + int i; + uint8 sum = 0; + uint8 all_or = 0; + + for( i = 0; i < (int)len; ++i, ++data ) { + sum += *data; + all_or |= *data; +// SHOW_FLOW( 2, "%x", *data ); + } + + if( all_or == 0 ) { + SHOW_INFO0( 2, "DDC information contains zeros only" ); + return B_ERROR; + } + +// SHOW_INFO( 2, "sum=%x", sum ); + + if( sum != 0 ) { + SHOW_INFO0( 2, "Checksum error of DDC information" ); + return B_IO_ERROR; + } + + return B_OK; +} + +// read ddc2 data from monitor +static status_t ddc2_read( const i2c_bus *bus, int start, uint8 *buffer, size_t len ) +{ + uint8 write_buffer[2]; + i2c_timing timing; + int i; + status_t res = B_ERROR; + + write_buffer[0] = start & 0xff; + write_buffer[1] = (start >> 8) & 0xff; + + i2c_get100k_timing( &timing ); + + timing.start_timeout = 550; + timing.byte_timeout = 2200; + timing.bit_timeout = 40; + timing.ack_start_timeout = 40; + timing.ack_timeout = 40; + + for( i = 0; i < READ_RETRIES; ++i ) { + res = i2c_send_receive( bus, &timing, + 0xa0, write_buffer, start < 0x100 ? 1 : 2, + buffer, len ); + if( res == B_OK && verify_checksum( buffer, len ) == B_OK ) + break; + + res = B_ERROR; + } + + return res; +} + + +// reading VDIF has not been tested. +// it seems that almost noone supports VDIF which makes testing hard, +// but what's the point anyway? +#if 0 +static status_t ddc2_read_vdif( const i2c_bus *bus, int start, + void **vdif, size_t *vdif_len ) +{ + status_t res; + uint8 *data, *cur_data; + int i; + uint8 buffer[64]; + + *vdif = NULL; + *vdif_len = 0; + + res = ddc2_read( bus, start, buffer, 64 ); + SHOW_INFO( 2, "%x", buffer[0] ); + if( res != B_OK || buffer[0] == 0 ) + return B_OK; + + // each block is 63 bytes plus 1 checksum long + // we strip the checksum but store data directly into + // buffer, so we need an extra byte for checksum of the last block + data = malloc( buffer[0] * 63 + 1 ); + if( data == NULL ) + return B_NO_MEMORY; + + cur_data = data; + for( i = 0; i < buffer[0]; ++i ) { + ddc2_read( bus, start + i * 64, cur_data, 64 ); + // strip checksum byte + cur_data += 63; + } + + *vdif_len = buffer[0] * 63; + *vdif = data; + return B_OK; +} +#endif + +// read EDID and VDIF from monitor via ddc2 +status_t ddc2_read_edid1( const i2c_bus *bus, edid1_info *edid, + void **vdif, size_t *vdif_len ) +{ + status_t res; + edid1_raw raw; + + // see edid_raw.h for values to be expected + SHOW_INFO( 5, "structure size test: %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld", + sizeof( edid1_header_raw ), + sizeof( edid1_vendor_raw ), + sizeof( edid1_version_raw ), + sizeof( edid1_display_raw ), + sizeof( edid1_established_timing ), + sizeof( edid1_std_timing_raw ), + sizeof( edid1_detailed_monitor_raw ), + sizeof( edid1_raw )); + + res = ddc2_read( bus, 0, (uint8 *)&raw, sizeof( raw )); + if( res != B_OK ) + return res; + + edid_decode( edid, &raw ); + + *vdif = NULL; + *vdif_len = 0; + + // skip vdif as long as it's not tested +#if 0 + res = ddc2_read_vdif( bus, sizeof( raw ) * (edid->num_sections + 1), + vdif, vdif_len ); + if( res != B_OK ) + return res; +#endif + + return B_OK; +} diff --git a/src/add-ons/accelerants/radeon/ddc.h b/src/add-ons/accelerants/radeon/ddc.h new file mode 100644 index 0000000000..5e8f7b3d56 --- /dev/null +++ b/src/add-ons/accelerants/radeon/ddc.h @@ -0,0 +1,21 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Main DDC communication +*/ + +#ifndef _DDC_H +#define _DDC_H + +#include "i2c.h" +#include "edid.h" + +// read EDID and VDIF from monitor via ddc2 +// (currently, *vdif and *vdif_len is always set to null) +status_t ddc2_read_edid1( const i2c_bus *bus, edid1_info *edid, + void **vdif, size_t *vdif_len ); + +#endif diff --git a/src/add-ons/accelerants/radeon/ddc_int.h b/src/add-ons/accelerants/radeon/ddc_int.h new file mode 100644 index 0000000000..e2b63e9394 --- /dev/null +++ b/src/add-ons/accelerants/radeon/ddc_int.h @@ -0,0 +1,23 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Internal header +*/ + +// no dprintf in user space, but if you know the trick ;) +void _kdprintf_(const char *format, ...); +//bool set_dprintf_enabled(bool); /* returns old enable flag */ + +#define dprintf _kdprintf_ + +// don't use variables here as this is a static library +// and thus the variables will collide with the main program +#define debug_level_flow 2 +#define debug_level_info 4 +#define debug_level_error 4 +#define DEBUG_MSG_PREFIX "DDC " + +#include "debug_ext.h" diff --git a/src/add-ons/accelerants/radeon/dpms.c b/src/add-ons/accelerants/radeon/dpms.c new file mode 100644 index 0000000000..43e2dff46a --- /dev/null +++ b/src/add-ons/accelerants/radeon/dpms.c @@ -0,0 +1,242 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Display Power Management (DPMS) support +*/ + +#include "radeon_accelerant.h" +#include "mmio.h" +#include "crtc_regs.h" +#include "fp_regs.h" +#include "GlobalData.h" + +// these static functions are moved to end of file to +// make sure gcc doesn't inline them - we prefer size and not +// speed for this file +static status_t Radeon_SetDPMS_CRTC1( accelerator_info *di, int mode ); +static status_t Radeon_SetDPMS_CRTC2( accelerator_info *di, int mode ); +static uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di ); +static uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di ); + +status_t SET_DPMS_MODE(uint32 dpms_flags); +uint32 DPMS_CAPABILITIES(void); +uint32 DPMS_MODE(void); + + +// public function: set DPMS mode +status_t SET_DPMS_MODE(uint32 dpms_flags) +{ + virtual_card *vc = ai->vc; + status_t result1, result2; + + result1 = Radeon_SetDPMS( ai, &vc->ports[0], dpms_flags ); + + if( vc->independant_ports > 1 ) + result2 = Radeon_SetDPMS( ai, &vc->ports[1], dpms_flags ); + else + result2 = B_OK; + + if( result1 == B_OK && result2 == B_OK ) + return B_OK; + else + return B_ERROR; +} + +// public function: report DPMS capabilities +uint32 DPMS_CAPABILITIES(void) +{ + return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF; +} + + +// public function: get current DPMS mode +uint32 DPMS_MODE(void) +{ + // we just ask the primary port what status it is in + return Radeon_GetDPMS( ai, &ai->vc->ports[0] ); +} + + +// set DPMS mode of one port +status_t Radeon_SetDPMS( accelerator_info *ai, virtual_port *port, int mode ) +{ + // if we have a laptop panel + // and we have a second screen connected + // and they both show the same content, + // then switch the laptop display always off + if( ai->si->ports[port->physical_port].disp_type == dt_lvds && + ai->vc->independant_ports > 1 && + ai->vc->different_ports == 1 ) + { + mode = B_DPMS_OFF; + } + + if( port->is_crtc2 ) + return Radeon_SetDPMS_CRTC2( ai, mode ); + else + return Radeon_SetDPMS_CRTC1( ai, mode ); +} + + +// get DPMS mode of one port +uint32 Radeon_GetDPMS( accelerator_info *ai, virtual_port *port ) +{ + if( port->is_crtc2 ) + return Radeon_GetDPMS_CRTC2( ai ); + else + return Radeon_GetDPMS_CRTC1( ai ); +} + + +// set DPMS mode for first port +status_t Radeon_SetDPMS_CRTC1( accelerator_info *ai, int mode ) +{ + vuint8 *regs = ai->regs; + shared_info *si = ai->si; + + int mask = RADEON_CRTC_DISPLAY_DIS + | RADEON_CRTC_HSYNC_DIS + | RADEON_CRTC_VSYNC_DIS; + + switch( mode ) { + case B_DPMS_ON: + /* Screen: On; HSync: On, VSync: On */ + OUTREGP( regs, RADEON_CRTC_EXT_CNTL, 0, ~mask ); + break; + case B_DPMS_STAND_BY: + /* Screen: Off; HSync: Off, VSync: On */ + OUTREGP( regs, RADEON_CRTC_EXT_CNTL, + RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS, ~mask ); + break; + case B_DPMS_SUSPEND: + /* Screen: Off; HSync: On, VSync: Off */ + OUTREGP( regs, RADEON_CRTC_EXT_CNTL, + RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS, ~mask ); + break; + case B_DPMS_OFF: + /* Screen: Off; HSync: Off, VSync: Off */ + OUTREGP( regs, RADEON_CRTC_EXT_CNTL, mask, ~mask ); + break; + default: + return B_BAD_VALUE; + } + + // if this is a flat panel, switch off backlight too + if( si->ports[0].disp_type == dt_dvi_1 || si->ports[0].disp_type == dt_lvds ) { + switch( mode ) { + case B_DPMS_ON: + // on my laptop, the display has problems to wake-up, this + // should hopefully cure that + // (you get a dark picture first that becomes brighter step by step, + // after a couple of seconds you have full brightness again) + OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON, ~RADEON_LVDS_BLON ); + //snooze( ai->si->fp_port.panel_pwr_delay * 1000 ); + OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_BLON | RADEON_LVDS_ON, + ~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) ); + break; + case B_DPMS_STAND_BY: + case B_DPMS_SUSPEND: + case B_DPMS_OFF: + OUTREGP( regs, RADEON_LVDS_GEN_CNTL, RADEON_LVDS_DISPLAY_DIS, + ~(RADEON_LVDS_DISPLAY_DIS | RADEON_LVDS_BLON | RADEON_LVDS_ON) ); + break; + } + } + + // it seems that DPMS doesn't work on DVI, so we disable FP completely + // (according to specs this is the official way to handle DVI though DPMS + // *should* be supported as well) + if( si->ports[0].disp_type == dt_dvi_1 ) { + switch( mode ) { + case B_DPMS_ON: + OUTREGP( regs, RADEON_FP_GEN_CNTL, RADEON_FP_FPON, ~RADEON_FP_FPON ); + break; + case B_DPMS_STAND_BY: + case B_DPMS_SUSPEND: + case B_DPMS_OFF: + OUTREGP( regs, RADEON_FP_GEN_CNTL, 0, ~RADEON_FP_FPON ); + break; + } + } + + return B_OK; +} + + +// set DPMS mode of second port +status_t Radeon_SetDPMS_CRTC2( accelerator_info *di, int mode ) +{ + vuint8 *regs = di->regs; + + int mask = RADEON_CRTC2_DISP_DIS + | RADEON_CRTC2_HSYNC_DIS + | RADEON_CRTC2_VSYNC_DIS; + + switch( mode ) { + case B_DPMS_ON: + /* Screen: On; HSync: On, VSync: On */ + OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, 0, ~mask ); + break; + case B_DPMS_STAND_BY: + /* Screen: Off; HSync: Off, VSync: On */ + OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, + RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS, ~mask ); + break; + case B_DPMS_SUSPEND: + /* Screen: Off; HSync: On, VSync: Off */ + OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, + RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS, ~mask ); + break; + case B_DPMS_OFF: + /* Screen: Off; HSync: Off, VSync: Off */ + OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, mask, ~mask ); + break; + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +// get DPMS mode of first port +uint32 Radeon_GetDPMS_CRTC1( accelerator_info *di ) +{ + uint32 tmp; + + tmp = INREG( di->regs, RADEON_CRTC_EXT_CNTL ); + + if( (tmp & RADEON_CRTC_DISPLAY_DIS) == 0 ) + return B_DPMS_ON; + + if( (tmp & RADEON_CRTC_VSYNC_DIS) == 0 ) + return B_DPMS_STAND_BY; + + if( (tmp & RADEON_CRTC_HSYNC_DIS) == 0 ) + return B_DPMS_SUSPEND; + + return B_DPMS_OFF; +} + + +// get DPMS mode of second port +uint32 Radeon_GetDPMS_CRTC2( accelerator_info *di ) +{ + uint32 tmp; + + tmp = INREG( di->regs, RADEON_CRTC2_GEN_CNTL ); + + if( (tmp & RADEON_CRTC2_DISP_DIS) == 0 ) + return B_DPMS_ON; + + if( (tmp & RADEON_CRTC2_VSYNC_DIS) == 0 ) + return B_DPMS_STAND_BY; + + if( (tmp & RADEON_CRTC2_HSYNC_DIS) == 0 ) + return B_DPMS_SUSPEND; + + return B_DPMS_OFF; +} diff --git a/src/add-ons/accelerants/radeon/dump_edid.c b/src/add-ons/accelerants/radeon/dump_edid.c new file mode 100644 index 0000000000..2e3568263e --- /dev/null +++ b/src/add-ons/accelerants/radeon/dump_edid.c @@ -0,0 +1,152 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Dumps EDID content +*/ + +#include "edid.h" +#include +#include "ddc_int.h" +#include + +void edid_dump( edid1_info *edid ) +{ + int i, j; + char buffer[256]; + + SHOW_INFO( 0, "Vendor: %s", edid->vendor.manufacturer ); + SHOW_INFO( 0, "Product ID: %d", (int)edid->vendor.prod_id ); + SHOW_INFO( 0, "Serial #: %d", (int)edid->vendor.serial ); + SHOW_INFO( 0, "Produced in week/year: %d/%d", edid->vendor.week, edid->vendor.year ); + + SHOW_INFO( 0, "EDID version: %d.%d", edid->version.version, edid->version.revision ); + + SHOW_INFO( 0, "Type: %s", edid->display.input_type ? "Digital" : "Analog" ); + SHOW_INFO( 0, "Size: %d cm x %d cm", edid->display.h_size, edid->display.v_size ); + SHOW_INFO( 0, "Gamma=%.3f", (edid->display.gamma + 100) / 100.0 ); + SHOW_INFO( 0, "White (X,Y)=(%.3f,%.3f)", edid->display.white_x / 1024.0, edid->display.white_y / 1024.0 ); + + SHOW_INFO0( 0, "Supported Future Video Modes:" ); + for( i = 0; i < EDID1_NUM_STD_TIMING; ++i ) { + if( edid->std_timing[i].h_size <= 256 ) + continue; + + SHOW_INFO( 0, "%dx%d@%dHz (id=%d)", + edid->std_timing[i].h_size, edid->std_timing[i].v_size, + edid->std_timing[i].refresh, edid->std_timing[i].id ); + } + + SHOW_INFO0( 0, "Supported VESA Video Modes:" ); + if( edid->established_timing.res_720x400x70 ) + SHOW_INFO0( 0, "720x400@70" ); + if( edid->established_timing.res_720x400x88 ) + SHOW_INFO0( 0, "720x400@88" ); + if( edid->established_timing.res_640x480x60 ) + SHOW_INFO0( 0, "640x480@60" ); + if( edid->established_timing.res_640x480x67 ) + SHOW_INFO0( 0, "640x480x67" ); + if( edid->established_timing.res_640x480x72 ) + SHOW_INFO0( 0, "640x480x72" ); + if( edid->established_timing.res_640x480x75 ) + SHOW_INFO0( 0, "640x480x75" ); + if( edid->established_timing.res_800x600x56 ) + SHOW_INFO0( 0, "800x600@56" ); + if( edid->established_timing.res_800x600x60 ) + SHOW_INFO0( 0, "800x600@60" ); + + if( edid->established_timing.res_800x600x72 ) + SHOW_INFO0( 0, "800x600@72" ); + if( edid->established_timing.res_800x600x75 ) + SHOW_INFO0( 0, "800x600@75" ); + if( edid->established_timing.res_832x624x75 ) + SHOW_INFO0( 0, "832x624@75" ); + if( edid->established_timing.res_1024x768x87i ) + SHOW_INFO0( 0, "1024x768@87 interlaced" ); + if( edid->established_timing.res_1024x768x60 ) + SHOW_INFO0( 0, "1024x768@60" ); + if( edid->established_timing.res_1024x768x70 ) + SHOW_INFO0( 0, "1024x768@70" ); + if( edid->established_timing.res_1024x768x75 ) + SHOW_INFO0( 0, "1024x768@75" ); + if( edid->established_timing.res_1280x1024x75 ) + SHOW_INFO0( 0, "1280x1024@75" ); + + if( edid->established_timing.res_1152x870x75 ) + SHOW_INFO0( 0, "1152x870@75" ); + + for( i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i ) { + edid1_detailed_monitor *monitor = &edid->detailed_monitor[i]; + + switch( monitor->monitor_desc_type ) { + case edid1_serial_number: + SHOW_INFO( 0, "Serial Number: %s", monitor->data.serial_number ); + break; + case edid1_ascii_data: + SHOW_INFO( 0, " %s", monitor->data.serial_number ); + break; + case edid1_monitor_ranges: { + edid1_monitor_range monitor_range = monitor->data.monitor_range; + + SHOW_INFO( 0, "Horizontal frequency range = %d..%d kHz", + monitor_range.min_h, monitor_range.max_h ); + SHOW_INFO( 0, "Vertical frequency range = %d..%d Hz", + monitor_range.min_v, monitor_range.max_v ); + SHOW_INFO( 0, "Maximum pixel clock = %d MHz", (uint16)monitor_range.max_clock * 10 ); + break; } + case edid1_monitor_name: + SHOW_INFO( 0, "Monitor Name: %s", monitor->data.serial_number ); + break; + case edid1_add_colour_pointer: { + for( j = 0; j < EDID1_NUM_EXTRA_WHITEPOINTS; ++j ) { + edid1_whitepoint *whitepoint = &monitor->data.whitepoint[j]; + + if( whitepoint->index == 0 ) + continue; + + sprintf( buffer, "Additional whitepoint: (X,Y)=(%f,%f) gamma=%f index=%i", + whitepoint->white_x / 1024.0, + whitepoint->white_y / 1024.0, + (whitepoint->gamma + 100) / 100.0, + whitepoint->index ); + SHOW_INFO( 0, "%s", buffer ); + } + break; } + case edid1_add_std_timing: { + for( j = 0; j < EDID1_NUM_EXTRA_STD_TIMING; ++j ) { + edid1_std_timing *timing = &monitor->data.std_timing[j]; + + if( timing->h_size <= 256 ) + continue; + + SHOW_INFO( 0, "%dx%d@%dHz (id=%d)", + timing->h_size, timing->v_size, + timing->refresh, timing->id ); + } + break; } + case edid1_is_detailed_timing: { + edid1_detailed_timing *timing = &monitor->data.detailed_timing; + + SHOW_INFO0( 0, "Additional Video Mode:" ); + sprintf( buffer, "clock=%f MHz", timing->pixel_clock / 100.0 ); + SHOW_INFO( 0, "%s", buffer ); + SHOW_INFO( 0, "h: (%d, %d, %d, %d)", + timing->h_active, timing->h_active + timing->h_sync_off, + timing->h_active + timing->h_sync_off + timing->h_sync_width, + timing->h_active + timing->h_blank ); + SHOW_INFO( 0, "v: (%d, %d, %d, %d)", + timing->v_active, timing->v_active + timing->v_sync_off, + timing->v_active + timing->v_sync_off + timing->v_sync_width, + timing->v_active + timing->v_blank ); + sprintf( buffer, "size: %.1f cm x %.1f cm", + timing->h_size / 10.0, timing->v_size / 10.0 ); + SHOW_INFO( 0, "%s", buffer ); + sprintf( buffer, "border: %.1f cm x %.1f cm", + timing->h_border / 10.0, timing->v_border / 10.0 ); + SHOW_INFO( 0, "%s", buffer ); + break; } + } + } +} diff --git a/src/add-ons/accelerants/radeon/edid.c b/src/add-ons/accelerants/radeon/edid.c new file mode 100644 index 0000000000..09747a42eb --- /dev/null +++ b/src/add-ons/accelerants/radeon/edid.c @@ -0,0 +1,220 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + EDID handling. +*/ + +#include "edid.h" +#include +#include "ddc_int.h" + +// +// from hereon a bunch of decoders follow for each EDID section +// + +static void decode_vendor( edid1_vendor *vendor, const edid1_vendor_raw *raw ) +{ + vendor->manufacturer[0] = raw->c1 + '@'; + vendor->manufacturer[1] = + ((raw->c2_high << 3) | raw->c2_low) + '@'; + vendor->manufacturer[2] = raw->c3 + '@'; + vendor->manufacturer[3] = 0; + vendor->prod_id = B_LENDIAN_TO_HOST_INT16( raw->prod_id ); + vendor->serial = B_LENDIAN_TO_HOST_INT32( raw->serial ); + vendor->week = raw->week; + vendor->year = raw->year + 1990; +} + +static void decode_version( edid1_version *version, const edid1_version_raw *raw ) +{ + version->version = raw->version; + version->revision = raw->revision; +} + +static void decode_display( edid1_display *display, const edid1_display_raw *raw ) +{ + display->input_type = raw->input_type; + display->input_voltage = raw->input_voltage; + display->setup = raw->setup; + display->sep_sync = raw->sep_sync; + display->comp_sync = raw->comp_sync; + display->sync_on_green = raw->sync_on_green; + display->sync_serr = raw->sync_serr; + + display->h_size = raw->h_size; + display->v_size = raw->v_size; + display->gamma = raw->gamma; + + display->dpms_standby = raw->dpms_standby; + display->dpms_suspend = raw->dpms_suspend; + display->dpms_off = raw->dpms_off; + display->display_type = raw->display_type; + display->std_colour_space = raw->std_colour_space; + display->preferred_timing_mode = raw->preferred_timing_mode; + display->gtf_supported = raw->gtf_supported; + + display->red_x = ((uint16)raw->red_x << 2) | raw->red_x_low; + display->red_y = ((uint16)raw->red_y << 2) | raw->red_y_low; + display->green_x = ((uint16)raw->green_x << 2) | raw->green_x_low; + display->green_y = ((uint16)raw->green_y << 2) | raw->green_y_low; + display->blue_x = ((uint16)raw->blue_x << 2) | raw->blue_x_low; + display->blue_y = ((uint16)raw->blue_y << 2) | raw->blue_y_low; + display->white_x = ((uint16)raw->white_x << 2) | raw->white_x_low; + display->white_y = ((uint16)raw->white_y << 2) | raw->white_y_low; +} + +static void decode_std_timing( edid1_std_timing *timing, + const edid1_std_timing_raw *raw ) +{ + timing->h_size = (raw->timing.h_size + 31) * 8; + timing->ratio = raw->timing.ratio; + switch( raw->timing.ratio ) { + case 0: + timing->v_size = timing->h_size; + break; + case 1: + timing->v_size = timing->h_size * 3 / 4; + break; + case 2: + timing->v_size = timing->h_size * 4 / 5; + break; + case 3: + timing->v_size = timing->h_size * 9 / 16; + break; + } + timing->refresh = raw->timing.refresh + 60; + timing->id = raw->id; +} + +static void decode_whitepoint( edid1_whitepoint *whitepoint, + const edid1_whitepoint_raw *raw ) +{ + whitepoint[0].index = raw->index1; + whitepoint[0].white_x = ((uint16)raw->white_x1 << 2) | raw->white_x1_low; + whitepoint[0].white_y = ((uint16)raw->white_y1 << 2) | raw->white_y1_low; + whitepoint[0].gamma = raw->gamma1; + + whitepoint[1].index = raw->index2; + whitepoint[1].white_x = ((uint16)raw->white_x2 << 2) | raw->white_x2_low; + whitepoint[1].white_y = ((uint16)raw->white_y2 << 2) | raw->white_y2_low; + whitepoint[1].gamma = raw->gamma2; +} + +static void decode_detailed_timing( edid1_detailed_timing *timing, + const edid1_detailed_timing_raw *raw ) +{ + timing->pixel_clock = raw->pixel_clock; + timing->h_active = ((uint16)raw->h_active_high << 8) | raw->h_active; + timing->h_blank = ((uint16)raw->h_blank_high << 8) | raw->h_blank; + timing->v_active = ((uint16)raw->v_active_high << 8) | raw->v_active; + timing->v_blank = ((uint16)raw->v_blank_high << 8) | raw->v_blank; + timing->h_sync_off = ((uint16)raw->h_sync_off_high << 8) | raw->h_sync_off; + timing->h_sync_width = ((uint16)raw->h_sync_width_high << 8) | raw->h_sync_width; + timing->v_sync_off = ((uint16)raw->v_sync_off_high << 4) | raw->v_sync_off; + timing->v_sync_width = ((uint16)raw->v_sync_width_high << 4) | raw->v_sync_width; + timing->h_size = ((uint16)raw->h_size_high << 8) | raw->h_size; + timing->v_size = ((uint16)raw->v_size_high << 8) | raw->v_size; + timing->h_border = raw->h_border; + timing->v_border = raw->v_border; + timing->interlaced = raw->interlaced; + timing->stereo = raw->stereo; + timing->sync = raw->sync; + timing->misc = raw->misc; +} + +// copy string until 0xa, removing trailing spaces +static void copy_str( char *dest, const uint8 *src, size_t len ) +{ + int i; + + // copy until 0xa + for( i = 0; i < (int)len; ++i ) { + if( src[i] == 0xa ) + break; + + *dest++ = *src++; + } + + // remove trailing spaces + for( i = i - 1; i >= 0; --i ) { + if( *dest-- != ' ' ) + break; + } + + *++dest = 0; +} + +static void decode_detailed_monitor( edid1_detailed_monitor *monitor, + const edid1_detailed_monitor_raw *raw, bool enable_extra ) +{ + int i, j; + + for( i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i, ++monitor, ++raw ) { + monitor->monitor_desc_type = edid1_is_detailed_timing; + + // workaround: normally, all four bytes must be zero for detailed + // description, but at least some Formac monitors violate that: + // they have some additional info that start at zero_4(!), + // so even if only the first two _or_ the other two bytes are + // zero, we accept it as a monitor description block + if( enable_extra && + ((raw->extra.zero_0[0] == 0 && raw->extra.zero_0[1] == 0) || + (raw->extra.zero_0[2] == 0 && raw->extra.zero_4 == 0)) ) + { + monitor->monitor_desc_type = raw->extra.monitor_desc_type; + + switch( raw->extra.monitor_desc_type ) { + case edid1_serial_number: + copy_str( monitor->data.serial_number, + raw->extra.data.serial_number, EDID1_EXTRA_STRING_LEN ); + break; + case edid1_ascii_data: + copy_str( monitor->data.ascii_data, + raw->extra.data.ascii_data, EDID1_EXTRA_STRING_LEN ); + break; + case edid1_monitor_ranges: + monitor->data.monitor_range = raw->extra.data.monitor_range; + break; + case edid1_monitor_name: + copy_str( monitor->data.monitor_name, + raw->extra.data.monitor_name, EDID1_EXTRA_STRING_LEN ); + break; + case edid1_add_colour_pointer: + decode_whitepoint( monitor->data.whitepoint, + &raw->extra.data.whitepoint ); + break; + case edid1_add_std_timing: + for( j = 0; j < EDID1_NUM_EXTRA_STD_TIMING; ++j ) + decode_std_timing( &monitor->data.std_timing[j], + &raw->extra.data.std_timing[j] ); + break; + } + } else { + decode_detailed_timing( &monitor->data.detailed_timing, + &raw->detailed_timing ); + } + } +} + +// main function to decode edid data +void edid_decode( edid1_info *edid, const edid1_raw *raw ) +{ + int i; + + memset( edid, 0, sizeof( edid )); + + decode_vendor( &edid->vendor, &raw->vendor ); + decode_version( &edid->version, &raw->version ); + decode_display( &edid->display, &raw->display ); + + edid->established_timing = raw->established_timing; + + for( i = 0; i < EDID1_NUM_STD_TIMING; ++i ) + decode_std_timing( &edid->std_timing[i], &raw->std_timing[i] ); + + decode_detailed_monitor( edid->detailed_monitor, raw->detailed_monitor, + edid->version.version == 1 && edid->version.revision >= 1 ); +} diff --git a/src/add-ons/accelerants/radeon/edid.h b/src/add-ons/accelerants/radeon/edid.h new file mode 100644 index 0000000000..3b0d038158 --- /dev/null +++ b/src/add-ons/accelerants/radeon/edid.h @@ -0,0 +1,146 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + EDID handling, including decoded EDID data block definitin. +*/ + +#ifndef _EDID_H +#define _EDID_H + +#include "edid_raw.h" + +// vendor info +typedef struct { + char manufacturer[4]; + uint16 prod_id; + uint32 serial; + uint8 week; + uint16 year; +} edid1_vendor; + + +// version info +typedef struct { + uint8 version; + uint8 revision; +} edid1_version; + + +// display info +typedef struct { + BBITFIELD8_7 ( + input_type : 1, // 1 : digital + input_voltage : 2, // 0=0.7V/0.3V, 1=0.714V/0.286, + // 2=1V/0.4V, 3=0.7V/0V + setup : 1, // true if voltage configurable + sep_sync : 1, + comp_sync : 1, + sync_on_green : 1, + sync_serr : 1 + ); + uint8 h_size; + uint8 v_size; + uint8 gamma; // (x+100)/100 + BBITFIELD8_7 ( + dpms_standby : 1, + dpms_suspend : 1, + dpms_off : 1, + display_type : 2, // 0=mono, 1=rgb, 2=multicolour + // since EDID version 1.1 + std_colour_space : 1, + preferred_timing_mode : 1, + gtf_supported : 1 + ); + uint16 red_x; // all colours are 0.10 fixed point + uint16 red_y; + uint16 green_x; + uint16 green_y; + uint16 blue_x; + uint16 blue_y; + uint16 white_x; + uint16 white_y; +} edid1_display; + + +// standard timing data +typedef struct { + uint16 h_size; + uint16 v_size; + uint16 id; + uint8 ratio; + uint8 refresh; +} edid1_std_timing; + + +// additional whitepoint +typedef struct { + uint8 index; + uint16 white_x; + uint16 white_y; + uint8 gamma; // (x+100)/100 +} edid1_whitepoint; + + +// detailed timing description +typedef struct { + uint16 pixel_clock; // in 10 kHz + uint16 h_active; + uint16 h_blank; + uint16 v_active; + uint16 v_blank; + uint16 h_sync_off; + uint16 h_sync_width; + uint16 v_sync_off; + uint16 v_sync_width; + uint16 h_size; + uint16 v_size; + uint16 h_border; + uint16 v_border; + BBITFIELD8_4 ( + interlaced : 1, + stereo : 2, // upper bit set - left on sync + // lower bit set - right on sync + sync : 2, + misc : 2 + ); +} edid1_detailed_timing; + + +// detailed monitor description +typedef struct { + uint8 monitor_desc_type; + union { + char serial_number[EDID1_EXTRA_STRING_LEN]; + char ascii_data[EDID1_EXTRA_STRING_LEN]; + edid1_monitor_range monitor_range; + char monitor_name[EDID1_EXTRA_STRING_LEN]; + edid1_whitepoint whitepoint[EDID1_NUM_EXTRA_WHITEPOINTS]; + edid1_std_timing std_timing[EDID1_NUM_EXTRA_STD_TIMING]; + edid1_detailed_timing detailed_timing; + } data; +} edid1_detailed_monitor; + + +// EDID data block +typedef struct{ + edid1_vendor vendor; + edid1_version version; + edid1_display display; + edid1_established_timing established_timing; + edid1_std_timing std_timing[EDID1_NUM_STD_TIMING]; + + // since EDID version 1.2 + edid1_detailed_monitor detailed_monitor[EDID1_NUM_DETAILED_MONITOR_DESC]; + + uint8 num_sections; +} edid1_info; + +// decode raw EDID info into usuable EDID info +void edid_decode( edid1_info *edid, const edid1_raw *raw ); +// dump EDID info to syslog +void edid_dump( edid1_info *edid ); + +#endif diff --git a/src/add-ons/accelerants/radeon/edid_raw.h b/src/add-ons/accelerants/radeon/edid_raw.h new file mode 100644 index 0000000000..fc1ade055f --- /dev/null +++ b/src/add-ons/accelerants/radeon/edid_raw.h @@ -0,0 +1,278 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Raw EDID data block. + + Raw data are packed in a really weird way. Never even + think about using it directly, instead translate it via decode_edidpixel_clock + first. I did my best to make the code endian-independant, but + I cannot guarantee that I haven't a missed something. +*/ + +#ifndef _EDID_RAW_H +#define _EDID_RAW_H + +#include "bendian_bitfield.h" + +#define EDID1_NUM_DETAILED_MONITOR_DESC 4 +#define EDID1_NUM_STD_TIMING 8 +#define EDID1_NUM_EXTRA_STD_TIMING 6 +#define EDID1_EXTRA_STRING_LEN 13 +#define EDID1_NUM_EXTRA_WHITEPOINTS 2 + + +// header +typedef struct _PACKED { + int8 pad[8]; // contains 0, -1, -1, -1, -1, -1, -1, 0 +} edid1_header_raw; + + +// vendor info +typedef struct _PACKED { + BBITFIELD8_3 ( // manufacturer + pad : 1, + c1 : 5, // add '@' to get ascii + c2_high : 2 + ); + BBITFIELD8_2 ( + c2_low : 3, + c3 : 5 + ); + uint16 prod_id; + uint32 serial; + uint8 week; + uint8 year; // x+1990 +} edid1_vendor_raw; + + +// version info +typedef struct _PACKED { + uint8 version; + uint8 revision; +} edid1_version_raw; + + +// display info +typedef struct _PACKED { + BBITFIELD8_7 ( + input_type : 1, // 1 : digital + input_voltage : 2, // 0=0.7V/0.3V, 1=0.714V/0.286, + // 2=1V/0.4V, 3=0.7V/0V + setup : 1, // true if voltage configurable + sep_sync : 1, + comp_sync : 1, + sync_on_green : 1, + sync_serr : 1 + ); + uint8 h_size; + uint8 v_size; + uint8 gamma; // (x+100)/100 + BBITFIELD8_7 ( + dpms_standby : 1, + dpms_suspend : 1, + dpms_off : 1, + display_type : 2, // 0=mono, 1=rgb, 2=multicolour + // since EDID version 1.1 + std_colour_space : 1, + preferred_timing_mode : 1, + gtf_supported : 1 + ); + BBITFIELD8_4 ( // low bits of red_x etc. + red_x_low : 2, + red_y_low : 2, + green_x_low : 2, + green_y_low : 2 + ); + BBITFIELD8_4 ( + blue_x_low : 2, + blue_y_low : 2, + white_x_low : 2, + white_y_low : 2 + ); + uint8 red_x; // all colours are 0.10 fixed point + uint8 red_y; + uint8 green_x; + uint8 green_y; + uint8 blue_x; + uint8 blue_y; + uint8 white_x; + uint8 white_y; +} edid1_display_raw; + + +// raw standard timing data +typedef union _PACKED { + struct _PACKED { + uint8 h_size; // (x+31)*8 + BBITFIELD8_2 ( + ratio : 2, // 0=1:1, 1=3/4, 2=4/5, 3=9/16 + refresh : 6 // (x+60) + ); + } timing; + uint16 id; +} edid1_std_timing_raw; + + +// list of supported fixed timings +typedef struct _PACKED { + BBITFIELD8_8 ( + res_720x400x70 : 1, + res_720x400x88 : 1, + res_640x480x60 : 1, + res_640x480x67 : 1, + res_640x480x72 : 1, + res_640x480x75 : 1, + res_800x600x56 : 1, + res_800x600x60 : 1 + ); + BBITFIELD8_8 ( + res_800x600x72 : 1, + res_800x600x75 : 1, + res_832x624x75 : 1, + res_1024x768x87i : 1, + res_1024x768x60 : 1, + res_1024x768x70 : 1, + res_1024x768x75 : 1, + res_1280x1024x75 : 1 + ); + BBITFIELD8_2 ( + res_1152x870x75 : 1, + pad : 7 + ); +} edid1_established_timing; + + +// types of detailed monitor description +enum { + edid1_serial_number = 0xff, + edid1_ascii_data = 0xfe, + edid1_monitor_ranges = 0xfd, + edid1_monitor_name = 0xfc, + edid1_add_colour_pointer = 0xfb, + edid1_add_std_timing = 0xfa, + edid1_is_detailed_timing = 1 +}; + + +// monitor frequency range +typedef struct _PACKED { + uint8 min_v; + uint8 max_v; + uint8 min_h; + uint8 max_h; + uint8 max_clock; // in 10 MHz (!) +} edid1_monitor_range; + + +// additional whitepoint +typedef struct _PACKED { + uint8 index1; + BBITFIELD8_3 ( + pad1 : 4, + white_x1_low : 2, + white_y1_low : 2 + ); + uint8 white_x1; + uint8 white_y1; + uint8 gamma1; // (x+100)/100 + uint8 index2; + BBITFIELD8_3 ( + pad2 : 4, + white_x2_low : 2, + white_y2_low : 2 + ); + uint8 white_x2; + uint8 white_y2; + uint8 gamma2; // (x+100)/100 +} edid1_whitepoint_raw; + + +// detailed timing description +typedef struct _PACKED { + uint16 pixel_clock; // in 10 kHz (!) + uint8 h_active; + uint8 h_blank; + BBITFIELD8_2 ( + h_active_high : 4, + h_blank_high : 4 + ); + uint8 v_active; + uint8 v_blank; + BBITFIELD8_2 ( + v_active_high : 4, + v_blank_high : 4 + ); + uint8 h_sync_off; + uint8 h_sync_width; + BBITFIELD8_2 ( + v_sync_off : 4, + v_sync_width : 4 + ); + BBITFIELD8_4 ( + h_sync_off_high : 2, + h_sync_width_high : 2, + v_sync_off_high : 2, + v_sync_width_high : 2 + ); + uint8 h_size; + uint8 v_size; + BBITFIELD8_2 ( + h_size_high : 4, + v_size_high : 4 + ); + uint8 h_border; + uint8 v_border; + BBITFIELD8_4 ( + interlaced : 1, + stereo : 2, // upper bit set - left on sync + // lower bit set - right on sync + sync : 2, + misc : 2 + ); +} edid1_detailed_timing_raw; + + +// detailed monitor description +typedef union _PACKED { + edid1_detailed_timing_raw detailed_timing; + struct _PACKED { + uint8 zero_0[3]; + uint8 monitor_desc_type; + uint8 zero_4; + union _PACKED { + uint8 serial_number[EDID1_EXTRA_STRING_LEN]; + uint8 ascii_data[EDID1_EXTRA_STRING_LEN]; + uint8 monitor_name[EDID1_EXTRA_STRING_LEN]; + edid1_monitor_range monitor_range; + edid1_whitepoint_raw whitepoint; + edid1_std_timing_raw std_timing[EDID1_NUM_EXTRA_STD_TIMING]; + + } data; + } extra; +} edid1_detailed_monitor_raw; + + +// raw EDID data +// everything is packed data, mixture of little endian and big endian +// and a bit brain dead overall - nothing your dad would be proud of +typedef struct _PACKED { + edid1_header_raw header; // 8 bytes + edid1_vendor_raw vendor; // 10 bytes + edid1_version_raw version; // 2 bytes + edid1_display_raw display; // 15 bytes + edid1_established_timing established_timing; // 3 bytes + edid1_std_timing_raw std_timing[EDID1_NUM_STD_TIMING]; + // 8 a 2 bytes -> 16 bytes + + // since EDID version 1.2 + edid1_detailed_monitor_raw detailed_monitor[EDID1_NUM_DETAILED_MONITOR_DESC]; + // 4 a 18 bytes -> 72 bytes + + uint8 num_sections; // 1 byte + uint8 check_sum; // 1 byte +} edid1_raw; // total: 128 bytes + +#endif diff --git a/src/add-ons/accelerants/radeon/engine_sync.c b/src/add-ons/accelerants/radeon/engine_sync.c new file mode 100644 index 0000000000..ff19beab27 --- /dev/null +++ b/src/add-ons/accelerants/radeon/engine_sync.c @@ -0,0 +1,247 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Syncing to graphics card engine +*/ + +#include "radeon_accelerant.h" +//#include "../include/radeon_regs.h" +#include "mmio.h" +#include "cp_regs.h" +#include "pll_regs.h" +#include "rbbm_regs.h" +#include "buscntrl_regs.h" + +#include "log_coll.h" +#include "log_enum.h" + +#include + +void Radeon_FlushPixelCache( accelerator_info *ai ); + +// send command to purge cache +// (may not work with pre-r200 as affected registers +// aren't described there) +void Radeon_SendPurgeCache( accelerator_info *ai ) +{ + uint32 buffer[2]; + + buffer[0] = CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ); + buffer[1] = RADEON_RB2D_DC_FLUSH_ALL; + + Radeon_SendCP( ai, buffer, 2 ); +} + +// send command to wait until everything is idle +void Radeon_SendWaitUntilIdle( accelerator_info *ai ) +{ + uint32 buffer[2]; + + buffer[0] = CP_PACKET0( RADEON_WAIT_UNTIL, 0 ); + buffer[1] = RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_3D_IDLECLEAN | + RADEON_WAIT_HOST_IDLECLEAN; + + Radeon_SendCP( ai, buffer, 2 ); +} + +// make sure all drawing is finished +void Radeon_Finish( accelerator_info *ai ) +{ + shared_info *si = ai->si; + + LOG( si->log, _Radeon_Finish ); + + OUTREG( ai->regs, RADEON_CP_RB_WPTR, si->ring.tail ); + Radeon_WaitForIdle( ai ); + Radeon_FlushPixelCache( ai ); +} + +// wait until engine is idle +int Radeon_WaitForIdle( accelerator_info *ai ) +{ + SHOW_FLOW0( 3, "" ); + + Radeon_WaitForFifo( ai, 64 ); + + while( 1 ) { + bigtime_t start_time = system_time(); + + do { + if( (INREG( ai->regs, RADEON_RBBM_STATUS ) & RADEON_RBBM_ACTIVE) == 0 ) { + Radeon_FlushPixelCache( ai ); + return 0; + } + + snooze( 1 ); + } while( system_time() - start_time < 1000000 ); + + SHOW_ERROR0( 3, "Engine didn't become idle" ); + + LOG( ai->si->log, _Radeon_WaitForIdle ); + + Radeon_ResetEngine( ai ); + } +} + +// wait until "entries" FIFO entries are empty +void Radeon_WaitForFifo( accelerator_info *ai, int entries ) +{ + SHOW_FLOW( 4, "entries=%ld", entries ); + + while( 1 ) { + bigtime_t start_time = system_time(); + + do { + int slots = INREG( ai->regs, RADEON_RBBM_STATUS ) & RADEON_RBBM_FIFOCNT_MASK; + + SHOW_FLOW( 4, "empty slots: %ld", slots ); + + if ( slots >= entries ) + return; + + snooze( 1 ); + } while( system_time() - start_time < 1000000 ); + + LOG( ai->si->log, _Radeon_WaitForFifo ); + + Radeon_ResetEngine( ai ); + } +} + +// flush pixel cache of graphics card +void Radeon_FlushPixelCache( accelerator_info *ai ) +{ + bigtime_t start_time; + + SHOW_FLOW0( 3, "" ); + + OUTREGP( ai->regs, RADEON_RB2D_DSTCACHE_CTLSTAT, RADEON_RB2D_DC_FLUSH_ALL, + ~RADEON_RB2D_DC_FLUSH_ALL ); + + start_time = system_time(); + + do { + if( (INREG( ai->regs, RADEON_RB2D_DSTCACHE_CTLSTAT ) + & RADEON_RB2D_DC_BUSY) == 0 ) + return; + + snooze( 1 ); + } while( system_time() - start_time < 1000000 ); + + LOG( ai->si->log, _Radeon_FlushPixelCache ); + + SHOW_ERROR0( 0, "pixel cache didn't become empty" ); +} + +// reset graphics card's engine +void Radeon_ResetEngine( accelerator_info *ai ) +{ + vuint8 *regs = ai->regs; + shared_info *si = ai->si; + uint32 clock_cntl_index, mclk_cntl, rbbm_soft_reset, host_path_cntl; + uint32 cur_read_ptr; + + SHOW_FLOW0( 3, "" ); + + Radeon_FlushPixelCache( ai ); + + clock_cntl_index = INREG( regs, RADEON_CLOCK_CNTL_INDEX ); + R300_PLLFix( ai ); + + // OUCH! + // XFree disables any kind of automatic power power management + // because of bugs of some ASIC revision (seems like the revisions + // cannot be read out) + // -> this is a very bad idea, especially when it comes to laptops + // I comment it out for now, let's hope noone takes notice + if( ai->si->has_crtc2 ) { + Radeon_OUTPLLP( ai, RADEON_SCLK_CNTL, + RADEON_CP_MAX_DYN_STOP_LAT | + RADEON_SCLK_FORCEON_MASK, + ~RADEON_DYN_STOP_LAT_MASK ); + +/* if( ai->si->asic == rt_rv200 ) { + Radeon_OUTPLLP( ai, RADEON_SCLK_MORE_CNTL, + RADEON_SCLK_MORE_FORCEON, ~0 ); + }*/ + } + + mclk_cntl = Radeon_INPLL( ai, RADEON_MCLK_CNTL ); + + // enable clock of units to be reset + Radeon_OUTPLL( ai, RADEON_MCLK_CNTL, mclk_cntl | + RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB | + RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | + RADEON_FORCEON_MC | + RADEON_FORCEON_AIC ); + + // do the reset + host_path_cntl = INREG( regs, RADEON_HOST_PATH_CNTL ); + rbbm_soft_reset = INREG( regs, RADEON_RBBM_SOFT_RESET ); + + switch( ai->si->asic ) { + case rt_r300: + OUTREG( regs, RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset | + RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_AIC )); + INREG( regs, RADEON_RBBM_SOFT_RESET); + OUTREG( regs, RADEON_RBBM_SOFT_RESET, 0); + // this bit has no description + OUTREGP( regs, RADEON_RB2D_DSTCACHE_MODE, (1 << 17), ~0 ); + + break; + default: + OUTREG( regs, RADEON_RBBM_SOFT_RESET, rbbm_soft_reset | + RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB | + RADEON_SOFT_RESET_AIC ); + INREG( regs, RADEON_RBBM_SOFT_RESET ); + OUTREG( regs, RADEON_RBBM_SOFT_RESET, rbbm_soft_reset & + ~( RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB | + RADEON_SOFT_RESET_AIC ) ); + INREG( regs, RADEON_RBBM_SOFT_RESET ); + } + + OUTREG( regs, RADEON_HOST_PATH_CNTL, host_path_cntl | RADEON_HDP_SOFT_RESET ); + INREG( regs, RADEON_HOST_PATH_CNTL ); + OUTREG( regs, RADEON_HOST_PATH_CNTL, host_path_cntl ); + + // restore regs + OUTREG( regs, RADEON_RBBM_SOFT_RESET, rbbm_soft_reset); + + OUTREG( regs, RADEON_CLOCK_CNTL_INDEX, clock_cntl_index ); + R300_PLLFix( ai ); + Radeon_OUTPLL( ai, RADEON_MCLK_CNTL, mclk_cntl ); + + // reset ring buffer + cur_read_ptr = INREG( regs, RADEON_CP_RB_RPTR ); + OUTREG( regs, RADEON_CP_RB_WPTR, cur_read_ptr ); + + if( si->ring.head ) { + *si->ring.head = cur_read_ptr; + si->ring.tail = cur_read_ptr; + } + + ++si->engine.count; + + return; +} diff --git a/src/add-ons/accelerants/radeon/flat_panel.c b/src/add-ons/accelerants/radeon/flat_panel.c new file mode 100644 index 0000000000..5c78a07c95 --- /dev/null +++ b/src/add-ons/accelerants/radeon/flat_panel.c @@ -0,0 +1,273 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Flat panel support +*/ + +#include "radeon_accelerant.h" +#include +#include "mmio.h" +#include "fp_regs.h" +#include "ddc_regs.h" +#include "utils.h" +#include "ddc.h" + +// calculcate flat panel crtc registers +void Radeon_CalcFPRegisters( accelerator_info *ai, virtual_port *port, fp_info *fp_port, display_mode *mode, port_regs *values ) +{ + vuint8 *regs = ai->regs; + uint xres = mode->timing.h_display; + uint yres = mode->timing.v_display; + uint64 Hratio, Vratio; + + // we read old values first, as we only want to change + // some bits of them + // (in general, we could setup all of them, but noone + // else does it, so we don't mess around with them as well) + values->fp_gen_cntl = INREG( regs, RADEON_FP_GEN_CNTL ); + values->fp_horz_stretch = INREG( regs, RADEON_FP_HORZ_STRETCH ); + values->fp_vert_stretch = INREG( regs, RADEON_FP_VERT_STRETCH ); + values->lvds_gen_cntl = INREG( regs, RADEON_LVDS_GEN_CNTL ); + + SHOW_FLOW( 2, "before: fp_gen_cntl=%lx, horz=%lx, vert=%lx, lvds_gen_cntl=%lx", + values->fp_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch, + values->lvds_gen_cntl ); + + if( xres > fp_port->panel_xres ) + xres = fp_port->panel_xres; + if( yres > fp_port->panel_yres ) + yres = fp_port->panel_yres; + + // ouch: we must not use floating point in kernel, + // we obey and use fixed point instead + Hratio = FIX_SCALE * (uint32)xres / fp_port->panel_xres; + Vratio = FIX_SCALE * (uint32)yres / fp_port->panel_yres; + + fp_port->h_ratio = Hratio; + fp_port->v_ratio = Vratio; + + if( Hratio == FIX_SCALE ) { + values->fp_horz_stretch &= + ~(RADEON_HORZ_STRETCH_BLEND | + RADEON_HORZ_STRETCH_ENABLE); + } else { + uint32 stretch; + + stretch = (uint32)((Hratio * RADEON_HORZ_STRETCH_RATIO_MAX + + FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_HORZ_STRETCH_RATIO_MASK; + + values->fp_horz_stretch = stretch + | (values->fp_horz_stretch & (RADEON_HORZ_PANEL_SIZE | + RADEON_HORZ_FP_LOOP_STRETCH | + RADEON_HORZ_AUTO_RATIO_INC)); + values->fp_horz_stretch |= + RADEON_HORZ_STRETCH_BLEND | + RADEON_HORZ_STRETCH_ENABLE; + } + values->fp_horz_stretch &= ~RADEON_HORZ_AUTO_RATIO; + + if( Vratio == FIX_SCALE ) { + values->fp_vert_stretch &= + ~(RADEON_VERT_STRETCH_ENABLE | + RADEON_VERT_STRETCH_BLEND); + } else { + uint32 stretch; + + stretch = (uint32)((Vratio * RADEON_VERT_STRETCH_RATIO_MAX + + FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_VERT_STRETCH_RATIO_MASK; + + values->fp_vert_stretch = stretch + | (values->fp_vert_stretch & (RADEON_VERT_PANEL_SIZE | + RADEON_VERT_STRETCH_RESERVED)); + values->fp_vert_stretch |= + RADEON_VERT_STRETCH_ENABLE | + RADEON_VERT_STRETCH_BLEND; + } + values->fp_vert_stretch &= ~RADEON_VERT_AUTO_RATIO_EN; + + values->fp_gen_cntl = values->fp_gen_cntl & (uint32) + ~(RADEON_FP_SEL_CRTC2 | + RADEON_FP_RMX_HVSYNC_CONTROL_EN | + RADEON_FP_DFP_SYNC_SEL | + RADEON_FP_CRT_SYNC_SEL | + RADEON_FP_CRTC_LOCK_8DOT | + RADEON_FP_USE_SHADOW_EN | + RADEON_FP_CRTC_USE_SHADOW_VEND | + RADEON_FP_CRT_SYNC_ALT); + values->fp_gen_cntl |= + RADEON_FP_CRTC_DONT_SHADOW_VPAR | + RADEON_FP_CRTC_DONT_SHADOW_HEND; + + values->fp_gen_cntl |= port->is_crtc2 ? RADEON_FP_SEL_CRTC2 : 0; +/* values->fp_gen_cntl |= RADEON_FP_SEL_CRTC2; + values->fp_gen_cntl &= ~RADEON_FP_USE_SHADOW_EN;*/ + + SHOW_FLOW( 3, "FP2: %d", INREG( ai->regs, RADEON_FP2_GEN_CNTL )); + + if( fp_port->disp_type == dt_lvds ) { + values->lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_BLON); + values->fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + } else if( fp_port->disp_type == dt_dvi_1 ) { + values->fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN); + // enabling 8 bit data may be dangerous; BIOS should have taken care of that + values->fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; + } + + /*values->fp_gen_cntl = RADEON_FP_SEL_CRTC2 + | RADEON_FP_CRTC_LOCK_8DOT;*/ + + SHOW_FLOW( 2, "after: fp_gen_cntl=%lx, horz=%lx, vert=%lx, lvds_gen_cntl=%lx", + values->fp_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch, + values->lvds_gen_cntl ); +} + +// write flat panel registers +void Radeon_ProgramFPRegisters( accelerator_info *ai, fp_info *fp_port, port_regs *values ) +{ + uint32 tmp; + vuint8 *regs = ai->regs; + + SHOW_FLOW0( 2, "" ); + + OUTREG( regs, RADEON_FP_HORZ_STRETCH, values->fp_horz_stretch ); + OUTREG( regs, RADEON_FP_VERT_STRETCH, values->fp_vert_stretch ); + OUTREG( regs, RADEON_FP_GEN_CNTL, values->fp_gen_cntl ); + + if( fp_port->disp_type == dt_lvds ) { + tmp = INREG( regs, RADEON_LVDS_GEN_CNTL ); + + SHOW_FLOW( 3, "old: %x, new: %x", tmp, values->lvds_gen_cntl ); + + if((tmp & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) == + (values->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) ) + { + SHOW_FLOW0( 3, "Write through" ); + OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); + } else { + if( values->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON) ) { + SHOW_FLOW0( 3, "Switching off" ); + //snooze( fp_port->panel_pwr_delay * 1000); + OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); + } else { + SHOW_FLOW0( 3, "Switching on" ); + OUTREG( regs, RADEON_LVDS_GEN_CNTL, + values->lvds_gen_cntl | RADEON_LVDS_BLON ); + //snooze( fp_port->panel_pwr_delay * 1000 ); + OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); + } + } + } +} + +typedef struct { + accelerator_info *ai; + uint32 port; +} ddc_port_info; + +static status_t get_signals( void *cookie, int *clk, int *data ) +{ + ddc_port_info *info = (ddc_port_info *)cookie; + vuint8 *regs = info->ai->regs; + uint32 value; + + value = INREG( regs, info->port ); + + *clk = (value >> RADEON_GPIO_Y_SHIFT_1) & 1; + *data = (value >> RADEON_GPIO_Y_SHIFT_0) & 1; + + return B_OK; +} + +static status_t set_signals( void *cookie, int clk, int data ) +{ + ddc_port_info *info = (ddc_port_info *)cookie; + vuint8 *regs = info->ai->regs; + uint32 value; + + value = INREG( regs, info->port ); + value &= ~(RADEON_GPIO_A_1 | RADEON_GPIO_A_0); + value &= ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1); + value |= ((1-clk) << RADEON_GPIO_EN_SHIFT_1) | ((1-data) << RADEON_GPIO_EN_SHIFT_0); + + OUTREG( regs, info->port, value ); + + return B_OK; +} + +// read edid data of flat panel and setup its timing accordingly +status_t Radeon_ReadFPEDID( accelerator_info *ai, shared_info *si ) +{ + i2c_bus bus; + ddc_port_info info; + edid1_info edid; + fp_info *fp = &si->fp_port; + status_t res; + void *vdif; + size_t vdif_len; + uint32 max_hsize, max_vsize; + int i; + + info.ai = ai; + info.port = RADEON_GPIO_DVI_DDC; +// info.port = RADEON_GPIO_VGA_DDC; + + bus.cookie = &info; + bus.set_signals = &set_signals; + bus.get_signals = &get_signals; + + // get edid + res = ddc2_read_edid1( &bus, &edid, &vdif, &vdif_len ); + if( res != B_OK ) + return res; + + if( vdif != NULL ) + free( vdif ); + + SHOW_FLOW0( 2, "EDID data read from DVI port via DDC2:" ); + edid_dump( &edid ); + + // find detailed timing with maximum resolution + max_hsize = max_vsize = 0; + + for( i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i ) { + if( edid.detailed_monitor[i].monitor_desc_type == edid1_is_detailed_timing ) { + edid1_detailed_timing *timing = &edid.detailed_monitor[i].data.detailed_timing; + + if( timing->h_size > max_hsize && timing->v_size > max_vsize ) { + SHOW_FLOW( 2, "Found DDC data for mode %dx%d", + (int)timing->h_active, (int)timing->v_active ); + + max_hsize = timing->h_active; + max_vsize = timing->v_active; + + // copy it to timing specification + fp->panel_xres = timing->h_active; + fp->h_blank = timing->h_blank; + fp->h_over_plus = timing->h_sync_off; + fp->h_sync_width = timing->h_sync_width; + + fp->panel_yres = timing->v_active; + fp->v_blank = timing->v_blank; + fp->v_over_plus = timing->v_sync_off; + fp->v_sync_width = timing->v_sync_width; + + // BeOS uses kHz, but the timing is in 10 kHz + fp->dot_clock = timing->pixel_clock * 10; + } + } + } + + if( max_hsize == 0 ) + return B_ERROR; + + SHOW_INFO( 2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d", + fp->panel_xres, fp->h_blank, fp->h_over_plus, fp->h_sync_width ); + SHOW_INFO( 2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d", + fp->panel_yres, fp->v_blank, fp->v_over_plus, fp->v_sync_width ); + SHOW_INFO( 2, "pixel_clock=%d kHz", fp->dot_clock ); + + return B_OK; +} diff --git a/src/add-ons/accelerants/radeon/generic.h b/src/add-ons/accelerants/radeon/generic.h new file mode 100644 index 0000000000..0509c956b7 --- /dev/null +++ b/src/add-ons/accelerants/radeon/generic.h @@ -0,0 +1,63 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(GENERIC_H) +#define GENERIC_H + +#include +#include "video_overlay.h" + +status_t INIT_ACCELERANT(int fd); +ssize_t ACCELERANT_CLONE_INFO_SIZE(void); +void GET_ACCELERANT_CLONE_INFO(void *data); +status_t CLONE_ACCELERANT(void *data); +void UNINIT_ACCELERANT(void); +status_t GET_ACCELERANT_DEVICE_INFO(accelerant_device_info *adi); +sem_id ACCELERANT_RETRACE_SEMAPHORE(void); + +uint32 ACCELERANT_MODE_COUNT(void); +status_t GET_MODE_LIST(display_mode *dm); +status_t PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high); +status_t SET_DISPLAY_MODE(display_mode *mode_to_set); +status_t GET_DISPLAY_MODE(display_mode *current_mode); +status_t GET_FRAME_BUFFER_CONFIG(frame_buffer_config *a_frame_buffer); +status_t GET_PIXEL_CLOCK_LIMITS(display_mode *dm, uint32 *low, uint32 *high); +status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start); +status_t GET_TIMING_CONSTRAINTS(display_timing_constraints *dtc); +void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags); + +uint32 DPMS_CAPABILITIES(void); +uint32 DPMS_MODE(void); +status_t SET_DPMS_MODE(uint32 dpms_flags); + +status_t SET_CURSOR_SHAPE(uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, uint8 *andMask, uint8 *xorMask); +void MOVE_CURSOR(uint16 x, uint16 y); +void SHOW_CURSOR(bool is_visible); + +uint32 ACCELERANT_ENGINE_COUNT(void); +status_t ACQUIRE_ENGINE(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et); +status_t RELEASE_ENGINE(engine_token *et, sync_token *st); +void WAIT_ENGINE_IDLE(void); +status_t GET_SYNC_TOKEN(engine_token *et, sync_token *st); +status_t SYNC_TO_TOKEN(sync_token *st); + +void SCREEN_TO_SCREEN_BLIT(engine_token *et, blit_params *list, uint32 count); +void FILL_RECTANGLE(engine_token *et, uint32 color, fill_rect_params *list, uint32 count); +void INVERT_RECTANGLE(engine_token *et, fill_rect_params *list, uint32 count); + +void FILL_SPAN(engine_token *et, uint32 color, uint16 *list, uint32 count); + +uint32 OVERLAY_COUNT(const display_mode *dm); +const uint32 *OVERLAY_SUPPORTED_SPACES(const display_mode *dm); +uint32 OVERLAY_SUPPORTED_FEATURES(uint32 a_color_space); +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER(color_space cs, uint16 width, uint16 height); +status_t RELEASE_OVERLAY_BUFFER(const overlay_buffer *ob); +status_t GET_OVERLAY_CONSTRAINTS(const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc); +overlay_token ALLOCATE_OVERLAY(void); +status_t RELEASE_OVERLAY(overlay_token ot); +status_t CONFIGURE_OVERLAY(overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov); + + +#endif diff --git a/src/add-ons/accelerants/radeon/i2c.c b/src/add-ons/accelerants/radeon/i2c.c new file mode 100644 index 0000000000..8bbd7aea58 --- /dev/null +++ b/src/add-ons/accelerants/radeon/i2c.c @@ -0,0 +1,422 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + I2C protocoll +*/ + +#include +#include + +#include "i2c.h" + +#include "ddc_int.h" + + +// there's no spin in user space, but we need it to wait a couple +// of microseconds only +// (in this case, snooze has much too much overhead) +void spin( bigtime_t delay ) +{ + bigtime_t start_time = system_time(); + + while( system_time() - start_time < delay ) + ; +} + + +// wait until slave releases clock signal ("clock stretching") +static status_t wait_for_clk( const i2c_bus *bus, const i2c_timing *timing, + bigtime_t timeout ) +{ + bigtime_t start_time; + + // wait for clock signal to raise + spin( timing->r ); + + start_time = system_time(); + + while( 1 ) { + int clk, data; + + bus->get_signals( bus->cookie, &clk, &data ); + if( clk != 0 ) + return B_OK; + + if( system_time() - start_time > timeout ) + return B_TIMEOUT; + + spin( timing->r ); + } +} + + +// send start or repeated start condition +static status_t send_start_condition( const i2c_bus *bus, const i2c_timing *timing ) +{ + status_t res; + + bus->set_signals( bus->cookie, 1, 1 ); + + res = wait_for_clk( bus, timing, timing->start_timeout ); + if( res != B_OK ) { + SHOW_FLOW0( 2, "Timeout sending start condition" ); + return res; + } + + spin( timing->su_sta ); + bus->set_signals( bus->cookie, 1, 0 ); + spin( timing->hd_sta ); + bus->set_signals( bus->cookie, 0, 0 ); + spin( timing->f ); + + return B_OK; +} + + +// send stop condition +static status_t send_stop_condition( const i2c_bus *bus, const i2c_timing *timing ) +{ + status_t res; + + bus->set_signals( bus->cookie, 0, 0 ); + spin( timing->r ); + bus->set_signals( bus->cookie, 1, 0 ); + + // a slave may wait for us, so let elapse the acknowledge timeout + // to make the slave release bus control + res = wait_for_clk( bus, timing, timing->ack_timeout ); + if( res != B_OK ) { + SHOW_FLOW0( 2, "Timeout sending stop condition" ); + return res; + } + + spin( timing->su_sto ); + bus->set_signals( bus->cookie, 1, 1 ); + spin( timing->buf ); + + SHOW_FLOW0( 3, "" ); + + return B_OK; +} + + +// send one bit +static status_t send_bit( const i2c_bus *bus, const i2c_timing *timing, bool bit, int timeout ) +{ + status_t res; + + //SHOW_FLOW( 3, "%d", bit & 1 ); + + bus->set_signals( bus->cookie, 0, bit & 1 ); + spin( timing->su_dat ); + bus->set_signals( bus->cookie, 1, bit & 1 ); + + res = wait_for_clk( bus, timing, timeout ); + if( res != B_OK ) { + SHOW_FLOW0( 2, "Timeout when sending next bit" ); + return res; + } + + spin( timing->high ); + bus->set_signals( bus->cookie, 0, bit & 1 ); + spin( timing->f + timing->low ); + + return B_OK; +} + + +// send acknowledge and wait for reply +static status_t send_acknowledge( const i2c_bus *bus, const i2c_timing *timing ) +{ + status_t res; + bigtime_t start_time; + + // release data so slave can modify it + bus->set_signals( bus->cookie, 0, 1 ); + spin( timing->su_dat ); + bus->set_signals( bus->cookie, 1, 1 ); + + res = wait_for_clk( bus, timing, timing->ack_start_timeout ); + if( res != B_OK ) { + SHOW_FLOW0( 2, "Timeout when sending acknowledge" ); + return res; + } + + // data and clock is high, now wait for slave to pull data low + // (according to spec, this can happen any time once clock is high) + start_time = system_time(); + + while( 1 ) { + int clk, data; + + bus->get_signals( bus->cookie, &clk, &data ); + + if( data == 0 ) + break; + + if( system_time() - start_time > timing->ack_timeout ) { + SHOW_FLOW0( 2, "Slave didn't acknowledge byte" ); + return B_TIMEOUT; + } + + spin( timing->r ); + } + + SHOW_FLOW0( 4, "Success!" ); + + // make sure we've waited at least t_high + spin( timing->high ); + + bus->set_signals( bus->cookie, 0, 1 ); + spin( timing->f + timing->low ); + + return B_OK; +} + + +// send byte and wait for acknowledge if is true +static status_t send_byte( const i2c_bus *bus, const i2c_timing *timing, + uint8 byte, bool acknowledge ) +{ + int i; + + SHOW_FLOW( 2, "%x ", byte ); + + for( i = 7; i >= 0; --i ) { + status_t res; + + res = send_bit( bus, timing, byte >> i, + i == 7 ? timing->byte_timeout : timing->bit_timeout ); + if( res != B_OK ) + return res; + } + + if( acknowledge ) + return send_acknowledge( bus, timing ); + else + return B_OK; +} + +// send slave address, obeying 10-bit addresses and general call addresses +static status_t send_slave_address( const i2c_bus *bus, + const i2c_timing *timing, int slave_address, bool is_write ) +{ + status_t res; + + res = send_byte( bus, timing, (slave_address & 0xfe) | !is_write, true ); + if( res != B_OK ) + return res; + + // there are the following special cases if the first byte looks like: + // - 0000 0000 - general call address (second byte with address follows) + // - 0000 0001 - start byte + // - 0000 001x - CBus address + // - 0000 010x - address reserved for different bus format + // - 0000 011x | + // - 0000 1xxx |-> reserved + // - 1111 1xxx | + // - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits) + + // the lsb is 0 for write and 1 for read (except for general call address) + if( (slave_address & 0xff) != 0 && + (slave_address & 0xf8) != 0xf0 ) + return B_OK; + + // send second byte if required + return send_byte( bus, timing, slave_address >> 8, true ); +} + + +// receive one bit +static status_t receive_bit( const i2c_bus *bus, const i2c_timing *timing, + bool *bit, int timeout ) +{ + status_t res; + int clk, data; + + // release clock + bus->set_signals( bus->cookie, 1, 1 ); + + // wait for slave to raise clock + res = wait_for_clk( bus, timing, timeout ); + if( res != B_OK ) { + SHOW_FLOW0( 2, "Timeout waiting for bit sent by slave" ); + return res; + } + + // sample data + bus->get_signals( bus->cookie, &clk, &data ); + // leave clock high for minimal time + spin( timing->high ); + // pull clock low so slave waits for us before next bit + bus->set_signals( bus->cookie, 0, 1 ); + // let it settle and leave it low for minimal time + // to make sure slave has finished bit transmission too + spin( timing->f + timing->low); + + *bit = data; + return B_OK; +} + +// receive byte +// send positive acknowledge afterwards if is true, else send negative one +static status_t receive_byte( const i2c_bus *bus, const i2c_timing *timing, + uint8 *res_byte, bool acknowledge ) +{ + uint8 byte = 0; + int i; + + // pull clock low to let slave wait for us + bus->set_signals( bus->cookie, 0, 1 ); + + for( i = 7; i >= 0; --i ) { + status_t res; + bool bit; + + res = receive_bit( bus, timing, &bit, + i == 7 ? timing->byte_timeout : timing->bit_timeout ); + if( res != B_OK ) + return res; + + byte = (byte << 1) | bit; + } + + //SHOW_FLOW( 3, "%x ", byte ); + + *res_byte = byte; + + return send_bit( bus, timing, acknowledge ? 0 : 1, timing->bit_timeout ); +} + +// send multiple bytes +static status_t send_bytes( const i2c_bus *bus, const i2c_timing *timing, + const uint8 *write_buffer, ssize_t write_len ) +{ + SHOW_FLOW( 3, "len=%ld", write_len ); + + for( ; write_len > 0; --write_len, ++write_buffer ) { + status_t res; + + res = send_byte( bus, timing, *write_buffer, true ); + if( res != B_OK ) + return res; + } + + return B_OK; +} + +// receive multiple bytes +static status_t receive_bytes( const i2c_bus *bus, const i2c_timing *timing, + uint8 *read_buffer, ssize_t read_len ) +{ + SHOW_FLOW( 3, "len=%ld", read_len ); + + for( ; read_len > 0; --read_len, ++read_buffer ) { + status_t res; + + res = receive_byte( bus, timing, read_buffer, read_len > 1 ); + if( res != B_OK ) + return res; + } + + return B_OK; +} + +// combined i2c send+receive format +status_t i2c_send_receive( const i2c_bus *bus, const i2c_timing *timing, + int slave_address, + const uint8 *write_buffer, size_t write_len, + uint8 *read_buffer, size_t read_len ) +{ + status_t res; + + res = send_start_condition( bus, timing ); + if( res != B_OK ) + return res; + + res = send_slave_address( bus, timing, slave_address, true ); + if( res != B_OK ) + goto err; + + res = send_bytes( bus, timing, write_buffer, write_len ); + if( res != B_OK ) + goto err; + + res = send_start_condition( bus, timing ); + if( res != B_OK ) + return res; + + res = send_slave_address( bus, timing, slave_address, false ); + if( res != B_OK ) + goto err; + + res = receive_bytes( bus, timing, read_buffer, read_len ); + if( res != B_OK ) + goto err; + + res = send_stop_condition( bus, timing ); + return res; + +err: + SHOW_FLOW0( 2, "Cancelling transmission" ); + send_stop_condition( bus, timing ); + return res; +} + +// timining for 100kHz bus (fractional parts are rounded up) +i2c_timing i2c_timing_100k = +{ + buf : 5, + hd_sta : 4, + low : 5, + high : 4, + su_sta : 5, + hd_dat : 0, + su_dat : 1, + r : 1, + f : 1, + su_sto : 4, + + // as these are unspecified, we use half a clock cycle as a safe guess + start_timeout : 5, + byte_timeout : 5, + bit_timeout : 5, + ack_start_timeout : 5, + ack_timeout : 5 +}; + +// timing for 400 kHz bus +// (argh! heavy up-rounding here) +i2c_timing i2c_timing_400k = +{ + buf : 2, + hd_sta : 1, + low : 2, + high : 1, + su_sta : 1, + hd_dat : 0, + su_dat : 1, + r : 1, + f : 1, + su_sto : 1, + + // see i2c_timing_100k + start_timeout : 2, + byte_timeout : 2, + bit_timeout : 2, + ack_start_timeout : 2, + ack_timeout : 2 +}; + +void i2c_get100k_timing( i2c_timing *timing ) +{ + *timing = i2c_timing_100k; +} + +void i2c_get400k_timing( i2c_timing *timing ) +{ + *timing = i2c_timing_400k; +} diff --git a/src/add-ons/accelerants/radeon/i2c.h b/src/add-ons/accelerants/radeon/i2c.h new file mode 100644 index 0000000000..17eee85661 --- /dev/null +++ b/src/add-ons/accelerants/radeon/i2c.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + I2C protocoll +*/ + +#ifndef _I2C_H +#define _I2C_H + +#include + +// timing for i2c bus +typedef struct i2c_timing { + // general timing as defined by standard + // (in microseconds for 100kHz/400kHz mode) + int buf; // bus free between start and stop (4.7/1.3) + int hd_sta; // hold time start condition (4.0/0.6) + int low; // low period of clock (4.7/1.3) + int high; // high period of clock (4.0/0.6) + int su_sta; // setup time of repeated start condition (4.7/0.6) + int hd_dat; // hold time data (5.0/- for CBUS, 0/0 for I2C) + int su_dat; // setup time data (0.250/0.100) + int r; // maximum raise time of clock and data signal (1.0/0.3) + int f; // maximum fall time of clock and data signal (0.3/0.3) + int su_sto; // setup time for stop condition (4.0/0.6) + + // clock stretching limits, not part of i2c standard + int start_timeout; // max. delay of start condition + int byte_timeout; // max. delay of first bit of byte + int bit_timeout; // max. delay of one bit within a byte transmission + int ack_start_timeout; // max. delay of acknowledge start + + // other timeouts, not part of i2c standard + int ack_timeout; // timeout of waiting for acknowledge +} i2c_timing; + + +// set signals on bus +typedef status_t (*i2c_set_signals)( void *cookie, int scl, int sda ); +// read signals from bus +typedef status_t (*i2c_get_signals)( void *cookie, int *scl, int *sda ); + + +// i2c bus definition +typedef struct i2c_bus { + void *cookie; // user-defined cookie + i2c_set_signals set_signals; // callback to set signals + i2c_get_signals get_signals; // callback to detect signals +} i2c_bus; + + +// send and receive data via i2c bus +status_t i2c_send_receive( const i2c_bus *bus, const i2c_timing *timing, + int slave_address, + const uint8 *write_buffer, size_t write_len, + uint8 *read_buffer, size_t read_len ); + + +// fill with standard 100kHz bus timing +void i2c_get100k_timing( i2c_timing *timing ); + +// fill with standard 400kHz bus timing +// (as timing resolution is 1 microsecond, we cannot reach full speed!) +void i2c_get400k_timing( i2c_timing *timing ); + +#endif diff --git a/src/add-ons/accelerants/radeon/log_coll.c b/src/add-ons/accelerants/radeon/log_coll.c new file mode 100644 index 0000000000..811e749e50 --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_coll.c @@ -0,0 +1,129 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Both kernel and user space part. + (init and clean-up must be done in + kernel space). +*/ + + +#include +#include +#include +#include +#include "log_coll.h" +#include +#include + +typedef struct log_info_t { + char *log_buffer; + uint32 log_buffer_len; + uint32 log_buffer_pos; + area_id area; +} log_info; + +#ifdef ENABLE_LOGGING + + +// write one log entry +void log( log_info *li, uint16 what, const uint8 num_args, ... ) +{ + uint32 pos; + va_list vl; + log_entry *entry; + uint32 i; + uint32 entry_size; + + entry_size = sizeof( log_entry ) + (num_args - 1) * sizeof( uint32 ); + pos = atomic_add( &li->log_buffer_pos, entry_size ); + + if( li->log_buffer_pos > li->log_buffer_len ) { + atomic_add( &li->log_buffer_pos, -entry_size ); + return; + } + + entry = (log_entry *)&li->log_buffer[pos]; + + entry->tsc = read_tsc(); + entry->what = what; + entry->num_args = num_args; + + va_start( vl, num_args ); + for( i = 0; i < num_args; ++i ) { + entry->args[i] = va_arg( vl, uint32 ); + } + va_end( vl ); +} + +#ifdef LOG_INCLUDE_STARTUP + +// create log buffer +log_info *log_init( uint32 size ) +{ + log_info *li; + area_id area; + + // buffer must be accessible from user mem + // to allow logging from there as well; + // you cannot clone this area as there are + // pointers which would break (it wouldn't be + // hard to get rid of them, but I don't care + // and keep it as simple as possible) + area = create_area( "fast_logger", + (void **)&li, B_ANY_KERNEL_ADDRESS, + (sizeof( log_info ) + size + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1), + B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA ); + + if( area < 0 ) + panic( "Radeon Fast logger: cannot allocate %ld byte for logging data\n", size ); + + li->area = area; + li->log_buffer = (char *)li + sizeof( log_info ); + li->log_buffer_len = size; + li->log_buffer_pos = 0; + + return li; +} + +// clean-up logging +void log_exit( log_info *li ) +{ + li->log_buffer_pos = 0; + //free( li->log_buffer ); + delete_area( li->area ); +} + +#endif + +#endif + + +#ifdef LOG_INCLUDE_STARTUP + +// get *current* size of logging data +uint32 log_getsize( log_info *li ) +{ + if( li == NULL ) + return 0; + + dprintf( "RADEON -- log_getsize: log_pos %ld\n", li->log_buffer_pos ); + return li->log_buffer_pos; +} + +// get up to max_size bytes of logging data +void log_getcopy( log_info *li, void *dest, uint32 max_size ) +{ + if( li == NULL ) + return; + + dprintf( "RADEON -- log_getcopy: max_size %ld, log_pos %ld\n", + max_size, li->log_buffer_pos ); + memcpy( dest, li->log_buffer, min( li->log_buffer_pos, max_size )); + + li->log_buffer_pos = 0; +} + +#endif diff --git a/src/add-ons/accelerants/radeon/log_coll.h b/src/add-ons/accelerants/radeon/log_coll.h new file mode 100644 index 0000000000..3bbc3978eb --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_coll.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Fast logger + + As syslog is very slow and tends to loose + data if its buffer overflows (which occurs much + too often), this module provides a fast (and memory- + wasting) logging mechanism. You need a seperate + application to retrieve the log. + + Everything is thread-safe. +*/ + + +#ifndef __LOG_COLL_H__ +#define __LOG_COLL_H__ + +#include + +// by undefining this flag, all logging functions +// are resolved to empty space, so don't add +// extra tests in your code +#undef ENABLE_LOGGING +//#define ENABLE_LOGGING + + +// add log entry with 0..3 (uint32) data +#define LOG( li, what ) log( li, what, 0 ) +#define LOG1( li, what, arg1 ) log( li, what, 1, arg1 ); +#define LOG2( li, what, arg1, arg2 ) log( li, what, 2, arg1, arg2 ); +#define LOG3( li, what, arg1, arg2, arg3 ) log( li, what, 3, arg1, arg2, arg3 ); + + +// one log entry +typedef struct log_entry_t { + uint64 tsc; + uint16 what; + uint8 num_args; + uint32 args[1]; +} log_entry; + +struct log_info_t; + + +#ifdef ENABLE_LOGGING +void log( struct log_info_t *li, uint16 what, const uint8 num_args, ... ); +#else +#define log( a, b, c, ... ) +#endif + + +// define LOG_INCLUDE_STARTUP in your device driver +#ifdef LOG_INCLUDE_STARTUP + +uint32 log_getsize( struct log_info_t *li ); +void log_getcopy( struct log_info_t *li, void *dest, uint32 max_size ); + +#ifdef ENABLE_LOGGING + +struct log_info_t *log_init( uint32 size ); +void log_exit( struct log_info_t *li ); + +#else + +#define log_init( a ) NULL +#define log_exit( a ) + +#endif + +#endif + +#endif diff --git a/src/add-ons/accelerants/radeon/log_dump.c b/src/add-ons/accelerants/radeon/log_dump.c new file mode 100644 index 0000000000..3b574cf1ca --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_dump.c @@ -0,0 +1,75 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Fast logger - functions to create dump +*/ + + +#include +#include + +#include "log_coll.h" +#include "log_dump.h" +#include "log_enum.h" +#include "log_names.h" + +void log_printentry( FILE *logfile, log_entry *entry ); + +system_info sysinfo; + +// dump one entry +void log_printentry( FILE *logfile, log_entry *entry ) +{ + uint64 time; + uint32 min, sec, mill, mic; + + time = entry->tsc / (sysinfo.cpu_clock_speed / 1000000); + mic = time % 1000; + time /= 1000; + mill = time % 1000; + time /= 1000; + sec = time % 60; + time /= 60; + min = time; + + fprintf( logfile, "%03ld:%02ld:%03ld.%03ld ", min, sec, mill, mic ); + if( entry->what < sizeof( log_names ) / sizeof( log_names[0] ) ) + fprintf( logfile, log_names[entry->what] ); + else + fprintf( logfile, "unknown %ld", (uint32)entry->what ); + + if( entry->num_args > 0 ) { + uint32 i; + + fprintf( logfile, " (" ); + for( i = 0; i < entry->num_args; ++i ) { + if( i > 0 ) + fprintf( logfile, ", " ); + + fprintf( logfile, "0x%08lx", entry->args[i] ); + } + fprintf( logfile, ")" ); + } + + fprintf( logfile, "\n" ); +} + + +// dump entire log +void log_printall( FILE *logfile, char *buffer, uint32 buffer_len ) +{ + uint32 pos; + + get_system_info( &sysinfo ); + + for( pos = 0; pos < buffer_len; ) { + log_entry *entry; + + entry = (log_entry *)(buffer + pos); + log_printentry( logfile, entry/*, &tsc*/ ); + pos += sizeof( log_entry ) + (entry->num_args - 1) * sizeof( uint32 ); + } +} diff --git a/src/add-ons/accelerants/radeon/log_dump.h b/src/add-ons/accelerants/radeon/log_dump.h new file mode 100644 index 0000000000..bb2a775b91 --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_dump.h @@ -0,0 +1,17 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Fast logger - functions to create dump +*/ + +#ifndef __LOG_DUMP_H__ +#define __LOG_DUMP_H__ + +#include + +void log_printall( FILE *logfile, char *buffer, uint32 buffer_len ); + +#endif diff --git a/src/add-ons/accelerants/radeon/log_enum.h b/src/add-ons/accelerants/radeon/log_enum.h new file mode 100644 index 0000000000..9b0aac938c --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_enum.h @@ -0,0 +1,18 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Fast logger - event codes +*/ + +enum { + _Radeon_FlushPixelCache, + _Radeon_WaitForFifo, + _Radeon_WaitForIdle, + _Radeon_WriteRegFifo, + _GetAvailRingBufferQueue, + _Radeon_Finish, + _Radeon_SendCP, +}; diff --git a/src/add-ons/accelerants/radeon/log_names.h b/src/add-ons/accelerants/radeon/log_names.h new file mode 100644 index 0000000000..ebc5b3a6f7 --- /dev/null +++ b/src/add-ons/accelerants/radeon/log_names.h @@ -0,0 +1,18 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Fast logger - event names +*/ + +char *log_names[] = { + "_Radeon_FlushPixelCache", + "_Radeon_WaitForFifo", + "_Radeon_WaitForIdle", + "_Radeon_WriteRegFifo", + "_GetAvailRingBufferQueue", + "_Radeon_Finish", + "_Radeon_SendCP", +}; diff --git a/src/add-ons/accelerants/radeon/multimon.c b/src/add-ons/accelerants/radeon/multimon.c new file mode 100644 index 0000000000..a97f52424b --- /dev/null +++ b/src/add-ons/accelerants/radeon/multimon.c @@ -0,0 +1,242 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Multi-monitor management +*/ + +#include "radeon_accelerant.h" +#include "generic.h" +#include "GlobalData.h" + + +// transform official mode to internal, multi-screen mode enhanced mode +void Radeon_DetectMultiMode( virtual_card *vc, display_mode *mode ) +{ +// uint32 x, y, offset; + + mode->timing.flags &= ~RADEON_MODE_MASK; + + switch( vc->wanted_multi_mode ) { + case mm_mirror: + mode->timing.flags |= RADEON_MODE_MIRROR; + break; + case mm_clone: + mode->timing.flags |= RADEON_MODE_CLONE; + break; + case mm_combine: + mode->timing.flags |= RADEON_MODE_COMBINE; + break; + case mm_none: + default: + } + + // swap displays if asked for + if( vc->swapDisplays ) + mode->timing.flags |= RADEON_MODE_DISPLAYS_SWAPPED; + else + mode->timing.flags &= ~RADEON_MODE_DISPLAYS_SWAPPED; + + // combine mode is used if virtual area is twice as visible area + // and if scrolling is enabled; if combining is impossible, use + // cloning instead + if( (mode->flags & B_SCROLL) == 0 ) { + if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE ) { + SHOW_FLOW0( 3, "This isn't a combine mode, falling back to clone" ); + mode->timing.flags &= ~RADEON_MODE_MASK; + mode->timing.flags |= RADEON_MODE_CLONE; + } + return; + } + + SHOW_FLOW0( 3, "possibly combine mode" ); + + // remove scroll flag - we don't need it anymore + mode->flags &= ~B_SCROLL; + + mode->timing.flags &= ~RADEON_MODE_POSITION_MASK; + + if( mode->virtual_width == 2 * mode->timing.h_display ) { + SHOW_FLOW0( 3, "horizontal combine mode" ); + mode->timing.flags |= RADEON_MODE_POSITION_HORIZONTAL; + mode->timing.flags &= ~RADEON_MODE_MASK; + mode->timing.flags |= RADEON_MODE_COMBINE; + } else if( mode->virtual_height == 2 * mode->timing.v_display ) { + SHOW_FLOW0( 3, "vertical combine mode" ); + mode->timing.flags |= RADEON_MODE_POSITION_VERTICAL; + mode->timing.flags &= ~RADEON_MODE_MASK; + mode->timing.flags |= RADEON_MODE_COMBINE; + } else { + // ups, this isn't really a combine mode - restore flags + SHOW_FLOW0( 3, "wasn't really a combine mode" ); + mode->timing.flags &= ~RADEON_MODE_MASK; + mode->timing.flags |= RADEON_MODE_CLONE; + mode->flags |= ~B_SCROLL; + } +} + +// make sure selected multi-screen mode is valid; adapt it if needed +void Radeon_VerifyMultiMode( virtual_card *vc, shared_info *si, display_mode *mode ) +{ + // if there is no second port or no second monitor connected, + // fall back to standard mode + if( vc->num_ports == 1 || + (si->ports[vc->ports[0].physical_port].disp_type == dt_none || + si->ports[vc->ports[1].physical_port].disp_type == dt_none) ) + { + SHOW_FLOW0( 3, "only one monitor - disabling any multi-mon mode" ); + // restore flags if combine mode is selected + if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE ) + mode->flags |= B_SCROLL; + + mode->timing.flags &= ~RADEON_MODE_MASK; + mode->timing.flags |= RADEON_MODE_STANDARD; + } +} + +// transform internal, multi-screen enabled display mode +// to official mode +void Radeon_HideMultiMode( virtual_card *vc, display_mode *mode ) +{ + // restore flags for combine mode + if( (mode->timing.flags & RADEON_MODE_MASK) == RADEON_MODE_COMBINE ) + mode->flags |= B_SCROLL; +} + + +// initialize multi-screen mode dependant variables +void Radeon_InitMultiModeVars( virtual_card *vc, display_mode *mode ) +{ +// uint32 offset; + uint32 x, y; + + // setup single-screen mode + vc->eff_width = mode->timing.h_display; + vc->eff_height = mode->timing.v_display; + + vc->ports[0].rel_x = 0; + vc->ports[0].rel_y = 0; + + switch( mode->timing.flags & RADEON_MODE_MASK ) { + case RADEON_MODE_CLONE: + // in clone mode, ports are independant but show the same + vc->ports[1].rel_x = 0; + vc->ports[1].rel_y = 0; + break; + + case RADEON_MODE_COMBINE: + // detect where second screen must be located and + // adapt total visible area accordingly + if( (mode->timing.flags & RADEON_MODE_POSITION_MASK) == RADEON_MODE_POSITION_HORIZONTAL ) { + vc->eff_width = 2 * mode->timing.h_display; + x = mode->timing.h_display; + y = 0; + } else { + vc->eff_height = 2 * mode->timing.v_display; + x = 0; + y = mode->timing.v_display; + } + + SHOW_FLOW( 3, "relative position of second screen: %d, %d", x, y ); + + vc->ports[1].rel_x = 0; + vc->ports[1].rel_y = 0; + + // set relative offset + if( (mode->timing.flags & RADEON_MODE_DISPLAYS_SWAPPED) == 0 ) { + vc->ports[1].rel_x = x; + vc->ports[1].rel_y = y; + } else { + vc->ports[0].rel_x = x; + vc->ports[0].rel_y = y; + } + break; + + case RADEON_MODE_STANDARD: + case RADEON_MODE_MIRROR: + break; + } +} + + +// check and execute tunnel settings command +status_t Radeon_CheckMultiMonTunnel( virtual_card *vc, display_mode *mode, + const display_mode *low, const display_mode *high, bool *isTunneled ) +{ + if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 && + (mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 ) + { + mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST; + mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY; + + // still process request, just in case someone set this flag + // combination by mistake + + // TBD: disabled to shorten syslog + *isTunneled = true; + return B_OK; + } + + // check magic params + if( mode->space != 0 || low->space != 0 || high->space != 0 + || low->virtual_width != 0xffff || low->virtual_height != 0xffff + || high->virtual_width != 0 || high->virtual_height != 0 + || mode->timing.pixel_clock != 0 + || low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' ) + { + *isTunneled = false; + return B_OK; + } + + *isTunneled = true; + + switch( mode->h_display_start ) { + case ms_swap: + if( mode->v_display_start != 0 ) + vc->swapDisplays = mode->timing.flags != 0; + else + mode->timing.flags = vc->swapDisplays; + + // write settings instantly + Radeon_WriteSettings( vc ); + return B_OK; + +/* case ms_overlay_port: + if( mode->v_display_start != 0 ) + vc->whished_overlay_port = mode->timing.flags; + else + mode->timing.flags = vc->whished_overlay_port; + + Radeon_WriteSettings( vc ); + return B_OK;*/ + + default: + return B_BAD_INDEX; + } +} + + +// return true if both ports must be programmed +bool Radeon_NeedsSecondPort( display_mode *mode ) +{ + switch( mode->timing.flags & RADEON_MODE_MASK ) { + case RADEON_MODE_COMBINE: + case RADEON_MODE_CLONE: + return true; + default: + return false; + } +} + +// return number of ports showing differents parts of frame buffer +bool Radeon_DifferentPorts( display_mode *mode ) +{ + switch( mode->timing.flags & RADEON_MODE_MASK ) { + case RADEON_MODE_COMBINE: + return 2; + default: + return 1; + } +} diff --git a/src/add-ons/accelerants/radeon/overlay.c b/src/add-ons/accelerants/radeon/overlay.c new file mode 100644 index 0000000000..aa2b2193f9 --- /dev/null +++ b/src/add-ons/accelerants/radeon/overlay.c @@ -0,0 +1,1052 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Hardware access routines for overlays +*/ + +#include "GlobalData.h" +#include "radeon_interface.h" +#include "mmio.h" +#include "overlay_regs.h" +#include "pll_regs.h" +#include "capture_regs.h" +#include "cp_regs.h" +#include "utils.h" +#include +#include + +void Radeon_InitOverlay( accelerator_info *ai, virtual_port *overlay_port ); +status_t Radeon_ShowOverlay( accelerator_info *ai, virtual_port *overlay_port ); +void Radeon_ReplaceOverlayBuffer( accelerator_info *ai ); + + +void Radeon_TempHideOverlay( accelerator_info *ai ); + +// standard (linear) gamma +static struct { + uint16 reg; + bool r200_or_above; + uint32 slope; + uint32 offset; +} std_gamma[] = { + { RADEON_OV0_GAMMA_0_F, false, 0x100, 0x0000 }, + { RADEON_OV0_GAMMA_10_1F, false, 0x100, 0x0020 }, + { RADEON_OV0_GAMMA_20_3F, false, 0x100, 0x0040 }, + { RADEON_OV0_GAMMA_40_7F, false, 0x100, 0x0080 }, + { RADEON_OV0_GAMMA_80_BF, true, 0x100, 0x0100 }, + { RADEON_OV0_GAMMA_C0_FF, true, 0x100, 0x0100 }, + { RADEON_OV0_GAMMA_100_13F, true, 0x100, 0x0200 }, + { RADEON_OV0_GAMMA_140_17F, true, 0x100, 0x0200 }, + { RADEON_OV0_GAMMA_180_1BF, true, 0x100, 0x0300 }, + { RADEON_OV0_GAMMA_1C0_1FF, true, 0x100, 0x0300 }, + { RADEON_OV0_GAMMA_200_23F, true, 0x100, 0x0400 }, + { RADEON_OV0_GAMMA_240_27F, true, 0x100, 0x0400 }, + { RADEON_OV0_GAMMA_280_2BF, true, 0x100, 0x0500 }, + { RADEON_OV0_GAMMA_2C0_2FF, true, 0x100, 0x0500 }, + { RADEON_OV0_GAMMA_300_33F, true, 0x100, 0x0600 }, + { RADEON_OV0_GAMMA_340_37F, true, 0x100, 0x0600 }, + { RADEON_OV0_GAMMA_380_3BF, false, 0x100, 0x0700 }, + { RADEON_OV0_GAMMA_3C0_3FF, false, 0x100, 0x0700 } +}; + + +// setup overlay unit before first use +void Radeon_InitOverlay( accelerator_info *ai, virtual_port *overlay_port ) +{ + vuint8 *regs = ai->regs; + shared_info *si = ai->si; + uint i; + uint32 ecp_div; + + SHOW_FLOW( 3, "physical_port=%d", overlay_port->physical_port ); + + Radeon_WaitForIdle( ai ); + + // make sure we really write this value as the "toggle" bit + // contained in it (which is zero initially) is edge-sensitive! + // for capturing, we need to select "software" video port + si->overlay_mgr.auto_flip_reg = RADEON_OV0_VID_PORT_SELECT_SOFTWARE; + + OUTREG( regs, RADEON_OV0_SCALE_CNTL, RADEON_SCALER_SOFT_RESET ); + OUTREG( regs, RADEON_OV0_AUTO_FLIP_CNTRL, si->overlay_mgr.auto_flip_reg ); + OUTREG( regs, RADEON_OV0_FILTER_CNTL, // use fixed filter coefficients + RADEON_OV0_HC_COEF_ON_HORZ_Y | + RADEON_OV0_HC_COEF_ON_HORZ_UV | + RADEON_OV0_HC_COEF_ON_VERT_Y | + RADEON_OV0_HC_COEF_ON_VERT_UV ); + OUTREG( regs, RADEON_OV0_KEY_CNTL, RADEON_GRAPHIC_KEY_FN_EQ | + RADEON_VIDEO_KEY_FN_FALSE | + RADEON_CMP_MIX_OR ); + OUTREG( regs, RADEON_OV0_TEST, 0 ); +// OUTREG( regs, RADEON_FCP_CNTL, RADEON_FCP_CNTL_GND ); // disable capture clock +// OUTREG( regs, RADEON_CAP0_TRIG_CNTL, 0 ); // disable capturing + OUTREG( regs, RADEON_OV0_REG_LOAD_CNTL, 0 ); + // tell deinterlacer to always show recent field + OUTREG( regs, RADEON_OV0_DEINTERLACE_PATTERN, + 0xaaaaa | (9 << RADEON_OV0_DEINT_PAT_LEN_M1_SHIFT) ); + + // set gamma + for( i = 0; i < sizeof( std_gamma ) / sizeof( std_gamma[0] ); ++i ) { + if( !std_gamma[i].r200_or_above || si->asic >= rt_r200 ) { + OUTREG( regs, std_gamma[i].reg, + (std_gamma[i].slope << 16) | std_gamma[i].offset ); + } + } + + // overlay unit can only handle up to 175 MHz, if pixel clock is higher, + // only every second pixel is handled + if( overlay_port->mode.timing.pixel_clock < 175000 ) + ecp_div = 0; + else + ecp_div = 1; + + Radeon_OUTPLLP( ai, RADEON_VCLK_ECP_CNTL, + ecp_div << RADEON_ECP_DIV_SHIFT, ~RADEON_ECP_DIV_MASK ); + + si->active_overlay.port = si->pending_overlay.port; + + // invalidate active colour space + si->active_overlay.ob.space = -1; + + // invalidate position/scaling + si->active_overlay.ob.width = -1; +} + +// colour space transformation matrix +typedef struct space_transform +{ + float RefLuma; // scaling of luma to use full RGB range + float RefRCb; // b/u -> r + float RefRY; // g/y -> r + float RefRCr; // r/v -> r + float RefGCb; + float RefGY; + float RefGCr; + float RefBCb; + float RefBY; + float RefBCr; +} space_transform; + + +// Parameters for ITU-R BT.601 and ITU-R BT.709 colour spaces +space_transform trans_yuv[2] = +{ + { 1.1678, 0.0, 1, 1.6007, -0.3929, 1, -0.8154, 2.0232, 1, 0.0 }, /* BT.601 */ + { 1.1678, 0.0, 1, 1.7980, -0.2139, 1, -0.5345, 2.1186, 1, 0.0 } /* BT.709 */ +}; + + +// RGB is a pass through +space_transform trans_rgb = + { 1, 0, 0, 1, 0, 1, 0, 1, 0, 0 }; + + +// set overlay colour space transformation matrix +static void Radeon_SetTransform( accelerator_info *ai, + float bright, + float cont, + float sat, + float hue, + float red_intensity, + float green_intensity, + float blue_intensity, + uint ref) +{ + vuint8 *regs = ai->regs; + shared_info *si = ai->si; + float OvHueSin, OvHueCos; + float CAdjOff; + float CAdjRY, CAdjGY, CAdjBY; + float CAdjRCb, CAdjRCr; + float CAdjGCb, CAdjGCr; + float CAdjBCb, CAdjBCr; + float RedAdj,GreenAdj,BlueAdj; + float OvROff, OvGOff, OvBOff; + float OvRY, OvGY, OvBY; + float OvRCb, OvRCr; + float OvGCb, OvGCr; + float OvBCb, OvBCr; + float Loff; + float Coff; + + uint32 dwOvROff, dwOvGOff, dwOvBOff; + uint32 dwOvRY, dwOvGY, dwOvBY; + uint32 dwOvRCb, dwOvRCr; + uint32 dwOvGCb, dwOvGCr; + uint32 dwOvBCb, dwOvBCr; + + space_transform *trans; + + SHOW_FLOW0( 3, "" ); + + // get proper conversion formula + switch( si->pending_overlay.ob.space ) { + case B_YCbCr422: + case B_YUV12: + Loff = 16 * 4; // internal representation is 10 Bits + Coff = 128 * 4; + + if (ref >= 2) + ref = 0; + + trans = &trans_yuv[ref]; + break; + + case B_RGB15: + case B_RGB16: + case B_RGB32: + default: + Loff = 0; + Coff = 0; + trans = &trans_rgb; + } + + OvHueSin = sin(hue); + OvHueCos = cos(hue); + + // get matrix values to convert overlay colour space to RGB + // applying colour adjustment, saturation and luma scaling + // (saturation doesn't work with RGB input, perhaps it did with some + // maths; this is left to the reader :) + CAdjRY = cont * trans->RefLuma * trans->RefRY; + CAdjGY = cont * trans->RefLuma * trans->RefGY; + CAdjBY = cont * trans->RefLuma * trans->RefBY; + + CAdjRCb = sat * -OvHueSin * trans->RefRCr; + CAdjRCr = sat * OvHueCos * trans->RefRCr; + CAdjGCb = sat * (OvHueCos * trans->RefGCb - OvHueSin * trans->RefGCr); + CAdjGCr = sat * (OvHueSin * trans->RefGCb + OvHueCos * trans->RefGCr); + CAdjBCb = sat * OvHueCos * trans->RefBCb; + CAdjBCr = sat * OvHueSin * trans->RefBCb; + + // adjust black level + CAdjOff = cont * trans[ref].RefLuma * bright * 1023.0; + RedAdj = cont * trans[ref].RefLuma * red_intensity * 1023.0; + GreenAdj = cont * trans[ref].RefLuma * green_intensity * 1023.0; + BlueAdj = cont * trans[ref].RefLuma * blue_intensity * 1023.0; + + OvRY = CAdjRY; + OvGY = CAdjGY; + OvBY = CAdjBY; + OvRCb = CAdjRCb; + OvRCr = CAdjRCr; + OvGCb = CAdjGCb; + OvGCr = CAdjGCr; + OvBCb = CAdjBCb; + OvBCr = CAdjBCr; + // apply offsets + OvROff = RedAdj + CAdjOff - CAdjRY * Loff - (OvRCb + OvRCr) * Coff; + OvGOff = GreenAdj + CAdjOff - CAdjGY * Loff - (OvGCb + OvGCr) * Coff; + OvBOff = BlueAdj + CAdjOff - CAdjBY * Loff - (OvBCb + OvBCr) * Coff; + + dwOvROff = ((int32)(OvROff * 2.0)) & 0x1fff; + dwOvGOff = ((int32)(OvGOff * 2.0)) & 0x1fff; + dwOvBOff = ((int32)(OvBOff * 2.0)) & 0x1fff; + + dwOvRY = (((int32)(OvRY * 2048.0))&0x7fff)<<17; + dwOvGY = (((int32)(OvGY * 2048.0))&0x7fff)<<17; + dwOvBY = (((int32)(OvBY * 2048.0))&0x7fff)<<17; + dwOvRCb = (((int32)(OvRCb * 2048.0))&0x7fff)<<1; + dwOvRCr = (((int32)(OvRCr * 2048.0))&0x7fff)<<17; + dwOvGCb = (((int32)(OvGCb * 2048.0))&0x7fff)<<1; + dwOvGCr = (((int32)(OvGCr * 2048.0))&0x7fff)<<17; + dwOvBCb = (((int32)(OvBCb * 2048.0))&0x7fff)<<1; + dwOvBCr = (((int32)(OvBCr * 2048.0))&0x7fff)<<17; + + OUTREG( regs, RADEON_OV0_LIN_TRANS_A, dwOvRCb | dwOvRY ); + OUTREG( regs, RADEON_OV0_LIN_TRANS_B, dwOvROff | dwOvRCr ); + OUTREG( regs, RADEON_OV0_LIN_TRANS_C, dwOvGCb | dwOvGY ); + OUTREG( regs, RADEON_OV0_LIN_TRANS_D, dwOvGOff | dwOvGCr ); + OUTREG( regs, RADEON_OV0_LIN_TRANS_E, dwOvBCb | dwOvBY ); + OUTREG( regs, RADEON_OV0_LIN_TRANS_F, dwOvBOff | dwOvBCr ); + + si->active_overlay.ob.space = si->pending_overlay.ob.space; +} + + +// convert Be colour key to rgb value +static uint32 colourKey2RGB32( uint32 space, uint8 red, uint8 green, uint8 blue ) +{ + uint32 res; + + SHOW_FLOW0( 3, "" ); + + // the way Be defines colour keys may be convinient to some driver developers, + // but it's not well defined - took me some time to find out the format used + // and still I have no idea how alpha is defined; Rudolf told me that alpha is + // never used + switch( space ) { + case B_RGB15: + res = + ((uint32)(red >> 0) << (16+3)) | + ((uint32)(green >> 0) << (8+3)) | + ((blue >> 0) << 3); + break; + case B_RGB16: + res = + ((uint32)(red >> 0) << (16+3)) | + ((uint32)(green >> 0) << (8+2)) | + ((blue >> 0) << 3); + break; + case B_RGB32: + case B_CMAP8: + res = ((uint32)(red) << 16) | ((uint32)(green) << 8) | blue; + break; + default: + res = 0; + } + + SHOW_FLOW( 3, "key=%lx", res ); + return res; +} + + +// set colour key of overlay +void Radeon_SetColourKey( accelerator_info *ai, const overlay_window *ow ) +{ + virtual_card *vc = ai->vc; + uint32 rgb32; + uint32 buffer[3*2]; + uint idx = 0; + + SHOW_FLOW0( 3, "" ); + + rgb32 = colourKey2RGB32( vc->mode.space, + ow->red.value, ow->green.value, ow->blue.value ); + + buffer[idx++] = CP_PACKET0( RADEON_OV0_GRAPHICS_KEY_CLR_LOW, 0 ); + buffer[idx++] = rgb32; + buffer[idx++] = CP_PACKET0( RADEON_OV0_GRAPHICS_KEY_CLR_HIGH, 0 ); + buffer[idx++] = rgb32; + buffer[idx++] = CP_PACKET0( RADEON_OV0_KEY_CNTL, 0 ); + buffer[idx++] = RADEON_GRAPHIC_KEY_FN_EQ | + RADEON_VIDEO_KEY_FN_FALSE | + RADEON_CMP_MIX_OR; + + Radeon_SendCP( ai, buffer, idx ); +} + +typedef struct { + uint max_scale; // maximum src_width/dest_width, + // i.e. source increment per screen pixel + uint8 group_size; // size of one filter group in pixels + uint8 p1_step_by, p23_step_by; // > 0: log(source pixel increment)+1, 2-tap filter + // = 0: source pixel increment = 1, 4-tap filter +} hscale_factor; + +#define count_of( a ) (sizeof( a ) / sizeof( a[0] )) + +// scaling/filter tables depending on overlay colour space: +// magnifying pixels is no problem, but minifying can lead to overload, +// so we have to skip pixels and/or use 2-tap filters +static hscale_factor scale_RGB16[] = { + { (2 << 12), 2, 1, 1 }, + { (4 << 12), 2, 2, 2 }, + { (8 << 12), 2, 3, 3 }, + { (16 << 12), 2, 4, 4 }, + { (32 << 12), 2, 5, 5 } +}; + +static hscale_factor scale_RGB32[] = { + { (2 << 12) / 3, 2, 0, 0 }, + { (4 << 12) / 3, 4, 1, 1 }, + { (8 << 12) / 3, 4, 2, 2 }, + { (4 << 12), 4, 2, 3 }, + { (16 << 12) / 3, 4, 3, 3 }, + { (8 << 12), 4, 3, 4 }, + { (32 << 12) / 3, 4, 4, 4 }, + { (16 << 12), 4, 5, 5 } +}; + +static hscale_factor scale_YUV[] = { + { (16 << 12) / 16, 2, 0, 0 }, + { (16 << 12) / 12, 2, 0, 1 }, // mode 4, 1, 0 (as used by YUV12) is impossible + { (16 << 12) / 8, 4, 1, 1 }, + { (16 << 12) / 6, 4, 1, 2 }, + { (16 << 12) / 4, 4, 2, 2 }, + { (16 << 12) / 3, 4, 2, 3 }, + { (16 << 12) / 2, 4, 3, 3 }, + { (16 << 12) / 1, 4, 4, 4 } +}; + +static hscale_factor scale_YUV12[] = { + { (16 << 12) / 16, 2, 0, 0 }, + { (16 << 12) / 12, 4, 1, 0 }, + { (16 << 12) / 12, 2, 0, 1 }, + { (16 << 12) / 8, 4, 1, 1 }, + { (16 << 12) / 6, 4, 1, 2 }, + { (16 << 12) / 4, 4, 2, 2 }, + { (16 << 12) / 3, 4, 2, 3 }, + { (16 << 12) / 2, 4, 3, 3 }, + { (int)((16 << 12) / 1.5), 4, 3, 4 }, + { (int)((16 << 12) / 1.0), 4, 4, 4 }, + { (int)((16 << 12) / 0.75), 4, 4, 5 }, + { (int)((16 << 12) / 0.5), 4, 5, 5 } +}; + +#define min3( a, b, c ) (min( (a), min( (b), (c) ))) + +static hscale_factor scale_YUV9[] = { + { min3( (16 << 12) / 12, (3 << 12) * 1, (2 << 12) * 4 * 1 ), 2, 0, 0 }, + { min3( (16 << 12) / 8, (3 << 12) * 1, (2 << 12) * 4 * 1 ), 4, 1, 0 }, + { min3( (16 << 12) / 10, (3 << 12) * 1, (2 << 12) * 4 * 1 ), 2, 0, 1 }, + { min3( (16 << 12) / 6, (3 << 12) * 1, (2 << 12) * 4 * 1 ), 4, 1, 1 }, + { min3( (16 << 12) / 5, (3 << 12) * 1, (2 << 12) * 4 * 2 ), 4, 1, 2 }, + { min3( (16 << 12) / 3, (3 << 12) * 2, (2 << 12) * 4 * 2 ), 4, 2, 2 }, + { min3( (int)((16 << 12) / 2.5), (3 << 12) * 1, (2 << 12) * 4 * 4 ), 4, 2, 3 }, // probably, it should be (3 << 12) * 2 + { min3( (int)((16 << 12) / 1.5), (3 << 12) * 4, (2 << 12) * 4 * 4 ), 4, 3, 3 }, + { min3( (int)((16 << 12) / 0.75), (3 << 12) * 8, (2 << 12) * 4 * 8 ), 4, 4, 4 }, + { min3( (int)((16 << 12) / 0.625), (3 << 12) * 8, (2 << 12) * 4 * 16 ), 4, 4, 5 }, + { min3( (int)((16 << 12) / 0.375), (3 << 12) * 16, (2 << 12) * 4 * 16 ), 4, 5, 5 } +}; + + +// parameters of an overlay colour space +typedef struct { + uint8 bpp_shift; // log2( bytes per pixel (main plain) ) + uint8 bpuv_shift; // log2( bytes per pixel (uv-plane) ); + // if there is one plane only: bpp=bpuv + uint8 num_planes; // number of planes + uint8 h_uv_sub_sample_shift; // log2( horizontal pixels per uv pair ) + uint8 v_uv_sub_sample_shift; // log2( vertical pixels per uv pair ) + hscale_factor *factors; // scaling/filter table + uint8 num_factors; +} space_params; + +static space_params space_params_table[16] = { + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 1, 1, 1, 0, 0, scale_RGB16, count_of( scale_RGB16 ) }, // RGB15 + { 1, 1, 1, 0, 0, scale_RGB16, count_of( scale_RGB16 ) }, // RGB16 + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 2, 2, 1, 0, 0, scale_RGB32, count_of( scale_RGB32 ) }, // RGB32 + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 0, 0, 0, 0, 0, NULL, 0 }, // reserved + { 0, 0, 3, 2, 2, scale_YUV9, count_of( scale_YUV9 ) }, // YUV9 + { 0, 0, 3, 1, 1, scale_YUV12, count_of( scale_YUV12 ) }, // YUV12, three-plane + { 1, 1, 1, 1, 0, scale_YUV, count_of( scale_YUV ) }, // VYUY422 + { 1, 1, 1, 1, 0, scale_YUV, count_of( scale_YUV ) }, // YVYU422 + { 0, 1, 2, 1, 1, scale_YUV12, count_of( scale_YUV12 ) }, // YUV12, two-plane + { 0, 1, 2, 1, 1, NULL, 0 }, // ??? + { 0, 0, 0, 0, 0, NULL, 0 } // reserved +}; + +// get appropriate scaling/filter parameters +static hscale_factor *getHScaleFactor( space_params *params, + uint32 src_left, uint32 src_right, uint32 *h_inc ) +{ + uint words_per_p1_line, words_per_p23_line, max_words_per_line; + bool p1_4tap_allowed, p23_4tap_allowed; + uint i; + uint num_factors; + hscale_factor *factors; + + SHOW_FLOW0( 3, "" ); + + // check whether fifo is large enough to feed vertical 4-tap-filter + + words_per_p1_line = + ceilShiftDiv( (src_right - 1) << params->bpp_shift, 4 ) - + ((src_left << params->bpp_shift) >> 4) + 1; + words_per_p23_line = + ceilShiftDiv( (src_right - 1) << params->bpuv_shift, 4 ) - + ((src_left << params->bpuv_shift) >> 4) + 1; + + // overlay buffer for one line; this value is probably + // higher on newer Radeons (or smaller on older Radeons?) + max_words_per_line = 96; + + switch( params->num_planes ) { + case 3: + p1_4tap_allowed = words_per_p1_line < max_words_per_line / 2; + p23_4tap_allowed = words_per_p23_line < max_words_per_line / 4; + break; + case 2: + p1_4tap_allowed = words_per_p1_line < max_words_per_line / 2; + p23_4tap_allowed = words_per_p23_line < max_words_per_line / 2; + break; + case 1: + default: + p1_4tap_allowed = p23_4tap_allowed = words_per_p1_line < max_words_per_line; + break; + } + + SHOW_FLOW( 3, "p1_4tap_allowed=%d, p23_4t_allowed=%d", + (int)p1_4tap_allowed, (int)p23_4tap_allowed ); + + // search for proper scaling/filter entry + factors = params->factors; + num_factors = params->num_factors; + + if( factors == NULL || num_factors == 0 ) + return NULL; + + for( i = 0; i < num_factors; ++i, ++factors ) { + if( *h_inc <= factors->max_scale && + (factors->p1_step_by > 0 || p1_4tap_allowed) && + (factors->p23_step_by > 0 || p23_4tap_allowed)) + break; + } + + if( i == num_factors ) { + // overlay is asked to be scaled down more than allowed, + // so use least scaling factor supported + --factors; + *h_inc = factors->max_scale; + } + + SHOW_FLOW( 3, "group_size=%d, p1_step_by=%d, p23_step_by=%d", + factors->group_size, factors->p1_step_by, factors->p23_step_by ); + + return factors; +} + + +#define I2FF( a, shift ) ((uint32)((a) * (1 << (shift)))) + + +// show overlay on screen +status_t Radeon_ShowOverlay( accelerator_info *ai, virtual_port *overlay_port ) +{ + virtual_card *vc = ai->vc; +// vuint8 *regs = ai->regs; + shared_info *si = ai->si; + overlay_info *overlay = &si->pending_overlay; + overlay_buffer_node *node = overlay->on; + + uint32 ecp_div; +// uint32 step_by; + uint32 v_inc, h_inc; + uint32 src_v_inc, src_h_inc; + uint32 src_left, src_top, src_right, src_bottom; + int32 dest_left, dest_top, dest_right, dest_bottom; + uint32 offset; + uint32 tmp; + uint32 p1_h_accum_init, p23_h_accum_init, p1_v_accum_init, p23_v_accum_init; + uint32 p1_active_lines, p23_active_lines; + hscale_factor *factors; + space_params *params; + + uint32 p1_h_inc, p23_h_inc; + uint32 p1_x_start, p1_x_end; + uint32 p23_x_start, p23_x_end; + + uint32 buffer[20*2]; + uint idx = 0; + + SHOW_FLOW0( 3, "" ); + + Radeon_SetColourKey( ai, &overlay->ow ); + + // overlay unit can only handle up to 175 MHz; if pixel clock is higher, + // only every second pixel is handled + // (this devider is gets written into PLL by OverlayInit, + // so we don't need to do it ourself) + if( overlay_port->mode.timing.pixel_clock < 175000 ) + ecp_div = 0; + else + ecp_div = 1; + + + // scaling is independant of clipping, get this first + { + uint32 src_width, src_height; + + src_width = overlay->ov.width; + src_height = overlay->ov.height; + + // this is for graphics card + v_inc = (src_height << 20) / overlay->ow.height; + h_inc = (src_width << (12 + ecp_div)) / overlay->ow.width; + + + // this is for us + src_v_inc = (src_height << 16) / overlay->ow.height; + src_h_inc = (src_width << 16) / overlay->ow.width; + } + + // calculate unclipped position/size + // TBD: I assume that overlay_window.offset_xyz is only a hint where + // no overlay is visible; another interpretation were to zoom + // the overlay so it fits into remaining space + src_left = (overlay->ov.h_start << 16) + overlay->ow.offset_left * src_h_inc; + src_top = (overlay->ov.v_start << 16) + overlay->ow.offset_top * src_v_inc; + src_right = ((overlay->ov.h_start + overlay->ov.width) << 16) - + overlay->ow.offset_right * src_h_inc; + src_bottom = ((overlay->ov.v_start + overlay->ov.height) << 16) - + overlay->ow.offset_top * src_v_inc; + dest_left = overlay->ow.h_start + overlay->ow.offset_left; + dest_top = overlay->ow.v_start + overlay->ow.offset_top; + dest_right = overlay->ow.h_start + overlay->ow.width - overlay->ow.offset_right; + dest_bottom = overlay->ow.v_start + overlay->ow.height - overlay->ow.offset_bottom; + + SHOW_FLOW( 3, "ow: h=%d, v=%d, width=%d, height=%d", + overlay->ow.h_start, overlay->ow.v_start, + overlay->ow.width, overlay->ow.height ); + + SHOW_FLOW( 3, "offset_left=%d, offset_right=%d, offset_top=%d, offset_bottom=%d", + overlay->ow.offset_left, overlay->ow.offset_right, + overlay->ow.offset_top, overlay->ow.offset_bottom ); + + + // apply virtual screen + dest_left -= vc->mode.h_display_start + overlay_port->rel_x; + dest_top -= vc->mode.v_display_start + overlay_port->rel_y; + dest_right -= vc->mode.h_display_start + overlay_port->rel_x; + dest_bottom -= vc->mode.v_display_start + overlay_port->rel_y; + + + // clip to visible area + if( dest_left < 0 ) { + src_left += -dest_left * src_h_inc; + dest_left = 0; + } + if( dest_top < 0 ) { + src_top += -dest_top * src_v_inc; + dest_top = 0; + } + + SHOW_FLOW( 3, "mode: w=%d, h=%d", + overlay_port->mode.timing.h_display, overlay_port->mode.timing.v_display ); + + if( dest_right > overlay_port->mode.timing.h_display ) + dest_right = overlay_port->mode.timing.h_display; + if( dest_bottom > overlay_port->mode.timing.v_display ) + dest_bottom = overlay_port->mode.timing.v_display; + + SHOW_FLOW( 3, "src=(%d, %d, %d, %d)", + src_left, src_top, src_right, src_bottom ); + SHOW_FLOW( 3, "dest=(%d, %d, %d, %d)", + dest_left, dest_top, dest_right, dest_bottom ); + + + // especially with multi-screen modes the overlay may not be on screen at all + if( dest_left >= dest_right || dest_top >= dest_bottom || + src_left >= src_right || src_top >= src_bottom ) + { + Radeon_TempHideOverlay( ai ); + goto done; + } + + + // let's calculate all those nice register values + SHOW_FLOW( 3, "ati_space=%d", node->ati_space ); + params = &space_params_table[node->ati_space]; + + // choose proper scaler + { + factors = getHScaleFactor( params, src_left >> 16, src_right >> 16, &h_inc ); + if( factors == NULL ) + return B_ERROR; + + p1_h_inc = factors->p1_step_by > 0 ? + h_inc >> (factors->p1_step_by - 1) : h_inc; + p23_h_inc = + (factors->p23_step_by > 0 ? h_inc >> (factors->p23_step_by - 1) : h_inc) + >> params->h_uv_sub_sample_shift; + + SHOW_FLOW( 3, "p1_h_inc=%x, p23_h_inc=%x", p1_h_inc, p23_h_inc ); + } + + // get register value for start/end position of overlay image (pixel-precise only) + { + uint32 p1_step_size, p23_step_size; + uint32 p1_left, p1_right, p1_width; + uint32 p23_left, p23_right, p23_width; + + p1_left = src_left >> 16; + p1_right = src_right >> 16; + p1_width = p1_right - p1_left; + + p1_step_size = factors->p1_step_by > 0 ? (1 << (factors->p1_step_by - 1)) : 1; + p1_x_start = p1_left % (16 >> params->bpp_shift); + p1_x_end = ((p1_x_start + p1_width - 1) / p1_step_size) * p1_step_size; + + SHOW_FLOW( 3, "p1_x_start=%d, p1_x_end=%d", p1_x_start, p1_x_end ); + + p23_left = (src_left >> 16) >> params->h_uv_sub_sample_shift; + p23_right = (src_right >> 16) >> params->h_uv_sub_sample_shift; + p23_width = p23_right - p23_left; + + p23_step_size = factors->p23_step_by > 0 ? (1 << (factors->p23_step_by - 1)) : 1; + // if resolution of Y and U/V differs but YUV are stored in one + // plane then UV alignment depends on Y data, therefore the hack + // (you are welcome to replace this with some cleaner code ;) + p23_x_start = p23_left % + ((16 >> params->bpuv_shift) / + (node->ati_space == 11 || node->ati_space == 12 ? 2 : 1)); + p23_x_end = (int)((p23_x_start + p23_width - 1) / p23_step_size) * p23_step_size; + + SHOW_FLOW( 3, "p23_x_start=%d, p23_x_end=%d", p23_x_start, p23_x_end ); + + // get memory location of first word to be read by scaler + // (save relative offset for fast update) + si->active_overlay.rel_offset = (src_top >> 16) * node->buffer.bytes_per_row + + ((p1_left << params->bpp_shift) & ~0xf); + offset = node->mem_offset + si->active_overlay.rel_offset; + + SHOW_FLOW( 3, "rel_offset=%x", si->active_overlay.rel_offset ); + } + + // get active lines for scaler + // (we could add additional blank lines for DVD letter box mode, + // but this is not supported by API; additionally, this only makes + // sense if want to put subtitles onto the black border, which is + // supported neither) + { + uint16 int_top, int_bottom; + + int_top = src_top >> 16; + int_bottom = (src_bottom >> 16); + + p1_active_lines = int_bottom - int_top - 1; + p23_active_lines = + ceilShiftDiv( int_bottom - 1, params->v_uv_sub_sample_shift ) - + (int_top >> params->v_uv_sub_sample_shift); + + SHOW_FLOW( 3, "p1_active_lines=%d, p23_active_lines=%d", + p1_active_lines, p23_active_lines ); + } + + // if picture is stretched for flat panel, we need to scale all + // vertical values accordingly + // TBD: there is no description at all concerning this, so v_accum_init may + // need to be initialized based on original value + { + display_type_e disp_type; + + disp_type = si->ports[overlay_port->physical_port].disp_type; + if( disp_type == dt_lvds || disp_type == dt_dvi_1 ) { + uint64 v_ratio; + + // convert 32.32 format to 16.16 format; else we + // cannot multiply two fixed point values without + // overflow + v_ratio = si->fp_port.v_ratio >> (FIX_SHIFT - 16); + + v_inc = (v_inc * v_ratio) >> 16; + } + + SHOW_FLOW( 3, "v_inc=%x", v_inc ); + } + + // get initial horizontal scaler values, taking care of precharge + // don't ask questions about formulas - take them as is + // (TBD: home-brewed sub-pixel source clipping may be wrong, + // especially for uv-planes) + { + uint32 p23_group_size; + + tmp = ((src_left & 0xffff) >> 11) + ( + ( + I2FF( p1_x_start % factors->group_size, 12 ) + + I2FF( 2.5, 12 ) + + p1_h_inc / 2 + + I2FF( 0.5, 12-5 ) // rounding + ) >> (12 - 5)); // scaled by 1 << 5 + + SHOW_FLOW( 3, "p1_h_accum_init=%x", tmp ); + + p1_h_accum_init = + ((tmp << 15) & RADEON_OV0_P1_H_ACCUM_INIT_MASK) | + ((tmp << 23) & RADEON_OV0_P1_PRESHIFT_MASK); + + + p23_group_size = 2; + + tmp = ((src_left & 0xffff) >> 11) + ( + ( + I2FF( p23_x_start % p23_group_size, 12 ) + + I2FF( 2.5, 12 ) + + p23_h_inc / 2 + + I2FF( 0.5, 12-5 ) // rounding + ) >> (12 - 5)); // scaled by 1 << 5 + + SHOW_FLOW( 3, "p23_h_accum_init=%x", tmp ); + + p23_h_accum_init = + ((tmp << 15) & RADEON_OV0_P23_H_ACCUM_INIT_MASK) | + ((tmp << 23) & RADEON_OV0_P23_PRESHIFT_MASK); + } + + // get initial vertical scaler values, taking care of precharge + { + uint extra_full_line; + + extra_full_line = factors->p1_step_by == 0 ? 1 : 0; + + tmp = ((src_top & 0x0000ffff) >> 11) + ( + (min( + I2FF( 1.5, 20 ) + I2FF( extra_full_line, 20 ) + v_inc / 2, + I2FF( 2.5, 20 ) + 2 * I2FF( extra_full_line, 20 ) + ) + I2FF( 0.5, 20-5 )) // rounding + >> (20 - 5)); // scaled by 1 << 5 + + SHOW_FLOW( 3, "p1_v_accum_init=%x", tmp ); + + p1_v_accum_init = + ((tmp << 15) & RADEON_OV0_P1_V_ACCUM_INIT_MASK) | 0x00000001; + + + extra_full_line = factors->p23_step_by == 0 ? 1 : 0; + + if( params->v_uv_sub_sample_shift > 0 ) { + tmp = ((src_top & 0x0000ffff) >> 11) + ( + (min( + I2FF( 1.5, 20 ) + + I2FF( extra_full_line, 20 ) + + ((v_inc / 2) >> params->v_uv_sub_sample_shift), + I2FF( 2.5, 20 ) + + 2 * I2FF( extra_full_line, 20 ) + ) + I2FF( 0.5, 20-5 )) // rounding + >> (20 - 5)); // scaled by 1 << 5 + } else { + tmp = ((src_top & 0x0000ffff) >> 11) + ( + ( + I2FF( 2.5, 20 ) + + 2 * I2FF( extra_full_line, 20 ) + + I2FF( 0.5, 20-5 ) // rounding + ) >> (20 - 5)); // scaled by 1 << 5 + } + + SHOW_FLOW( 3, "p23_v_accum_init=%x", tmp ); + + p23_v_accum_init = + ((tmp << 15) & RADEON_OV0_P23_V_ACCUM_INIT_MASK) | 0x00000001; + } + + // show me what you've got! + // we could lock double buffering of overlay unit during update + // (new values are copied during vertical blank, so if we've updated + // only some of them, you get a whole frame of mismatched values) + // but during tests I couldn't get the artifacts go away, so + // we use the dangerous way which has the pro to not require any + // waiting + buffer[idx++] = CP_PACKET0( RADEON_OV0_VID_BUF0_BASE_ADRS, 0 ); + buffer[idx++] = offset; + buffer[idx++] = CP_PACKET0( RADEON_OV0_VID_BUF_PITCH0_VALUE, 0 ); + buffer[idx++] = node->buffer.bytes_per_row; + + buffer[idx++] = CP_PACKET0( RADEON_OV0_H_INC, 0 ); + buffer[idx++] = p1_h_inc | (p23_h_inc << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_STEP_BY, 0 ); + buffer[idx++] = factors->p1_step_by | (factors->p23_step_by << 8); + buffer[idx++] = CP_PACKET0( RADEON_OV0_V_INC, 0 ); + buffer[idx++] = v_inc; + + buffer[idx++] = CP_PACKET0( + overlay_port->is_crtc2 ? RADEON_OV1_Y_X_START : RADEON_OV0_Y_X_START, 0 ); + buffer[idx++] = (dest_left) | (dest_top << 16); + buffer[idx++] = CP_PACKET0( + overlay_port->is_crtc2 ? RADEON_OV1_Y_X_END : RADEON_OV0_Y_X_END, 0 ); + buffer[idx++] = (dest_right - 1) | ((dest_bottom - 1) << 16); + + buffer[idx++] = CP_PACKET0( RADEON_OV0_P1_BLANK_LINES_AT_TOP, 0 ); + buffer[idx++] = RADEON_P1_BLNK_LN_AT_TOP_M1_MASK | (p1_active_lines << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_P1_X_START_END, 0 ); + buffer[idx++] = p1_x_end | (p1_x_start << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_P1_H_ACCUM_INIT, 0 ); + buffer[idx++] = p1_h_accum_init; + buffer[idx++] = CP_PACKET0( RADEON_OV0_P1_V_ACCUM_INIT, 0 ); + buffer[idx++] = p1_v_accum_init; + + buffer[idx++] = CP_PACKET0( RADEON_OV0_P23_BLANK_LINES_AT_TOP, 0 ); + buffer[idx++] = RADEON_P23_BLNK_LN_AT_TOP_M1_MASK | (p23_active_lines << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_P2_X_START_END, 0 ); + buffer[idx++] = p23_x_end | (p23_x_start << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_P3_X_START_END, 0 ); + buffer[idx++] = p23_x_end | (p23_x_start << 16); + buffer[idx++] = CP_PACKET0( RADEON_OV0_P23_H_ACCUM_INIT, 0 ); + buffer[idx++] = p23_h_accum_init; + buffer[idx++] = CP_PACKET0( RADEON_OV0_P23_V_ACCUM_INIT, 0 ); + buffer[idx++] = p23_v_accum_init; + + buffer[idx++] = CP_PACKET0( RADEON_OV0_TEST, 0 ); + buffer[idx++] = node->test_reg; + buffer[idx++] = CP_PACKET0( RADEON_OV0_SCALE_CNTL, 0 ); + buffer[idx++] = RADEON_SCALER_ENABLE | + RADEON_SCALER_DOUBLE_BUFFER | + (node->ati_space << 8) | + /*RADEON_SCALER_ADAPTIVE_DEINT |*/ + (overlay_port->is_crtc2 ? RADEON_SCALER_CRTC_SEL : 0 ); + + si->overlay_mgr.auto_flip_reg ^= RADEON_OV0_SOFT_EOF_TOGGLE; + + buffer[idx++] = CP_PACKET0( RADEON_OV0_AUTO_FLIP_CNTRL, 0 ); + buffer[idx++] = si->overlay_mgr.auto_flip_reg; + + Radeon_SendCP( ai, buffer, idx ); + +done: + ai->si->active_overlay.on = ai->si->pending_overlay.on; + ai->si->active_overlay.ow = ai->si->pending_overlay.ow; + ai->si->active_overlay.ov = ai->si->pending_overlay.ov; + ai->si->active_overlay.ob = ai->si->pending_overlay.ob; + ai->si->active_overlay.h_display_start = vc->mode.h_display_start; + ai->si->active_overlay.v_display_start = vc->mode.v_display_start; + + return B_OK; +} + + +// hide overlay, but not permanently +void Radeon_TempHideOverlay( accelerator_info *ai ) +{ + SHOW_FLOW0( 3, "" ); + + Radeon_WriteRegCP( ai, RADEON_OV0_SCALE_CNTL, 0 ); +} + + +// hide overlay (can be called even if there is none visible) +void Radeon_HideOverlay( accelerator_info *ai ) +{ + shared_info *si = ai->si; + + Radeon_TempHideOverlay( ai ); + + // save that there is no overlay to be shown + si->active_overlay.on = NULL; + si->pending_overlay.on = NULL; + + // invalidate active port so it will be setup again once + // a new overlay is shown + si->active_overlay.port = -1; +} + + +// show new overlay buffer with same parameters as last one +void Radeon_ReplaceOverlayBuffer( accelerator_info *ai ) +{ + shared_info *si = ai->si; +// vuint8 *regs = ai->regs; + uint32 offset; + uint32 buffer[2*2]; + uint idx = 0; + + offset = si->pending_overlay.on->mem_offset + si->active_overlay.rel_offset; + + buffer[idx++] = CP_PACKET0( RADEON_OV0_VID_BUF0_BASE_ADRS, 0 ); + buffer[idx++] = offset; + + si->overlay_mgr.auto_flip_reg ^= RADEON_OV0_SOFT_EOF_TOGGLE; + buffer[idx++] = CP_PACKET0( RADEON_OV0_AUTO_FLIP_CNTRL, 0 ); + buffer[idx++] = si->overlay_mgr.auto_flip_reg; + + Radeon_SendCP( ai, buffer, idx ); + + ai->si->active_overlay.on = ai->si->pending_overlay.on; +} + + +// get number of pixels of overlay shown on virtual port +static int getIntersectArea( virtual_card *vc, overlay_window *ow, virtual_port *port ) +{ + int left, top, right, bottom; + + left = ow->h_start - (vc->mode.h_display_start + port->rel_x); + top = ow->v_start - (vc->mode.v_display_start + port->rel_y); + right = left + ow->width; + bottom = top + ow->height; + + if( left < 0 ) + left = 0; + if( top < 0 ) + top = 0; + if( right > port->mode.timing.h_display ) + right = port->mode.timing.h_display; + if( bottom > port->mode.timing.v_display ) + bottom = port->mode.timing.v_display; + + if( right < left || bottom < top ) + return 0; + + return (right - left) * (bottom - top); +} + + +// update overlay, to be called whenever something in terms of +// overlay have or can have been changed +status_t Radeon_UpdateOverlay( accelerator_info *ai ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + virtual_port *overlay_port; + + float brightness = 0.0f; + float contrast = 1.0f; + float saturation = 1.0f; + float hue = 0.0f; + int32 ref = 0; + + SHOW_FLOW0( 3, "" ); + + // don't mess around with overlay of someone else + if( !vc->uses_overlay ) + return B_OK; + + // make sure there really is an overlay + if( si->pending_overlay.on == NULL ) + return B_OK; + + // verify that the overlay is still valid + if( (uint32)si->pending_overlay.ot != si->overlay_mgr.token ) + return B_BAD_VALUE; + +/* SHOW_FLOW( 3, "num_ports=%d, whished_overlay_port=%d", + vc->num_ports, vc->whished_overlay_port );*/ + + if( vc->different_ports > 1 ) { + int area0, area1; + + // determine on which port most of the overlay is shown + area0 = getIntersectArea( vc, &si->pending_overlay.ow, &vc->ports[0] ); + area1 = getIntersectArea( vc, &si->pending_overlay.ow, &vc->ports[1] ); + + SHOW_FLOW( 3, "area0=%d, area1=%d", area0, area1 ); + + if( area0 >= area1 ) + overlay_port = &vc->ports[0]; + else + overlay_port = &vc->ports[1]; + } else { + // both ports show the same, use "swap displays" to decide + // where to show the overlay (to be improved as this flag isn't + // really designed for that) + if( vc->independant_ports > 1 && vc->swapDisplays ) + overlay_port = &vc->ports[1]; + else + overlay_port = &vc->ports[0]; + } + + si->pending_overlay.port = overlay_port->physical_port; + + // only update registers that have been changed to minimize work + if( si->active_overlay.port != si->pending_overlay.port ) { + Radeon_InitOverlay( ai, overlay_port ); + } + + if( si->active_overlay.ob.space != si->pending_overlay.ob.space ) { + Radeon_SetTransform( ai, brightness, contrast, saturation, hue, 0, 0, 0, ref ); + } + + if( memcmp( &si->active_overlay.ow, &si->pending_overlay.ow, sizeof( si->active_overlay.ow )) != 0 || + memcmp( &si->active_overlay.ov, &si->pending_overlay.ov, sizeof( si->active_overlay.ov )) != 0 || + si->active_overlay.h_display_start != vc->mode.h_display_start || + si->active_overlay.v_display_start != vc->mode.v_display_start || + si->active_overlay.ob.width != si->pending_overlay.ob.width || + si->active_overlay.ob.height != si->pending_overlay.ob.height || + si->active_overlay.ob.bytes_per_row != si->pending_overlay.ob.bytes_per_row ) + Radeon_ShowOverlay( ai, overlay_port ); + + else if( si->active_overlay.on != si->pending_overlay.on ) + Radeon_ReplaceOverlayBuffer( ai ); + + SHOW_FLOW0( 3, "success" ); + + return B_OK; +} diff --git a/src/add-ons/accelerants/radeon/overlay_management.c b/src/add-ons/accelerants/radeon/overlay_management.c new file mode 100644 index 0000000000..e539ab1c37 --- /dev/null +++ b/src/add-ons/accelerants/radeon/overlay_management.c @@ -0,0 +1,375 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Overlay interface +*/ + +#include "GlobalData.h" +#include "radeon_interface.h" +#include "video_overlay.h" +#include +#include +#include +#include "overlay_regs.h" + +uint32 OVERLAY_COUNT( const display_mode *dm ); +const uint32 *OVERLAY_SUPPORTED_SPACES( const display_mode *dm ); +uint32 OVERLAY_SUPPORTED_FEATURES( uint32 color_space ); +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER( color_space cs, uint16 width, uint16 height ); +status_t RELEASE_OVERLAY_BUFFER( const overlay_buffer *ob ); +status_t GET_OVERLAY_CONSTRAINTS( const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc ); +overlay_token ALLOCATE_OVERLAY( void ); +status_t RELEASE_OVERLAY(overlay_token ot); +status_t CONFIGURE_OVERLAY( overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov ); + +// we could add support of planar modes and YUV modes +// but I neither know how planar modes are defined nor +// whether there is any program that makes use of them +static uint32 overlay_colorspaces [] = +{ + B_RGB15, B_RGB16, B_RGB32, B_YCbCr422, 0 +}; + + +// public function: number of overlay units +uint32 OVERLAY_COUNT( const display_mode *dm ) +{ + SHOW_FLOW0( 3, "" ); + + return 1; +} + + +// public function: return list of supported overlay colour spaces +// dm - display mode where overlay is to be used +const uint32 *OVERLAY_SUPPORTED_SPACES( const display_mode *dm ) +{ + SHOW_FLOW0( 3, "" ); + + return overlay_colorspaces; +} + + +// public function: returns supported features +// color_space - overlay's colour space +uint32 OVERLAY_SUPPORTED_FEATURES( uint32 color_space ) +{ + SHOW_FLOW0( 3, "" ); + + return + B_OVERLAY_COLOR_KEY | + B_OVERLAY_HORIZONTAL_FILTERING | + B_OVERLAY_VERTICAL_FILTERING; +} + + +// public function: allocates overlay buffer +// cs - overlay's colour space +// width, height - width and height of overlay buffer +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER( color_space cs, uint16 width, uint16 height ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + radeon_alloc_local_mem am; + overlay_buffer_node *node; + overlay_buffer *buffer; + status_t result; + uint ati_space, test_reg, bpp; + + SHOW_FLOW0( 3, "" ); + + switch( cs ) { + case B_RGB15: + SHOW_FLOW0( 3, "RGB15" ); + bpp = 2; + ati_space = RADEON_SCALER_SOURCE_15BPP >> 8; + test_reg = 0; + break; + case B_RGB16: + SHOW_FLOW0( 3, "RGB16" ); + bpp = 2; + ati_space = RADEON_SCALER_SOURCE_16BPP >> 8; + test_reg = 0; + break; + case B_RGB32: + SHOW_FLOW0( 3, "RGB32" ); + bpp = 4; + ati_space = RADEON_SCALER_SOURCE_32BPP >> 8; + test_reg = 0; + break; + case B_YCbCr422: + SHOW_FLOW0( 3, "YCbCr422" ); + bpp = 2; + // strange naming convention: VYUY has to be read backward, + // i.e. you get (low to high address) YUYV, which is what we want! + ati_space = RADEON_SCALER_SOURCE_VYUY422 >> 8; + test_reg = 0; + break; + // YUV12 is planar pixel format consisting of two or three planes + // I have no clue whether and how this format is used in BeOS + // (don't even know how it is defined officially) +/* case B_YUV12: + SHOW_FLOW0( 3, "YUV12" ); + bpp = 2; + uvpp = 1; + ati_space = RADEON_SCALER_SOURCE_YUV12 >> 8; + testreg = 0; + break;*/ + default: + SHOW_FLOW( 3, "Unsupported format (%x)", (int)cs ); + return NULL; + } + + node = malloc( sizeof( overlay_buffer_node )); + if( node == NULL ) + return NULL; + + node->ati_space = ati_space; + node->test_reg = test_reg; + + ACQUIRE_BEN( si->engine.lock ); + + // alloc graphics mem + buffer = &node->buffer; + + buffer->space = cs; + buffer->width = width; + buffer->height = height; + buffer->bytes_per_row = (width * bpp + 0xf) & ~0xf; + + am.magic = RADEON_PRIVATE_DATA_MAGIC; + am.size = buffer->bytes_per_row * height; + + result = ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ); + if( result != B_OK ) + goto err; + + node->mem_handle = am.handle; + node->mem_offset = am.fb_offset; + buffer->buffer = (int8*)si->framebuffer + am.fb_offset; + buffer->buffer_dma = (int8*)si->framebuffer_pci + am.fb_offset; + + // add to list of overlays + node->next = vc->overlay_buffers; + node->prev = NULL; + if( node->next ) + node->next->prev = node; + + vc->overlay_buffers = node; + + RELEASE_BEN( si->engine.lock ); + + SHOW_FLOW( 3, "success: mem_handle=%x, offset=%x", node->mem_handle, node->mem_offset ); + + return buffer; + +err: + RELEASE_BEN( si->engine.lock ); + return NULL; +} + + +// public function: discard overlay buffer +status_t RELEASE_OVERLAY_BUFFER( const overlay_buffer *ob ) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + overlay_buffer_node *node; + radeon_free_local_mem fm; + status_t result; + + SHOW_FLOW0( 3, "" ); + + node = (overlay_buffer_node *)((char *)ob - offsetof( overlay_buffer_node, buffer )); + + if( si->active_overlay.on == node ) + Radeon_HideOverlay( ai ); + + // free memory + fm.magic = RADEON_PRIVATE_DATA_MAGIC; + fm.handle = node->mem_handle; + result = ioctl( ai->fd, RADEON_FREE_LOCAL_MEM, &fm ); + if( result != B_OK ) { + SHOW_FLOW( 3, "ups - couldn't free memory (handle=%x, status=%s)", + node->mem_handle, strerror( result )); + } + + ACQUIRE_BEN( si->engine.lock ); + + // remove from list + if( node->next ) + node->next->prev = node->prev; + + if( node->prev ) + node->prev->next = node->next; + else + vc->overlay_buffers = node->next; + + RELEASE_BEN( si->engine.lock ); + + SHOW_FLOW0( 3, "success" ); + + return B_OK; +} + + +// public function: get constraints of overlay unit +status_t GET_OVERLAY_CONSTRAINTS( const display_mode *dm, const overlay_buffer *ob, + overlay_constraints *oc ) +{ + SHOW_FLOW0( 3, "" ); + + // probably, this is paranoia as we only get called by app_server + // which should know what it's doing + if( dm == NULL || ob == NULL || oc == NULL ) + return B_BAD_VALUE; + + // scaler input restrictions + // TBD: check all these values; I reckon that + // most of them are too restrictive + + // position + oc->view.h_alignment = 0; + oc->view.v_alignment = 0; + + // alignment + switch (ob->space) { + case B_RGB15: + oc->view.width_alignment = 7; + break; + case B_RGB16: + oc->view.width_alignment = 7; + break; + case B_RGB32: + oc->view.width_alignment = 3; + break; + case B_YCbCr422: + oc->view.width_alignment = 7; + break; + case B_YUV12: + oc->view.width_alignment = 7; + default: + return B_BAD_VALUE; + } + oc->view.height_alignment = 0; + + // size + oc->view.width.min = 4; // make 4-tap filter happy + oc->view.height.min = 4; + oc->view.width.max = ob->width; + oc->view.height.max = ob->height; + + // scaler output restrictions + oc->window.h_alignment = 0; + oc->window.v_alignment = 0; + oc->window.width_alignment = 0; + oc->window.height_alignment = 0; + oc->window.width.min = 2; + oc->window.width.max = dm->virtual_width; + oc->window.height.min = 2; + oc->window.height.max = dm->virtual_height; + + // TBD: these values need to be checked + // (shamelessly copied from Matrix driver) + oc->h_scale.min = 1.0f / (1 << 4); + oc->h_scale.max = 1 << 12; + oc->v_scale.min = 1.0f / (1 << 4); + oc->v_scale.max = 1 << 12; + + SHOW_FLOW0( 3, "success" ); + + return B_OK; +} + + +// public function: allocate overlay unit +overlay_token ALLOCATE_OVERLAY( void ) +{ + shared_info *si = ai->si; + virtual_card *vc = ai->vc; + + SHOW_FLOW0( 3, "" ); + + if( atomic_or( &si->overlay_mgr.inuse, 1 ) != 0 ) { + SHOW_FLOW0( 3, "already in use" ); + return NULL; + } + + SHOW_FLOW0( 3, "success" ); + + vc->uses_overlay = true; + + return (void *)++si->overlay_mgr.token; +} + + +// public function: release overlay unit +status_t RELEASE_OVERLAY(overlay_token ot) +{ + virtual_card *vc = ai->vc; + shared_info *si = ai->si; + + SHOW_FLOW0( 3, "" ); + + if( (void *)si->overlay_mgr.token != ot ) + return B_BAD_VALUE; + + if( si->overlay_mgr.inuse == 0 ) + return B_ERROR; + + if( si->active_overlay.on ) + Radeon_HideOverlay( ai ); + + si->overlay_mgr.inuse = 0; + vc->uses_overlay = false; + + SHOW_FLOW0( 3, "released" ); + + return B_OK; +} + + +// public function: show/hide overlay +status_t CONFIGURE_OVERLAY( overlay_token ot, const overlay_buffer *ob, + const overlay_window *ow, const overlay_view *ov ) +{ + shared_info *si = ai->si; +// virtual_card *vc = ai->vc; + status_t result; + + SHOW_FLOW0( 4, "" ); + + if( (uint32)ot != si->overlay_mgr.token ) + return B_BAD_VALUE; + + if( !si->overlay_mgr.inuse ) + return B_BAD_VALUE; + + if( ow == NULL || ov == NULL ) { + SHOW_FLOW0( 3, "hide only" ); + Radeon_HideOverlay( ai ); + return B_OK; + } + + if( ob == NULL ) + return B_ERROR; + + ACQUIRE_BEN( si->engine.lock ); + + // store whished values + si->pending_overlay.ot = ot; + si->pending_overlay.ob = *ob; + si->pending_overlay.ow = *ow; + si->pending_overlay.ov = *ov; + + si->pending_overlay.on = (overlay_buffer_node *)((char *)ob - offsetof( overlay_buffer_node, buffer )); + + result = Radeon_UpdateOverlay( ai ); + + RELEASE_BEN( si->engine.lock ); + + return result; +} diff --git a/src/add-ons/accelerants/radeon/pll.c b/src/add-ons/accelerants/radeon/pll.c new file mode 100644 index 0000000000..63aef83ead --- /dev/null +++ b/src/add-ons/accelerants/radeon/pll.c @@ -0,0 +1,213 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Takes of PLL +*/ + + +#include "radeon_accelerant.h" + +#include "pll_regs.h" +#include "utils.h" + + +// read value "val" from PLL-register "addr" +uint32 Radeon_INPLL( accelerator_info *ai, int addr ) +{ + vuint8 *regs = ai->regs; + uint32 res; + + OUTREG8( regs, RADEON_CLOCK_CNTL_INDEX, addr & 0x3f ); + res = INREG( regs, RADEON_CLOCK_CNTL_DATA ); + + R300_PLLFix( ai ); + return res; +} + +// write value "val" to PLL-register "addr" +void Radeon_OUTPLL( accelerator_info *ai, uint8 addr, uint32 val ) +{ + vuint8 *regs = ai->regs; + + OUTREG8( regs, RADEON_CLOCK_CNTL_INDEX, ((addr & 0x3f ) | + RADEON_PLL_WR_EN)); + + OUTREG( regs, RADEON_CLOCK_CNTL_DATA, val ); + + // TBD: on XFree, there is no call of R300_PLLFix here, + // though it should as we've accessed CLOCK_CNTL_INDEX + //R300_PLLFix( ai ); +} + +// write "val" to PLL-register "addr" keeping bits "mask" +void Radeon_OUTPLLP( accelerator_info *ai, uint8 addr, + uint32 val, uint32 mask ) +{ + uint32 tmp = Radeon_INPLL( ai, addr ); + tmp &= mask; + tmp |= val; + Radeon_OUTPLL( ai, addr, tmp ); +} + + +static void Radeon_PLLWaitForReadUpdateComplete( accelerator_info *ai, virtual_port *port ) +{ + int i; + + // we should wait forever, but + // 1. this is unsafe + // 2. some r300 loop forever (reported by XFree86) + for( i = 0; i < 10000; ++i ) { + if( (Radeon_INPLL( ai, port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV ) + & RADEON_PPLL_ATOMIC_UPDATE_R) == 0 ) + return; + } +} + +static void Radeon_PLLWriteUpdate( accelerator_info *ai, virtual_port *port ) +{ + Radeon_PLLWaitForReadUpdateComplete( ai, port ); + + Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV, + RADEON_PPLL_ATOMIC_UPDATE_W, + ~RADEON_PPLL_ATOMIC_UPDATE_W ); +} + +// r300: to be called after each CLOCK_CNTL_INDEX access +// (hardware bug fix suggested by XFree86) +void R300_PLLFix( accelerator_info *ai ) +{ + vuint8 *regs = ai->regs; + uint32 save, tmp; + + if( ai->si->asic != rt_r300 ) + return; + + save = INREG( regs, RADEON_CLOCK_CNTL_INDEX ); + tmp = save & ~(0x3f | RADEON_PLL_WR_EN); + OUTREG( regs, RADEON_CLOCK_CNTL_INDEX, tmp ); + tmp = INREG( regs, RADEON_CLOCK_CNTL_DATA ); + OUTREG( regs, RADEON_CLOCK_CNTL_INDEX, save ); +} + + +// table to map divider to register value +typedef struct { + int divider; + int bitvalue; +} post_div_entry; + +static post_div_entry post_divs[] = { + { 1, 0 }, + { 2, 1 }, + { 4, 2 }, + { 8, 3 }, + { 3, 4 }, + { 16, 5 }, + { 6, 6 }, + { 12, 7 }, + { 0, 0 } +}; + +// calculate PLL dividers (freq is in 10kHz) +void Radeon_CalcPLLDividers( pll_info *pll, unsigned long freq, port_regs *values ) +{ + post_div_entry *post_div; + + SHOW_FLOW( 2, "freq=%ld", freq ); + + // formula is for generated frequency is: + // (ref_freq * feedback_div) / (ref_div * post_div ) + + // find proper divider by trial-and-error + for( post_div = &post_divs[0]; post_div->divider; ++post_div ) { + values->pll_output_freq = post_div->divider * freq; + + if( values->pll_output_freq >= pll->min_pll_freq + && values->pll_output_freq <= pll->max_pll_freq ) + break; + } + + if( post_div->divider == 0 ) + SHOW_ERROR( 2, "Frequency (%d kHz) is out of PLL range!", freq ); + + values->dot_clock_freq = freq; + values->feedback_div = RoundDiv( pll->ref_div * values->pll_output_freq, + pll->ref_freq); + values->post_div = post_div->divider; + + values->ppll_ref_div = pll->ref_div; + values->ppll_div_3 = (values->feedback_div | (post_div->bitvalue << 16)); + values->htotal_cntl = 0; + + SHOW_FLOW( 2, "dot_clock_freq=%ld, pll_output_freq=%ld, ref_div=%d, feedback_div=%d, post_div=%d", + values->dot_clock_freq, values->pll_output_freq, + pll->ref_div, values->feedback_div, values->post_div ); +} + +// write values into PLL registers +void Radeon_ProgramPLL( accelerator_info *ai, virtual_port *port, port_regs *values ) +{ + vuint8 *regs = ai->regs; + + SHOW_FLOW0( 2, "" ); + + // use some other PLL for pixel clock source to not fiddling with PLL + // while somebody is using it + Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_PIXCLKS_CNTL : RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_CPU_CLK, ~RADEON_VCLK_SRC_SEL_MASK ); + + Radeon_OUTPLLP( ai, + port->is_crtc2 ? RADEON_P2PLL_CNTL : RADEON_PPLL_CNTL, + RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN, + ~(RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); + + // select divider 3 (well, only required for first PLL) + OUTREGP( regs, RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL_DIV3, + ~RADEON_PLL_DIV_SEL_MASK ); + + // probably this register doesn't need to be set as is not + // touched by anyone (anyway - it doesn't hurt) + Radeon_OUTPLLP( ai, + port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV, + values->ppll_ref_div, + ~RADEON_PPLL_REF_DIV_MASK ); + + Radeon_OUTPLLP( ai, + port->is_crtc2 ? RADEON_P2PLL_DIV_0 : RADEON_PPLL_DIV_3, + values->ppll_div_3, + ~RADEON_PPLL_FB3_DIV_MASK ); + + Radeon_OUTPLLP( ai, + port->is_crtc2 ? RADEON_P2PLL_DIV_0 : RADEON_PPLL_DIV_3, + values->ppll_div_3, + ~RADEON_PPLL_POST3_DIV_MASK ); + + Radeon_PLLWriteUpdate( ai, port ); + Radeon_PLLWaitForReadUpdateComplete( ai, port ); + + Radeon_OUTPLL( ai, + port->is_crtc2 ? RADEON_HTOTAL2_CNTL : RADEON_HTOTAL_CNTL, + values->htotal_cntl ); + + Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_P2PLL_CNTL : RADEON_PPLL_CNTL, 0, + ~(RADEON_PPLL_RESET + | RADEON_PPLL_SLEEP + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); + + // there is no way to check whether PLL has settled, so wait a bit + snooze( 5000 ); + + // use PLL for pixel clock again + Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_PIXCLKS_CNTL : RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_PPLL_CLK, ~RADEON_VCLK_SRC_SEL_MASK ); +} diff --git a/src/add-ons/accelerants/radeon/radeon_accelerant.h b/src/add-ons/accelerants/radeon/radeon_accelerant.h new file mode 100644 index 0000000000..ee31812aad --- /dev/null +++ b/src/add-ons/accelerants/radeon/radeon_accelerant.h @@ -0,0 +1,113 @@ +/* + Copyright (c) 2002/03, Thomas Kurschel + + + Part of Radeon accelerant + + Internal header file +*/ + +#ifndef _RADEON_ACCELERANT_H +#define _RADEON_ACCELERANT_H + + +#include "radeon_interface.h" +#include "accelerant_ext.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void _kdprintf_(const char *format, ...); +//bool set_dprintf_enabled(bool); /* returns old enable flag */ + +#define dprintf _kdprintf_ + +extern int debug_level_flow; +extern int debug_level_info; +extern int debug_level_error; + +#define DEBUG_MSG_PREFIX "Radeon - " + +//#define DEBUG_MAX_LEVEL_FLOW 2 + +#include "debug_ext.h" + +typedef struct accelerator_info { + virtual_card *vc; + vuint8 *regs; // pointer to mapped registers + // !! dont't make it vuint32, access macros rely on 8 bits !! + area_id shared_info_area; + area_id regs_area; + area_id virtual_card_area; + int accelerant_is_clone; + + int fd; // file descriptor of kernel driver + struct log_info_t *log; + + area_id mode_list_area; // cloned list of standard display modes + display_mode *mode_list; + shared_info *si; +} accelerator_info; + + +uint32 Radeon_RoundVWidth( int virtual_width, int bpp ); +uint16 Radeon_GetHSyncFudge( shared_info *si, physical_port *port, int datatype ); +void Radeon_HideMultiMode( virtual_card *vc, display_mode *mode ); +bool Radeon_GetFormat( int space, int *format, int *bpp ); +status_t Radeon_CreateModeList( shared_info *si ); + +void Radeon_DetectMultiMode( virtual_card *vc, display_mode *mode ); +void Radeon_VerifyMultiMode( virtual_card *vc, shared_info *si, display_mode *mode ); +void Radeon_InitMultiModeVars( virtual_card *vc, display_mode *mode ); +status_t Radeon_CheckMultiMonTunnel( virtual_card *vc, display_mode *mode, + const display_mode *low, const display_mode *high, bool *isTunnel ); +bool Radeon_NeedsSecondPort( display_mode *mode ); +bool Radeon_DifferentPorts( display_mode *mode ); + +void Radeon_CalcCRTCRegisters( accelerator_info *ai, virtual_port *port, + display_mode *mode, port_regs *values ); +void Radeon_ProgramCRTCRegisters( accelerator_info *ai, virtual_port *port, + port_regs *values ); + +void Radeon_CalcPLLDividers( pll_info *pll, unsigned long freq, port_regs *values ); +void Radeon_ProgramPLL( accelerator_info *ai, virtual_port *port, port_regs *values ); + +void Radeon_CalcFPRegisters( accelerator_info *ai, virtual_port *port, fp_info *fp_port, display_mode *mode, port_regs *values ); +void Radeon_ProgramFPRegisters( accelerator_info *ai, fp_info *fp_port, port_regs *values ); +status_t Radeon_ReadFPEDID( accelerator_info *ai, shared_info *si ); + +status_t Radeon_SetDPMS( accelerator_info *ai, virtual_port *port, int mode ); +uint32 Radeon_GetDPMS( accelerator_info *ai, virtual_port *port ); + +void Radeon_SetCursorColors( accelerator_info *ai, virtual_port *port ); + +void Radeon_Init2D( accelerator_info *ai, uint32 datatype ); + +int Radeon_WaitForIdle( accelerator_info *ai ); +void Radeon_ResetEngine( accelerator_info *ai ); +void Radeon_SendWaitUntilIdle( accelerator_info *ai ); +void Radeon_SendPurgeCache( accelerator_info *ai ); +void Radeon_WaitForFifo( accelerator_info *ai, int entries ); +void Radeon_Finish( accelerator_info *ai ); + +status_t Radeon_InitCP( accelerator_info *ai ); +void Radeon_SendCP( accelerator_info *ai, uint32 *buffer, uint32 num_dwords ); +void Radeon_WriteRegCP( accelerator_info *ai, uint32 reg, uint32 value ); + +void Radeon_ActivateVirtualCard( accelerator_info *ai ); + +void Radeon_ReadSettings( virtual_card *vc ); +void Radeon_WriteSettings( virtual_card *vc ); + +void Radeon_HideOverlay( accelerator_info *ai ); +status_t Radeon_UpdateOverlay( accelerator_info *ai ); +void Radeon_SetColourKey( accelerator_info *ai, const overlay_window *ow ); + +status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/add-ons/accelerants/radeon/radeonlog_dump.c b/src/add-ons/accelerants/radeon/radeonlog_dump.c new file mode 100644 index 0000000000..6ca4f77b2f --- /dev/null +++ b/src/add-ons/accelerants/radeon/radeonlog_dump.c @@ -0,0 +1,73 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + Fast logger - application to write log file +*/ + + +#include +#include +#include +#include +#include "log_dump.h" +#include +#include "radeon_interface.h" + + +// usage: "radeonlog_dump device_name" +// result gets written into "radeonlog" in home directory +int main(int argc, char **argv) +{ + int device; + uint32 size; + char *buffer; + status_t res; + FILE *logfile; + const char *logfile_name; + + if( argc < 2 ) { + fprintf( stderr, "radeonlog: missing device name\n" ); + return 3; + } + + device = open( argv[1], O_RDONLY ); + + if( device < 0 ) { + fprintf( stderr, "radeonlog: cannot open log helper %s (%s)\n", + argv[1], strerror( device )); + return 3; + } + + logfile_name = "/boot/home/radeonlog"; + + logfile = fopen( logfile_name, "at" ); + + if( logfile == NULL ) { + fprintf( stderr, "idelog: cannot open log file %s\n", logfile_name ); + return 3; + } + + if( (res = ioctl( device, RADEON_GET_LOG_SIZE, &size, sizeof( size ))) != B_OK ) { + fprintf( stderr, "idelog: RADEON_GET_LOG_SIZE failed, %s\n", strerror( res )); + return 3; + } + + fprintf( logfile, "buffer size: %ld\n", size ); + + buffer = malloc( size + sizeof( int32 )); + ((uint32*)buffer)[0] = size; + + if( (res = ioctl( device, RADEON_GET_LOG_DATA, buffer, size )) != B_OK ) { + fprintf( stderr, "idelog: RADEON_GET_LOG_DATA failed, %s\n", strerror( res )); + return 3; + } + + log_printall( logfile, buffer, size ); + + fclose( logfile ); + + return 0; +} diff --git a/src/add-ons/accelerants/radeon/settings.cpp b/src/add-ons/accelerants/radeon/settings.cpp new file mode 100644 index 0000000000..9f667156f5 --- /dev/null +++ b/src/add-ons/accelerants/radeon/settings.cpp @@ -0,0 +1,119 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon accelerant + + Settings file + + We shouldn't really need settings as this info + should be stored by app_server, but especially + BWindowScreen programs cannot now about extra + features/settings, so we need to store the flags + internally (until I have a better idea ;) + + Especially "SwapWindow" should be mode-independant + (you don't swap monitors when you select another + workspace, do you?) +*/ + +#include "radeon_accelerant.h" +#include "generic.h" +#include "GlobalData.h" + +#ifdef ENABLE_SETTINGS_FILE +#include +#include +#include +#endif + +void Radeon_ReadSettings( virtual_card *vc ) +{ +#ifdef ENABLE_SETTINGS_FILE + BPath path; + int32 tmp; + + // per default we enable combine mode; + // if actual mode isn't combine mode, we fall back to clone mode + vc->wanted_multi_mode = mm_combine; + vc->swapDisplays = false; + + // per default, show overlay on first port + //vc->whished_overlay_port = 0; + + // this is problematic during boot: if there is multi-user support, + // you don't have a user when app_server gets launched; + // on the other hand, storing settings globally is not user-friendly... + if( find_directory( B_USER_SETTINGS_DIRECTORY, &path ) != B_OK ) + return; + + path.Append( "radeon" ); + + BFile file( path.Path(), B_READ_ONLY ); + + if( file.InitCheck() != B_OK ) + return; + + BMessage settings; + + if( settings.Unflatten( &file ) != B_OK ) + return; + + if( settings.FindBool( "SwapDisplays", &vc->swapDisplays ) != B_OK ) + vc->swapDisplays = false; + + if( settings.FindInt32( "MultiMonitorMode", &tmp ) != B_OK ) + tmp = mm_combine; + + switch( tmp ) { + case mm_none: + case mm_mirror: + case mm_combine: + case mm_clone: + vc->wanted_multi_mode = (multi_mode_e) tmp; + break; + default: + vc->wanted_multi_mode = mm_combine; + } + + if( settings.FindInt32( "OverlayPort", &tmp ) != B_OK ) + tmp = 0; + + //vc->whished_overlay_port = tmp; +#else + vc->wanted_multi_mode = mm_combine; + vc->swapDisplays = false; + vc->swapDisplays = false; +#endif +} + +void Radeon_WriteSettings( virtual_card *vc ) +{ +#ifdef ENABLE_SETTINGS_FILE + BPath path; + int32 tmp; + + // this is problematic during boot: if there is multi-user support, + // you don't have a user when app_server gets launched; + // on the other hand, storing settings globally is not user-friendly... + if( find_directory( B_USER_SETTINGS_DIRECTORY, &path ) != B_OK ) + return; + + path.Append( "radeon" ); + + BFile file( path.Path(), B_CREATE_FILE | B_WRITE_ONLY ); + + if( file.InitCheck() != B_OK ) + return; + + BMessage settings; + + settings.AddBool( "SwapDisplays", vc->swapDisplays ); + tmp = vc->wanted_multi_mode; + settings.AddInt32( "MultiMonitorMode", tmp ); + /*tmp = vc->whished_overlay_port; + settings.AddInt32( "OverlayPort", tmp );*/ + + settings.Flatten( &file ); +#endif +} diff --git a/src/add-ons/accelerants/radeon/test_ddc.c b/src/add-ons/accelerants/radeon/test_ddc.c new file mode 100644 index 0000000000..e839ee1f16 --- /dev/null +++ b/src/add-ons/accelerants/radeon/test_ddc.c @@ -0,0 +1,127 @@ +/* + Copyright (c) 2003, Thomas Kurschel + + + Part of DDC driver + + Test program, using Radeon Kernel Driver for I2C access. + + !DANGER! You can specify _any_ io-port to use for i2c + transfer - this is a good way to mess up your hardware. + Usual addresses are 96, 100, 104 and 108. + You've been warned. +*/ + +#include "ddc.h" +#include "radeon_interface.h" +#include +#include +#include +#include +#include + +status_t get_signals( void *cookie, int *clk, int *data ); +status_t set_signals( void *cookie, int clk, int data ); + +int io_port; + +status_t get_signals( void *cookie, int *clk, int *data ) +{ + int fd = (int)cookie; + radeon_getset_i2c buffer; + status_t res; + + buffer.magic = RADEON_PRIVATE_DATA_MAGIC; + buffer.port = io_port; + + res = ioctl( fd, RADEON_GET_I2C_SIGNALS, &buffer, sizeof( buffer )); + if( res != B_OK ) + return res; + + *clk = (buffer.value >> 9) & 1; + *data = (buffer.value >> 8) & 1; + + //printf( "read: %i, %i\n", *clk, *data ); + + return B_OK; +} + +bigtime_t max_time = 0; +bigtime_t old_time; + +status_t set_signals( void *cookie, int clk, int data ) +{ + int fd = (int)cookie; + radeon_getset_i2c buffer; + status_t res; + bigtime_t new_time; + + old_time = system_time(); + + buffer.magic = RADEON_PRIVATE_DATA_MAGIC; + buffer.port = io_port; + + res = ioctl( fd, RADEON_GET_I2C_SIGNALS, &buffer, sizeof( buffer )); + if( res != B_OK ) + return res; + + buffer.value &= ~((1 << 1) | (1 << 0)); + buffer.value &= ~((1 << 16) | (1 << 17)); + buffer.value |= ((1-clk) << 17) | ((1-data) << 16); + //buffer.value |= (1 << 16) | (1 <<17 ); + + //printf( "write: %i, %i\n", clk, data ); + new_time = system_time(); + max_time = max( max_time, new_time - old_time ); + + old_time = new_time; + + + return ioctl( fd, RADEON_SET_I2C_SIGNALS, &buffer, sizeof( buffer )); +} + +int main( int argc, char **argv ) +{ + int fd; + i2c_bus bus; + status_t res; + edid1_info edid; + void *vdif; + size_t vdif_len; + char *name = argv[1]; + + if( argc < 3 ) { + fprintf( stderr, "usage: test_ddc driver_name io_port\n" ); + return 2; + } + + //name = "/dev/graphics/1002_4c59_010000"; + + fd = open( name, O_RDWR ); + if( fd < 0 ) { + fprintf( stderr, "Cannot open device %s\n", argv[1] ); + return 3; + } + + io_port = atoi( argv[2] ); + + fprintf( stderr, "io-port: %x\n", io_port ); + + bus.cookie = (void *)fd; + bus.set_signals = &set_signals; + bus.get_signals = &get_signals; + + old_time = system_time(); + + res = ddc2_read_edid1( &bus, &edid, &vdif, &vdif_len ); + if( res < 0 ) { + printf( "%i", (int)max_time ); + fprintf( stderr, "Error reading edid: %s\n", strerror( res )); + return 1; + } + + edid_dump( &edid ); + + fprintf( stderr, "success\n" ); + return 0; +} diff --git a/src/add-ons/accelerants/radeon/utils.c b/src/add-ons/accelerants/radeon/utils.c new file mode 100644 index 0000000000..05071e2755 --- /dev/null +++ b/src/add-ons/accelerants/radeon/utils.c @@ -0,0 +1,26 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon driver + + some utility functions +*/ + +#include "OS.h" +#include "utils.h" + +// get ceil( log2( size )) +int log2( uint32 x ) +{ + int res; + uint32 tmp; + + for( res = 0, tmp = x ; tmp > 1 ; ++res ) + tmp >>= 1; + + if( (x & ((1 << res) - 1)) != 0 ) + ++res; + + return res; +} diff --git a/src/add-ons/accelerants/radeon/utils.h b/src/add-ons/accelerants/radeon/utils.h new file mode 100644 index 0000000000..193a3a12f4 --- /dev/null +++ b/src/add-ons/accelerants/radeon/utils.h @@ -0,0 +1,25 @@ +#ifndef _UTILS_H +#define _UTILS_H + +extern int log2( uint32 x ); + +static inline int RoundDiv( int num, int den ) +{ + return (num + (den / 2)) / den; +} + +static inline int ceilShiftDiv( int num, int shift ) +{ + return (num + (1 << shift) - 1) >> shift; +} + +static inline int ceilDiv( int num, int den ) +{ + return (num + den - 1) / den; +} + +// macros for fix-point calculation +#define FIX_SHIFT 32 +#define FIX_SCALE (1LL << FIX_SHIFT) + +#endif