From dcdc3ec9edaeb09f1f29ee072823342400a9b003 Mon Sep 17 00:00:00 2001 From: Rudolf Cornelissen Date: Mon, 9 Jun 2008 14:37:03 +0000 Subject: [PATCH] copied nvidia driver over to nvidia_gpgpu driver. Does nothing but compile. I hope to be fidding around with a EN8500GT soon. If for some reason I shouldn't be creating these folders, feel free to remove it again, and let me know :) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25873 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/accelerants/Jamfile | 1 + .../accelerants/nvidia_gpgpu/Acceleration.c | 159 + src/add-ons/accelerants/nvidia_gpgpu/Cursor.c | 195 + .../nvidia_gpgpu/EngineManagment.c | 98 + .../nvidia_gpgpu/GetAccelerantHook.c | 241 ++ .../accelerants/nvidia_gpgpu/GetDeviceInfo.c | 25 + .../accelerants/nvidia_gpgpu/GetModeInfo.c | 163 + .../nvidia_gpgpu/GetTimingConstraints.c | 33 + .../accelerants/nvidia_gpgpu/InitAccelerant.c | 369 ++ src/add-ons/accelerants/nvidia_gpgpu/Jamfile | 30 + .../accelerants/nvidia_gpgpu/Overlay.c | 626 ++++ .../nvidia_gpgpu/ProposeDisplayMode.c | 571 +++ .../accelerants/nvidia_gpgpu/SetDisplayMode.c | 611 +++ .../accelerants/nvidia_gpgpu/acc_std.h | 17 + .../nvidia_gpgpu/be_driver_proto.h | 72 + .../accelerants/nvidia_gpgpu/engine/Jamfile | 22 + .../accelerants/nvidia_gpgpu/engine/nv_acc.c | 1566 ++++++++ .../nvidia_gpgpu/engine/nv_acc_dma.c | 2035 ++++++++++ .../accelerants/nvidia_gpgpu/engine/nv_agp.c | 222 ++ .../accelerants/nvidia_gpgpu/engine/nv_bes.c | 876 +++++ .../nvidia_gpgpu/engine/nv_brooktreetv.c | 1749 +++++++++ .../accelerants/nvidia_gpgpu/engine/nv_crtc.c | 1114 ++++++ .../nvidia_gpgpu/engine/nv_crtc2.c | 1020 +++++ .../accelerants/nvidia_gpgpu/engine/nv_dac.c | 573 +++ .../accelerants/nvidia_gpgpu/engine/nv_dac2.c | 392 ++ .../nvidia_gpgpu/engine/nv_general.c | 961 +++++ .../nvidia_gpgpu/engine/nv_globals.c | 36 + .../nvidia_gpgpu/engine/nv_globals.h | 75 + .../accelerants/nvidia_gpgpu/engine/nv_i2c.c | 344 ++ .../accelerants/nvidia_gpgpu/engine/nv_info.c | 3285 +++++++++++++++++ .../nvidia_gpgpu/engine/nv_proto.h | 140 + .../accelerants/nvidia_gpgpu/engine/nv_std.h | 10 + .../nvidia_gpgpu/engine/nv_support.c | 38 + .../accelerants/nvidia_gpgpu/valid_mode_list | 34 + 34 files changed, 17703 insertions(+) create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/Acceleration.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/Cursor.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/EngineManagment.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/GetAccelerantHook.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/GetDeviceInfo.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/GetModeInfo.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/GetTimingConstraints.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/InitAccelerant.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/Jamfile create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/Overlay.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/ProposeDisplayMode.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/SetDisplayMode.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/acc_std.h create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/be_driver_proto.h create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/Jamfile create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc_dma.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_agp.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_bes.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_brooktreetv.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc2.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac2.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_general.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.h create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_i2c.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_info.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_proto.h create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_std.h create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/engine/nv_support.c create mode 100644 src/add-ons/accelerants/nvidia_gpgpu/valid_mode_list diff --git a/src/add-ons/accelerants/Jamfile b/src/add-ons/accelerants/Jamfile index 107c0ab6fc..2f3433cd72 100644 --- a/src/add-ons/accelerants/Jamfile +++ b/src/add-ons/accelerants/Jamfile @@ -5,6 +5,7 @@ SubInclude HAIKU_TOP src add-ons accelerants et6x00 ; SubInclude HAIKU_TOP src add-ons accelerants intel_extreme ; SubInclude HAIKU_TOP src add-ons accelerants matrox ; SubInclude HAIKU_TOP src add-ons accelerants neomagic ; +SubInclude HAIKU_TOP src add-ons accelerants nvidia_gpgpu ; SubInclude HAIKU_TOP src add-ons accelerants nvidia ; SubInclude HAIKU_TOP src add-ons accelerants radeon ; SubInclude HAIKU_TOP src add-ons accelerants s3 ; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/Acceleration.c b/src/add-ons/accelerants/nvidia_gpgpu/Acceleration.c new file mode 100644 index 0000000000..893de5150c --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/Acceleration.c @@ -0,0 +1,159 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Rudolf Cornelissen 9/2003-2/2005. +*/ + +/* + note: + moved DMA acceleration 'top-level' routines to be integrated in the engine: + it is costly to call the engine for every single function within a loop! + (measured with BeRoMeter 1.2.6: upto 15% speed increase on all CPU's.) + Leaving PIO acceleration as it is for now, for the purpose of benchmarking :-) + + note also: + attempting DMA on NV40 and higher because without it I can't get them going ATM. + Maybe later we can forget about PIO mode acceleration totally (depends on 3D + acceleration attempts). +*/ + +#define MODULE_BIT 0x40000000 + +#include "acc_std.h" + +void SCREEN_TO_SCREEN_BLIT_PIO(engine_token *et, blit_params *list, uint32 count) +{ + int i; + + /* init acc engine for blit function */ + nv_acc_setup_blit(); + + /* do each blit */ + i=0; + while (count--) + { + nv_acc_blit + ( + list[i].src_left, + list[i].src_top, + list[i].dest_left, + list[i].dest_top, + list[i].width, + list[i].height + ); + i++; + } +} + +void SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT_PIO(engine_token *et, scaled_blit_params *list, uint32 count) +{ + int i; + + /* do each blit */ + i=0; + while (count--) + { + nv_acc_video_blit + ( + list[i].src_left, + list[i].src_top, + list[i].src_width, + list[i].src_height, + list[i].dest_left, + list[i].dest_top, + list[i].dest_width, + list[i].dest_height + ); + i++; + } +} + +void SCREEN_TO_SCREEN_TRANSPARENT_BLIT_PIO(engine_token *et, uint32 transparent_colour, blit_params *list, uint32 count) +{ + int i; + + /* do each blit */ + i=0; + while (count--) + { + nv_acc_transparent_blit + ( + list[i].src_left, + list[i].src_top, + list[i].dest_left, + list[i].dest_top, + list[i].width, + list[i].height, + transparent_colour + ); + i++; + } +} + +void FILL_RECTANGLE_PIO(engine_token *et, uint32 colorIndex, fill_rect_params *list, uint32 count) +{ + int i; + + /* init acc engine for fill function */ + nv_acc_setup_rectangle(colorIndex); + + /* draw each rectangle */ + i=0; + while (count--) + { + nv_acc_rectangle + ( + list[i].left, + (list[i].right)+1, + list[i].top, + (list[i].bottom-list[i].top)+1 + ); + i++; + } +} + +void INVERT_RECTANGLE_PIO(engine_token *et, fill_rect_params *list, uint32 count) +{ + int i; + + /* init acc engine for invert function */ + nv_acc_setup_rect_invert(); + + /* invert each rectangle */ + i=0; + while (count--) + { + nv_acc_rectangle_invert + ( + list[i].left, + (list[i].right)+1, + list[i].top, + (list[i].bottom-list[i].top)+1 + ); + i++; + } +} + +void FILL_SPAN_PIO(engine_token *et, uint32 colorIndex, uint16 *list, uint32 count) +{ + int i; + + /* init acc engine for fill function */ + nv_acc_setup_rectangle(colorIndex); + + /* draw each span */ + i=0; + while (count--) + { + nv_acc_rectangle + ( + list[i+1], + list[i+2]+1, + list[i], + 1 + ); + i+=3; + } +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/Cursor.c b/src/add-ons/accelerants/nvidia_gpgpu/Cursor.c new file mode 100644 index 0000000000..6ec7033655 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/Cursor.c @@ -0,0 +1,195 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson, + Rudolf Cornelissen 4/2003-5/2004 +*/ + +#define MODULE_BIT 0x20000000 + +#include "acc_std.h" + +status_t SET_CURSOR_SHAPE(uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, uint8 *andMask, uint8 *xorMask) +{ + LOG(4,("SET_CURSOR_SHAPE: width %d, height %d, hot_x %d, hot_y %d\n", + width, height, hot_x, hot_y)); + + if ((width != 16) || (height != 16)) + { + return B_ERROR; + } + else if ((hot_x >= width) || (hot_y >= height)) + { + return B_ERROR; + } + else + { + head1_cursor_define(andMask,xorMask); + if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_OFF) + head2_cursor_define(andMask,xorMask); + + /* Update cursor variables appropriately. */ + si->cursor.width = width; + si->cursor.height = height; + si->cursor.hot_x = hot_x; + si->cursor.hot_y = hot_y; + } + + return B_OK; +} + +/* Move the cursor to the specified position on the desktop, taking account of virtual/dual issues */ +void MOVE_CURSOR(uint16 x, uint16 y) +{ + uint16 hds = si->dm.h_display_start; /* the current horizontal starting pixel */ + uint16 vds = si->dm.v_display_start; /* the current vertical starting line */ + uint16 h_adjust; + + /* clamp cursor to display */ + if (x >= si->dm.virtual_width) x = si->dm.virtual_width - 1; + if (y >= si->dm.virtual_height) y = si->dm.virtual_height - 1; + + /* store, for our info */ + si->cursor.x = x; + si->cursor.y = y; + + /* setting up minimum amount to scroll not needed: + * Nvidia cards can always do pixelprecise panning on both heads */ + h_adjust = 0x00; + + /* adjust h/v_display_start to move cursor onto screen */ + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if (x >= ((si->dm.timing.h_display * 2) + hds)) + { + hds = ((x - (si->dm.timing.h_display * 2)) + 1 + h_adjust) & ~h_adjust; + /* make sure we stay within the display! */ + if ((hds + (si->dm.timing.h_display * 2)) > si->dm.virtual_width) + hds -= (h_adjust + 1); + } + else if (x < hds) + hds = x & ~h_adjust; + break; + default: + if (x >= (si->dm.timing.h_display + hds)) + { + hds = ((x - si->dm.timing.h_display) + 1 + h_adjust) & ~h_adjust; + /* make sure we stay within the display! */ + if ((hds + si->dm.timing.h_display) > si->dm.virtual_width) + hds -= (h_adjust + 1); + } + else if (x < hds) + hds = x & ~h_adjust; + break; + } + + if (y >= (si->dm.timing.v_display + vds)) + vds = y - si->dm.timing.v_display + 1; + else if (y < vds) + vds = y; + + /* reposition the desktop _and_ the overlay on the display if required */ + if ((hds!=si->dm.h_display_start) || (vds!=si->dm.v_display_start)) + { + MOVE_DISPLAY(hds,vds); + nv_bes_move_overlay(); + } + + /* put cursor in correct physical position, so stay onscreen (rel. to CRTC) */ + if (x > (hds + si->cursor.hot_x)) x -= (hds + si->cursor.hot_x); + else x = 0; + if (y > (vds + si->cursor.hot_y)) y -= (vds + si->cursor.hot_y); + else y = 0; + + /* position the cursor on the display */ + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_CLONE: + head1_cursor_position(x,y); + head2_cursor_position(x,y); + break; + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if (x < si->dm.timing.h_display) + { + if (si->cursor.dh_right) + { + LOG(4,("MOVE_CURSOR: now on left side\n")); + head2_cursor_hide(); + head1_cursor_show(); + si->cursor.dh_right = false; + } + head1_cursor_position(x, y); + } + else + { + if (!si->cursor.dh_right) + { + LOG(4,("MOVE_CURSOR: now on right side\n")); + head1_cursor_hide(); + head2_cursor_show(); + si->cursor.dh_right = true; + } + head2_cursor_position((x - si->dm.timing.h_display), y); + } + break; + default: /* singlehead mode */ + head1_cursor_position(x,y); + break; + } +} + +void SHOW_CURSOR(bool is_visible) +{ + /* record for our info */ + si->cursor.is_visible = is_visible; + + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_CLONE: + if (is_visible) + { + head1_cursor_show(); + head2_cursor_show(); + } + else + { + head1_cursor_hide(); + head2_cursor_hide(); + } + break; + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if (is_visible) + { + if (!si->cursor.dh_right) + { + head1_cursor_show(); + } + else + { + head2_cursor_show(); + } + } + else + { + head1_cursor_hide(); + head2_cursor_hide(); + } + break; + default: /* singlehead mode */ + if (is_visible) + { + head1_cursor_show(); + } + else + { + head1_cursor_hide(); + } + break; + } +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/EngineManagment.c b/src/add-ons/accelerants/nvidia_gpgpu/EngineManagment.c new file mode 100644 index 0000000000..f0b78b5dd0 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/EngineManagment.c @@ -0,0 +1,98 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + other authors: + Mark Watson + Rudolf Cornelissen 3/2004-2/2005 +*/ + +/* + note: + attempting DMA on NV40 and higher because without it I can't get it going ATM. + Later on this can become a nv.settings switch, and maybe later we can even + forget about non-DMA completely (depends on 3D acceleration attempts). +*/ + +#define MODULE_BIT 0x10000000 + +#include "acc_std.h" + + +static engine_token nv_engine_token = { 1, B_2D_ACCELERATION, NULL }; + +uint32 ACCELERANT_ENGINE_COUNT(void) +{ + /* we have one acceleration engine */ + return 1; +} + +status_t ACQUIRE_ENGINE_PIO(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et) +{ + /* acquire the shared benaphore */ + AQUIRE_BEN(si->engine.lock) + /* sync if required */ + if (st) SYNC_TO_TOKEN(st); + + /* make sure all needed engine cmd's are mapped to the FIFO */ + nv_acc_assert_fifo(); + + /* return an engine token */ + *et = &nv_engine_token; + return B_OK; +} + +status_t ACQUIRE_ENGINE_DMA(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et) +{ + /* acquire the shared benaphore */ + AQUIRE_BEN(si->engine.lock) + /* sync if required */ + if (st) SYNC_TO_TOKEN(st); + + /* make sure all needed engine cmd's are mapped to the FIFO */ + nv_acc_assert_fifo_dma(); + + /* return an engine token */ + *et = &nv_engine_token; + return B_OK; +} + +status_t RELEASE_ENGINE(engine_token *et, sync_token *st) +{ + /* update the sync token, if any */ + if (st) GET_SYNC_TOKEN(et,st); + + /* release the shared benaphore */ + RELEASE_BEN(si->engine.lock) + return B_OK; +} + +void WAIT_ENGINE_IDLE(void) +{ + /*wait for the engine to be totally idle*/ + if (!si->settings.dma_acc) + nv_acc_wait_idle(); + else + nv_acc_wait_idle_dma(); +} + +status_t GET_SYNC_TOKEN(engine_token *et, sync_token *st) +{ + /* engine count will always be zero: we don't support syncing to token (yet) */ + st->engine_id = et->engine_id; + st->counter = si->engine.count; + return B_OK; +} + +status_t SYNC_TO_TOKEN(sync_token *st) +{ + /* wait until the engine is totally idle: we don't support syncing to token (yet) */ + /* note: + * AFAIK in order to be able to setup sync_to_token, we'd need a circular fifo + * buffer in (main) memory instead of directly programming the GPU fifo so we + * can tell (via a hardware maintained pointer into this circular fifo) where + * the acc engine is with executing commands! */ + WAIT_ENGINE_IDLE(); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/GetAccelerantHook.c b/src/add-ons/accelerants/nvidia_gpgpu/GetAccelerantHook.c new file mode 100644 index 0000000000..dccb1557dc --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/GetAccelerantHook.c @@ -0,0 +1,241 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson, + Rudolf Cornelissen 10/2002-6/2008 +*/ + +#define MODULE_BIT 0x08000000 + +#include "acc_std.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. +*/ + +/* +These definitions are out of pure lazyness. +*/ +#define CHKO(x) case B_##x: \ + if (check_overlay_capability(B_##x) == B_OK) return (void *)x; else return (void *)0 +#define CHKA(x) case B_##x: \ + if (check_acc_capability(B_##x) == B_OK) \ + {if(!si->settings.dma_acc) return (void *)x##_PIO; else return (void *)x##_DMA;} \ + else return (void *)0 +#define CHKS(x) case B_##x: \ + if(!si->settings.dma_acc) return (void *)x##_PIO; else return (void *)x##_DMA +#define HOOK(x) case B_##x: return (void *)x +#define ZERO(x) case B_##x: return (void *)0 +#define HRDC(x) case B_##x: return si->settings.hardcursor? (void *)x: (void *)0; // apsed + +void * get_accelerant_hook(uint32 feature, void *data) +{ + switch (feature) + { + /* + 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 */ + //HRDC(SET_CURSOR_SHAPE); + //HRDC(MOVE_CURSOR); + //HRDC(SHOW_CURSOR); + + /* synchronization */ + HOOK(ACCELERANT_ENGINE_COUNT); + CHKS(ACQUIRE_ENGINE); + HOOK(RELEASE_ENGINE); + HOOK(WAIT_ENGINE_IDLE); + HOOK(GET_SYNC_TOKEN); + HOOK(SYNC_TO_TOKEN); + + /* + Depending on the engine architecture, you may choose to provide a different + function to be used with each bit-depth for example. + + Note: These hooks are re-acquired by the app_server after each mode switch. + */ + + /* only export video overlay functions if card is capable of it */ + //CHKO(OVERLAY_COUNT); + //CHKO(OVERLAY_SUPPORTED_SPACES); + //CHKO(OVERLAY_SUPPORTED_FEATURES); + //CHKO(ALLOCATE_OVERLAY_BUFFER); + //CHKO(RELEASE_OVERLAY_BUFFER); + //CHKO(GET_OVERLAY_CONSTRAINTS); + //CHKO(ALLOCATE_OVERLAY); + //CHKO(RELEASE_OVERLAY); + //CHKO(CONFIGURE_OVERLAY); + + /* + 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. + + Note: These hooks are re-acquired by the app_server after each mode switch. + */ + + /* only export 2D acceleration functions in modes that are capable of it */ + /* used by the app_server and applications (BWindowScreen) */ + //CHKA(SCREEN_TO_SCREEN_BLIT); + //CHKA(FILL_RECTANGLE); + //CHKA(INVERT_RECTANGLE); + //CHKA(FILL_SPAN); + /* not (yet) used by the app_server: + * so just for application use (BWindowScreen) */ +// CHKA(SCREEN_TO_SCREEN_TRANSPARENT_BLIT); + //CHKA(SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT); + } + + /* Return a null pointer for any feature we don't understand. */ + return 0; +} +#undef CHKO +#undef CHKA +#undef CHKD +#undef HOOK +#undef ZERO +#undef HRDC + +status_t check_overlay_capability(uint32 feature) +{ + char *msg = ""; + + /* setup logmessage text */ + switch (feature) + { + case B_OVERLAY_COUNT: + msg = "B_OVERLAY_COUNT"; + break; + case B_OVERLAY_SUPPORTED_SPACES: + msg = "B_OVERLAY_SUPPORTED_SPACES"; + break; + case B_OVERLAY_SUPPORTED_FEATURES: + msg = "B_OVERLAY_SUPPORTED_FEATURES"; + break; + case B_ALLOCATE_OVERLAY_BUFFER: + msg = "B_ALLOCATE_OVERLAY_BUFFER"; + break; + case B_RELEASE_OVERLAY_BUFFER: + msg = "B_RELEASE_OVERLAY_BUFFER"; + break; + case B_GET_OVERLAY_CONSTRAINTS: + msg = "B_GET_OVERLAY_CONSTRAINTS"; + break; + case B_ALLOCATE_OVERLAY: + msg = "B_ALLOCATE_OVERLAY"; + break; + case B_RELEASE_OVERLAY: + msg = "B_RELEASE_OVERLAY"; + break; + case B_CONFIGURE_OVERLAY: + msg = "B_CONFIGURE_OVERLAY"; + break; + default: + msg = "UNKNOWN"; + break; + } + + /* all older cards have a supported bes */ + if ((si->ps.card_type <= NV40) || (si->ps.card_type == NV45)) + { + LOG(4, ("Overlay: Exporting hook %s.\n", msg)); + return B_OK; + } + + /* all newer NV40 architecture cards have a new HDTV capable bes except for + * GeForce 6800's. Unfortunately we have no info about the new bes yet. */ + LOG(4, ("Overlay: Not exporting hook %s.\n", msg)); + return B_ERROR; +} + +status_t check_acc_capability(uint32 feature) +{ + char *msg = ""; + + /* setup logmessage text */ + switch (feature) + { + case B_SCREEN_TO_SCREEN_BLIT: + msg = "B_SCREEN_TO_SCREEN_BLIT"; + break; + case B_FILL_RECTANGLE: + msg = "B_FILL_RECTANGLE"; + break; + case B_INVERT_RECTANGLE: + msg = "B_INVERT_RECTANGLE"; + break; + case B_FILL_SPAN: + msg = "B_FILL_SPAN"; + break; + case B_SCREEN_TO_SCREEN_TRANSPARENT_BLIT: + msg = "B_SCREEN_TO_SCREEN_TRANSPARENT_BLIT"; + break; + case B_SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT: + msg = "B_SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT"; + /* this function is only defined for DMA acceleration, + * but doesn't support the B_CMAP8 colorspace */ + //fixme: checkout B_CMAP8 support sometime, as some cards seem to support it? + if (!si->settings.dma_acc || (si->dm.space == B_CMAP8)) + { + LOG(4, ("Acc: Not exporting hook %s.\n", msg)); + return B_ERROR; + } + break; + default: + msg = "UNKNOWN"; + break; + } + + /* hardware acceleration is only supported in modes with upto a certain + * memory pitch.. */ + if (si->acc_mode) + { + LOG(4, ("Acc: Exporting hook %s.\n", msg)); + return B_OK; + } + else + { + LOG(4, ("Acc: Not exporting hook %s.\n", msg)); + return B_ERROR; + } +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/GetDeviceInfo.c b/src/add-ons/accelerants/nvidia_gpgpu/GetDeviceInfo.c new file mode 100644 index 0000000000..dac99cdcc3 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/GetDeviceInfo.c @@ -0,0 +1,25 @@ +/* + Author: + Rudolf Cornelissen 7/2004-01/2006 +*/ + +#define MODULE_BIT 0x04000000 + +#include "acc_std.h" + +/* Get some info about the device */ +status_t GET_ACCELERANT_DEVICE_INFO(accelerant_device_info * adi) +{ + LOG(4,("GET_ACCELERANT_DEVICE_INFO: returning info\n")); + + /* no info on version is provided, so presumably this is for my info */ + adi->version = 1; + + sprintf(adi->name, si->adi.name); + sprintf(adi->chipset, si->adi.chipset); + sprintf(adi->serial_no, "unknown"); + adi->memory = si->ps.memory_size; + adi->dac_speed = si->ps.max_dac1_clock; + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/GetModeInfo.c b/src/add-ons/accelerants/nvidia_gpgpu/GetModeInfo.c new file mode 100644 index 0000000000..5a454d0c05 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/GetModeInfo.c @@ -0,0 +1,163 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson + Rudolf Cornelissen 9/2002-10/2005 +*/ + +#define MODULE_BIT 0x02000000 + +#include "acc_std.h" + +/* + Return the current display mode. The only time you might return an + error is if a mode hasn't been set. Or if the system hands you a NULL pointer. +*/ +status_t GET_DISPLAY_MODE(display_mode *current_mode) +{ + /* check for NULL pointer */ + if (current_mode == NULL) return B_ERROR; + + *current_mode = si->dm; + return B_OK; +} + +/* Return the frame buffer configuration information. */ +status_t GET_FRAME_BUFFER_CONFIG(frame_buffer_config *afb) +{ + /* check for NULL pointer */ + if (afb == NULL) return B_ERROR; + + *afb = si->fbc; + return B_OK; +} + +/* Return the maximum and minium pixelclock limits for the specified mode. */ +/* NOTE: + * Due to BeOS constraints output for all heads will be limited to the head with + * the least capabilities. */ +status_t GET_PIXEL_CLOCK_LIMITS(display_mode *dm, uint32 *low, uint32 *high) +{ + uint32 max_pclk = 0; + uint32 min_pclk = 0; + + /* check for NULL pointers */ + if ((dm == NULL) || (low == NULL) || (high == NULL)) return B_ERROR; + + /* specify requested info */ + if (dm->flags & DUALHEAD_BITS) + { + /* dualhead mode */ + /* find min. value */ + switch (si->ps.card_type) + { + default: + *low = ((si->ps.min_video_vco * 1000) / 16); + break; + } + /* find max. value: + * using decondary DAC specs because they could be narrower (twinview) */ + switch (dm->space) + { + case B_CMAP8: + max_pclk = si->ps.max_dac2_clock_8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_pclk = si->ps.max_dac2_clock_16; + break; + case B_RGB24_LITTLE: + max_pclk = si->ps.max_dac2_clock_24; + break; + case B_RGB32_LITTLE: + /* specially noted because of RAM speed constraints! */ + max_pclk = si->ps.max_dac2_clock_32dh; + break; + default: + /* use fail-safe value */ + max_pclk = si->ps.max_dac2_clock_32dh; + break; + } + /* return values in kHz */ + *high = max_pclk * 1000; + } + else + { + /* singlehead mode */ + /* find min. value */ + switch (si->ps.card_type) + { + default: + *low = ((si->ps.min_pixel_vco * 1000) / 16); + break; + } + /* find max. value: depends on which head is used as primary head */ + if (!si->ps.crtc2_prim) + { + switch (dm->space) + { + case B_CMAP8: + max_pclk = si->ps.max_dac1_clock_8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_pclk = si->ps.max_dac1_clock_16; + break; + case B_RGB24_LITTLE: + max_pclk = si->ps.max_dac1_clock_24; + break; + case B_RGB32_LITTLE: + max_pclk = si->ps.max_dac1_clock_32; + break; + default: + /* use fail-safe value */ + max_pclk = si->ps.max_dac1_clock_32; + break; + } + } + else + { + switch (dm->space) + { + case B_CMAP8: + max_pclk = si->ps.max_dac2_clock_8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_pclk = si->ps.max_dac2_clock_16; + break; + case B_RGB24_LITTLE: + max_pclk = si->ps.max_dac2_clock_24; + break; + case B_RGB32_LITTLE: + max_pclk = si->ps.max_dac2_clock_32; + break; + default: + /* use fail-safe value */ + max_pclk = si->ps.max_dac2_clock_32; + break; + } + } + /* return values in kHz */ + *high = max_pclk * 1000; + } + + /* clamp lower limit to 48Hz vertical refresh for now. + * Apparantly the BeOS screenprefs app does limit the upper refreshrate to 90Hz, + * while it does not limit the lower refreshrate. */ + min_pclk = ((uint32)dm->timing.h_total * (uint32)dm->timing.v_total * 48) / 1000; + if (min_pclk > *low) *low = min_pclk; + + return B_OK; +} + +/* Return the semaphore id that will be used to signal a vertical sync occured. */ +sem_id ACCELERANT_RETRACE_SEMAPHORE(void) +{ + if (si->ps.int_assigned) + return si->vblank; + else + return B_ERROR; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/GetTimingConstraints.c b/src/add-ons/accelerants/nvidia_gpgpu/GetTimingConstraints.c new file mode 100644 index 0000000000..2b00bbbf87 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/GetTimingConstraints.c @@ -0,0 +1,33 @@ +/* + Author: + Rudolf Cornelissen 7/2004 +*/ + +#define MODULE_BIT 0x01000000 + +#include "acc_std.h" + +/* Used to help generate mode lines */ +status_t GET_TIMING_CONSTRAINTS(display_timing_constraints * dtc) +{ + LOG(4, ("GET_TIMING_CONSTRAINTS: returning info\n")); + + /* specs are identical for all nVidia cards */ + dtc->h_res = 8; + dtc->h_sync_min = 8; + dtc->h_sync_max = 248; + /* Note: + * h_blank info is used to determine the max. diff. between h_total and h_display! */ + dtc->h_blank_min = 8; + dtc->h_blank_max = 1016; + + dtc->v_res = 1; + dtc->v_sync_min = 1; + dtc->v_sync_max = 15; + /* Note: + * v_blank info is used to determine the max. diff. between v_total and v_display! */ + dtc->v_blank_min = 1; + dtc->v_blank_max = 255; + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/InitAccelerant.c b/src/add-ons/accelerants/nvidia_gpgpu/InitAccelerant.c new file mode 100644 index 0000000000..a56b9baec5 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/InitAccelerant.c @@ -0,0 +1,369 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson, + Rudolf Cornelissen 10/2002-4/2006. +*/ + +#define MODULE_BIT 0x00800000 + +#include +#include +#include "acc_std.h" + +static status_t init_common(int the_fd); + +/* Initialization code shared between primary and cloned accelerants */ +static status_t init_common(int the_fd) { + status_t result; + nv_get_private_data gpd; + + // LOG not available from here to next LOG: NULL si + + /* memorize the file descriptor */ + fd = the_fd; + /* set the magic number so the driver knows we're for real */ + gpd.magic = NV_PRIVATE_DATA_MAGIC; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, NV_GET_PRIVATE_DATA, &gpd, sizeof(gpd)); + if (result != B_OK) goto error0; + + /* clone the shared area for our use */ + shared_info_area = clone_area(DRIVER_PREFIX " shared", (void **)&si, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, gpd.shared_info_area); + if (shared_info_area < 0) { + result = shared_info_area; + goto error0; + } + // LOG is now available, si !NULL + LOG(4,("init_common: logmask 0x%08x, memory %dMB, hardcursor %d, usebios %d, switchhead %d\n", + si->settings.logmask, si->settings.memory, si->settings.hardcursor, si->settings.usebios, si->settings.switchhead)); + LOG(4,("init_common: dumprom %d, pgm_panel %d, dma_acc %d, tv_output %d, vga_on_tv %d\n", + si->settings.dumprom, si->settings.pgm_panel, si->settings.dma_acc, si->settings.tv_output, si->settings.vga_on_tv)); + LOG(4,("init_common: force_sync %d, gpu_clk %dMhz, ram_clk %dMhz, force_ws %d\n", + si->settings.force_sync, si->settings.gpu_clk, si->settings.ram_clk, si->settings.force_ws)); + + /*Check for R4.5.0 and if it is running, use work around*/ + { + if (si->use_clone_bugfix) + { + /*check for R4.5.0 bug and attempt to work around*/ + LOG(2,("InitACC: Found R4.5.0 bug - attempting to work around\n")); + regs = si->clone_bugfix_regs; + } + else + { + /* clone the memory mapped registers for our use - does not work on <4.5.2 (but is better this way)*/ + regs_area = clone_area(DRIVER_PREFIX " regs", (void **)®s, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, si->regs_area); + if (regs_area < 0) { + result = regs_area; + goto error1; + } + } + } + + /* all done */ + goto error0; + +error1: + delete_area(shared_info_area); +error0: + return result; +} + +/* Clean up code shared between primary and cloned accelrants */ +static void uninit_common(void) { + /* release the memory mapped registers */ + delete_area(regs_area); + /* a little cheap paranoia */ + regs = 0; + /* release our copy of the shared info from the kernel driver */ + delete_area(shared_info_area); + /* more cheap paranoia */ + si = 0; +} + +/* +Initialize the accelerant. the_fd is the file handle of the device (in +/dev/graphics) that has been opened by the app_server (or some test harness). +We need to determine if the kernel driver and the accelerant are compatible. +If they are, get the accelerant ready to handle other hook functions and +report success or failure. +*/ +status_t INIT_ACCELERANT(int the_fd) +{ + status_t result; + int pointer_reservation; //mem reserved for pointer + int cnt; //used for iteration through the overlay buffers + + if (0) { + time_t now = time (NULL); + // LOG not available from here to next LOG: NULL si + MSG(("INIT_ACCELERANT: %s", ctime (&now))); + } + + /* note that we're the primary accelerant (accelerantIsClone is global) */ + accelerantIsClone = 0; + + /* do the initialization common to both the primary and the clones */ + result = init_common(the_fd); + + /* bail out if the common initialization failed */ + if (result != B_OK) goto error0; + // LOG now available: !NULL si + + /* ensure that INIT_ACCELERANT is executed just once (copies should be clones) */ + if (si->accelerant_in_use) + { + result = B_NOT_ALLOWED; + goto error1; + } + + /* call the device specific init code */ + result = nv_general_powerup(); + + /* bail out if it failed */ + if (result != B_OK) goto error1; + + /* + Now would be a good time to figure out what video modes your card supports. + We'll place the list of modes in another shared area so all of the copies + of the driver can see them. The primary copy of the accelerant (ie the one + initialized with this routine) will own the "one true copy" of the list. + Everybody else get's a read-only clone. + */ + result = create_mode_list(); + if (result != B_OK) + { + goto error1; + } + + /* + Put the cursor at the start of the frame buffer. + Nvidia cursor is 32x32 16 color? takes up 4096 bytes of RAM. + */ + /* Initialize the rest of the cursor information while we're here */ + si->cursor.width = 16; + si->cursor.height = 16; + si->cursor.hot_x = 0; + si->cursor.hot_y = 0; + si->cursor.x = 0; + si->cursor.y = 0; + si->cursor.dh_right = false; + + /* + Put the frame buffer immediately following the cursor data. We store this + info in a frame_buffer_config structure to make it convienient to return + to the app_server later. + */ + pointer_reservation = 0; + /* Nvidia hardcursor needs 2kB space */ + if (si->settings.hardcursor) pointer_reservation = 2048; + + si->fbc.frame_buffer = (void *)((char *)si->framebuffer+pointer_reservation); + si->fbc.frame_buffer_dma = (void *)((char *)si->framebuffer_pci+pointer_reservation); + + /* count of issued parameters or commands */ + si->engine.last_idle = si->engine.count = 0; + /* no 3D clones are currently loaded */ + si->engine.threeD.clones = 0; + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; + INIT_BEN(si->engine.lock); + + INIT_BEN(si->overlay.lock); + for (cnt = 0; cnt < MAXBUFFERS; cnt++) + { + /* make sure overlay buffers are 'marked' as being free */ + si->overlay.myBuffer[cnt].buffer = NULL; + si->overlay.myBuffer[cnt].buffer_dma = NULL; + } + + /* make sure overlay unit is 'marked' as being free */ + si->overlay.myToken = NULL; + + /* note that overlay is not in use (for nv_bes_move_overlay()) */ + si->overlay.active = false; + + /* bail out if something failed */ + if (result != B_OK) goto error1; + + /* initialise various cursor stuff */ + head1_cursor_init(); + if (si->ps.secondary_head) head2_cursor_init(); + + /* ensure cursor state */ + head1_cursor_hide(); + if (si->ps.secondary_head) head2_cursor_hide(); + + /* ensure DPMS state */ + si->dpms_flags = B_DPMS_ON; + + /* ensure TVout state: + * TVencoder is on head to be assigned primary, no dualhead switch mode active. */ + //fixme: actually check on what CRTC TVout was active during boot (if any)... + si->dm.flags = TV_PRIMARY; + + /* make sure a possible 3D add-on will block rendering and re-initialize itself. + * note: update in _this_ order only */ + /* SET_DISPLAY_MODE will reset this flag when it's done. */ + si->engine.threeD.mode_changing = true; + /* every 3D add-on will reset this bit-flag when it's done. */ + si->engine.threeD.newmode = 0xffffffff; + + /* a winner! */ + result = B_OK; + /* ensure that INIT_ACCELERANT won't be executed again (copies should be clones) */ + si->accelerant_in_use = true; + goto error0; + +error1: + /* + Initialization failed after init_common() succeeded, so we need to clean + up before quiting. + */ + uninit_common(); + +error0: + return result; +} + +/* +Return the number of bytes required to hold the information required +to clone the device. +*/ +ssize_t ACCELERANT_CLONE_INFO_SIZE(void) { + /* + Since we're passing the name of the device as the only required + info, return the size of the name buffer + */ + return B_OS_NAME_LENGTH; // apsed, was MAX_NV_DEVICE_NAME_LENGTH; +} + + +/* +Return the info required to clone the device. void *data points to +a buffer at least ACCELERANT_CLONE_INFO_SIZE() bytes in length. +*/ +void GET_ACCELERANT_CLONE_INFO(void *data) { + nv_device_name dn; + status_t result; + + /* call the kernel driver to get the device name */ + dn.magic = NV_PRIVATE_DATA_MAGIC; + /* store the returned info directly into the passed buffer */ + dn.name = (char *)data; + result = ioctl(fd, NV_DEVICE_NAME, &dn, sizeof(dn)); +} + +/* +Initialize a copy of the accelerant as a clone. void *data points to +a copy of the data returned by GET_ACCELERANT_CLONE_INFO(). +*/ +status_t CLONE_ACCELERANT(void *data) +{ + status_t result; + char path[MAXPATHLEN]; + + /* the data is the device name */ + /* Note: the R4 graphics driver kit is in error here (missing trailing '/') */ + strcpy(path, "/dev/"); + strcat(path, (const char *)data); + /* open the device, the permissions aren't important */ + fd = open(path, B_READ_WRITE); + if (fd < 0) + { + /* we can't use LOG because we didn't get the shared_info struct.. */ + char fname[64]; + FILE *myhand = NULL; + + sprintf (fname, "/boot/home/" DRIVER_PREFIX ".accelerant.0.log"); + myhand=fopen(fname,"a+"); + fprintf(myhand, "CLONE_ACCELERANT: couldn't open kerneldriver %s! Aborting.\n", path); + fclose(myhand); + + /* abort with resultcode from open attempt on kerneldriver */ + result = fd; + goto error0; + } + + /* note that we're a clone accelerant */ + accelerantIsClone = 1; + + /* call the shared initialization code */ + result = init_common(fd); + + /* bail out if the common initialization failed */ + if (result != B_OK) goto error1; + + /* ensure that INIT_ACCELERANT is executed first (i.e. primary accelerant exists) */ + if (!(si->accelerant_in_use)) + { + result = B_NOT_ALLOWED; + goto error2; + } + + /* setup CRTC and DAC functions access */ + //fixme: setup_virtualized_heads is a problem for clones: needs to be run + //for each clone if the mode is changed! + if (si->ps.secondary_head) + setup_virtualized_heads(si->crtc_switch_mode); + else + setup_virtualized_heads(si->ps.crtc2_prim); + + /* get shared area for display modes */ + result = my_mode_list_area = clone_area( + DRIVER_PREFIX " cloned display_modes", + (void **)&my_mode_list, + B_ANY_ADDRESS, + B_READ_AREA, + si->mode_area + ); + if (result < B_OK) goto error2; + + /* all done */ + LOG(4,("CLONE_ACCELERANT: cloning was succesfull.\n")); + + result = B_OK; + goto error0; + +error2: + /* free up the areas we cloned */ + uninit_common(); +error1: + /* close the device we opened */ + close(fd); +error0: + return result; +} + +void UNINIT_ACCELERANT(void) +{ + if (accelerantIsClone) + { + LOG(4,("UNINIT_ACCELERANT: shutting down clone accelerant.\n")); + } + else + { + LOG(4,("UNINIT_ACCELERANT: shutting down primary accelerant.\n")); + + /* delete benaphores ONLY if we are the primary accelerant */ + DELETE_BEN(si->engine.lock); + DELETE_BEN(si->overlay.lock); + + /* ensure that INIT_ACCELERANT can be executed again */ + si->accelerant_in_use = false; + } + + /* free our mode list area */ + delete_area(my_mode_list_area); + /* paranoia */ + my_mode_list = 0; + /* release our cloned data */ + uninit_common(); + /* close the file handle ONLY if we're the clone */ + if (accelerantIsClone) close(fd); +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/Jamfile b/src/add-ons/accelerants/nvidia_gpgpu/Jamfile new file mode 100644 index 0000000000..36ce715fe4 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/Jamfile @@ -0,0 +1,30 @@ +SubDir HAIKU_TOP src add-ons accelerants nvidia_gpgpu ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics nvidia_gpgpu ] ; +UseHeaders [ FDirName $(SUBDIR) engine ] ; + +Addon nvidia_gpgpu.accelerant : + Acceleration.c + Cursor.c + EngineManagment.c + GetAccelerantHook.c + GetDeviceInfo.c + GetModeInfo.c + GetTimingConstraints.c + InitAccelerant.c + Overlay.c + ProposeDisplayMode.c + SetDisplayMode.c + : libnvidia_gpgpu_engine.a +; + +Package haiku-nvidia_gpgpu-cvs : + nvidia_gpgpu.accelerant : + boot home config add-ons accelerants ; + +Depends nvidia_gpgpu.accelerant : nvidia_gpgpu ; + +SubInclude HAIKU_TOP src add-ons accelerants nvidia_gpgpu engine ; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/Overlay.c b/src/add-ons/accelerants/nvidia_gpgpu/Overlay.c new file mode 100644 index 0000000000..30d37420a6 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/Overlay.c @@ -0,0 +1,626 @@ +/* Written by Rudolf Cornelissen 05/2002-4/2006 */ + +/* Note on 'missing features' in BeOS 5.0.3 and DANO: + * BeOS needs to define more colorspaces! It would be nice if BeOS would support the FourCC 'definitions' + * of colorspaces. These colorspaces are 32bit words, so it could be simply done (or is it already so?) + */ + +#define MODULE_BIT 0x00000400 + +#include "acc_std.h" + +/* define the supported overlay input colorspaces */ +/* It would be nice to have the YUV4:2:0 2-plane mode implemented also later on, but the Be colorspace + * definitions (in GraphicsDefs.h, R5.0.3 and DANO5.1d0) do not include this one... */ +static uint32 overlay_colorspaces [] = { (uint32)B_YCbCr422, (uint32)B_NO_COLOR_SPACE }; + +uint32 OVERLAY_COUNT(const display_mode *dm) +// This method is never used AFAIK though it *is* exported on R5.0.3 and DANO. +// Does someone know howto invoke it? +{ + LOG(4,("Overlay: count called\n")); + + /* check for NULL pointer */ + if (dm == NULL) + { + LOG(4,("Overlay: No display mode specified!\n")); + } + /* apparantly overlay count should report the number of 'overlay units' on the card */ + return 1; +} + +const uint32 *OVERLAY_SUPPORTED_SPACES(const display_mode *dm) +// This method is never used AFAIK though it *is* exported on R5.0.3 and DANO. +// Does someone know howto invoke it? +{ + LOG(4,("Overlay: supported_spaces called.\n")); + + /* check for NULL pointer */ + if (dm == NULL) + { + LOG(4,("Overlay: No display mode specified!\n")); + return NULL; + } + + /* assuming interlaced VGA is not supported */ + if (dm->timing.flags & B_TIMING_INTERLACED) + { + return NULL; + } + /* return a B_NO_COLOR_SPACE terminated list */ + return &overlay_colorspaces[0]; +} + +uint32 OVERLAY_SUPPORTED_FEATURES(uint32 a_color_space) +// This method is never used AFAIK. On R5.0.3 and DANO it is not even exported! +{ + LOG(4,("Overlay: supported_features: color_space $%08x\n",a_color_space)); + + /* check what features are supported for the current overlaybitmap colorspace */ + switch (a_color_space) + { + default: + return + ( B_OVERLAY_KEYING_USES_ALPHA | + B_OVERLAY_COLOR_KEY | + B_OVERLAY_HORIZONTAL_FILTERING | + B_OVERLAY_VERTICAL_FILTERING ); + } +} + +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER(color_space cs, uint16 width, uint16 height) +{ + int offset = 0; /* used to determine next buffer to create */ + uint32 adress, adress2, temp32; /* used to calculate buffer adresses */ + uint32 oldsize = 0; /* used to 'squeeze' new buffers between already existing ones */ + int cnt; /* loopcounter */ + + /* acquire the shared benaphore */ + AQUIRE_BEN(si->overlay.lock) + + LOG(4,("Overlay: cardRAM_start = $%08x\n",(uint32)((uint8*)si->framebuffer))); + LOG(4,("Overlay: cardRAM_start_DMA = $%08x\n",(uint32)((uint8*)si->framebuffer_pci))); + LOG(4,("Overlay: cardRAM_size = %3.3fMb\n",(si->ps.memory_size / (1024.0 * 1024.0)))); + + /* find first empty slot (room for another buffer?) */ + for (offset = 0; offset < MAXBUFFERS; offset++) + { + if (si->overlay.myBuffer[offset].buffer == NULL) break; + } + + LOG(4,("Overlay: Allocate_buffer offset = %d\n",offset)); + + if (offset < MAXBUFFERS) + /* setup new scaler input buffer */ + { + switch (cs) + { + case B_YCbCr422: + if (si->ps.card_arch < NV10A) + { + /* check if slopspace is needed: RIVA128 and TNT need ~0x000f. */ + si->overlay.myBuffer[offset].width = ((width + 0x000f) & ~0x000f); + } + else + { + /* check if slopspace is needed: GeForce need ~0x001f. */ + /* fixme: + * update needed for GF DVDmax support to adhere to CRTC2 constraints?? */ + si->overlay.myBuffer[offset].width = ((width + 0x001f) & ~0x001f); + } + si->overlay.myBuffer[offset].bytes_per_row = 2 * si->overlay.myBuffer[offset].width; + + /* check if the requested horizontal pitch is supported: */ + //fixme: tune for GF and TNT... + if (si->overlay.myBuffer[offset].width > 4088) + { + LOG(4,("Overlay: Sorry, requested buffer pitch not supported, aborted\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } + break; + default: + /* unsupported colorspace! */ + LOG(4,("Overlay: Sorry, colorspace $%08x not supported, aborted\n",cs)); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + break; + } + + /* check if the requested buffer width is supported */ + if (si->overlay.myBuffer[offset].width > 1024) + { + LOG(4,("Overlay: Sorry, requested buffer width not supported, aborted\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } + /* check if the requested buffer height is supported */ + if (height > 1024) + { + LOG(4,("Overlay: Sorry, requested buffer height not supported, aborted\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } + + /* store slopspace (in pixels) for each bitmap for use by 'overlay unit' (BES) */ + si->overlay.myBufInfo[offset].slopspace = si->overlay.myBuffer[offset].width - width; + + si->overlay.myBuffer[offset].space = cs; + si->overlay.myBuffer[offset].height = height; + + /* we define the overlay buffers to reside 'in the back' of the cards RAM */ + /* NOTE to app programmers: + * Beware that an app using overlay needs to track workspace switches and screenprefs + * changes. If such an action is detected, the app needs to reset it's pointers to the + * newly created overlay bitmaps, which will be assigned by BeOS automatically after such + * an event. (Also the app needs to respect the new overlay_constraints that will be applicable!) + * + * It is entirely possible that new bitmaps may *not* be re-setup at all, or less of them + * than previously setup by the app might be re-setup. This is due to cardRAM restraints then. + * This means that the app should also check for NULL pointers returned by the bitmaps, + * and if this happens, it needs to fallback to single buffered overlay or even fallback to + * bitmap output for the new situation. */ + + /* Another NOTE for app programmers: + * A *positive* side-effect of assigning the first overlay buffer exactly at the end of the + * cardRAM is that apps that try to write beyond the buffer's space get a segfault immediately. + * This *greatly* simplifies tracking such errors! + * Of course such errors may lead to strange effects in the app or driver behaviour if they are + * not hunted down and removed.. */ + + /* calculate first free RAM adress in card: + * Driver setup is as follows: + * card base: - hardware cursor bitmap (if used), + * directly above - screen memory for both heads */ + adress2 = (((uint32)((uint8*)si->fbc.frame_buffer)) + /* cursor already included here */ + (si->fbc.bytes_per_row * si->dm.virtual_height)); /* size in bytes of screen(s) */ + LOG(4,("Overlay: first free cardRAM virtual adress $%08x\n", adress2)); + + /* calculate 'preliminary' buffer size including slopspace */ + oldsize = si->overlay.myBufInfo[offset].size; + si->overlay.myBufInfo[offset].size = + si->overlay.myBuffer[offset].bytes_per_row * si->overlay.myBuffer[offset].height; + + /* calculate virtual memory adress that would be needed for a new bitmap */ + /* NOTE to app programmers: + * For testing app behaviour regarding workspace switches or screen prefs changes to settings + * that do not have enough cardRAM left for allocation of overlay bitmaps, you need a card with + * a low amount of RAM. Or you can set in the file nv.settings for example: + * memory 8 #8Mb RAM on card + * and reboot (this simulates 8Mb RAM on the card). + * + * If you switch now to settings: 1600x1200x32bit (single head) the app needs to fallback to + * bitmap output or maybe single buffered overlay output if small bitmaps are used. */ + + adress = (((uint32)((uint8*)si->framebuffer)) + si->ps.memory_size); + /* Keep some extra distance as a workaround for certain bugs (see + * DriverInterface.h for an explanation). */ + if (si->ps.card_arch < NV40A) + adress -= PRE_NV40_OFFSET; + else + adress -= NV40_PLUS_OFFSET; + + for (cnt = 0; cnt <= offset; cnt++) + { + adress -= si->overlay.myBufInfo[cnt].size; + } + + /* the > G200 scalers require buffers to be aligned to 16 byte pages cardRAM offset, G200 can do with + * 8 byte pages cardRAM offset. Compatible settings used, has no real downside consequences here */ + + /* Check if we need to modify the buffers starting adress and thus the size */ + /* calculate 'would be' cardRAM offset */ + temp32 = (adress - ((uint32)((vuint32 *)si->framebuffer))); + /* check if it is aligned */ + if (temp32 != (temp32 & 0xfffffff0)) + { + /* update the (already calculated) buffersize to get it aligned */ + si->overlay.myBufInfo[offset].size += (temp32 - (temp32 & 0xfffffff0)); + /* update the (already calculated) adress to get it aligned */ + adress -= (temp32 - (temp32 & 0xfffffff0)); + } + LOG(4,("Overlay: new buffer needs virtual adress $%08x\n", adress)); + + /* First check now if buffer to be defined is 'last one' in memory (speaking backwards): + * this is done to prevent a large buffer getting created in the space a small buffer + * occupied earlier, if not all buffers created were deleted. + * Note also that the app can delete the buffers in any order desired. */ + + /* NOTE to app programmers: + * If you are going to delete a overlay buffer you created, you should delete them *all* and + * then re-create only the new ones needed. This way you are sure not to get unused memory- + * space in between your overlay buffers for instance, so cardRAM is used 'to the max'. + * If you don't, you might not get a buffer at all if you are trying to set up a larger one + * than before. + * (Indeed: not all buffers *have* to be of the same type and size...) */ + + for (cnt = offset; cnt < MAXBUFFERS; cnt++) + { + if (si->overlay.myBuffer[cnt].buffer != NULL) + { + /* Check if the new buffer would fit into the space the single old one used here */ + if (si->overlay.myBufInfo[offset].size <= oldsize) + { + /* It does, so we reset to the old size and adresses to prevent the space from shrinking + * if we get here again... */ + adress -= (oldsize - si->overlay.myBufInfo[offset].size); + si->overlay.myBufInfo[offset].size = oldsize; + LOG(4,("Overlay: 'squeezing' in buffer:\n" + "Overlay: resetting it to virtual adress $%08x and size $%08x\n", adress,oldsize)); + /* force exiting the FOR loop */ + cnt = MAXBUFFERS; + } + else + { + /* nogo, sorry */ + LOG(4,("Overlay: Other buffer(s) exist after this one:\n" + "Overlay: not enough space to 'squeeze' this one in, aborted\n")); + + /* Reset to the old size to prevent the space from 'growing' if we get here again... */ + si->overlay.myBufInfo[offset].size = oldsize; + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } + } + } + + /* check if we have enough space to setup this new bitmap + * (preventing overlap of desktop RAMspace & overlay bitmap RAMspace here) */ + if (adress < adress2) + /* nope, sorry */ + { + LOG(4,("Overlay: Sorry, no more space for buffers: aborted\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } + /* continue buffer setup */ + si->overlay.myBuffer[offset].buffer = (void *) adress; + + /* calculate physical memory adress (for dma use) */ + adress = (((uint32)((uint8*)si->framebuffer_pci)) + si->ps.memory_size); + /* Keep some extra distance as a workaround for certain bugs (see + * DriverInterface.h for an explanation). */ + if (si->ps.card_arch < NV40A) + adress -= PRE_NV40_OFFSET; + else + adress -= NV40_PLUS_OFFSET; + + for (cnt = 0; cnt <= offset; cnt++) + { + adress -= si->overlay.myBufInfo[cnt].size; + } + /* this adress is already aligned to the scaler's requirements (via the already modified sizes) */ + si->overlay.myBuffer[offset].buffer_dma = (void *) adress; + + LOG(4,("Overlay: New buffer: addr $%08x, dma_addr $%08x, color space $%08x\n", + (uint32)((uint8*)si->overlay.myBuffer[offset].buffer), + (uint32)((uint8*)si->overlay.myBuffer[offset].buffer_dma), cs)); + LOG(4,("Overlay: New buffer's size is $%08x\n", si->overlay.myBufInfo[offset].size)); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return &si->overlay.myBuffer[offset]; + } + else + /* sorry, no more room for buffers */ + { + LOG(4,("Overlay: Sorry, no more space for buffers: aborted\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } +} + +status_t RELEASE_OVERLAY_BUFFER(const overlay_buffer *ob) +/* Note that the user can delete the buffers in any order desired! */ +{ + int offset = 0; + + if (ob != NULL) + { + /* find the buffer */ + for (offset = 0; offset < MAXBUFFERS; offset++) + { + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + } + + if (offset < MAXBUFFERS) + /* delete current buffer */ + { + si->overlay.myBuffer[offset].buffer = NULL; + si->overlay.myBuffer[offset].buffer_dma = NULL; + + LOG(4,("Overlay: Release_buffer offset = %d, buffer released\n",offset)); + + return B_OK; + } + else + { + /* this is no buffer of ours! */ + LOG(4,("Overlay: Release_overlay_buffer: not ours, aborted!\n")); + + return B_ERROR; + } + } + else + /* no buffer specified! */ + { + LOG(4,("Overlay: Release_overlay_buffer: no buffer specified, aborted!\n")); + + return B_ERROR; + } +} + +status_t GET_OVERLAY_CONSTRAINTS + (const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc) +{ + int offset = 0; + + LOG(4,("Overlay: Get_overlay_constraints called\n")); + + /* check for NULL pointers */ + if ((dm == NULL) || (ob == NULL) || (oc == NULL)) + { + LOG(4,("Overlay: Get_overlay_constraints: Null pointer(s) detected!\n")); + return B_ERROR; + } + + /* find the buffer */ + for (offset = 0; offset < MAXBUFFERS; offset++) + { + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + } + + if (offset < MAXBUFFERS) + { + /* scaler input (values are in pixels) */ + oc->view.h_alignment = 0; + oc->view.v_alignment = 0; + + switch (ob->space) + { + case B_YCbCr422: + if (si->ps.card_arch < NV10A) + { + /* RIVA128 and TNT need 15. + * Note: this has to be in sync with the slopspace setup during buffer allocation.. */ + oc->view.width_alignment = 15; + } + else + { + /* GeForce need 31. + * Note: this has to be in sync with the slopspace setup during buffer allocation.. */ + oc->view.width_alignment = 31; + } + break; + default: + /* we should not be here, but set the worst-case value just to be safe anyway */ + oc->view.width_alignment = 31; + break; + } + + oc->view.height_alignment = 0; + oc->view.width.min = 1; + oc->view.height.min = 2; /* two fields */ + 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; + /* GeForce cards can output upto and including 2046 pixels in width */ + //fixme: how about TNT? + if (dm->virtual_width > 2046) + { + oc->window.width.max = 2046; + } + else + { + oc->window.width.max = dm->virtual_width; + } + oc->window.height.min = 2; + /* GeForce cards can output upto and including 2046 pixels in height */ + //fixme: how about TNT? + if (dm->virtual_height > 2046) + { + oc->window.height.max = 2046; + } + else + { + oc->window.height.max = dm->virtual_height; + } + + /* GeForce scaling restrictions */ + switch (si->ps.card_arch) + { + case NV04A: + /* Riva128-TNT2 series have an old BES engine... */ + oc->h_scale.min = 1.0; + oc->v_scale.min = 1.0; + break; + case NV30A: + case NV40A: + /* GeForceFX series and up have a new BES engine... */ + oc->h_scale.min = 0.5; + oc->v_scale.min = 0.5; + /* NV31 (confirmed GeForceFX 5600) has NV20A scaling limits! + * So let it fall through... */ + if (si->ps.card_type != NV31) break; + default: + /* the rest in between... */ + oc->h_scale.min = 0.125; + oc->v_scale.min = 0.125; + break; + } + /* all cards have a upscaling limit of 8.0 (see official nVidia specsheets) */ + oc->h_scale.max = 8.0; + oc->v_scale.max = 8.0; + + return B_OK; + } + else + { + /* this is no buffer of ours! */ + LOG(4,("Overlay: Get_overlay_constraints: buffer is not ours, aborted!\n")); + + return B_ERROR; + } +} + +overlay_token ALLOCATE_OVERLAY(void) +{ + uint32 tmpToken; + LOG(4,("Overlay: Allocate_overlay called: ")); + + /* come up with a token */ + tmpToken = 0x12345678; + + /* acquire the shared benaphore */ + AQUIRE_BEN(si->overlay.lock) + + /* overlay unit already in use? */ + if (si->overlay.myToken == NULL) + /* overlay unit is available */ + { + LOG(4,("succesfull\n")); + + si->overlay.myToken = &tmpToken; + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return si->overlay.myToken; + } + else + /* sorry, overlay unit is occupied */ + { + LOG(4,("failed: already in use!\n")); + + /* release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + + return NULL; + } +} + +status_t RELEASE_OVERLAY(overlay_token ot) +{ + LOG(4,("Overlay: Release_overlay called: ")); + + /* is this call for real? */ + if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken)) + /* nope, abort */ + { + LOG(4,("failed, not in use!\n")); + + return B_ERROR; + } + else + /* call is for real */ + { + + nv_release_bes(); + + LOG(4,("succesfull\n")); + + si->overlay.myToken = NULL; + return B_OK; + } +} + +status_t CONFIGURE_OVERLAY + (overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov) +{ + int offset = 0; /* used for buffer index */ + + LOG(4,("Overlay: Configure_overlay called: ")); + + /* Note: + * When a Workspace switch, screen prefs change, or overlay app shutdown occurs, BeOS will + * release all overlay buffers. The buffer currently displayed at that moment, may need some + * 'hardware releasing' in the CONFIGURE_OVERLAY routine. This is why CONFIGURE_OVERLAY gets + * called one more time then, with a null pointer for overlay_window and overlay_view, while + * the currently displayed overlay_buffer is given. + * The G200-G550 do not need to do anything on such an occasion, so we simply return if we + * get called then. */ + if ((ow == NULL) || (ov == NULL)) + { + LOG(4,("output properties changed\n")); + + return B_OK; + } + + /* Note: + * If during overlay use the screen prefs are changed, or the workspace has changed, it + * may be that we were not able to re-allocate the requested overlay buffers (or only partly) + * due to lack of cardRAM. If the app does not respond properly to this, we might end up + * with a NULL pointer instead of a overlay_buffer to work with here. + * Of course, we need to abort then to prevent the system from 'going down'. + * The app will probably crash because it will want to write into this non-existant buffer + * at some point. */ + if (ob == NULL) + { + LOG(4,("no overlay buffer specified\n")); + + return B_ERROR; + } + + /* is this call done by the app that owns us? */ + if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken)) + /* nope, abort */ + { + LOG(4,("failed\n")); + + return B_ERROR; + } + else + /* call is for real */ + { + /* find the buffer's offset */ + for (offset = 0; offset < MAXBUFFERS; offset++) + { + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + } + + if (offset < MAXBUFFERS) + { + LOG(4,("succesfull, switching to buffer %d\n", offset)); + + /* program overlay hardware */ + nv_configure_bes(ob, ow, ov, offset); + + return B_OK; + } + else + { + /* this is no buffer of ours! */ + LOG(4,("buffer is not ours, aborted!\n")); + + return B_ERROR; + } + } +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/ProposeDisplayMode.c b/src/add-ons/accelerants/nvidia_gpgpu/ProposeDisplayMode.c new file mode 100644 index 0000000000..352a2c7275 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/ProposeDisplayMode.c @@ -0,0 +1,571 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors for NV driver: + Mark Watson, + Rudolf Cornelissen 9/2002-4/2006 +*/ + +#define MODULE_BIT 0x00400000 + +#include "acc_std.h" + +#define T_POSITIVE_SYNC (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC) +/* mode flags will be setup as status info by PROPOSEMODE! */ +#define MODE_FLAGS 0 +#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode)) + +/*some monitors only handle a fixed set of modes*/ +#include "valid_mode_list" + +/* Standard VESA modes, + * plus panel specific resolution modes which are internally modified during run-time depending on the requirements of the actual + * panel connected. The modes as listed here, should timing-wise be as compatible with analog (CRT) monitors as can be... */ +static const display_mode mode_list[] = { +/* 4:3 modes; 307.2k pixels */ +{ { 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) */ +/* 4:3 modes; 480k pixels */ +{ { 36000, 800, 824, 896, 1024, 600, 601, 603, 625, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@56Hz_(800X600) from Be, Inc. driver + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +/* 4:3 modes; 786.432k pixels */ +{ { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) + XFree86 */ +{ { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +/* 4:3 modes; 995.328k pixels */ +{ { 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) */ +{ { 97800, 1152, 1216, 1344, 1552, 864, 865, 868, 900, 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) + XFree86 */ +{ { 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) */ +/* 5:4 modes; 1.311M pixels */ +{ { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024) from Be, Inc. driver + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +/* 4:3 panel mode; 1.47M pixels */ +{ { 122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */ +/* 4:3 modes; 1.92M pixels */ +{ { 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) + XFree86 */ +/* identical lines to above one, apart from refreshrate.. */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +{ { 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) + XFree86 */ +/* end identical lines. */ +/* 4:3 modes; 2.408M pixels */ +{ { 204750, 1792, 1920, 2120, 2448, 1344, 1345, 1348, 1394, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1792X1344) from Be, Inc. driver + XFree86 */ +{ { 261000, 1792, 1888, 2104, 2456, 1344, 1345, 1348, 1417, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1792X1344) from Be, Inc. driver + XFree86 */ +/* 4:3 modes; 2.584M pixels */ +{ { 218250, 1856, 1952, 2176, 2528, 1392, 1393, 1396, 1439, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1856X1392) from Be, Inc. driver + XFree86 */ +{ { 288000, 1856, 1984, 2208, 2560, 1392, 1393, 1396, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1856X1392) from Be, Inc. driver + XFree86 */ +/* 4:3 modes; 2.765M pixels */ +{ { 234000, 1920, 2048, 2256, 2600, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1440) from Be, Inc. driver + XFree86 */ +{ { 297000, 1920, 2064, 2288, 2640, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1920X1440) from Be, Inc. driver + XFree86 */ +/* 4:3 modes; 3.146M pixels */ +{ { 266950, 2048, 2200, 2424, 2800, 1536, 1537, 1540, 1589, B_POSITIVE_VSYNC}, B_CMAP8, 2048, 1536, 0, 0, MODE_FLAGS}, /* From XFree86 posting @60Hz + XFree86 */ +/* 16:10 panel mode; 400k pixels */ +{ { 31300, 800, 848, 928, 1008, 500, 501, 504, 518, T_POSITIVE_SYNC}, B_CMAP8, 800, 500, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X500) */ +/* 16:10 panel mode; 655.36k pixels */ +{ { 52800, 1024, 1072, 1176, 1328, 640, 641, 644, 663, T_POSITIVE_SYNC}, B_CMAP8, 1024, 640, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X640) */ +/* 16:10 panel-TV mode; 983.04k pixels */ +{ { 80135, 1280, 1344, 1480, 1680, 768, 769, 772, 795, T_POSITIVE_SYNC}, B_CMAP8, 1280, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X768) */ +/* 16:10 panel mode; 1.024M pixels */ +{ { 83500, 1280, 1344, 1480, 1680, 800, 801, 804, 828, T_POSITIVE_SYNC}, B_CMAP8, 1280, 800, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X800) */ +/* 16:10 panel mode; 1.296M pixels */ +{ { 106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, T_POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */ +/* 16:10 panel mode; 1.764M pixels */ +{ { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */ +/* 16:10 panel mode; 2.304M pixels */ +{ { 160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */ +/* 16:9 panel mode; 1280x720 */ +{ { 74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, T_POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */ +}; + + +/*! + Check mode is between low and high limits. + Returns: + B_OK - found one + B_BAD_VALUE - mode can be made, but outside limits + B_ERROR - not possible +*/ +/* BOUNDS WARNING: + * BeOS (tested R5.0.3PE) is failing BWindowScreen.SetFrameBuffer() if PROPOSEMODE + * returns B_BAD_VALUE. It's called by the OS with target, low and high set to + * have the same settings for BWindowScreen! + * Which means we should not return B_BAD_VALUE on anything except for deviations on: + * display_mode.virtual_width; + * display_mode.virtual_height; + * display_mode.timing.h_display; + * display_mode.timing.v_display; + */ +/* Note: + * The target mode should be modified to correspond to the mode as it can be made. */ +status_t +PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high) +{ + status_t status = B_OK; + float pix_clock_found, target_aspect; + uint8 m,n,p, bpp; + status_t result; + uint32 max_vclk, row_bytes, mem_reservation; + bool acc_mode; + double target_refresh = ((double)target->timing.pixel_clock * 1000.0) + / ((double)target->timing.h_total * (double)target->timing.v_total); + bool want_same_width = target->timing.h_display == target->virtual_width; + bool want_same_height = target->timing.v_display == target->virtual_height; + + LOG(1, ("PROPOSEMODE: (ENTER) requested virtual_width %d, virtual_height %d\n", + target->virtual_width, target->virtual_height)); + + /*check valid list: + if (VALID_REQUIRED is set) + { + if (find modes with same size) + { + pick one with nearest pixel clock + } + else + { + pick next largest with nearest pixel clock and modify visible portion as far as possible + } + } + */ + +#ifdef VALID_MODE_REQUIRED + { + int i; + int closest_mode_ptr; + uint32 closest_mode_clock; + + LOG(1, ("PROPOSEMODE: valid mode required!\n")); + + closest_mode_ptr = 0xbad; + closest_mode_clock = 0; + for (i = 0; i < VALID_MODES; i++) { + /*check size is ok and clock is better than any found before*/ + if (target->timing.h_display == valid_mode_list[i].h_display + && target->timing.v_display == valid_mode_list[i].v_display) { + if (abs(valid_mode_list[i].pixel_clock-target->timing.pixel_clock) + < abs(closest_mode_clock-target->timing.pixel_clock)) { + closest_mode_clock = valid_mode_list[i].pixel_clock; + closest_mode_ptr = i; + } + } + } + + if (closest_mode_ptr == 0xbad) { + /* if no modes of correct size */ + LOG(4, ("PROPOSEMODE: no valid mode found, aborted.\n")); + return B_ERROR; + } else { + target->timing = valid_mode_list[closest_mode_ptr]; + /* I require this refresh */ + target_refresh = ((double)target->timing.pixel_clock * 1000.0) + / ((double)target->timing.h_total * (double)target->timing.v_total); + } + } +#endif + + /*find a nearby valid timing from that given*/ + result = head1_validate_timing(&target->timing.h_display, + &target->timing.h_sync_start, &target->timing.h_sync_end, + &target->timing.h_total, &target->timing.v_display, + &target->timing.v_sync_start, &target->timing.v_sync_end, + &target->timing.v_total); + if (result == B_ERROR) { + LOG(4, ("PROPOSEMODE: could not validate timing, aborted.\n")); + return result; + } + + /* disable aspect checks for a requested TVout mode when mode is TVout capable */ + if (!si->ps.tvout + || !(BT_check_tvmode(*target) && (target->flags & TV_BITS))) { + /* check if all connected output devices can display the requested mode's aspect: */ + /* calculate display mode aspect */ + target_aspect = (target->timing.h_display / ((float)target->timing.v_display)); + /* NOTE: + * allow 0.10 difference so 5:4 aspect panels will be able to use 4:3 aspect modes! */ + switch (si->ps.monitors) { + case 0x01: /* digital panel on head 1, nothing on head 2 */ + if (si->ps.panel1_aspect < (target_aspect - 0.10)) { + LOG(4, ("PROPOSEMODE: connected panel1 is not widescreen type, aborted.\n")); + return B_ERROR; + } + break; + case 0x10: /* nothing on head 1, digital panel on head 2 */ + if (si->ps.panel2_aspect < (target_aspect - 0.10)) { + LOG(4, ("PROPOSEMODE: connected panel2 is not widescreen type, aborted.\n")); + return B_ERROR; + } + break; + case 0x11: /* digital panels on both heads */ + if ((si->ps.panel1_aspect < (target_aspect - 0.10)) + || (si->ps.panel2_aspect < (target_aspect - 0.10))) { + LOG(4, ("PROPOSEMODE: not all connected panels are widescreen type, aborted.\n")); + return B_ERROR; + } + break; + default: +#if 0 + /* at least one analog monitor is connected, or nothing detected at all */ + /* (if forcing widescreen type was requested don't block mode) */ + if (target_aspect > 1.34 && !si->settings.force_ws) { + LOG(4, ("PROPOSEMODE: not all output devices can display widescreen modes, aborted.\n")); + return B_ERROR; + } +#endif + break; + } + +// Wide screen modes are pretty common these days... - better use EDID! +#if 0 + /* only export widescreen panel-TV modes when an exact resolution match exists, + * to prevent the modelist from becoming too crowded */ + if (target_aspect > 1.61 && !si->settings.force_ws) { + status_t panel_TV_stat = B_ERROR; + + if (si->ps.tmds1_active) { + if (target->timing.h_display == si->ps.p1_timing.h_display + && target->timing.v_display == si->ps.p1_timing.v_display) + panel_TV_stat = B_OK; + } + if (si->ps.tmds2_active) { + if (target->timing.h_display == si->ps.p2_timing.h_display + && target->timing.v_display == si->ps.p2_timing.v_display) + panel_TV_stat = B_OK; + } + if (panel_TV_stat != B_OK) { + LOG(4, ("PROPOSEMODE: WS panel_TV mode requested but no such TV here, aborted.\n")); + return B_ERROR; + } + } +#endif + } + + /* check if panel(s) can display the requested resolution (if connected) */ + if (si->ps.tmds1_active) { + if (target->timing.h_display > si->ps.p1_timing.h_display + || target->timing.v_display > si->ps.p1_timing.v_display) { + LOG(4, ("PROPOSEMODE: panel1 can't display requested resolution, aborted.\n")); + return B_ERROR; + } + } + if (si->ps.tmds2_active) { + if (target->timing.h_display > si->ps.p2_timing.h_display + || target->timing.v_display > si->ps.p2_timing.v_display) { + LOG(4, ("PROPOSEMODE: panel2 can't display requested resolution, aborted.\n")); + return B_ERROR; + } + } + + /* validate display vs. virtual */ + 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; + + /* nail virtual size and 'subsequently' calculate rowbytes */ + result = nv_general_validate_pic_size(target, &row_bytes, &acc_mode); + if (result == B_ERROR) { + LOG(4, ("PROPOSEMODE: could not validate virtual picture size, aborted.\n")); + return result; + } + + /* check if virtual_width is still within the requested limits */ + if (target->virtual_width < low->virtual_width + || target->virtual_width > high->virtual_width) { + status = B_BAD_VALUE; + LOG(4, ("PROPOSEMODE: WARNING: virtual_width deviates too much\n")); + } + + /* check if timing found is within the requested horizontal 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) { + /* BWindowScreen workaround: we accept everything except h_display deviations */ + if (target->timing.h_display < low->timing.h_display + || target->timing.h_display > high->timing.h_display) + status = B_BAD_VALUE; + + LOG(4, ("PROPOSEMODE: WARNING: horizontal timing deviates too much\n")); + } + + /* check if timing found is within the requested vertical 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.v_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) { + /* BWindowScreen workaround: we accept everything except v_display deviations */ + if (target->timing.v_display < low->timing.v_display + || target->timing.v_display > high->timing.v_display) + status = B_BAD_VALUE; + + LOG(4, ("PROPOSEMODE: WARNING: vertical timing deviates too much\n")); + } + + /* adjust pixelclock for possible timing modifications done above */ + target->timing.pixel_clock = target_refresh * ((double)target->timing.h_total) + * ((double)target->timing.v_total) / 1000.0; + + /* Now find the nearest valid pixelclock we actually can setup for the target mode, + * this also makes sure we don't generate more pixel bandwidth than the device can handle */ + /* calculate settings, but do not actually test anything (that costs too much time!) */ + result = head1_pix_pll_find(*target, &pix_clock_found, &m, &n, &p, 0); + /* update the target mode */ + target->timing.pixel_clock = pix_clock_found * 1000; + + /* note if we fell outside the limits */ + if (target->timing.pixel_clock < low->timing.pixel_clock + || target->timing.pixel_clock > high->timing.pixel_clock) { + /* BWindowScreen workaround: we accept deviations <= 1Mhz */ + if (target->timing.pixel_clock < low->timing.pixel_clock - 1000 + || target->timing.pixel_clock > high->timing.pixel_clock + 1000) + status = B_BAD_VALUE; + + LOG(4, ("PROPOSEMODE: WARNING: pixelclock deviates too much\n")); + } + + mem_reservation = 0; + /* checkout space needed for hardcursor (if any) */ + if (si->settings.hardcursor) + mem_reservation = 2048; + + /* Reserve extra space as a workaround for certain bugs (see DriverInterface.h + * for an explanation). */ + if (si->ps.card_arch < NV40A) + mem_reservation += PRE_NV40_OFFSET; + else + mem_reservation += NV40_PLUS_OFFSET; + + /* memory requirement for frame buffer */ + if (row_bytes * target->virtual_height > si->ps.memory_size - mem_reservation) { + target->virtual_height = (si->ps.memory_size - mem_reservation) / row_bytes; + } + if (target->virtual_height < target->timing.v_display) { + LOG(4,("PROPOSEMODE: not enough memory for current mode, aborted.\n")); + return B_ERROR; + } + + LOG(4,("PROPOSEMODE: validated virtual_width %d, virtual_height %d pixels\n", + target->virtual_width, target->virtual_height)); + + if (target->virtual_height < low->virtual_height + || target->virtual_height > high->virtual_height) { + status = B_BAD_VALUE; + LOG(4, ("PROPOSEMODE: WARNING: virtual_height deviates too much\n")); + } + + /* setup status flags */ + LOG(1, ("PROPOSEMODE: initial modeflags: $%08x\n", target->flags)); + /* preset to singlehead card without TVout, no overlay support and no hardcursor. + * also advice system that app_server and acc engine may touch the framebuffer + * simultaneously (fixed). */ + target->flags &= + ~(DUALHEAD_CAPABLE | TV_CAPABLE | B_SUPPORTS_OVERLAYS | B_HARDWARE_CURSOR | B_IO_FB_NA); + /* we always allow parallel access (fixed), the DAC is always in 'enhanced' + * mode (fixed), and all modes support DPMS (fixed); + * We support scrolling and panning in every mode, so we 'send a signal' to + * BWindowScreen.CanControlFrameBuffer() by setting B_SCROLL. */ + /* BTW: B_PARALLEL_ACCESS in combination with a hardcursor enables + * BDirectWindow windowed modes. */ + target->flags |= (B_PARALLEL_ACCESS | B_8_BIT_DAC | B_DPMS | B_SCROLL); + + /* determine the 'would be' max. pixelclock for the second DAC for the current videomode if dualhead were activated */ + switch (target->space) { + case B_CMAP8: + max_vclk = si->ps.max_dac2_clock_8; + bpp = 1; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_vclk = si->ps.max_dac2_clock_16; + bpp = 2; + break; + case B_RGB24_LITTLE: + max_vclk = si->ps.max_dac2_clock_24; + bpp = 3; + break; + case B_RGB32_LITTLE: + max_vclk = si->ps.max_dac2_clock_32dh; + bpp = 4; + break; + default: + /* use fail-safe value */ + max_vclk = si->ps.max_dac2_clock_32dh; + bpp = 4; + break; + } + + /* set DUALHEAD_CAPABLE if suitable */ + //fixme: update for independant secondary head use! (reserve fixed memory then) + if (si->ps.secondary_head && target->timing.pixel_clock <= (max_vclk * 1000)) { + switch (target->flags & DUALHEAD_BITS) { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if (si->ps.memory_size - mem_reservation + >= row_bytes * target->virtual_height + && (uint16)(row_bytes / bpp) >= target->timing.h_display * 2) + target->flags |= DUALHEAD_CAPABLE; + break; + case DUALHEAD_CLONE: + if (si->ps.memory_size - mem_reservation + >= row_bytes * target->virtual_height) + target->flags |= DUALHEAD_CAPABLE; + break; + case DUALHEAD_OFF: + if (si->ps.memory_size - mem_reservation + >= row_bytes * target->virtual_height * 2) + target->flags |= DUALHEAD_CAPABLE; + break; + } + } + + /* if not dualhead capable card clear dualhead flags */ + if (!(target->flags & DUALHEAD_CAPABLE)) + target->flags &= ~DUALHEAD_BITS; + + /* set TV_CAPABLE if suitable: pixelclock is not important (defined by TVstandard) */ + if (si->ps.tvout && BT_check_tvmode(*target)) + target->flags |= TV_CAPABLE; + + /* if not TVout capable card clear TVout flags */ + if (!(target->flags & TV_CAPABLE)) + target->flags &= ~TV_BITS; + + /* make sure TV head assignment is sane */ + if (target->flags & TV_BITS) { + if (!si->ps.secondary_head) + target->flags |= TV_PRIMARY; + else if ((target->flags & DUALHEAD_BITS) == DUALHEAD_OFF) + target->flags |= TV_PRIMARY; + } else + target->flags &= ~TV_PRIMARY; + + /* set HARDWARE_CURSOR mode if suitable */ + if (si->settings.hardcursor) + target->flags |= B_HARDWARE_CURSOR; + + /* set SUPPORTS_OVERLAYS if suitable */ + if (si->ps.card_type <= NV40 || si->ps.card_type == NV45) + target->flags |= B_SUPPORTS_OVERLAYS; + + LOG(1, ("PROPOSEMODE: validated modeflags: $%08x\n", target->flags)); + + /* overrule timing command flags to be (fixed) blank_pedestal = 0.0IRE, + * progressive scan (fixed), and sync_on_green not avaible. */ + target->timing.flags &= ~(B_BLANK_PEDESTAL | B_TIMING_INTERLACED | B_SYNC_ON_GREEN); + /* The HSYNC and VSYNC command flags are actually executed by the driver. */ + + if (status == B_OK) + LOG(4, ("PROPOSEMODE: completed successfully.\n")); + else + LOG(4, ("PROPOSEMODE: mode can be made, but outside given limits.\n")); + return status; +} + + +/*! + Return the number of modes this device will return from GET_MODE_LIST(). + This is precalculated in create_mode_list (called from InitAccelerant stuff) +*/ +uint32 +ACCELERANT_MODE_COUNT(void) +{ + LOG(1, ("ACCELERANT_MODE_COUNT: the modelist contains %d modes\n",si->mode_count)); + return si->mode_count; +} + + +/*! Copy the list of guaranteed supported video modes to the location provided. +*/ +status_t +GET_MODE_LIST(display_mode *dm) +{ + LOG(1, ("GET_MODE_LIST: exporting the modelist created before.\n")); + + memcpy(dm, my_mode_list, si->mode_count * sizeof(display_mode)); + return B_OK; +} + + +/*! Create a list of display_modes to pass back to the caller. +*/ +status_t +create_mode_list(void) +{ + size_t max_size; + uint32 i, j, pix_clk_range; + const display_mode *src; + display_mode *dst, low, high; + color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8}; + + /* figure out how big the list could be, and adjust up to nearest multiple of B_PAGE_SIZE */ + max_size = (((MODE_COUNT * 4) * sizeof(display_mode)) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); + + /* create an area to hold the info */ + si->mode_area = my_mode_list_area = create_area("NV accelerant mode info", + (void **)&my_mode_list, B_ANY_ADDRESS, max_size, B_NO_LOCK, + B_READ_AREA | B_WRITE_AREA); + if (my_mode_list_area < B_OK) + return my_mode_list_area; + + /* walk through our predefined list and see which modes fit this device */ + src = mode_list; + dst = my_mode_list; + si->mode_count = 0; + for (i = 0; i < MODE_COUNT; i++) { + /* set ranges for acceptable values */ + low = high = *src; + /* 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; + /* 'some cards need wider virtual widths for certain modes': + * Not true. They might need a wider pitch, but this is _not_ reflected in + * virtual_width, but in fbc.bytes_per_row. */ + //So disable next line: + //high.virtual_width = 4096; + /* do it once for each depth we want to support */ + for (j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) { + /* set target values */ + *dst = *src; + /* poke the specific space */ + dst->space = low.space = high.space = spaces[j]; + /* ask for a compatible mode */ + /* We have to check for B_OK, because otherwise the pix_clk_range + * won't be taken into account!! */ + //So don't do this: + //if (PROPOSE_DISPLAY_MODE(dst, &low, &high) != B_ERROR) { + //Instead, do this: + if (PROPOSE_DISPLAY_MODE(dst, &low, &high) == B_OK) { + /* count it, and move on to next mode */ + dst++; + si->mode_count++; + } + } + /* advance to next mode */ + src++; + } + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/SetDisplayMode.c b/src/add-ons/accelerants/nvidia_gpgpu/SetDisplayMode.c new file mode 100644 index 0000000000..3ae1cdd21c --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/SetDisplayMode.c @@ -0,0 +1,611 @@ + +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson, + Apsed, + Rudolf Cornelissen 11/2002-10/2007 +*/ + +#define MODULE_BIT 0x00200000 + +#include "acc_std.h" + +/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */ +status_t SET_DISPLAY_MODE(display_mode *mode_to_set) +{ + /* BOUNDS WARNING: + * It's impossible to deviate whatever small amount in a display_mode if the lower + * and upper limits are the same! + * Besides: + * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE + * returns B_BAD_VALUE! + * Which means PROPOSEMODE should not return that on anything except on + * deviations for: + * display_mode.virtual_width; + * display_mode.virtual_height; + * display_mode.timing.h_display; + * display_mode.timing.v_display; + * So: + * We don't use bounds here by making sure bounds and target are the same struct! + * (See the call to PROPOSE_DISPLAY_MODE below) */ + display_mode /*bounds,*/ target; + + uint8 colour_depth1 = 32; + uint32 startadd,startadd_right; +// bool crt1, crt2, cross; + + /* Adjust mode to valid one and fail if invalid */ + target /*= bounds*/ = *mode_to_set; + /* show the mode bits */ + LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags)); + LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock)); + LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n", + target.virtual_width, target.virtual_height)); + + /* See BOUNDS WARNING above... */ + if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR; + + /* make sure a possible 3D add-on will block rendering and re-initialize itself. + * note: update in _this_ order only */ + /* SET_DISPLAY_MODE will reset this flag when it's done. */ + si->engine.threeD.mode_changing = true; + /* every 3D add-on will reset this bit-flag when it's done. */ + si->engine.threeD.newmode = 0xffffffff; + /* every 3D clone needs to reclaim a slot. + * note: this also cleans up reserved channels for killed 3D clones.. */ + si->engine.threeD.clones = 0x00000000; + + /* disable interrupts using the kernel driver */ +// head1_interrupt_enable(false); +// if (si->ps.secondary_head) head2_interrupt_enable(false); + + /* disable TVout if supported */ +// if (si->ps.tvout) BT_stop_tvout(); + + /* turn off screen(s) _after_ TVout is disabled (if applicable) */ +// head1_dpms(false, false, false, true); +// if (si->ps.secondary_head) head2_dpms(false, false, false, true); +// if (si->ps.tvout) BT_dpms(false); + + /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ + startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; + + /* calculate and set new mode bytes_per_row */ + nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); + + /*Perform the very long mode switch!*/ + if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ + { + uint8 colour_depth2 = colour_depth1; + + /* init display mode for secondary head */ + display_mode target2 = target; + + LOG(1,("SETMODE: setting DUALHEAD mode\n")); + + /* validate flags for secondary TVout */ + //fixme: remove or block on autodetect fail. (is now shutoff) + if ((0) && (target2.flags & TV_BITS)) + { + target.flags &= ~TV_BITS;//still needed for some routines... + target2.flags &= ~TV_BITS; + LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n")); + } + + /* detect which connectors have a CRT connected */ + //fixme: 'hot-plugging' for analog monitors removed: remove code as well; + //or make it work with digital panels connected as well. +// crt1 = nv_dac_crt_connected(); +// crt2 = nv_dac2_crt_connected(); + /* connect outputs 'straight-through' */ +// if (crt1) +// { + /* connector1 is used as primary output */ +// cross = false; +// } +// else +// { +// if (crt2) + /* connector2 is used as primary output */ +// cross = true; +// else + /* no CRT detected: assume connector1 is used as primary output */ +// cross = false; +// } + /* set output connectors assignment if possible */ +// if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH) + /* invert output assignment in switch mode */ +// nv_general_head_select(true); +// else +// nv_general_head_select(false); + + /* set the pixel clock PLL(s) */ + LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); +// if (head1_set_pix_pll(target) == B_ERROR) +// LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); + + LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); +// if (head2_set_pix_pll(target2) == B_ERROR) +// LOG(8,("SETMODE: error setting pixel clock (DAC2)\n")); + + /*set the colour depth for CRTC1 and the DAC */ + switch(target.space) + { + case B_CMAP8: + colour_depth1 = 8; +// head1_mode(BPP8, 1.0); +// head1_depth(BPP8); + break; + case B_RGB15_LITTLE: + colour_depth1 = 16; +// head1_mode(BPP15, 1.0); +// head1_depth(BPP15); + break; + case B_RGB16_LITTLE: + colour_depth1 = 16; +// head1_mode(BPP16, 1.0); +// head1_depth(BPP16); + break; + case B_RGB32_LITTLE: + colour_depth1 = 32; +// head1_mode(BPP32, 1.0); +// head1_depth(BPP32); + break; + } + /*set the colour depth for CRTC2 and DAC2 */ + switch(target2.space) + { + case B_CMAP8: + colour_depth2 = 8; +// head2_mode(BPP8, 1.0); +// head2_depth(BPP8); + break; + case B_RGB15_LITTLE: + colour_depth2 = 16; +// head2_mode(BPP15, 1.0); +// head2_depth(BPP15); + break; + case B_RGB16_LITTLE: + colour_depth2 = 16; +// head2_mode(BPP16, 1.0); +// head2_depth(BPP16); + break; + case B_RGB32_LITTLE: + colour_depth2 = 32; +// head2_mode(BPP32, 1.0); +// head2_depth(BPP32); + break; + } + + /* check if we are doing interlaced TVout mode */ + //fixme: we don't support interlaced mode? + si->interlaced_tv_mode = false; + + /*set the display(s) pitches*/ +// head1_set_display_pitch (); + //fixme: seperate for real dualhead modes: + //we need a secondary si->fbc! +// head2_set_display_pitch (); + + /*work out where the "right" screen starts*/ + startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3)); + + /* Tell card what memory to display */ + switch (target.flags & DUALHEAD_BITS) + { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: +// head1_set_display_start(startadd,colour_depth1); +// head2_set_display_start(startadd_right,colour_depth2); + break; + case DUALHEAD_CLONE: +// head1_set_display_start(startadd,colour_depth1); +// head2_set_display_start(startadd,colour_depth2); + break; + } + + /* set the timing */ +// head1_set_timing(target); +// head2_set_timing(target2); + + /* TVout support: program TVout encoder and modify CRTC timing */ +// if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2); + } + else /* single head mode */ + { + int colour_mode = BPP32; + + /* connect output */ + if (si->ps.secondary_head) + { + /* detect which connectors have a CRT connected */ + //fixme: 'hot-plugging' for analog monitors removed: remove code as well; + //or make it work with digital panels connected as well. +// crt1 = nv_dac_crt_connected(); +// crt2 = nv_dac2_crt_connected(); + /* connect outputs 'straight-through' */ +// if (crt1) +// { + /* connector1 is used as primary output */ +// cross = false; +// } +// else +// { +// if (crt2) + /* connector2 is used as primary output */ +// cross = true; +// else + /* no CRT detected: assume connector1 is used as primary output */ +// cross = false; +// } + /* set output connectors assignment if possible */ + nv_general_head_select(false); + } + + switch(target.space) + { + case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; + case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; + case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; + case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; + default: + LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); + return B_ERROR; + } + + /* set the pixel clock PLL */ +// if (head1_set_pix_pll(target) == B_ERROR) +// LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); + + /* set the colour depth for CRTC1 and the DAC */ + /* first set the colordepth */ +// head1_depth(colour_mode); + /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ +// head1_mode(colour_mode,1.0); + + /* set the display pitch */ +// head1_set_display_pitch(); + + /* tell the card what memory to display */ +// head1_set_display_start(startadd,colour_depth1); + + /* set the timing */ +// head1_set_timing(target); + + /* TVout support: program TVout encoder and modify CRTC timing */ +// if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target); + + //fixme: shut-off the videoPLL if it exists... + } + + /* update driver's mode store */ + si->dm = target; + + /* update FIFO data fetching according to mode */ +// nv_crtc_update_fifo(); +// if (si->ps.secondary_head) nv_crtc2_update_fifo(); + + /* set up acceleration for this mode */ + /* note: + * Maybe later we can forget about non-DMA mode (depends on 3D acceleration + * attempts). */ +//no acc support for G8x yet! +if (si->ps.card_arch < NV50A) +{ + if (!si->settings.dma_acc) + nv_acc_init(); + else + nv_acc_init_dma(); +} + /* set up overlay unit for this mode */ +// nv_bes_init(); + + /* note freemem range */ + /* first free adress follows hardcursor and workspace */ + si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; + if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; + /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ + //fixme possible: + //if overlay buffers are allocated subtract buffersize from mem_high; + //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) + si->engine.threeD.mem_high = si->ps.memory_size - 1; + /* Keep some extra distance as a workaround for certain bugs (see + * DriverInterface.h for an explanation). */ + if (si->ps.card_arch < NV40A) + si->engine.threeD.mem_high -= PRE_NV40_OFFSET; + else + si->engine.threeD.mem_high -= NV40_PLUS_OFFSET; + + si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */ + + /* restore screen(s) output state(s) */ +// SET_DPMS_MODE(si->dpms_flags); + + /* enable interrupts using the kernel driver */ + //fixme: + //add head2 once we use one driver instance 'per head' (instead of 'per card') +// head1_interrupt_enable(true); + + /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ + si->engine.threeD.mode_changing = false; + + /* optimize memory-access if needed */ +// head1_mem_priority(colour_depth1); + + /* Tune RAM CAS-latency if needed. Must be done *here*! */ +// nv_set_cas_latency(); + + LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); + + return B_OK; +} + +/* + Set which pixel of the virtual frame buffer will show up in the + top left corner of the display device. Used for page-flipping + games and virtual desktops. +*/ +status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { + uint8 colour_depth; + uint32 startadd,startadd_right; + + LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); + + /* nVidia cards support pixelprecise panning on both heads in all modes: + * No stepping granularity needed! */ + + /* determine bits used for the colordepth */ + switch(si->dm.space) + { + case B_CMAP8: + colour_depth=8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + colour_depth=16; + break; + case B_RGB24_LITTLE: + colour_depth=24; + break; + case B_RGB32_LITTLE: + colour_depth=32; + break; + default: + return B_ERROR; + } + + /* do not run past end of display */ + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) + return B_ERROR; + break; + default: + if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) + return B_ERROR; + break; + } + if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) + return B_ERROR; + + /* everybody remember where we parked... */ + si->dm.h_display_start = h_display_start; + si->dm.v_display_start = v_display_start; + + /* actually set the registers */ + //fixme: seperate both heads: we need a secondary si->fbc! + startadd = v_display_start * si->fbc.bytes_per_row; + startadd += h_display_start * (colour_depth >> 3); + startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; + startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); + + /* disable interrupts using the kernel driver */ + head1_interrupt_enable(false); + if (si->ps.secondary_head) head2_interrupt_enable(false); + + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + head1_set_display_start(startadd,colour_depth); + head2_set_display_start(startadd_right,colour_depth); + break; + case DUALHEAD_OFF: + head1_set_display_start(startadd,colour_depth); + break; + case DUALHEAD_CLONE: + head1_set_display_start(startadd,colour_depth); + head2_set_display_start(startadd,colour_depth); + break; + } + + //fixme: + //add head2 once we use one driver instance 'per head' (instead of 'per card') + head1_interrupt_enable(true); + + return B_OK; +} + +/* Set the indexed color palette */ +void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { + int i; + uint8 *r,*g,*b; + + /* Protect gamma correction when not in CMAP8 */ + if (si->dm.space != B_CMAP8) return; + + r=si->color_data; + g=r+256; + b=g+256; + + i=first; + while (count--) + { + r[i]=*color_data++; + g[i]=*color_data++; + b[i]=*color_data++; + i++; + } + head1_palette(r,g,b); + if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); +} + +/* Put the display into one of the Display Power Management modes. */ +status_t SET_DPMS_MODE(uint32 dpms_flags) +{ + bool display, h1h, h1v, h2h, h2v, do_p1, do_p2; + + /* disable interrupts using the kernel driver */ + head1_interrupt_enable(false); + if (si->ps.secondary_head) head2_interrupt_enable(false); + + LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags)); + + /* note current DPMS state for our reference */ + si->dpms_flags = dpms_flags; + + /* preset: DPMS for panels should be executed */ + do_p1 = do_p2 = true; + + /* determine signals to send to head(s) */ + display = h1h = h1v = h2h = h2v = true; + switch(dpms_flags) + { + case B_DPMS_ON: /* H: on, V: on, display on */ + break; + case B_DPMS_STAND_BY: + display = h1h = h2h = false; + break; + case B_DPMS_SUSPEND: + display = h1v = h2v = false; + break; + case B_DPMS_OFF: /* H: off, V: off, display off */ + display = h1h = h1v = h2h = h2v = false; + break; + default: + LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags)); + //fixme: + //add head2 once we use one driver instance 'per head' (instead of 'per card') + head1_interrupt_enable(true); + + return B_ERROR; + } + + /* CRTC used for TVout needs specific DPMS programming */ + if (si->dm.flags & TV_BITS) + { + /* TV_PRIMARY tells us that the head to be used with TVout is the head that's + * actually assigned as being the primary head at powerup: + * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ + if (si->dm.flags & TV_PRIMARY) + { + LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n")); + + if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + { + if (!(si->settings.vga_on_tv)) + { + /* block VGA output on head displaying on TV */ + /* Note: + * this specific sync setting is required: Vsync is used to keep TVout + * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs). + * This leaves Hsync only for shutting off the VGA screen. */ + h1h = false; + h1v = true; + /* block panel DPMS updates */ + do_p1 = false; + } + else + { + /* when concurrent VGA is used alongside TVout on a head, DPMS is safest + * applied this way: Vsync is needed for stopping TVout successfully when + * a (new) modeswitch occurs. + * (see routine BT_stop_tvout() in nv_brooktreetv.c) */ + /* Note: + * applying 'normal' DPMS here and forcing Vsync on in the above mentioned + * routine seems to not always be enough: sometimes image generation will + * not resume in that case. */ + h1h = display; + h1v = true; + } + } + else + { + if (!(si->settings.vga_on_tv)) + { + h2h = false; + h2v = true; + do_p2 = false; + } + else + { + h2h = display; + h2v = true; + } + } + } + else + { + LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n")); + + if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + { + if (!(si->settings.vga_on_tv)) + { + h2h = false; + h2v = true; + do_p2 = false; + } + else + { + h2h = display; + h2v = true; + } + } + else + { + if (!(si->settings.vga_on_tv)) + { + h1h = false; + h1v = true; + do_p1 = false; + } + else + { + h1h = display; + h1v = true; + } + } + } + } + + /* issue actual DPMS commands as far as applicable */ + head1_dpms(display, h1h, h1v, do_p1); + if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS)) + head2_dpms(display, h2h, h2v, do_p2); + if (si->dm.flags & TV_BITS) + BT_dpms(display); + + //fixme: + //add head2 once we use one driver instance 'per head' (instead of 'per card') + head1_interrupt_enable(true); + + return B_OK; +} + +/* Report device DPMS capabilities */ +uint32 DPMS_CAPABILITIES(void) +{ + return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); +} + +/* Return the current DPMS mode */ +uint32 DPMS_MODE(void) +{ + return si->dpms_flags; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/acc_std.h b/src/add-ons/accelerants/nvidia_gpgpu/acc_std.h new file mode 100644 index 0000000000..560ab04a67 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/acc_std.h @@ -0,0 +1,17 @@ +/* + 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 + +#include +#include +#include "DriverInterface.h" +#include "nv_globals.h" +//apsed #include "nv_extern.h" +#include "nv_proto.h" +#include "be_driver_proto.h" + +#endif diff --git a/src/add-ons/accelerants/nvidia_gpgpu/be_driver_proto.h b/src/add-ons/accelerants/nvidia_gpgpu/be_driver_proto.h new file mode 100644 index 0000000000..b1b6045f61 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/be_driver_proto.h @@ -0,0 +1,72 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Modified by Rudolf Cornelissen 2/2005. +*/ + +#if !defined(GENERIC_H) +#define GENERIC_H + +#include +#include "video_overlay.h" + +#define DEBUG 1 + +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_PIO(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et); +status_t ACQUIRE_ENGINE_DMA(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); + +/* PIO acceleration */ +void SCREEN_TO_SCREEN_BLIT_PIO(engine_token *et, blit_params *list, uint32 count); +void SCREEN_TO_SCREEN_TRANSPARENT_BLIT_PIO(engine_token *et, uint32 transparent_colour, blit_params *list, uint32 count); +void SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT_PIO(engine_token *et, scaled_blit_params *list, uint32 count); +void FILL_RECTANGLE_PIO(engine_token *et, uint32 color, fill_rect_params *list, uint32 count); +void INVERT_RECTANGLE_PIO(engine_token *et, fill_rect_params *list, uint32 count); +void FILL_SPAN_PIO(engine_token *et, uint32 color, uint16 *list, uint32 count); + +/* video_overlay */ +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); + +status_t create_mode_list(void); + +#endif diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/Jamfile b/src/add-ons/accelerants/nvidia_gpgpu/engine/Jamfile new file mode 100644 index 0000000000..73d1703a7f --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src add-ons accelerants nvidia_gpgpu engine ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics nvidia_gpgpu ] ; + +StaticLibrary libnvidia_gpgpu_engine.a : + nv_acc.c + nv_acc_dma.c + nv_bes.c + nv_brooktreetv.c + nv_crtc.c + nv_crtc2.c + nv_dac.c + nv_dac2.c + nv_general.c + nv_globals.c + nv_i2c.c + nv_info.c + nv_support.c + ; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc.c new file mode 100644 index 0000000000..b3cee7dc59 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc.c @@ -0,0 +1,1566 @@ +/* NV Acceleration functions */ +/* Author: + Rudolf Cornelissen 8/2003-5/2005. + + This code was possible thanks to: + - the Linux XFree86 NV driver, + - the Linux UtahGLX 3D driver. +*/ + +/* + note: + Can't get NV40 and higher going using this PIO mode acceleration system ATM. + Here's the problem: + The FIFO is not functioning correctly: the proof of this is that you can only + readout the PIO FIFO fill-level register (FifoFree) once before it stops responding + (returns only zeros on all reads after the first one). + You can see the issued commands are actually placed in the FIFO because the first + read of FifoFree corresponds to what you'd expect. + There is no visual confirmation of any command actually being executed by the + acceleration engine, so we don't know if the FIFO places commands in the engine. + BTW: + The FifoFree register exhibits the exact same behaviour in DMA acceleration mode. + It's no problem there because we use the DMAPut and DMAGet registers instead. + + The non-functioning Fifo in PIO mode might have one of these reasons: + - lack of specs: maybe additional programming is required. + - hardware fault: as probably no-one uses PIO mode acceleration anymore these days, + nVidia might not care about this any longer. + + note also: + Keeping this PIO mode acceleration stuff here for now to guarantee compatibility + with current 3D acceleration attempts: the utahGLX 3D driver cooperated with the + PIO mode acceleration functions in the XFree drivers (upto/including XFree 4.2.0). +*/ + +#define MODULE_BIT 0x00080000 + +#include "nv_std.h" + +static void nv_init_for_3D(void); + +/*acceleration notes*/ + +/*functions Be's app_server uses: +fill span (horizontal only) +fill rectangle (these 2 are very similar) +invert rectangle +blit +*/ + +/* + nVidia hardware info: + We should be able to do FIFO assignment setup changes on-the-fly now, using + all the engine-command-handles that are pre-defined on any FIFO channel. + Also we should be able to setup new additional handles to previously unused + engine commands now. +*/ + +/* FIFO channel pointers */ +/* note: + * every instance of the accelerant needs to have it's own pointers, as the registers + * are cloned to different adress ranges for each one */ +static cmd_nv_rop5_solid* nv_rop5_solid_ptr; +static cmd_nv_image_black_rectangle* nv_image_black_rectangle_ptr; +static cmd_nv_image_pattern* nv_image_pattern_ptr; +static cmd_nv_image_blit* nv_image_blit_ptr; +static cmd_nv3_gdi_rectangle_text* nv3_gdi_rectangle_text_ptr; + +status_t nv_acc_wait_idle() +{ +return B_OK; + /* wait until engine completely idle */ + while (ACCR(STATUS)) + { + /* snooze a bit so I do not hammer the bus */ + snooze (100); + } + + return B_OK; +} + +/* AFAIK this must be done for every new screenmode. + * Engine required init. */ +status_t nv_acc_init() +{ + uint16 cnt; + + /* a hanging engine only recovers from a complete power-down/power-up cycle */ + NV_REG32(NV32_PWRUPCTRL) = 0x13110011; + snooze(1000); + NV_REG32(NV32_PWRUPCTRL) = 0x13111111; + + /* setup PTIMER: */ + //fixme? how about NV28 setup as just after coldstarting? (see nv_info.c) + /* set timer numerator to 8 (in b0-15) */ + ACCW(PT_NUMERATOR, 0x00000008); + /* set timer denominator to 3 (in b0-15) */ + ACCW(PT_DENOMINATR, 0x00000003); + + /* disable timer-alarm INT requests (b0) */ + ACCW(PT_INTEN, 0x00000000); + /* reset timer-alarm INT status bit (b0) */ + ACCW(PT_INTSTAT, 0xffffffff); + + /* enable PRAMIN write access on pre NV10 before programming it! */ + if (si->ps.card_arch == NV04A) + { + /* set framebuffer config: type = notiling, PRAMIN write access enabled */ + NV_REG32(NV32_PFB_CONFIG_0) = 0x00001114; + } + + /*** PFIFO ***/ + /* (setup caches) */ + /* disable caches reassign */ + ACCW(PF_CACHES, 0x00000000); + /* PFIFO mode for all 32 channels is PIO (instead of DMA) */ + ACCW(PF_MODE, 0x00000000); + /* cache1 push0 access disabled */ + ACCW(PF_CACH1_PSH0, 0x00000000); + /* cache1 pull0 access disabled */ + ACCW(PF_CACH1_PUL0, 0x00000000); + /* cache1 push1 mode = pio (disable DMA use) */ + ACCW(PF_CACH1_PSH1, 0x00000000); + /* cache1 DMA Put offset = 0 (b2-28) */ + ACCW(PF_CACH1_DMAP, 0x00000000); + /* cache1 DMA Get offset = 0 (b2-28) */ + ACCW(PF_CACH1_DMAG, 0x00000000); + /* cache1 DMA instance adress = none (b0-15); + * instance being b4-19 with baseadress NV_PRAMIN_CTX_0 (0x00700000). */ + /* note: + * should point to a DMA definition in CTX register space (which is sort of RAM). + * This define tells the engine where the DMA cmd buffer is and what it's size is. + * Inside that cmd buffer you'll find the actual issued engine commands. */ + ACCW(PF_CACH1_DMAI, 0x00000000); + /* cache0 push0 access disabled */ + ACCW(PF_CACH0_PSH0, 0x00000000); + /* cache0 pull0 access disabled */ + ACCW(PF_CACH0_PUL0, 0x00000000); + /* RAM HT (hash table) baseadress = $10000 (b4-8), size = 4k, + * search = 128 (is byte offset between hash 'sets') */ + /* note: + * so HT base is $00710000, last is $00710fff. + * In this space you define the engine command handles (HT_HANDL_XX), which + * in turn points to the defines in CTX register space (which is sort of RAM) */ + ACCW(PF_RAMHT, 0x03000100); + /* RAM FC baseadress = $11000 (b3-8) (size is fixed to 0.5k(?)) */ + /* note: + * so FC base is $00711000, last is $007111ff. (not used?) */ + ACCW(PF_RAMFC, 0x00000110); + /* RAM RO baseadress = $11200 (b1-8), size = 0.5k */ + /* note: + * so RO base is $00711200, last is $007113ff. (not used?) */ + /* note also: + * This means(?) the PRAMIN CTX registers are accessible from base $00711400. */ + ACCW(PF_RAMRO, 0x00000112); + /* PFIFO size: ch0-15 = 512 bytes, ch16-31 = 124 bytes */ + ACCW(PF_SIZE, 0x0000ffff); + /* cache1 hash instance = $ffff (b0-15) */ + ACCW(PF_CACH1_HASH, 0x0000ffff); + /* disable all PFIFO INTs */ + ACCW(PF_INTEN, 0x00000000); + /* reset all PFIFO INT status bits */ + ACCW(PF_INTSTAT, 0xffffffff); + /* cache0 pull0 engine = acceleration engine (graphics) */ + ACCW(PF_CACH0_PUL1, 0x00000001); + /* cache1 DMA control: disable some stuff */ + ACCW(PF_CACH1_DMAC, 0x00000000); + /* cache1 engine 0 upto/including 7 is software (could also be graphics or DVD) */ + ACCW(PF_CACH1_ENG, 0x00000000); + /* cache1 DMA fetch: trigger at 128 bytes, size is 32 bytes, max requests is 15, + * use little endian */ + ACCW(PF_CACH1_DMAF, 0x000f0078); + /* cache1 DMA push: b0=0 is access disabled */ + ACCW(PF_CACH1_DMAS, 0x00000000); + /* cache1 push0 access enabled */ + ACCW(PF_CACH1_PSH0, 0x00000001); + /* cache1 pull0 access enabled */ + ACCW(PF_CACH1_PUL0, 0x00000001); + /* cache1 pull1 engine = acceleration engine (graphics) */ + ACCW(PF_CACH1_PUL1, 0x00000001); + /* enable PFIFO caches reassign */ + ACCW(PF_CACHES, 0x00000001); + + /*** PRAMIN ***/ + /* first clear the entire RAMHT (hash-table) space to a defined state. It turns + * out at least NV11 will keep the previously programmed handles over resets and + * power-outages upto about 15 seconds!! Faulty entries might well hang the + * engine (confirmed on NV11). + * Note: + * this behaviour is not very strange: even very old DRAM chips are known to be + * able to do this, even though you should refresh them every few milliseconds or + * so. (Large memory cell capacitors, though different cells vary a lot in their + * capacity.) + * Of course data validity is not certain by a long shot over this large + * amount of time.. */ + for(cnt = 0; cnt < 0x0400; cnt++) + NV_REG32(NVACC_HT_HANDL_00 + (cnt << 2)) = 0; + /* RAMHT space (hash-table) SETUP FIFO HANDLES */ + /* note: + * 'instance' tells you where the engine command is stored in 'PR_CTXx_x' sets + * below: instance being b4-19 with baseadress NV_PRAMIN_CTX_0 (0x00700000). + * That command is linked to the handle noted here. This handle is then used to + * tell the FIFO to which engine command it is connected! + * (CTX registers are actually a sort of RAM space.) */ + if (si->ps.card_arch >= NV40A) + { + /* (first set) */ + ACCW(HT_HANDL_00, (0x80000000 | NV10_CONTEXT_SURFACES_2D)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_00, 0x0010114c); /* instance $114c, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_01, (0x80000000 | NV_IMAGE_BLIT)); /* 32bit handle */ + ACCW(HT_VALUE_01, 0x00101148); /* instance $1148, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_02, (0x80000000 | NV4_GDI_RECTANGLE_TEXT)); /* 32bit handle */ + ACCW(HT_VALUE_02, 0x0010114a); /* instance $114a, engine = acc engine, CHID = $00 */ + + /* (second set) */ + ACCW(HT_HANDL_10, (0x80000000 | NV_ROP5_SOLID)); /* 32bit handle */ + ACCW(HT_VALUE_10, 0x00101142); /* instance $1142, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_11, (0x80000000 | NV_IMAGE_BLACK_RECTANGLE)); /* 32bit handle */ + ACCW(HT_VALUE_11, 0x00101144); /* instance $1144, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_12, (0x80000000 | NV_IMAGE_PATTERN)); /* 32bit handle */ + ACCW(HT_VALUE_12, 0x00101146); /* instance $1146, engine = acc engine, CHID = $00 */ + } + else + { + /* (first set) */ + ACCW(HT_HANDL_00, (0x80000000 | NV4_SURFACE)); /* 32bit handle */ + ACCW(HT_VALUE_00, 0x80011145); /* instance $1145, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_01, (0x80000000 | NV_IMAGE_BLIT)); /* 32bit handle */ + ACCW(HT_VALUE_01, 0x80011146); /* instance $1146, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_02, (0x80000000 | NV4_GDI_RECTANGLE_TEXT)); /* 32bit handle */ + ACCW(HT_VALUE_02, 0x80011147); /* instance $1147, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_03, (0x80000000 | NV4_CONTEXT_SURFACES_ARGB_ZS)); /* 32bit handle (3D) */ + ACCW(HT_VALUE_03, 0x80011148); /* instance $1148, engine = acc engine, CHID = $00 */ + + /* NV4_ and NV10_DX5_TEXTURE_TRIANGLE should be identical */ + ACCW(HT_HANDL_04, (0x80000000 | NV4_DX5_TEXTURE_TRIANGLE)); /* 32bit handle (3D) */ + ACCW(HT_VALUE_04, 0x80011149); /* instance $1149, engine = acc engine, CHID = $00 */ + + /* NV4_ and NV10_DX6_MULTI_TEXTURE_TRIANGLE should be identical */ + ACCW(HT_HANDL_05, (0x80000000 | NV4_DX6_MULTI_TEXTURE_TRIANGLE)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_05, 0x8001114a); /* instance $114a, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_06, (0x80000000 | NV1_RENDER_SOLID_LIN)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_06, 0x8001114b); /* instance $114b, engine = acc engine, CHID = $00 */ + + /* (second set) */ + ACCW(HT_HANDL_10, (0x80000000 | NV_ROP5_SOLID)); /* 32bit handle */ + ACCW(HT_VALUE_10, 0x80011142); /* instance $1142, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_11, (0x80000000 | NV_IMAGE_BLACK_RECTANGLE)); /* 32bit handle */ + ACCW(HT_VALUE_11, 0x80011143); /* instance $1143, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_12, (0x80000000 | NV_IMAGE_PATTERN)); /* 32bit handle */ + ACCW(HT_VALUE_12, 0x80011144); /* instance $1144, engine = acc engine, CHID = $00 */ + } + + /* program CTX registers: CTX1 is mostly done later (colorspace dependant) */ + /* note: + * CTX determines which HT handles point to what engine commands. */ + /* note also: + * CTX registers are in fact in the same GPU internal RAM space as the engine's + * hashtable. This means that stuff programmed in here also survives resets and + * power-outages! (confirmed NV11) */ + if (si->ps.card_arch >= NV40A) + { + /* setup a DMA define for use by command defines below. */ + ACCW(PR_CTX0_R, 0x00003000); /* DMA page table present and of linear type; + * DMA target node is NVM (non-volatile memory?) + * (instead of doing PCI or AGP transfers) */ + ACCW(PR_CTX1_R, (si->ps.memory_size - 1)); /* DMA limit: size is all cardRAM */ + ACCW(PR_CTX2_R, ((0x00000000 & 0xfffff000) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * memory starts at start of cardRAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + ACCW(PR_CTX3_R, 0x00000002); /* unknown (looks like this is rubbish/not needed?) */ + /* setup set '0' for cmd NV_ROP5_SOLID */ + ACCW(PR_CTX0_0, 0x02080043); /* NVclass $043, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_0, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_0, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_1, 0x00000000); /* extra */ + ACCW(PR_CTX1_1, 0x00000000); /* extra */ + /* setup set '1' for cmd NV_IMAGE_BLACK_RECTANGLE */ + ACCW(PR_CTX0_2, 0x02080019); /* NVclass $019, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_2, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_2, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_3, 0x00000000); /* extra */ + ACCW(PR_CTX1_3, 0x00000000); /* extra */ + /* setup set '2' for cmd NV_IMAGE_PATTERN */ + ACCW(PR_CTX0_4, 0x02080018); /* NVclass $018, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_4, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_4, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_5, 0x00000000); /* extra */ + ACCW(PR_CTX1_5, 0x00000000); /* extra */ + /* setup set '4' for cmd NV_IMAGE_BLIT */ + ACCW(PR_CTX0_6, 0x0208005f); /* NVclass $05f, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_6, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_6, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_7, 0x00000000); /* extra */ + ACCW(PR_CTX1_7, 0x00000000); /* extra */ + /* setup set '5' for cmd NV4_GDI_RECTANGLE_TEXT */ + ACCW(PR_CTX0_8, 0x0208004a); /* NVclass $04a, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_8, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_8, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_9, 0x00000000); /* extra */ + ACCW(PR_CTX1_9, 0x00000000); /* extra */ + /* setup set '6' for cmd NV10_CONTEXT_SURFACES_2D */ + ACCW(PR_CTX0_A, 0x02080062); /* NVclass $062, nv10+: little endian */ + ACCW(PR_CTX2_A, 0x00001140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_A, 0x00001140); /* method trap 0 is $1140, trap 1 disabled */ + ACCW(PR_CTX0_B, 0x00000000); /* extra */ + ACCW(PR_CTX1_B, 0x00000000); /* extra */ + } + else + { + /* setup a DMA define for use by command defines below. + * (would currently be used by CTX 'sets' 0x6 upto/including 0xe: 3D stuff.) */ + ACCW(PR_CTX0_R, 0x00003000); /* DMA page table present and of linear type; + * DMA target node is NVM (non-volatile memory?) + * (instead of doing PCI or AGP transfers) */ + ACCW(PR_CTX1_R, (si->ps.memory_size - 1)); /* DMA limit: size is all cardRAM */ + ACCW(PR_CTX2_R, ((0x00000000 & 0xfffff000) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * memory starts at start of cardRAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + ACCW(PR_CTX3_R, 0x00000002); /* unknown (looks like this is rubbish/not needed?) */ + /* setup set '0' for cmd NV_ROP5_SOLID */ + ACCW(PR_CTX0_0, 0x01008043); /* NVclass $043, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_0, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_0, 0x00000000); /* method traps disabled */ + /* setup set '1' for cmd NV_IMAGE_BLACK_RECTANGLE */ + ACCW(PR_CTX0_1, 0x01008019); /* NVclass $019, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_1, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_1, 0x00000000); /* method traps disabled */ + /* setup set '2' for cmd NV_IMAGE_PATTERN */ + ACCW(PR_CTX0_2, 0x01008018); /* NVclass $018, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_2, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_2, 0x00000000); /* method traps disabled */ +//fixme: update 3D add-on and this code for the NV4_SURFACE command. + /* setup set '3' for ... */ + if(si->ps.card_arch >= NV10A) + { + /* ... cmd NV10_CONTEXT_SURFACES_2D */ + ACCW(PR_CTX0_3, 0x01008062); /* NVclass $062, nv10+: little endian */ + } + else + { + /* ... cmd NV4_SURFACE */ + ACCW(PR_CTX0_3, 0x01008042); /* NVclass $042, nv10+: little endian */ + } + ACCW(PR_CTX1_3, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_3, 0x11401140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_3, 0x00000000); /* method trap 0 is $1140, trap 1 disabled */ + /* setup set '4' for cmd NV_IMAGE_BLIT */ + ACCW(PR_CTX0_4, 0x0100805f); /* NVclass $05f, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_4, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_4, 0x00000000); /* method traps disabled */ + /* setup set '5' for cmd NV4_GDI_RECTANGLE_TEXT */ + ACCW(PR_CTX0_5, 0x0100804a); /* NVclass $04a, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX2_5, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_5, 0x00000000); /* method traps disabled */ + /* setup set '6' ... */ + if (si->ps.card_arch != NV04A) + { + /* ... for cmd NV10_CONTEXT_SURFACES_ARGB_ZS */ + ACCW(PR_CTX0_6, 0x00000093); /* NVclass $093, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_CONTEXT_SURFACES_ARGB_ZS */ + ACCW(PR_CTX0_6, 0x00000053); /* NVclass $053, nv10+: little endian */ + } + ACCW(PR_CTX2_6, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_6, 0x00000000); /* method traps disabled */ + /* setup set '7' ... */ + if (si->ps.card_arch != NV04A) + { + /* ... for cmd NV10_DX5_TEXTURE_TRIANGLE */ + ACCW(PR_CTX0_7, 0x0300a094); /* NVclass $094, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_DX5_TEXTURE_TRIANGLE */ + ACCW(PR_CTX0_7, 0x0300a054); /* NVclass $054, patchcfg ROP_AND, userclip enable, + * context surface0 valid */ + } + ACCW(PR_CTX1_7, 0x00000d01); /* format is A8RGB24, MSB mono */ + ACCW(PR_CTX2_7, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_7, 0x00000000); /* method traps disabled */ + /* setup set '8' ... */ + if (si->ps.card_arch != NV04A) + { + /* ... for cmd NV10_DX6_MULTI_TEXTURE_TRIANGLE (not used) */ + ACCW(PR_CTX0_8, 0x0300a095); /* NVclass $095, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_DX6_MULTI_TEXTURE_TRIANGLE (not used) */ + ACCW(PR_CTX0_8, 0x0300a055); /* NVclass $055, patchcfg ROP_AND, userclip enable, + * context surface0 valid */ + } + ACCW(PR_CTX1_8, 0x00000d01); /* format is A8RGB24, MSB mono */ + ACCW(PR_CTX2_8, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_8, 0x00000000); /* method traps disabled */ + /* setup set '9' for cmd NV1_RENDER_SOLID_LIN (not used) */ + ACCW(PR_CTX0_9, 0x0300a01c); /* NVclass $01c, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + ACCW(PR_CTX2_9, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_9, 0x00000000); /* method traps disabled */ +//fixme: update 3D add-on and this code for the NV4_SURFACE command. + /* setup set '9' for cmd NV3_SURFACE_0 */ +// ACCW(PR_CTX0_9, 0x00000058); /* NVclass $058, nv10+: little endian */ +// ACCW(PR_CTX2_9, 0x11401140); /* DMA0, DMA1 instance = $1140 */ +// ACCW(PR_CTX3_9, 0x00000000); /* method traps disabled */ + /* setup set 'A' for cmd NV3_SURFACE_1 */ +// ACCW(PR_CTX0_A, 0x00000059); /* NVclass $059, nv10+: little endian */ +// ACCW(PR_CTX2_A, 0x11401140); /* DMA0, DMA1 instance = $1140 */ +// ACCW(PR_CTX3_A, 0x00000000); /* method traps disabled */ + } + + /*** PGRAPH ***/ + switch (si->ps.card_arch) + { + case NV40A: + /* set resetstate for most function blocks */ + ACCW(DEBUG0, 0x0003ffff);//? + /* init some function blocks */ + ACCW(DEBUG1, 0x401287c0); + ACCW(DEBUG2, 0x24f82ad9);//? + ACCW(DEBUG3, 0x60de8051); + /* end resetstate for the function blocks */ + ACCW(DEBUG0, 0x00000000);//? + /* disable specific functions, but enable SETUP_SPARE2 register */ + ACCW(NV10_DEBUG4, 0x00008000); + /* set limit_viol_pix_adress(?): more likely something unknown.. */ + ACCW(NV25_WHAT0, 0x00be3c5f); + /* unknown.. */ + switch (si->ps.card_type) + { + case NV40: + case NV45: + ACCW(NV40_WHAT0, 0x83280fff); + ACCW(NV40_WHAT1, 0x000000a0); + ACCW(NV40_WHAT2, 0x0078e366); + ACCW(NV40_WHAT3, 0x0000014c); + break; + case NV41: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV40P_WHAT2, 0x007596ff); + ACCW(NV40P_WHAT3, 0x00000108); + break; + case NV43: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV40P_WHAT2, 0x0072cb77); + ACCW(NV40P_WHAT3, 0x00000108); + break; + case NV44: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV44_WHAT2, 0x00000000); + ACCW(NV44_WHAT3, 0x00000000); + /* unknown.. */ + NV_REG32(NV32_NV44_WHAT10) = NV_REG32(NV32_NV10STRAPINFO); + NV_REG32(NV32_NV44_WHAT11) = 0x00000000; + NV_REG32(NV32_NV44_WHAT12) = 0x00000000; + NV_REG32(NV32_NV44_WHAT13) = NV_REG32(NV32_NV10STRAPINFO); + break; + default: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + break; + } + break; + case NV04A: + /* init some function blocks */ + ACCW(DEBUG0, 0x1231c001); + ACCW(DEBUG1, 0x72111101); + ACCW(DEBUG2, 0x11d5f071); + ACCW(DEBUG3, 0x10d4ff31); + break; + default: + /* set resetstate for most function blocks */ + ACCW(DEBUG0, 0x0003ffff); + /* init some function blocks */ + ACCW(DEBUG1, 0x00118701); + ACCW(DEBUG2, 0x24f82ad9); + ACCW(DEBUG3, 0x55de0030); + /* end resetstate for the function blocks */ + ACCW(DEBUG0, 0x00000000); + /* disable specific functions */ + ACCW(NV10_DEBUG4, 0); + break; + } + + /* reset all cache sets */ + ACCW(CACHE1_1, 0); + ACCW(CACHE1_2, 0); + ACCW(CACHE1_3, 0); + ACCW(CACHE1_4, 0); + ACCW(CACHE1_5, 0); + ACCW(CACHE2_1, 0); + ACCW(CACHE2_2, 0); + ACCW(CACHE2_3, 0); + ACCW(CACHE2_4, 0); + ACCW(CACHE2_5, 0); + ACCW(CACHE3_1, 0); + ACCW(CACHE3_2, 0); + ACCW(CACHE3_3, 0); + ACCW(CACHE3_4, 0); + ACCW(CACHE3_5, 0); + ACCW(CACHE4_1, 0); + ACCW(CACHE4_2, 0); + ACCW(CACHE4_3, 0); + ACCW(CACHE4_4, 0); + ACCW(CACHE4_5, 0); + if (si->ps.card_arch != NV04A) + ACCW(NV10_CACHE5_1, 0); + ACCW(CACHE5_2, 0); + ACCW(CACHE5_3, 0); + ACCW(CACHE5_4, 0); + ACCW(CACHE5_5, 0); + if (si->ps.card_arch != NV04A) + ACCW(NV10_CACHE6_1, 0); + ACCW(CACHE6_2, 0); + ACCW(CACHE6_3, 0); + ACCW(CACHE6_4, 0); + ACCW(CACHE6_5, 0); + if (si->ps.card_arch != NV04A) + ACCW(NV10_CACHE7_1, 0); + ACCW(CACHE7_2, 0); + ACCW(CACHE7_3, 0); + ACCW(CACHE7_4, 0); + ACCW(CACHE7_5, 0); + if (si->ps.card_arch != NV04A) + ACCW(NV10_CACHE8_1, 0); + ACCW(CACHE8_2, 0); + ACCW(CACHE8_3, 0); + ACCW(CACHE8_4, 0); + ACCW(CACHE8_5, 0); + + if (si->ps.card_arch != NV04A) + { + /* reset (disable) context switch stuff */ + ACCW(NV10_CTX_SW1, 0); + ACCW(NV10_CTX_SW2, 0); + ACCW(NV10_CTX_SW3, 0); + ACCW(NV10_CTX_SW4, 0); + ACCW(NV10_CTX_SW5, 0); + } + + /* setup accesible card memory range for acc engine */ + ACCW(BBASE0, 0x00000000); + ACCW(BBASE1, 0x00000000); + ACCW(BBASE2, 0x00000000); + ACCW(BBASE3, 0x00000000); + ACCW(BLIMIT0, (si->ps.memory_size - 1)); + ACCW(BLIMIT1, (si->ps.memory_size - 1)); + ACCW(BLIMIT2, (si->ps.memory_size - 1)); + ACCW(BLIMIT3, (si->ps.memory_size - 1)); + if (si->ps.card_arch >= NV10A) + { + ACCW(NV10_BBASE4, 0x00000000); + ACCW(NV10_BBASE5, 0x00000000); + ACCW(NV10_BLIMIT4, (si->ps.memory_size - 1)); + ACCW(NV10_BLIMIT5, (si->ps.memory_size - 1)); + } + if (si->ps.card_arch >= NV20A) + { + if ((si->ps.card_type > NV40) && (si->ps.card_type != NV45)) + { + ACCW(NV40P_BLIMIT6, (si->ps.memory_size - 1)); + ACCW(NV40P_BLIMIT7, (si->ps.memory_size - 1)); + } + else + { + /* fixme(?): assuming more BLIMIT registers here: Then how about BBASE6-9? */ + ACCW(NV20_BLIMIT6, (si->ps.memory_size - 1)); + ACCW(NV20_BLIMIT7, (si->ps.memory_size - 1)); + if (si->ps.card_type < NV40) + { + ACCW(NV20_BLIMIT8, (si->ps.memory_size - 1)); + ACCW(NV20_BLIMIT9, (si->ps.memory_size - 1)); + } + } + } + + /* disable all acceleration engine INT reguests */ + ACCW(ACC_INTE, 0x00000000); + + /* reset all acceration engine INT status bits */ + ACCW(ACC_INTS, 0xffffffff); + if (si->ps.card_arch != NV04A) + { + /* context control enabled */ + ACCW(NV10_CTX_CTRL, 0x10010100); + /* all acceleration buffers, pitches and colors are valid */ + ACCW(NV10_ACC_STAT, 0xffffffff); + } + else + { + /* context control enabled */ + ACCW(NV04_CTX_CTRL, 0x10010100); + /* all acceleration buffers, pitches and colors are valid */ + ACCW(NV04_ACC_STAT, 0xffffffff); + } + /* enable acceleration engine command FIFO */ + ACCW(FIFO_EN, 0x00000001); + /* pattern shape value = 8x8, 2 color */ + ACCW(PAT_SHP, 0x00000000); + if (si->ps.card_arch != NV04A) + { + /* surface type is non-swizzle */ + ACCW(NV10_SURF_TYP, 0x00000001); + } + else + { + /* surface type is non-swizzle */ + ACCW(NV04_SURF_TYP, 0x00000001); + } + + /*** Set pixel width and format ***/ + //info: + //the BPIXEL register holds the colorspaces for different engine 'contexts' or so. + //B0-3 is 'channel' 0, b4-7 is 'channel '1', etc. + //It looks like we are only using channel 0, so the settings for other channels + //shouldn't matter yet. + //When for instance rect_fill is going to be used on other buffers than the actual + //screen, it's colorspace should be corrected. When the engine is setup in 32bit + //desktop mode for example, the pixel's alpha channel doesn't get touched currently. + //choose mode $d (which is Y32) to get alpha filled too. + switch(si->dm.space) + { + case B_CMAP8: + /* acc engine */ + ACCW(FORMATS, 0x00001010); + if (si->ps.card_arch < NV30A) + /* set depth 0-5: $1 = Y8 */ + ACCW(BPIXEL, 0x00111111); + else + /* set depth 0-1: $1 = Y8, $2 = X1R5G5B5_Z1R5G5B5 */ + ACCW(BPIXEL, 0x00000021); + ACCW(STRD_FMT, 0x03020202); + /* PRAMIN */ + if (si->ps.card_arch < NV40A) + { + ACCW(PR_CTX1_0, 0x00000302); /* format is X24Y8, LSB mono */ + ACCW(PR_CTX1_1, 0x00000302); /* format is X24Y8, LSB mono */ + ACCW(PR_CTX1_2, 0x00000202); /* format is X16A8Y8, LSB mono */ + ACCW(PR_CTX1_3, 0x00000302); /* format is X24Y8, LSB mono */ + ACCW(PR_CTX1_4, 0x00000302); /* format is X24Y8, LSB mono */ + ACCW(PR_CTX1_5, 0x00000302); /* format is X24Y8, LSB mono */ + if (si->ps.card_arch == NV04A) + { + ACCW(PR_CTX1_6, 0x00000302); /* format is X24Y8, LSB mono */ + } + else + { + ACCW(PR_CTX1_6, 0x00000000); /* format is invalid */ + } + ACCW(PR_CTX1_9, 0x00000302); /* format is X24Y8, LSB mono */ +//fixme: update 3D add-on and this code for the NV4_SURFACE command. +//old surf0 and 1: +// ACCW(PR_CTX1_9, 0x00000302); /* format is X24Y8, LSB mono */ +// ACCW(PR_CTX2_9, 0x00000302); /* dma_instance 0 valid, instance 1 invalid */ + } + else + { + //fixme: select colorspace here (and in other depths), or add + //the appropriate SURFACE command(s). + ACCW(PR_CTX1_0, 0x00000000); /* NV_ROP5_SOLID */ + ACCW(PR_CTX1_2, 0x00000000); /* NV_IMAGE_BLACK_RECTANGLE */ + ACCW(PR_CTX1_4, 0x02000000); /* NV_IMAGE_PATTERN */ + ACCW(PR_CTX1_6, 0x00000000); /* NV_IMAGE_BLIT */ + ACCW(PR_CTX1_8, 0x02000000); /* NV4_GDI_RECTANGLE_TEXT */ + ACCW(PR_CTX1_A, 0x02000000); /* NV10_CONTEXT_SURFACES_2D */ + } + break; + case B_RGB15_LITTLE: + /* acc engine */ + ACCW(FORMATS, 0x00002071); + if (si->ps.card_arch < NV30A) + /* set depth 0-5: $2 = X1R5G5B5_Z1R5G5B5, $6 = Y16 */ + ACCW(BPIXEL, 0x00226222); + else + /* set depth 0-1: $2 = X1R5G5B5_Z1R5G5B5, $4 = A1R5G5B5 */ + ACCW(BPIXEL, 0x00000042); + ACCW(STRD_FMT, 0x09080808); + /* PRAMIN */ + ACCW(PR_CTX1_0, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_1, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_2, 0x00000802); /* format is X16A1RGB15, LSB mono */ + ACCW(PR_CTX1_3, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_4, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_5, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_6, 0x00000902); /* format is X17RGB15, LSB mono */ + ACCW(PR_CTX1_9, 0x00000902); /* format is X17RGB15, LSB mono */ +//old surf0 and 1: +// ACCW(PR_CTX1_9, 0x00000902); /* format is X17RGB15, LSB mono */ +// ACCW(PR_CTX2_9, 0x00000902); /* dma_instance 0 valid, instance 1 invalid */ + break; + case B_RGB16_LITTLE: + /* acc engine */ + ACCW(FORMATS, 0x000050C2); + if (si->ps.card_arch < NV30A) + /* set depth 0-5: $5 = R5G6B5, $6 = Y16 */ + ACCW(BPIXEL, 0x00556555); + else + /* set depth 0-1: $5 = R5G6B5, $a = X1A7R8G8B8_O1A7R8G8B8 */ + ACCW(BPIXEL, 0x000000a5); + if (si->ps.card_arch == NV04A) + ACCW(STRD_FMT, 0x0c0b0b0b); + else + ACCW(STRD_FMT, 0x000b0b0c); + /* PRAMIN */ + ACCW(PR_CTX1_0, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_1, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_2, 0x00000b02); /* format is A16RGB16, LSB mono */ + ACCW(PR_CTX1_3, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_4, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_5, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_6, 0x00000c02); /* format is X16RGB16, LSB mono */ + ACCW(PR_CTX1_9, 0x00000c02); /* format is X16RGB16, LSB mono */ +//old surf0 and 1: +// ACCW(PR_CTX1_9, 0x00000c02); /* format is X16RGB16, LSB mono */ +// ACCW(PR_CTX2_9, 0x00000c02); /* dma_instance 0 valid, instance 1 invalid */ + break; + case B_RGB32_LITTLE: + case B_RGBA32_LITTLE: + /* acc engine */ + ACCW(FORMATS, 0x000070e5); + if (si->ps.card_arch < NV30A) + /* set depth 0-5: $7 = X8R8G8B8_Z8R8G8B8, $d = Y32 */ + ACCW(BPIXEL, 0x0077d777); + else + /* set depth 0-1: $7 = X8R8G8B8_Z8R8G8B8, $e = V8YB8U8YA8 */ + ACCW(BPIXEL, 0x000000e7); + ACCW(STRD_FMT, 0x0e0d0d0d); + /* PRAMIN */ + ACCW(PR_CTX1_0, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_1, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_2, 0x00000d02); /* format is A8RGB24, LSB mono */ + ACCW(PR_CTX1_3, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_4, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_5, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_6, 0x00000e02); /* format is X8RGB24, LSB mono */ + ACCW(PR_CTX1_9, 0x00000e02); /* format is X8RGB24, LSB mono */ +//old surf0 and 1: +// ACCW(PR_CTX1_9, 0x00000e02); /* format is X8RGB24, LSB mono */ +// ACCW(PR_CTX2_9, 0x00000e02); /* dma_instance 0 valid, instance 1 invalid */ + break; + default: + LOG(8,("ACC: init, invalid bit depth\n")); + return B_ERROR; + } + + /* setup some extra stuff for NV30A and later */ + if (si->ps.card_arch >= NV30A) + { + /* activate Zcullflush(?) */ + ACCW(DEBUG3, (ACCR(DEBUG3) | 0x00000001)); + /* unknown */ + ACCW(NV25_WHAT1, (ACCR(NV25_WHAT1) | 0x00040000)); + } + + /*** setup screen location and pitch ***/ + switch (si->ps.card_arch) + { + case NV04A: + case NV10A: + /* location of active screen in framebuffer */ + /* (confirmed NV05: OFFSET0 is 2D destination buffer offset) */ + ACCW(OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* (confirmed NV05: OFFSET1 is 2D source buffer offset) */ + ACCW(OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* (confirmed NV05: OFFSET2 is 3D color buffer offset) */ + ACCW(OFFSET2, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* (confirmed NV05: OFFSET3 is 3D depth buffer offset) */ + ACCW(OFFSET3, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(OFFSET4, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(OFFSET5, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + + /* setup buffer pitch */ + /* (confirmed NV05: PITCH0 is 2D destination buffer pitch) */ + ACCW(PITCH0, (si->fbc.bytes_per_row & 0x0000ffff)); + /* (confirmed NV05: PITCH1 is 2D source buffer pitch) */ + ACCW(PITCH1, (si->fbc.bytes_per_row & 0x0000ffff)); + /* (confirmed NV05: PITCH2 is 3D color buffer pitch) */ + ACCW(PITCH2, (si->fbc.bytes_per_row & 0x0000ffff)); + /* (confirmed NV05: PITCH3 is 3D depth buffer pitch) */ + ACCW(PITCH3, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(PITCH4, (si->fbc.bytes_per_row & 0x0000ffff)); + break; + case NV20A: + case NV30A: + /* location of active screen in framebuffer */ + ACCW(NV20_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET2, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET3, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + + /* setup buffer pitch */ + ACCW(NV20_PITCH0, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH1, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH2, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH3, (si->fbc.bytes_per_row & 0x0000ffff)); + break; + case NV40A: + if ((si->ps.card_type == NV40) || (si->ps.card_type == NV45)) + { + /* location of active screen in framebuffer */ + ACCW(NV20_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + //ACCW(NV20_OFFSET2, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + //ACCW(NV20_OFFSET3, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + + /* setup buffer pitch */ + //fixme? + ACCW(NV20_PITCH0, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH1, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH2, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV20_PITCH3, (si->fbc.bytes_per_row & 0x0000ffff)); + } + else + { + /* location of active screen in framebuffer */ + ACCW(NV40P_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV40P_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + + /* setup buffer pitch */ + //fixme? + ACCW(NV40P_PITCH0, (si->fbc.bytes_per_row & 0x0000ffff)); + ACCW(NV40P_PITCH1, (si->fbc.bytes_per_row & 0x0000ffff)); + } + break; + } + + /*** setup tile and pipe stuff ***/ + if (si->ps.card_arch >= NV10A) + { + /* setup acc engine tile stuff: */ + /* reset tile adresses */ + ACCW(NV10_FBTIL0AD, 0); + ACCW(NV10_FBTIL1AD, 0); + ACCW(NV10_FBTIL2AD, 0); + ACCW(NV10_FBTIL3AD, 0); + ACCW(NV10_FBTIL4AD, 0); + ACCW(NV10_FBTIL5AD, 0); + ACCW(NV10_FBTIL6AD, 0); + ACCW(NV10_FBTIL7AD, 0); + /* copy some RAM configuration info(?) */ + if (si->ps.card_arch >= NV20A) + { + if ((si->ps.card_type > NV40) && (si->ps.card_type != NV45)) + { + ACCW(NV40P_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40P_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + ACCW(NV40P_WHAT_T2, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40P_WHAT_T3, NV_REG32(NV32_PFB_CONFIG_1)); + } + else + { + ACCW(NV20_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV20_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + if ((si->ps.card_type == NV40) || (si->ps.card_type == NV45)) + { + ACCW(NV40_WHAT_T2, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40_WHAT_T3, NV_REG32(NV32_PFB_CONFIG_1)); + } + } + } + /* copy tile setup stuff from 'source' to acc engine */ + /* tile 0: */ + /* tile invalid, tile adress = $00000 (18bit) */ + ACCW(NV10_TIL0AD, ACCR(NV10_FBTIL0AD)); + /* set tile end adress (18bit) */ + ACCW(NV10_TIL0ED, ACCR(NV10_FBTIL0ED)); + /* set tile size pitch (8bit: b8-15) */ + ACCW(NV10_TIL0PT, ACCR(NV10_FBTIL0PT)); + /* set tile status */ + ACCW(NV10_TIL0ST, ACCR(NV10_FBTIL0ST)); + /* tile 1: */ + ACCW(NV10_TIL1AD, ACCR(NV10_FBTIL1AD)); + ACCW(NV10_TIL1ED, ACCR(NV10_FBTIL1ED)); + ACCW(NV10_TIL1PT, ACCR(NV10_FBTIL1PT)); + ACCW(NV10_TIL1ST, ACCR(NV10_FBTIL1ST)); + /* tile 2: */ + ACCW(NV10_TIL2AD, ACCR(NV10_FBTIL2AD)); + ACCW(NV10_TIL2ED, ACCR(NV10_FBTIL2ED)); + ACCW(NV10_TIL2PT, ACCR(NV10_FBTIL2PT)); + ACCW(NV10_TIL2ST, ACCR(NV10_FBTIL2ST)); + /* tile 3: */ + ACCW(NV10_TIL3AD, ACCR(NV10_FBTIL3AD)); + ACCW(NV10_TIL3ED, ACCR(NV10_FBTIL3ED)); + if (si->ps.card_arch >= NV40A) + { + ACCW(NV10_TIL3PT, 0x2ffff800); + ACCW(NV10_TIL3ST, 0x00006000); + } + else + { + ACCW(NV10_TIL3PT, ACCR(NV10_FBTIL3PT)); + ACCW(NV10_TIL3ST, ACCR(NV10_FBTIL3ST)); + } + /* tile 4: */ + ACCW(NV10_TIL4AD, ACCR(NV10_FBTIL4AD)); + ACCW(NV10_TIL4ED, ACCR(NV10_FBTIL4ED)); + ACCW(NV10_TIL4PT, ACCR(NV10_FBTIL4PT)); + ACCW(NV10_TIL4ST, ACCR(NV10_FBTIL4ST)); + /* tile 5: */ + ACCW(NV10_TIL5AD, ACCR(NV10_FBTIL5AD)); + ACCW(NV10_TIL5ED, ACCR(NV10_FBTIL5ED)); + ACCW(NV10_TIL5PT, ACCR(NV10_FBTIL5PT)); + ACCW(NV10_TIL5ST, ACCR(NV10_FBTIL5ST)); + /* tile 6: */ + ACCW(NV10_TIL6AD, ACCR(NV10_FBTIL6AD)); + ACCW(NV10_TIL6ED, ACCR(NV10_FBTIL6ED)); + ACCW(NV10_TIL6PT, ACCR(NV10_FBTIL6PT)); + ACCW(NV10_TIL6ST, ACCR(NV10_FBTIL6ST)); + /* tile 7: */ + ACCW(NV10_TIL7AD, ACCR(NV10_FBTIL7AD)); + ACCW(NV10_TIL7ED, ACCR(NV10_FBTIL7ED)); + ACCW(NV10_TIL7PT, ACCR(NV10_FBTIL7PT)); + ACCW(NV10_TIL7ST, ACCR(NV10_FBTIL7ST)); + + if (si->ps.card_arch >= NV40A) + { + /* unknown.. */ + ACCW(NV4X_WHAT1, 0x01000000); + /* engine data source DMA instance is invalid */ + ACCW(NV4X_DMA_SRC, 0x00000000); + } + + /* setup (clear) pipe */ + /* set eyetype to local, lightning is off */ + ACCW(NV10_XFMOD0, 0x10000000); + /* disable all lights */ + ACCW(NV10_XFMOD1, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00000040); + ACCW(NV10_PIPEDAT, 0x00000008); + + /* note: upon writing data into the PIPEDAT register, the PIPEADR is + * probably auto-incremented! */ + ACCW(NV10_PIPEADR, 0x00000200); + for (cnt = 0; cnt < (3 * 16); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00000040); + ACCW(NV10_PIPEDAT, 0x00000000); + + //fixme: this 'set' seems to hang the NV43 engine if executed: + //status remains 'busy' forever in this case. + if (si->ps.card_arch < NV40A) + { + ACCW(NV10_PIPEADR, 0x00000800); + for (cnt = 0; cnt < (16 * 16); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + } + + /* turn lightning on */ + ACCW(NV10_XFMOD0, 0x30000000); + /* set light 1 to infinite type, other lights remain off */ + ACCW(NV10_XFMOD1, 0x00000004); + + ACCW(NV10_PIPEADR, 0x00006400); + for (cnt = 0; cnt < (59 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006800); + for (cnt = 0; cnt < (47 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006c00); + for (cnt = 0; cnt < (3 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00007000); + for (cnt = 0; cnt < (19 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00007400); + for (cnt = 0; cnt < (12 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00007800); + for (cnt = 0; cnt < (12 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00004400); + for (cnt = 0; cnt < (8 * 4); cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00000000); + for (cnt = 0; cnt < 16; cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00000040); + for (cnt = 0; cnt < 4; cnt++) ACCW(NV10_PIPEDAT, 0x00000000); + } + + /* setup 3D specifics */ + nv_init_for_3D(); + + /*** setup acceleration engine command shortcuts (so via fifo) ***/ + /* set object handles (b31 = 1 selects 'config' function?) */ + /* note: + * probably depending on some other setup, there are 8 or 32 FIFO channels + * available. Assuming the current setup only has 8 channels because the 'rest' + * isn't setup here... */ + si->engine.fifo.handle[0] = NV_ROP5_SOLID; + si->engine.fifo.handle[1] = NV_IMAGE_BLACK_RECTANGLE; + si->engine.fifo.handle[2] = NV_IMAGE_PATTERN; + si->engine.fifo.handle[3] = NV4_SURFACE; /* NV10_CONTEXT_SURFACES_2D is identical */ + si->engine.fifo.handle[4] = NV_IMAGE_BLIT; + si->engine.fifo.handle[5] = NV4_GDI_RECTANGLE_TEXT; + if (si->ps.card_arch < NV40A) + { + si->engine.fifo.handle[6] = NV1_RENDER_SOLID_LIN; + si->engine.fifo.handle[7] = NV4_DX5_TEXTURE_TRIANGLE; + } + /* preset no FIFO channels assigned to cmd's */ + for (cnt = 0; cnt < 0x20; cnt++) + { + si->engine.fifo.ch_ptr[cnt] = 0; + } + /* set handle's pointers to their assigned FIFO channels */ + for (cnt = 0; cnt < 0x08; cnt++) + { + si->engine.fifo.ch_ptr[(si->engine.fifo.handle[cnt])] = + (NVACC_FIFO + (cnt * 0x00002000)); + } + /* program FIFO assignments */ + ACCW(FIFO_CH0, (0x80000000 | si->engine.fifo.handle[0])); /* Raster OPeration */ + ACCW(FIFO_CH1, (0x80000000 | si->engine.fifo.handle[1])); /* Clip */ + ACCW(FIFO_CH2, (0x80000000 | si->engine.fifo.handle[2])); /* Pattern */ + ACCW(FIFO_CH3, (0x80000000 | si->engine.fifo.handle[3])); /* 2D Surface */ + ACCW(FIFO_CH4, (0x80000000 | si->engine.fifo.handle[4])); /* Blit */ + ACCW(FIFO_CH5, (0x80000000 | si->engine.fifo.handle[5])); /* Bitmap */ + if (si->ps.card_arch < NV40A) + { + ACCW(FIFO_CH6, (0x80000000 | si->engine.fifo.handle[6])); /* Line (not used) */ + ACCW(FIFO_CH7, (0x80000000 | si->engine.fifo.handle[7])); /* Textured Triangle (3D only) */ + } + + /* initialize our local pointers */ + nv_acc_assert_fifo(); + + /* do first actual acceleration engine command: + * setup clipping region (workspace size) to 32768 x 32768 pixels: + * wait for room in fifo for clipping cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_image_black_rectangle_ptr->FifoFree) >> 2) < 2) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup clipping (writing 2 32bit words) */ + nv_image_black_rectangle_ptr->TopLeft = 0x00000000; + nv_image_black_rectangle_ptr->HeightWidth = 0x80008000; + + return B_OK; +} + +static void nv_init_for_3D(void) +{ + /* setup PGRAPH unknown registers and modify (pre-cleared) pipe stuff for 3D use */ + if (si->ps.card_arch >= NV10A) + { + /* setup unknown PGRAPH stuff */ + ACCW(PGWHAT_00, 0x00000000); + ACCW(PGWHAT_01, 0x00000000); + ACCW(PGWHAT_02, 0x00000000); + ACCW(PGWHAT_03, 0x00000000); + + ACCW(PGWHAT_04, 0x00001000); + ACCW(PGWHAT_05, 0x00001000); + ACCW(PGWHAT_06, 0x4003ff80); + + ACCW(PGWHAT_07, 0x00000000); + ACCW(PGWHAT_08, 0x00000000); + ACCW(PGWHAT_09, 0x00000000); + ACCW(PGWHAT_0A, 0x00000000); + ACCW(PGWHAT_0B, 0x00000000); + + ACCW(PGWHAT_0C, 0x00080008); + ACCW(PGWHAT_0D, 0x00080008); + + ACCW(PGWHAT_0E, 0x00000000); + ACCW(PGWHAT_0F, 0x00000000); + ACCW(PGWHAT_10, 0x00000000); + ACCW(PGWHAT_11, 0x00000000); + ACCW(PGWHAT_12, 0x00000000); + ACCW(PGWHAT_13, 0x00000000); + ACCW(PGWHAT_14, 0x00000000); + ACCW(PGWHAT_15, 0x00000000); + ACCW(PGWHAT_16, 0x00000000); + ACCW(PGWHAT_17, 0x00000000); + ACCW(PGWHAT_18, 0x00000000); + + ACCW(PGWHAT_19, 0x10000000); + + ACCW(PGWHAT_1A, 0x00000000); + ACCW(PGWHAT_1B, 0x00000000); + ACCW(PGWHAT_1C, 0x00000000); + ACCW(PGWHAT_1D, 0x00000000); + ACCW(PGWHAT_1E, 0x00000000); + ACCW(PGWHAT_1F, 0x00000000); + ACCW(PGWHAT_20, 0x00000000); + ACCW(PGWHAT_21, 0x00000000); + + ACCW(PGWHAT_22, 0x08000000); + + ACCW(PGWHAT_23, 0x00000000); + ACCW(PGWHAT_24, 0x00000000); + ACCW(PGWHAT_25, 0x00000000); + ACCW(PGWHAT_26, 0x00000000); + + ACCW(PGWHAT_27, 0x4b7fffff); + + ACCW(PGWHAT_28, 0x00000000); + ACCW(PGWHAT_29, 0x00000000); + ACCW(PGWHAT_2A, 0x00000000); + + /* setup window clipping */ + /* b0-11 = min; b16-27 = max. + * note: + * probably two's complement values, so setting to max range here: + * which would be -2048 upto/including +2047. */ + /* horizontal */ + ACCW(WINCLIP_H_0, 0x07ff0800); + ACCW(WINCLIP_H_1, 0x07ff0800); + ACCW(WINCLIP_H_2, 0x07ff0800); + ACCW(WINCLIP_H_3, 0x07ff0800); + ACCW(WINCLIP_H_4, 0x07ff0800); + ACCW(WINCLIP_H_5, 0x07ff0800); + ACCW(WINCLIP_H_6, 0x07ff0800); + ACCW(WINCLIP_H_7, 0x07ff0800); + /* vertical */ + ACCW(WINCLIP_V_0, 0x07ff0800); + ACCW(WINCLIP_V_1, 0x07ff0800); + ACCW(WINCLIP_V_2, 0x07ff0800); + ACCW(WINCLIP_V_3, 0x07ff0800); + ACCW(WINCLIP_V_4, 0x07ff0800); + ACCW(WINCLIP_V_5, 0x07ff0800); + ACCW(WINCLIP_V_6, 0x07ff0800); + ACCW(WINCLIP_V_7, 0x07ff0800); + + /* setup (initialize) pipe */ + /* set eyetype to local, lightning etc. is off */ + ACCW(NV10_XFMOD0, 0x10000000); + /* disable all lights */ + ACCW(NV10_XFMOD1, 0x00000000); + + /* note: upon writing data into the PIPEDAT register, the PIPEADR is + * probably auto-incremented! */ + /* (pipe adress = b2-16, pipe data = b0-31) */ + ACCW(NV10_PIPEADR, 0x00006740); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006750); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + + ACCW(NV10_PIPEADR, 0x00006760); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006770); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006780); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000067a0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006ab0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006ac0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006c10); + ACCW(NV10_PIPEDAT, 0xbf800000); + + ACCW(NV10_PIPEADR, 0x00007030); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007040); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007050); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007060); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007070); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007080); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007090); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x000070a0); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00006a80); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006aa0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00000040); + ACCW(NV10_PIPEDAT, 0x00000005); + + ACCW(NV10_PIPEADR, 0x00006400); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x4b7fffff); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006410); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006420); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006430); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064c0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x477fffff); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x000064d0); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064e0); + ACCW(NV10_PIPEDAT, 0xc4fff000); + ACCW(NV10_PIPEDAT, 0xc4fff000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064f0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + /* turn lightning on */ + ACCW(NV10_XFMOD0, 0x30000000); + /* set light 1 to infinite type, other lights remain off */ + ACCW(NV10_XFMOD1, 0x00000004); + + /* Z-buffer state is: + * initialized, set to: 'fixed point' (integer?); Z-buffer; 16bits depth */ + /* note: + * other options possible are: floating point; 24bits depth; W-buffer(?) */ + ACCW(GLOB_STAT_0, 0x10000000); + /* set DMA instance 2 and 3 to be invalid */ + ACCW(GLOB_STAT_1, 0x00000000); + } +} + +/* fixme? (check this out..) + * Looks like this stuff can be very much simplified and speed-up, as it seems it's not + * nessesary to wait for the engine to become idle before re-assigning channels. + * Because the cmd handles are actually programmed _inside_ the fifo channels, it might + * well be that the assignment is buffered along with the commands that still have to + * be executed! + * (sounds very plausible to me :) */ +void nv_acc_assert_fifo(void) +{ + /* does every engine cmd this accelerant needs have a FIFO channel? */ + //fixme: can probably be optimized for both speed and channel selection... + if (!si->engine.fifo.ch_ptr[NV_ROP5_SOLID] || + !si->engine.fifo.ch_ptr[NV_IMAGE_BLACK_RECTANGLE] || + !si->engine.fifo.ch_ptr[NV_IMAGE_PATTERN] || + !si->engine.fifo.ch_ptr[NV_IMAGE_BLIT] || + !si->engine.fifo.ch_ptr[NV4_GDI_RECTANGLE_TEXT]) + { + uint16 cnt; + + /* no, wait until the engine is idle before re-assigning the FIFO */ + nv_acc_wait_idle(); + + /* free the FIFO channels we want from the currently assigned cmd's */ + si->engine.fifo.ch_ptr[si->engine.fifo.handle[0]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[1]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[2]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[4]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[5]] = 0; + + /* set new object handles */ + si->engine.fifo.handle[0] = NV_ROP5_SOLID; + si->engine.fifo.handle[1] = NV_IMAGE_BLACK_RECTANGLE; + si->engine.fifo.handle[2] = NV_IMAGE_PATTERN; + si->engine.fifo.handle[4] = NV_IMAGE_BLIT; + si->engine.fifo.handle[5] = NV4_GDI_RECTANGLE_TEXT; + + /* set handle's pointers to their assigned FIFO channels */ + for (cnt = 0; cnt < 0x08; cnt++) + { + si->engine.fifo.ch_ptr[(si->engine.fifo.handle[cnt])] = + (NVACC_FIFO + (cnt * 0x00002000)); + } + + /* program new FIFO assignments */ + ACCW(FIFO_CH0, (0x80000000 | si->engine.fifo.handle[0])); /* Raster OPeration */ + ACCW(FIFO_CH1, (0x80000000 | si->engine.fifo.handle[1])); /* Clip */ + ACCW(FIFO_CH2, (0x80000000 | si->engine.fifo.handle[2])); /* Pattern */ + ACCW(FIFO_CH4, (0x80000000 | si->engine.fifo.handle[4])); /* Blit */ + ACCW(FIFO_CH5, (0x80000000 | si->engine.fifo.handle[5])); /* Bitmap */ + } + + /* update our local pointers */ + nv_rop5_solid_ptr = (cmd_nv_rop5_solid*) + &(regs[(si->engine.fifo.ch_ptr[NV_ROP5_SOLID]) >> 2]); + + nv_image_black_rectangle_ptr = (cmd_nv_image_black_rectangle*) + &(regs[(si->engine.fifo.ch_ptr[NV_IMAGE_BLACK_RECTANGLE]) >> 2]); + + nv_image_pattern_ptr = (cmd_nv_image_pattern*) + &(regs[(si->engine.fifo.ch_ptr[NV_IMAGE_PATTERN]) >> 2]); + + nv_image_blit_ptr = (cmd_nv_image_blit*) + &(regs[(si->engine.fifo.ch_ptr[NV_IMAGE_BLIT]) >> 2]); + + nv3_gdi_rectangle_text_ptr = (cmd_nv3_gdi_rectangle_text*) + &(regs[(si->engine.fifo.ch_ptr[NV4_GDI_RECTANGLE_TEXT]) >> 2]); +} + +/* screen to screen blit - i.e. move windows around and scroll within them. */ +status_t nv_acc_setup_blit() +{ + /* setup solid pattern: + * wait for room in fifo for pattern cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_image_pattern_ptr->FifoFree) >> 2) < 5) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup pattern (writing 5 32bit words) */ + nv_image_pattern_ptr->SetShape = 0x00000000; /* 0 = 8x8, 1 = 64x1, 2 = 1x64 */ + nv_image_pattern_ptr->SetColor0 = 0xffffffff; + nv_image_pattern_ptr->SetColor1 = 0xffffffff; + nv_image_pattern_ptr->SetPattern[0] = 0xffffffff; + nv_image_pattern_ptr->SetPattern[1] = 0xffffffff; + + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_rop5_solid_ptr->FifoFree) >> 2) < 1) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup ROP (writing 1 32bit word) */ + nv_rop5_solid_ptr->SetRop5 = 0xcc; + + return B_OK; +} + +status_t nv_acc_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h) +{ + /* Note: blit-copy direction is determined inside riva hardware: no setup needed */ + + /* instruct engine what to blit: + * wait for room in fifo for blit cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_image_blit_ptr->FifoFree) >> 2) < 3) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup blit (writing 3 32bit words) */ + nv_image_blit_ptr->SourceOrg = ((ys << 16) | xs); + nv_image_blit_ptr->DestOrg = ((yd << 16) | xd); + nv_image_blit_ptr->HeightWidth = (((h + 1) << 16) | (w + 1)); + + return B_OK; +} + +/* rectangle fill - i.e. workspace and window background color */ +/* span fill - i.e. (selected) menuitem background color (Dano) */ +status_t nv_acc_setup_rectangle(uint32 color) +{ + /* setup solid pattern: + * wait for room in fifo for pattern cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_image_pattern_ptr->FifoFree) >> 2) < 5) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup pattern (writing 5 32bit words) */ + nv_image_pattern_ptr->SetShape = 0x00000000; /* 0 = 8x8, 1 = 64x1, 2 = 1x64 */ + nv_image_pattern_ptr->SetColor0 = 0xffffffff; + nv_image_pattern_ptr->SetColor1 = 0xffffffff; + nv_image_pattern_ptr->SetPattern[0] = 0xffffffff; + nv_image_pattern_ptr->SetPattern[1] = 0xffffffff; + + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_rop5_solid_ptr->FifoFree) >> 2) < 1) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup ROP (writing 1 32bit word) for GXcopy */ + nv_rop5_solid_ptr->SetRop5 = 0xcc; + + /* setup fill color: + * wait for room in fifo for bitmap cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv3_gdi_rectangle_text_ptr->FifoFree) >> 2) < 1) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup color (writing 1 32bit word) */ + nv3_gdi_rectangle_text_ptr->Color1A = color; + + return B_OK; +} + +status_t nv_acc_rectangle(uint32 xs,uint32 xe,uint32 ys,uint32 yl) +{ + /* instruct engine what to fill: + * wait for room in fifo for bitmap cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv3_gdi_rectangle_text_ptr->FifoFree) >> 2) < 2) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup fill (writing 2 32bit words) */ + nv3_gdi_rectangle_text_ptr->UnclippedRectangle[0].LeftTop = + ((xs << 16) | (ys & 0x0000ffff)); + nv3_gdi_rectangle_text_ptr->UnclippedRectangle[0].WidthHeight = + (((xe - xs) << 16) | (yl & 0x0000ffff)); + + return B_OK; +} + +/* rectangle invert - i.e. text cursor and text selection */ +status_t nv_acc_setup_rect_invert() +{ + /* setup solid pattern: + * wait for room in fifo for pattern cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_image_pattern_ptr->FifoFree) >> 2) < 5) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup pattern (writing 5 32bit words) */ + nv_image_pattern_ptr->SetShape = 0x00000000; /* 0 = 8x8, 1 = 64x1, 2 = 1x64 */ + nv_image_pattern_ptr->SetColor0 = 0xffffffff; + nv_image_pattern_ptr->SetColor1 = 0xffffffff; + nv_image_pattern_ptr->SetPattern[0] = 0xffffffff; + nv_image_pattern_ptr->SetPattern[1] = 0xffffffff; + + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv_rop5_solid_ptr->FifoFree) >> 2) < 1) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup ROP (writing 1 32bit word) for GXinvert */ + nv_rop5_solid_ptr->SetRop5 = 0x55; + + /* reset fill color: + * wait for room in fifo for bitmap cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv3_gdi_rectangle_text_ptr->FifoFree) >> 2) < 1) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now reset color (writing 1 32bit word) */ + nv3_gdi_rectangle_text_ptr->Color1A = 0x00000000; + + return B_OK; +} + +status_t nv_acc_rectangle_invert(uint32 xs,uint32 xe,uint32 ys,uint32 yl) +{ + /* instruct engine what to invert: + * wait for room in fifo for bitmap cmd if needed. + * (fifo holds 256 32bit words: count those, not bytes) */ + while (((nv3_gdi_rectangle_text_ptr->FifoFree) >> 2) < 2) + { + /* snooze a bit so I do not hammer the bus */ + snooze (10); + } + /* now setup invert (writing 2 32bit words) */ + nv3_gdi_rectangle_text_ptr->UnclippedRectangle[0].LeftTop = + ((xs << 16) | (ys & 0x0000ffff)); + nv3_gdi_rectangle_text_ptr->UnclippedRectangle[0].WidthHeight = + (((xe - xs) << 16) | (yl & 0x0000ffff)); + + return B_OK; +} + +/* screen to screen tranparent blit */ +status_t nv_acc_transparent_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h,uint32 colour) +{ + //fixme: implement. + + return B_ERROR; +} + +/* screen to screen scaled filtered blit - i.e. scale video in memory */ +status_t nv_acc_video_blit(uint16 xs,uint16 ys,uint16 ws, uint16 hs, + uint16 xd,uint16 yd,uint16 wd,uint16 hd) +{ + //fixme: implement. + + return B_ERROR; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc_dma.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc_dma.c new file mode 100644 index 0000000000..2179cc3b84 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_acc_dma.c @@ -0,0 +1,2035 @@ +/* NV Acceleration functions */ + +/* Author: + Rudolf Cornelissen 8/2003-9/2007. + + This code was possible thanks to: + - the Linux XFree86 NV driver, + - the Linux UtahGLX 3D driver. +*/ + +#define MODULE_BIT 0x00080000 + +#include "nv_std.h" + +/*acceleration notes*/ + +/*functions Be's app_server uses: +fill span (horizontal only) +fill rectangle (these 2 are very similar) +invert rectangle +blit +*/ + +static void nv_init_for_3D_dma(void); +static void nv_start_dma(void); +static status_t nv_acc_fifofree_dma(uint16 cmd_size); +static void nv_acc_cmd_dma(uint32 cmd, uint16 offset, uint16 size); +static void nv_acc_set_ch_dma(uint16 ch, uint32 handle); + +/* used to track engine DMA stalls */ +static uint8 err; + +/* wait until engine completely idle */ +status_t nv_acc_wait_idle_dma() +{ + /* we'd better check for timeouts on the DMA engine as it's theoretically + * breakable by malfunctioning software */ + uint16 cnt = 0; +return B_OK; + + /* wait until all upcoming commands are in execution at least. Do this until + * we hit a timeout; abort if we failed at least three times before: + * if DMA stalls, we have to forget about it alltogether at some point, or + * the system will almost come to a complete halt.. */ + /* note: + * it doesn't matter which FIFO channel's DMA registers we access, they are in + * fact all the same set. It also doesn't matter if the channel was assigned a + * command or not. */ + while ((NV_REG32(NVACC_FIFO + NV_GENERAL_DMAGET) != (si->engine.dma.put << 2)) && + (cnt < 10000) && (err < 3)) + { + /* snooze a bit so I do not hammer the bus */ + snooze (100); + cnt++; + } + + /* log timeout if we had one */ + if (cnt == 10000) + { + if (err < 3) err++; + LOG(4,("ACC_DMA: wait_idle; DMA timeout #%d, engine trouble!\n", err)); + } + + /* wait until execution completed */ + while (ACCR(STATUS)) + { + /* snooze a bit so I do not hammer the bus */ + snooze (100); + } + + return B_OK; +} + +/* AFAIK this must be done for every new screenmode. + * Engine required init. */ +status_t nv_acc_init_dma() +{ + uint32 cnt, tmp; + uint32 surf_depth, cmd_depth; + /* reset the engine DMA stalls counter */ + err = 0; + + /* a hanging engine only recovers from a complete power-down/power-up cycle */ + NV_REG32(NV32_PWRUPCTRL) = 0x13110011; + snooze(1000); + NV_REG32(NV32_PWRUPCTRL) = 0x13111111; + + /* don't try this on NV20 and later.. */ + /* note: + * the specific register that's responsible for the speedfix on NV18 is + * $00400ed8: bit 6 needs to be zero for fastest rendering (confirmed). */ + /* note also: + * on NV28 the following ranges could be reset (confirmed): + * $00400000 upto/incl. $004002fc; + * $00400400 upto/incl. $004017fc; + * $0040180c upto/incl. $00401948; + * $00401994 upto/incl. $00401a80; + * $00401a94 upto/incl. $00401ffc. + * The intermediate ranges hang the engine upon resetting. */ + if (si->ps.card_arch < NV20A) + { + /* actively reset the PGRAPH registerset (acceleration engine) */ + for (cnt = 0x00400000; cnt < 0x00402000; cnt +=4) + { + NV_REG32(cnt) = 0x00000000; + } + } + + /* setup PTIMER: */ + //fixme? how about NV28 setup as just after coldstarting? (see nv_info.c) + /* set timer numerator to 8 (in b0-15) */ + ACCW(PT_NUMERATOR, 0x00000008); + /* set timer denominator to 3 (in b0-15) */ + ACCW(PT_DENOMINATR, 0x00000003); + + /* disable timer-alarm INT requests (b0) */ + ACCW(PT_INTEN, 0x00000000); + /* reset timer-alarm INT status bit (b0) */ + ACCW(PT_INTSTAT, 0xffffffff); + + /* enable PRAMIN write access on pre NV10 before programming it! */ + if (si->ps.card_arch == NV04A) + { + /* set framebuffer config: type = notiling, PRAMIN write access enabled */ + NV_REG32(NV32_PFB_CONFIG_0) = 0x00001114; + } + else + { + /* setup acc engine 'source' tile adressranges */ + if ((si->ps.card_type <= NV40) || (si->ps.card_type == NV45)) + { + ACCW(NV10_FBTIL0AD, 0); + ACCW(NV10_FBTIL1AD, 0); + ACCW(NV10_FBTIL2AD, 0); + ACCW(NV10_FBTIL3AD, 0); + ACCW(NV10_FBTIL4AD, 0); + ACCW(NV10_FBTIL5AD, 0); + ACCW(NV10_FBTIL6AD, 0); + ACCW(NV10_FBTIL7AD, 0); + ACCW(NV10_FBTIL0ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL1ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL2ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL3ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL4ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL5ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL6ED, (si->ps.memory_size - 1)); + ACCW(NV10_FBTIL7ED, (si->ps.memory_size - 1)); + } + else + { + /* NV41, 43, 44, G70 and up */ + ACCW(NV41_FBTIL0AD, 0); + ACCW(NV41_FBTIL1AD, 0); + ACCW(NV41_FBTIL2AD, 0); + ACCW(NV41_FBTIL3AD, 0); + ACCW(NV41_FBTIL4AD, 0); + ACCW(NV41_FBTIL5AD, 0); + ACCW(NV41_FBTIL6AD, 0); + ACCW(NV41_FBTIL7AD, 0); + ACCW(NV41_FBTIL8AD, 0); + ACCW(NV41_FBTIL9AD, 0); + ACCW(NV41_FBTILAAD, 0); + ACCW(NV41_FBTILBAD, 0); + ACCW(NV41_FBTIL0ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL1ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL2ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL3ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL4ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL5ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL6ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL7ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL8ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTIL9ED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTILAED, (si->ps.memory_size - 1)); + ACCW(NV41_FBTILBED, (si->ps.memory_size - 1)); + + if (si->ps.card_type >= G70) + { + ACCW(G70_FBTILCAD, 0); + ACCW(G70_FBTILDAD, 0); + ACCW(G70_FBTILEAD, 0); + ACCW(G70_FBTILCED, (si->ps.memory_size - 1)); + ACCW(G70_FBTILDED, (si->ps.memory_size - 1)); + ACCW(G70_FBTILEED, (si->ps.memory_size - 1)); + } + } + } + + /*** PRAMIN ***/ + /* first clear the entire RAMHT (hash-table) space to a defined state. It turns + * out at least NV11 will keep the previously programmed handles over resets and + * power-outages upto about 15 seconds!! Faulty entries might well hang the + * engine (confirmed on NV11). + * Note: + * this behaviour is not very strange: even very old DRAM chips are known to be + * able to do this, even though you should refresh them every few milliseconds or + * so. (Large memory cell capacitors, though different cells vary a lot in their + * capacity.) + * Of course data validity is not certain by a long shot over this large + * amount of time.. */ + for(cnt = 0; cnt < 0x0400; cnt++) + NV_REG32(NVACC_HT_HANDL_00 + (cnt << 2)) = 0; + /* RAMHT (hash-table) space SETUP FIFO HANDLES */ + /* note: + * 'instance' tells you where the engine command is stored in 'PR_CTXx_x' sets + * below: instance being b4-19 with baseadress NV_PRAMIN_CTX_0 (0x00700000). + * That command is linked to the handle noted here. This handle is then used to + * tell the FIFO to which engine command it is connected! + * (CTX registers are actually a sort of RAM space.) */ + if (si->ps.card_arch >= NV40A) + { + /* (first set) */ + ACCW(HT_HANDL_00, (0x80000000 | NV10_CONTEXT_SURFACES_2D)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_00, 0x0010114c); /* instance $114c, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_01, (0x80000000 | NV_IMAGE_BLIT)); /* 32bit handle */ + ACCW(HT_VALUE_01, 0x00101148); /* instance $1148, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_02, (0x80000000 | NV4_GDI_RECTANGLE_TEXT)); /* 32bit handle */ + ACCW(HT_VALUE_02, 0x0010114a); /* instance $114a, engine = acc engine, CHID = $00 */ + + /* (second set) */ + ACCW(HT_HANDL_10, (0x80000000 | NV_ROP5_SOLID)); /* 32bit handle */ + ACCW(HT_VALUE_10, 0x00101142); /* instance $1142, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_11, (0x80000000 | NV_IMAGE_BLACK_RECTANGLE)); /* 32bit handle */ + ACCW(HT_VALUE_11, 0x00101144); /* instance $1144, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_12, (0x80000000 | NV_IMAGE_PATTERN)); /* 32bit handle */ + ACCW(HT_VALUE_12, 0x00101146); /* instance $1146, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_13, (0x80000000 | NV_SCALED_IMAGE_FROM_MEMORY)); /* 32bit handle */ + ACCW(HT_VALUE_13, 0x0010114e); /* instance $114e, engine = acc engine, CHID = $00 */ + } + else + { + /* (first set) */ + ACCW(HT_HANDL_00, (0x80000000 | NV4_SURFACE)); /* 32bit handle */ + ACCW(HT_VALUE_00, 0x80011145); /* instance $1145, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_01, (0x80000000 | NV_IMAGE_BLIT)); /* 32bit handle */ + ACCW(HT_VALUE_01, 0x80011146); /* instance $1146, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_02, (0x80000000 | NV4_GDI_RECTANGLE_TEXT)); /* 32bit handle */ + ACCW(HT_VALUE_02, 0x80011147); /* instance $1147, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_03, (0x80000000 | NV4_CONTEXT_SURFACES_ARGB_ZS)); /* 32bit handle (3D) */ + ACCW(HT_VALUE_03, 0x80011148); /* instance $1148, engine = acc engine, CHID = $00 */ + + /* NV4_ and NV10_DX5_TEXTURE_TRIANGLE should be identical */ + ACCW(HT_HANDL_04, (0x80000000 | NV4_DX5_TEXTURE_TRIANGLE)); /* 32bit handle (3D) */ + ACCW(HT_VALUE_04, 0x80011149); /* instance $1149, engine = acc engine, CHID = $00 */ + + /* NV4_ and NV10_DX6_MULTI_TEXTURE_TRIANGLE should be identical */ + ACCW(HT_HANDL_05, (0x80000000 | NV4_DX6_MULTI_TEXTURE_TRIANGLE)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_05, 0x8001114a); /* instance $114a, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_06, (0x80000000 | NV1_RENDER_SOLID_LIN)); /* 32bit handle (not used) */ + ACCW(HT_VALUE_06, 0x8001114c); /* instance $114c, engine = acc engine, CHID = $00 */ + + /* (second set) */ + ACCW(HT_HANDL_10, (0x80000000 | NV_ROP5_SOLID)); /* 32bit handle */ + ACCW(HT_VALUE_10, 0x80011142); /* instance $1142, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_11, (0x80000000 | NV_IMAGE_BLACK_RECTANGLE)); /* 32bit handle */ + ACCW(HT_VALUE_11, 0x80011143); /* instance $1143, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_12, (0x80000000 | NV_IMAGE_PATTERN)); /* 32bit handle */ + ACCW(HT_VALUE_12, 0x80011144); /* instance $1144, engine = acc engine, CHID = $00 */ + + ACCW(HT_HANDL_13, (0x80000000 | NV_SCALED_IMAGE_FROM_MEMORY)); /* 32bit handle */ + ACCW(HT_VALUE_13, 0x8001114b); /* instance $114b, engine = acc engine, CHID = $00 */ + + //2007 3D tests.. + if (si->ps.card_type == NV15) + { + ACCW(HT_HANDL_14, (0x80000000 | NV_TCL_PRIMITIVE_3D)); /* 32bit handle */ + ACCW(HT_VALUE_14, 0x8001114d); /* instance $114d, engine = acc engine, CHID = $00 */ + } + + } + + /* program CTX registers: CTX1 is mostly done later (colorspace dependant) */ + /* note: + * CTX determines which HT handles point to what engine commands. */ + /* note also: + * CTX registers are in fact in the same GPU internal RAM space as the engine's + * hashtable. This means that stuff programmed in here also survives resets and + * power-outages! (confirmed NV11) */ + if (si->ps.card_arch >= NV40A) + { + /* setup a DMA define for use by command defines below. */ + ACCW(PR_CTX0_R, 0x00003000); /* DMA page table present and of linear type; + * DMA target node is NVM (non-volatile memory?) + * (instead of doing PCI or AGP transfers) */ + ACCW(PR_CTX1_R, (si->ps.memory_size - 1)); /* DMA limit: size is all cardRAM */ + ACCW(PR_CTX2_R, ((0x00000000 & 0xfffff000) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * memory starts at start of cardRAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + ACCW(PR_CTX3_R, 0x00000002); /* unknown (looks like this is rubbish/not needed?) */ + /* setup set '0' for cmd NV_ROP5_SOLID */ + ACCW(PR_CTX0_0, 0x02080043); /* NVclass $043, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_0, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_0, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_0, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_1, 0x00000000); /* extra */ + ACCW(PR_CTX1_1, 0x00000000); /* extra */ + /* setup set '1' for cmd NV_IMAGE_BLACK_RECTANGLE */ + ACCW(PR_CTX0_2, 0x02080019); /* NVclass $019, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_2, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_2, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_2, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_3, 0x00000000); /* extra */ + ACCW(PR_CTX1_3, 0x00000000); /* extra */ + /* setup set '2' for cmd NV_IMAGE_PATTERN */ + ACCW(PR_CTX0_4, 0x02080018); /* NVclass $018, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_4, 0x02000000); /* colorspace not set, notify instance is $0200 (b16-31) */ + ACCW(PR_CTX2_4, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_4, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_5, 0x00000000); /* extra */ + ACCW(PR_CTX1_5, 0x00000000); /* extra */ + /* setup set '4' for cmd NV12_IMAGE_BLIT */ + ACCW(PR_CTX0_6, 0x0208009f); /* NVclass $09f, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_6, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_6, 0x00001140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_6, 0x00001140); /* method trap 0 is $1140, trap 1 disabled */ + ACCW(PR_CTX0_7, 0x00000000); /* extra */ + ACCW(PR_CTX1_7, 0x00000000); /* extra */ + /* setup set '5' for cmd NV4_GDI_RECTANGLE_TEXT */ + ACCW(PR_CTX0_8, 0x0208004a); /* NVclass $04a, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_8, 0x02000000); /* colorspace not set, notify instance is $0200 (b16-31) */ + ACCW(PR_CTX2_8, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_8, 0x00000000); /* method traps disabled */ + ACCW(PR_CTX0_9, 0x00000000); /* extra */ + ACCW(PR_CTX1_9, 0x00000000); /* extra */ + /* setup set '6' for cmd NV10_CONTEXT_SURFACES_2D */ + ACCW(PR_CTX0_A, 0x02080062); /* NVclass $062, nv10+: little endian */ + ACCW(PR_CTX1_A, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_A, 0x00001140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_A, 0x00001140); /* method trap 0 is $1140, trap 1 disabled */ + ACCW(PR_CTX0_B, 0x00000000); /* extra */ + ACCW(PR_CTX1_B, 0x00000000); /* extra */ + /* setup set '7' for cmd NV_SCALED_IMAGE_FROM_MEMORY */ + ACCW(PR_CTX0_C, 0x02080077); /* NVclass $077, nv10+: little endian */ + ACCW(PR_CTX1_C, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_C, 0x00001140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_C, 0x00001140); /* method trap 0 is $1140, trap 1 disabled */ + ACCW(PR_CTX0_D, 0x00000000); /* extra */ + ACCW(PR_CTX1_D, 0x00000000); /* extra */ + /* setup DMA set pointed at by PF_CACH1_DMAI */ + ACCW(PR_CTX0_E, 0x00003002); /* DMA page table present and of linear type; + * DMA class is $002 (b0-11); + * DMA target node is NVM (non-volatile memory?) + * (instead of doing PCI or AGP transfers) */ + ACCW(PR_CTX1_E, 0x00007fff); /* DMA limit: tablesize is 32k bytes */ + ACCW(PR_CTX2_E, (((si->ps.memory_size - 1) & 0xffff8000) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * table is located at end of cardRAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + } + else + { + /* setup a DMA define for use by command defines below. */ + ACCW(PR_CTX0_R, 0x00003000); /* DMA page table present and of linear type; + * DMA target node is NVM (non-volatile memory?) + * (instead of doing PCI or AGP transfers) */ + ACCW(PR_CTX1_R, (si->ps.memory_size - 1)); /* DMA limit: size is all cardRAM */ + ACCW(PR_CTX2_R, ((0x00000000 & 0xfffff000) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * memory starts at start of cardRAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + ACCW(PR_CTX3_R, 0x00000002); /* unknown (looks like this is rubbish/not needed?) */ + /* setup set '0' for cmd NV_ROP5_SOLID */ + ACCW(PR_CTX0_0, 0x01008043); /* NVclass $043, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_0, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_0, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_0, 0x00000000); /* method traps disabled */ + /* setup set '1' for cmd NV_IMAGE_BLACK_RECTANGLE */ + ACCW(PR_CTX0_1, 0x01008019); /* NVclass $019, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_1, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_1, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_1, 0x00000000); /* method traps disabled */ + /* setup set '2' for cmd NV_IMAGE_PATTERN */ + ACCW(PR_CTX0_2, 0x01008018); /* NVclass $018, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_2, 0x00000002); /* colorspace not set, notify instance is $0200 (b16-31) */ + ACCW(PR_CTX2_2, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_2, 0x00000000); /* method traps disabled */ + /* setup set '3' for ... */ + if(si->ps.card_arch >= NV10A) + { + /* ... cmd NV10_CONTEXT_SURFACES_2D */ + ACCW(PR_CTX0_3, 0x01008062); /* NVclass $062, nv10+: little endian */ + } + else + { + /* ... cmd NV4_SURFACE */ + ACCW(PR_CTX0_3, 0x01008042); /* NVclass $042, nv10+: little endian */ + } + ACCW(PR_CTX1_3, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_3, 0x11401140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_3, 0x00000000); /* method trap 0 is $1140, trap 1 disabled */ + /* setup set '4' for ... */ + if (si->ps.card_type >= NV11) + { + /* ... cmd NV12_IMAGE_BLIT */ + ACCW(PR_CTX0_4, 0x0100809f); /* NVclass $09f, patchcfg ROP_AND, nv10+: little endian */ + } + else + { + /* ... cmd NV_IMAGE_BLIT */ + ACCW(PR_CTX0_4, 0x0100805f); /* NVclass $05f, patchcfg ROP_AND, nv10+: little endian */ + } + ACCW(PR_CTX1_4, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_4, 0x11401140); /* DMA0 instance is $1140, DMA1 instance invalid */ + ACCW(PR_CTX3_4, 0x00000000); /* method trap 0 is $1140, trap 1 disabled */ + /* setup set '5' for cmd NV4_GDI_RECTANGLE_TEXT */ + ACCW(PR_CTX0_5, 0x0100804a); /* NVclass $04a, patchcfg ROP_AND, nv10+: little endian */ + ACCW(PR_CTX1_5, 0x00000002); /* colorspace not set, notify instance is $0200 (b16-31) */ + ACCW(PR_CTX2_5, 0x00000000); /* DMA0 and DMA1 instance invalid */ + ACCW(PR_CTX3_5, 0x00000000); /* method traps disabled */ + /* setup set '6' ... */ + if (si->ps.card_arch >= NV10A) + { + /* ... for cmd NV10_CONTEXT_SURFACES_ARGB_ZS */ + ACCW(PR_CTX0_6, 0x00000093); /* NVclass $093, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_CONTEXT_SURFACES_ARGB_ZS */ + ACCW(PR_CTX0_6, 0x00000053); /* NVclass $053, nv10+: little endian */ + } + ACCW(PR_CTX1_6, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_6, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_6, 0x00000000); /* method traps disabled */ + /* setup set '7' ... */ + if (si->ps.card_arch >= NV10A) + { + /* ... for cmd NV10_DX5_TEXTURE_TRIANGLE */ + ACCW(PR_CTX0_7, 0x0300a094); /* NVclass $094, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_DX5_TEXTURE_TRIANGLE */ + ACCW(PR_CTX0_7, 0x0300a054); /* NVclass $054, patchcfg ROP_AND, userclip enable, + * context surface0 valid */ + } + ACCW(PR_CTX1_7, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_7, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_7, 0x00000000); /* method traps disabled */ + /* setup set '8' ... */ + if (si->ps.card_arch >= NV10A) + { + /* ... for cmd NV10_DX6_MULTI_TEXTURE_TRIANGLE (not used) */ + ACCW(PR_CTX0_8, 0x0300a095); /* NVclass $095, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + } + else + { + /* ... for cmd NV4_DX6_MULTI_TEXTURE_TRIANGLE (not used) */ + ACCW(PR_CTX0_8, 0x0300a055); /* NVclass $055, patchcfg ROP_AND, userclip enable, + * context surface0 valid */ + } + ACCW(PR_CTX1_8, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_8, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_8, 0x00000000); /* method traps disabled */ + /* setup set '9' for cmd NV_SCALED_IMAGE_FROM_MEMORY */ + ACCW(PR_CTX0_9, 0x01018077); /* NVclass $077, patchcfg SRC_COPY, + * context surface0 valid, nv10+: little endian */ + ACCW(PR_CTX1_9, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_9, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_9, 0x00000000); /* method traps disabled */ + /* setup set 'A' for cmd NV1_RENDER_SOLID_LIN (not used) */ + ACCW(PR_CTX0_A, 0x0300a01c); /* NVclass $01c, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + ACCW(PR_CTX1_A, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_A, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_A, 0x00000000); /* method traps disabled */ + //2007 3D tests.. + /* setup set 'B' ... */ + if (si->ps.card_type == NV15) + { + /* ... for cmd NV11_TCL_PRIMITIVE_3D */ + ACCW(PR_CTX0_B, 0x0300a096); /* NVclass $096, patchcfg ROP_AND, userclip enable, + * context surface0 valid, nv10+: little endian */ + ACCW(PR_CTX1_B, 0x00000000); /* colorspace not set, notify instance invalid (b16-31) */ + ACCW(PR_CTX2_B, 0x11401140); /* DMA0, DMA1 instance = $1140 */ + ACCW(PR_CTX3_B, 0x00000000); /* method traps disabled */ + } + /* setup DMA set pointed at by PF_CACH1_DMAI */ + if (0)//si->engine.agp_mode) + { + /* DMA page table present and of linear type; + * DMA class is $002 (b0-11); + * DMA target node is AGP */ + ACCW(PR_CTX0_C, 0x00033002); + } + else + { + /* DMA page table present and of linear type; + * DMA class is $002 (b0-11); + * DMA target node is PCI */ + ACCW(PR_CTX0_C, 0x00023002); + } + ACCW(PR_CTX1_C, 0x000fffff); /* DMA limit: tablesize is 1M bytes */ + ACCW(PR_CTX2_C, (((uint32)((uint8 *)(si->dma_buffer_pci))) | 0x00000002)); + /* DMA access type is READ_AND_WRITE; + * table is located in main system RAM (b12-31): + * It's adress needs to be at a 4kb boundary! */ + + /* set the 3D rendering functions colordepth via BPIXEL's 'depth 2' */ + /* note: + * setting a depth to 'invalid' (zero) makes the engine report + * ready with drawing 'immediately'. */ + //fixme: NV30A and above (probably) needs to be corrected... + switch(si->dm.space) + { + case B_CMAP8: + if (si->ps.card_arch < NV30A) + /* set depth 2: $1 = Y8 */ + ACCW(BPIXEL, 0x00000100); + else + /* set depth 0-1: $1 = Y8, $2 = X1R5G5B5_Z1R5G5B5 */ + ACCW(BPIXEL, 0x00000021); + break; + case B_RGB15_LITTLE: + if (si->ps.card_arch < NV30A) + /* set depth 2: $4 = A1R5G5B5 */ + ACCW(BPIXEL, 0x00000400); + else + /* set depth 0-1: $2 = X1R5G5B5_Z1R5G5B5, $4 = A1R5G5B5 */ + ACCW(BPIXEL, 0x00000042); + break; + case B_RGB16_LITTLE: + if (si->ps.card_arch < NV30A) + /* set depth 2: $5 = R5G6B5 */ + ACCW(BPIXEL, 0x00000500); + else + /* set depth 0-1: $5 = R5G6B5, $a = X1A7R8G8B8_O1A7R8G8B8 */ + ACCW(BPIXEL, 0x000000a5); + break; + case B_RGB32_LITTLE: + case B_RGBA32_LITTLE: + if (si->ps.card_arch < NV30A) + /* set depth 2: $c = A8R8G8B8 */ + ACCW(BPIXEL, 0x00000c00); + else + /* set depth 0-1: $7 = X8R8G8B8_Z8R8G8B8, $e = V8YB8U8YA8 */ + ACCW(BPIXEL, 0x000000e7); + break; + default: + LOG(8,("ACC: init, invalid bit depth\n")); + return B_ERROR; + } + } + + if (si->ps.card_arch == NV04A) + { + /* do a explicit engine reset */ + ACCW(DEBUG0, 0x000001ff); + + /* init some function blocks */ + /* DEBUG0, b20 and b21 should be high, this has a big influence on + * 3D rendering speed! (on all cards, confirmed) */ + ACCW(DEBUG0, 0x1230c000); + /* DEBUG1, b19 = 1 increases 3D rendering speed on TNT2 (M64) a bit, + * TNT1 rendering speed stays the same (all cards confirmed) */ + ACCW(DEBUG1, 0x72191101); + ACCW(DEBUG2, 0x11d5f071); + ACCW(DEBUG3, 0x0004ff31); + /* init OP methods */ + ACCW(DEBUG3, 0x4004ff31); + + /* disable all acceleration engine INT reguests */ + ACCW(ACC_INTE, 0x00000000); + /* reset all acceration engine INT status bits */ + ACCW(ACC_INTS, 0xffffffff); + /* context control enabled */ + ACCW(NV04_CTX_CTRL, 0x10010100); + /* all acceleration buffers, pitches and colors are valid */ + ACCW(NV04_ACC_STAT, 0xffffffff); + /* enable acceleration engine command FIFO */ + ACCW(FIFO_EN, 0x00000001); + + /* setup location of active screen in framebuffer */ + ACCW(OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* setup accesible card memory range */ + ACCW(BLIMIT0, (si->ps.memory_size - 1)); + ACCW(BLIMIT1, (si->ps.memory_size - 1)); + + /* pattern shape value = 8x8, 2 color */ + //fixme: not needed, unless the engine has a hardware fault (setting via cmd)! + //ACCW(PAT_SHP, 0x00000000); + /* Pgraph Beta AND value (fraction) b23-30 */ + ACCW(BETA_AND_VAL, 0xffffffff); + } + else + { + /* do a explicit engine reset */ + ACCW(DEBUG0, 0xffffffff); + ACCW(DEBUG0, 0x00000000); + /* disable all acceleration engine INT reguests */ + ACCW(ACC_INTE, 0x00000000); + /* reset all acceration engine INT status bits */ + ACCW(ACC_INTS, 0xffffffff); + /* context control enabled */ + ACCW(NV10_CTX_CTRL, 0x10010100); + /* all acceleration buffers, pitches and colors are valid */ + ACCW(NV10_ACC_STAT, 0xffffffff); + /* enable acceleration engine command FIFO */ + ACCW(FIFO_EN, 0x00000001); + /* setup surface type: + * b1-0 = %01 = surface type is non-swizzle; + * this is needed to enable 3D on NV1x (confirmed) and maybe others? */ + ACCW(NV10_SURF_TYP, ((ACCR(NV10_SURF_TYP)) & 0x0007ff00)); + ACCW(NV10_SURF_TYP, ((ACCR(NV10_SURF_TYP)) | 0x00020101)); + } + + if (si->ps.card_arch == NV10A) + { + /* init some function blocks */ + ACCW(DEBUG1, 0x00118700); + /* DEBUG2 has a big influence on 3D speed for NV11 and NV15 + * (confirmed b3 and b18 should both be '1' on both cards!) + * (b16 should also be '1', increases 3D speed on NV11 a bit more) */ + ACCW(DEBUG2, 0x24fd2ad9); + ACCW(DEBUG3, 0x55de0030); + /* NV10_DEBUG4 has a big influence on 3D speed for NV11, NV15 and NV18 + * (confirmed b14 and b15 should both be '1' on these cards!) + * (confirmed b8 should be '0' on NV18 to prevent complete engine crash!) */ + ACCW(NV10_DEBUG4, 0x0000c000); + + /* copy tile setup stuff from 'source' to acc engine */ + for (cnt = 0; cnt < 32; cnt++) + { + NV_REG32(NVACC_NV10_TIL0AD + (cnt << 2)) = + NV_REG32(NVACC_NV10_FBTIL0AD + (cnt << 2)); + } + + /* setup location of active screen in framebuffer */ + ACCW(OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* setup accesible card memory range */ + ACCW(BLIMIT0, (si->ps.memory_size - 1)); + ACCW(BLIMIT1, (si->ps.memory_size - 1)); + + /* pattern shape value = 8x8, 2 color */ + //fixme: not needed, unless the engine has a hardware fault (setting via cmd)! + //ACCW(PAT_SHP, 0x00000000); + /* Pgraph Beta AND value (fraction) b23-30 */ + ACCW(BETA_AND_VAL, 0xffffffff); + } + + if (si->ps.card_arch >= NV20A) + { + switch (si->ps.card_arch) + { + case NV40A: + /* init some function blocks */ + ACCW(DEBUG1, 0x401287c0); + ACCW(DEBUG3, 0x60de8051); + /* disable specific functions, but enable SETUP_SPARE2 register */ + ACCW(NV10_DEBUG4, 0x00008000); + /* set limit_viol_pix_adress(?): more likely something unknown.. */ + ACCW(NV25_WHAT0, 0x00be3c5f); + + /* setup some unknown serially accessed registers (?) */ + tmp = (NV_REG32(NV32_NV4X_WHAT0) & 0x000000ff); + for (cnt = 0; (tmp && !(tmp & 0x00000001)); tmp >>= 1, cnt++); + { + ACCW(NV4X_WHAT2, cnt); + } + + /* unknown.. */ + switch (si->ps.card_type) + { + case NV40: + case NV45: + /* and NV48: but these are pgm'd as NV45 currently */ + ACCW(NV40_WHAT0, 0x83280fff); + ACCW(NV40_WHAT1, 0x000000a0); + ACCW(NV40_WHAT2, 0x0078e366); + ACCW(NV40_WHAT3, 0x0000014c); + break; + case NV41: + /* and ID == 0x012x: but no cards defined yet */ + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV40P_WHAT2, 0x007596ff); + ACCW(NV40P_WHAT3, 0x00000108); + break; + case NV43: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV40P_WHAT2, 0x0072cb77); + ACCW(NV40P_WHAT3, 0x00000108); + break; + case NV44: + case G72: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + + NV_REG32(NV32_NV44_WHAT10) = NV_REG32(NV32_NV10STRAPINFO); + NV_REG32(NV32_NV44_WHAT11) = 0x00000000; + NV_REG32(NV32_NV44_WHAT12) = 0x00000000; + NV_REG32(NV32_NV44_WHAT13) = NV_REG32(NV32_NV10STRAPINFO); + + ACCW(NV44_WHAT2, 0x00000000); + ACCW(NV44_WHAT3, 0x00000000); + break; +/* case NV44 type 2: (cardID 0x022x) + //fixme if needed: doesn't seem to need the strapinfo thing.. + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + + ACCW(NV44_WHAT2, 0x00000000); + ACCW(NV44_WHAT3, 0x00000000); + break; +*/ case G70: + case G71: + case G73: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + ACCW(NV40P_WHAT2, 0x07830610); + ACCW(NV40P_WHAT3, 0x0000016a); + break; + default: + ACCW(NV40P_WHAT0, 0x83280eff); + ACCW(NV40P_WHAT1, 0x000000a0); + break; + } + + ACCW(NV10_TIL3PT, 0x2ffff800); + ACCW(NV10_TIL3ST, 0x00006000); + ACCW(NV4X_WHAT1, 0x01000000); + /* engine data source DMA instance = $1140 */ + ACCW(NV4X_DMA_SRC, 0x00001140); + break; + case NV30A: + /* init some function blocks, but most is unknown.. */ + ACCW(DEBUG1, 0x40108700); + ACCW(NV25_WHAT1, 0x00140000); + ACCW(DEBUG3, 0xf00e0431); + ACCW(NV10_DEBUG4, 0x00008000); + ACCW(NV25_WHAT0, 0xf04b1f36); + ACCW(NV20_WHAT3, 0x1002d888); + ACCW(NV25_WHAT2, 0x62ff007f); + break; + case NV20A: + /* init some function blocks, but most is unknown.. */ + ACCW(DEBUG1, 0x00118700); + ACCW(DEBUG3, 0xf20e0431); + ACCW(NV10_DEBUG4, 0x00000000); + ACCW(NV20_WHAT1, 0x00000040); + if (si->ps.card_type < NV25) + { + ACCW(NV20_WHAT2, 0x00080000); + ACCW(NV10_DEBUG5, 0x00000005); + ACCW(NV20_WHAT3, 0x45caa208); + ACCW(NV20_WHAT4, 0x24000000); + ACCW(NV20_WHAT5, 0x00000040); + + /* copy some fixed RAM(?) configuration info(?) to some indexed registers: */ + /* b16-24 is select; b2-13 is adress in 32-bit words */ + ACCW(RDI_INDEX, 0x00e00038); + /* data is 32-bit */ + ACCW(RDI_DATA, 0x00000030); + /* copy some fixed RAM(?) configuration info(?) to some indexed registers: */ + /* b16-24 is select; b2-13 is adress in 32-bit words */ + ACCW(RDI_INDEX, 0x00e10038); + /* data is 32-bit */ + ACCW(RDI_DATA, 0x00000030); + } + else + { + ACCW(NV25_WHAT1, 0x00080000); + ACCW(NV25_WHAT0, 0x304b1fb6); + ACCW(NV20_WHAT3, 0x18b82880); + ACCW(NV20_WHAT4, 0x44000000); + ACCW(NV20_WHAT5, 0x40000080); + ACCW(NV25_WHAT2, 0x000000ff); + } + break; + } + + /* NV20A, NV30A and NV40A: */ + /* copy tile setup stuff from previous setup 'source' to acc engine + * (pattern colorRAM?) */ + if ((si->ps.card_type <= NV40) || (si->ps.card_type == NV45)) + { + for (cnt = 0; cnt < 32; cnt++) + { + /* copy NV10_FBTIL0AD upto/including NV10_FBTIL7ST */ + NV_REG32(NVACC_NV20_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV10_FBTIL0AD + (cnt << 2)); + + /* copy NV10_FBTIL0AD upto/including NV10_FBTIL7ST */ + NV_REG32(NVACC_NV20_2_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV10_FBTIL0AD + (cnt << 2)); + } + } + else + { + /* NV41, 43, 44, G70 and later */ + if (si->ps.card_type >= G70) + { + for (cnt = 0; cnt < 60; cnt++) + { + /* copy NV41_FBTIL0AD upto/including G70_FBTILEST */ + NV_REG32(NVACC_NV41_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV41_FBTIL0AD + (cnt << 2)); + + /* copy NV41_FBTIL0AD upto/including G70_FBTILEST */ + NV_REG32(NVACC_NV20_2_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV41_FBTIL0AD + (cnt << 2)); + } + } + else + { + /* NV41, 43, 44 */ + for (cnt = 0; cnt < 48; cnt++) + { + /* copy NV41_FBTIL0AD upto/including NV41_FBTILBST */ + NV_REG32(NVACC_NV20_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV41_FBTIL0AD + (cnt << 2)); + + if (si->ps.card_type != NV44) + { + /* copy NV41_FBTIL0AD upto/including NV41_FBTILBST */ + NV_REG32(NVACC_NV20_2_WHAT0 + (cnt << 2)) = + NV_REG32(NVACC_NV41_FBTIL0AD + (cnt << 2)); + } + } + } + } + + if (si->ps.card_arch >= NV40A) + { + if ((si->ps.card_type == NV40) || (si->ps.card_type == NV45)) + { + /* copy some RAM configuration info(?) */ + ACCW(NV20_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV20_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + ACCW(NV40_WHAT_T2, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40_WHAT_T3, NV_REG32(NV32_PFB_CONFIG_1)); + + /* setup location of active screen in framebuffer */ + ACCW(NV20_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* setup accesible card memory range */ + ACCW(NV20_BLIMIT6, (si->ps.memory_size - 1)); + ACCW(NV20_BLIMIT7, (si->ps.memory_size - 1)); + } + else + { + /* NV41, 43, 44, G70 and later */ + + /* copy some RAM configuration info(?) */ + if (si->ps.card_type >= G70) + { + ACCW(G70_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(G70_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + } + else + { + /* NV41, 43, 44 */ + ACCW(NV40P_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40P_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + } + ACCW(NV40P_WHAT_T2, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV40P_WHAT_T3, NV_REG32(NV32_PFB_CONFIG_1)); + + /* setup location of active screen in framebuffer */ + ACCW(NV40P_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV40P_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* setup accesible card memory range */ + ACCW(NV40P_BLIMIT6, (si->ps.memory_size - 1)); + ACCW(NV40P_BLIMIT7, (si->ps.memory_size - 1)); + } + } + else /* NV20A and NV30A: */ + { + /* copy some RAM configuration info(?) */ + ACCW(NV20_WHAT_T0, NV_REG32(NV32_PFB_CONFIG_0)); + ACCW(NV20_WHAT_T1, NV_REG32(NV32_PFB_CONFIG_1)); + /* copy some RAM configuration info(?) to some indexed registers: */ + /* b16-24 is select; b2-13 is adress in 32-bit words */ + ACCW(RDI_INDEX, 0x00ea0000); + /* data is 32-bit */ + ACCW(RDI_DATA, NV_REG32(NV32_PFB_CONFIG_0)); + /* b16-24 is select; b2-13 is adress in 32-bit words */ + ACCW(RDI_INDEX, 0x00ea0004); + /* data is 32-bit */ + ACCW(RDI_DATA, NV_REG32(NV32_PFB_CONFIG_1)); + + /* setup location of active screen in framebuffer */ + ACCW(NV20_OFFSET0, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + ACCW(NV20_OFFSET1, ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)); + /* setup accesible card memory range */ + ACCW(NV20_BLIMIT6, (si->ps.memory_size - 1)); + ACCW(NV20_BLIMIT7, (si->ps.memory_size - 1)); + } + + /* NV20A, NV30A and NV40A: */ + /* setup some acc engine tile stuff */ + ACCW(NV10_TIL2AD, 0x00000000); + ACCW(NV10_TIL0ED, 0xffffffff); + } + + /* all cards: */ + /* setup clipping: rect size is 32768 x 32768, probably max. setting */ + /* note: + * can also be done via the NV_IMAGE_BLACK_RECTANGLE engine command. */ + ACCW(ABS_UCLP_XMIN, 0x00000000); + ACCW(ABS_UCLP_YMIN, 0x00000000); + ACCW(ABS_UCLP_XMAX, 0x00007fff); + ACCW(ABS_UCLP_YMAX, 0x00007fff); + + /* setup sync parameters for NV12_IMAGE_BLIT command for the current mode: + * values given are CRTC vertical counter limit values. The NV12 command will wait + * for the specified's CRTC's vertical counter to be in between the given values */ + if (si->ps.card_type >= NV11) + { + ACCW(NV11_CRTC_LO, si->dm.timing.v_display - 1); + ACCW(NV11_CRTC_HI, si->dm.timing.v_display + 1); + } + + /*** PFIFO ***/ + /* (setup caches) */ + /* disable caches reassign */ + ACCW(PF_CACHES, 0x00000000); + /* PFIFO mode: channel 0 is in DMA mode, channels 1 - 32 are in PIO mode */ + ACCW(PF_MODE, 0x00000001); + /* cache1 push0 access disabled */ + ACCW(PF_CACH1_PSH0, 0x00000000); + /* cache1 pull0 access disabled */ + ACCW(PF_CACH1_PUL0, 0x00000000); + /* cache1 push1 mode = DMA */ + if (si->ps.card_arch >= NV40A) + ACCW(PF_CACH1_PSH1, 0x00010000); + else + ACCW(PF_CACH1_PSH1, 0x00000100); + /* cache1 DMA Put offset = 0 (b2-28) */ + ACCW(PF_CACH1_DMAP, 0x00000000); + /* cache1 DMA Get offset = 0 (b2-28) */ + ACCW(PF_CACH1_DMAG, 0x00000000); + /* cache1 DMA instance adress = $114e (b0-15); + * instance being b4-19 with baseadress NV_PRAMIN_CTX_0 (0x00700000). */ + /* note: + * should point to a DMA definition in CTX register space (which is sort of RAM). + * This define tells the engine where the DMA cmd buffer is and what it's size is. + * Inside that cmd buffer you'll find the actual issued engine commands. */ + if (si->ps.card_arch >= NV40A) + ACCW(PF_CACH1_DMAI, 0x00001150); + else + //2007 3d test.. + ACCW(PF_CACH1_DMAI, 0x0000114e); + /* cache0 push0 access disabled */ + ACCW(PF_CACH0_PSH0, 0x00000000); + /* cache0 pull0 access disabled */ + ACCW(PF_CACH0_PUL0, 0x00000000); + /* RAM HT (hash table) baseadress = $10000 (b4-8), size = 4k, + * search = 128 (is byte offset between hash 'sets') */ + /* note: + * so HT base is $00710000, last is $00710fff. + * In this space you define the engine command handles (HT_HANDL_XX), which + * in turn points to the defines in CTX register space (which is sort of RAM) */ + ACCW(PF_RAMHT, 0x03000100); + /* RAM FC baseadress = $11000 (b3-8) (size is fixed to 0.5k(?)) */ + /* note: + * so FC base is $00711000, last is $007111ff. (not used?) */ + ACCW(PF_RAMFC, 0x00000110); + /* RAM RO baseadress = $11200 (b1-8), size = 0.5k */ + /* note: + * so RO base is $00711200, last is $007113ff. (not used?) */ + /* note also: + * This means(?) the PRAMIN CTX registers are accessible from base $00711400. */ + ACCW(PF_RAMRO, 0x00000112); + /* PFIFO size: ch0-15 = 512 bytes, ch16-31 = 124 bytes */ + ACCW(PF_SIZE, 0x0000ffff); + /* cache1 hash instance = $ffff (b0-15) */ + ACCW(PF_CACH1_HASH, 0x0000ffff); + /* disable all PFIFO INTs */ + ACCW(PF_INTEN, 0x00000000); + /* reset all PFIFO INT status bits */ + ACCW(PF_INTSTAT, 0xffffffff); + /* cache0 pull0 engine = acceleration engine (graphics) */ + ACCW(PF_CACH0_PUL1, 0x00000001); + /* cache1 DMA control: disable some stuff */ + ACCW(PF_CACH1_DMAC, 0x00000000); + /* cache1 engine 0 upto/including 7 is software (could also be graphics or DVD) */ + ACCW(PF_CACH1_ENG, 0x00000000); + /* cache1 DMA fetch: trigger at 128 bytes, size is 32 bytes, max requests is 15, + * use little endian */ + ACCW(PF_CACH1_DMAF, 0x000f0078); + /* cache1 DMA push: b0 = 1: access is enabled */ + ACCW(PF_CACH1_DMAS, 0x00000001); + /* cache1 push0 access enabled */ + ACCW(PF_CACH1_PSH0, 0x00000001); + /* cache1 pull0 access enabled */ + ACCW(PF_CACH1_PUL0, 0x00000001); + /* cache1 pull1 engine = acceleration engine (graphics) */ + ACCW(PF_CACH1_PUL1, 0x00000001); + /* enable PFIFO caches reassign */ + ACCW(PF_CACHES, 0x00000001); + + /* setup 3D specifics */ + nv_init_for_3D_dma(); + + /*** init acceleration engine command info ***/ + /* set object handles */ + /* note: + * probably depending on some other setup, there are 8 or 32 FIFO channels + * available. Assuming the current setup only has 8 channels because the 'rest' + * isn't setup here... */ + si->engine.fifo.handle[0] = NV_ROP5_SOLID; + si->engine.fifo.handle[1] = NV_IMAGE_BLACK_RECTANGLE; + si->engine.fifo.handle[2] = NV_IMAGE_PATTERN; + si->engine.fifo.handle[3] = NV4_SURFACE; /* NV10_CONTEXT_SURFACES_2D is identical */ + si->engine.fifo.handle[4] = NV_IMAGE_BLIT; + si->engine.fifo.handle[5] = NV4_GDI_RECTANGLE_TEXT; + si->engine.fifo.handle[6] = NV4_CONTEXT_SURFACES_ARGB_ZS;//NV1_RENDER_SOLID_LIN; + si->engine.fifo.handle[7] = NV4_DX5_TEXTURE_TRIANGLE; + /* preset no FIFO channels assigned to cmd's */ + for (cnt = 0; cnt < 0x20; cnt++) + { + si->engine.fifo.ch_ptr[cnt] = 0; + } + /* set handle's pointers to their assigned FIFO channels */ + /* note: + * b0-1 aren't used as adressbits. Using b0 to indicate a valid pointer. */ + for (cnt = 0; cnt < 0x08; cnt++) + { + si->engine.fifo.ch_ptr[(si->engine.fifo.handle[cnt])] = + (0x00000001 + (cnt * 0x00002000)); + } + + /*** init DMA command buffer info ***/ + if (si->ps.card_arch >= NV40A) //main mem DMA buf on pre-NV40 + { + si->dma_buffer = (void *)((char *)si->framebuffer + + ((si->ps.memory_size - 1) & 0xffff8000)); + } + LOG(4,("ACC_DMA: command buffer is at adress $%08x\n", + ((uint32)(si->dma_buffer)))); + /* we have issued no DMA cmd's to the engine yet */ + si->engine.dma.put = 0; + /* the current first free adress in the DMA buffer is at offset 0 */ + si->engine.dma.current = 0; + /* the DMA buffer can hold 8k 32-bit words (it's 32kb in size), + * or 256k 32-bit words (1Mb in size) dependant on architecture (for now) */ + /* note: + * one word is reserved at the end of the DMA buffer to be able to instruct the + * engine to do a buffer wrap-around! + * (DMA opcode 'noninc method': issue word $20000000.) */ + if (si->ps.card_arch < NV40A) + si->engine.dma.max = ((1 * 1024 * 1024) >> 2) - 1; + else + si->engine.dma.max = 8192 - 1; + /* note the current free space we have left in the DMA buffer */ + si->engine.dma.free = si->engine.dma.max - si->engine.dma.current; + + /*** init FIFO via DMA command buffer. ***/ + /* wait for room in fifo for new FIFO assigment cmds if needed: */ + if (si->ps.card_arch >= NV40A) + { + if (nv_acc_fifofree_dma(12) != B_OK) return B_ERROR; + } + else + { + if (nv_acc_fifofree_dma(16) != B_OK) return B_ERROR; + } + + /* program new FIFO assignments */ + /* Raster OPeration: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH0, si->engine.fifo.handle[0]); + /* Clip: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH1, si->engine.fifo.handle[1]); + /* Pattern: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH2, si->engine.fifo.handle[2]); + /* 2D Surfaces: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH3, si->engine.fifo.handle[3]); + /* Blit: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH4, si->engine.fifo.handle[4]); + /* Bitmap: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH5, si->engine.fifo.handle[5]); + if (si->ps.card_arch < NV40A) + { + /* 3D surfaces: (3D related only) */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH6, si->engine.fifo.handle[6]); + /* Textured Triangle: (3D only) */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH7, si->engine.fifo.handle[7]); + } + + /*** Set pixel width ***/ + switch(si->dm.space) + { + case B_CMAP8: + surf_depth = 0x00000001; + cmd_depth = 0x00000003; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + surf_depth = 0x00000004; + cmd_depth = 0x00000001; + break; + case B_RGB32_LITTLE: + case B_RGBA32_LITTLE: + surf_depth = 0x00000006; + cmd_depth = 0x00000003; + break; + default: + LOG(8,("ACC_DMA: init, invalid bit depth\n")); + return B_ERROR; + } + + /* wait for room in fifo for surface setup cmd if needed */ + if (nv_acc_fifofree_dma(5) != B_OK) return B_ERROR; + /* now setup 2D surface (writing 5 32bit words) */ + nv_acc_cmd_dma(NV4_SURFACE, NV4_SURFACE_FORMAT, 4); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = surf_depth; /* Format */ + /* setup screen pitch */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((si->fbc.bytes_per_row & 0x0000ffff) | (si->fbc.bytes_per_row << 16)); /* Pitch */ + /* setup screen location */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer); /* OffsetSource */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer); /* OffsetDest */ + + /* wait for room in fifo for pattern colordepth setup cmd if needed */ + if (nv_acc_fifofree_dma(2) != B_OK) return B_ERROR; + /* set pattern colordepth (writing 2 32bit words) */ + nv_acc_cmd_dma(NV_IMAGE_PATTERN, NV_IMAGE_PATTERN_SETCOLORFORMAT, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = cmd_depth; /* SetColorFormat */ + + /* wait for room in fifo for bitmap colordepth setup cmd if needed */ + if (nv_acc_fifofree_dma(2) != B_OK) return B_ERROR; + /* set bitmap colordepth (writing 2 32bit words) */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_SETCOLORFORMAT, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = cmd_depth; /* SetColorFormat */ + + /* Load our pattern into the engine: */ + /* wait for room in fifo for pattern cmd if needed. */ + if (nv_acc_fifofree_dma(7) != B_OK) return B_ERROR; + /* now setup pattern (writing 7 32bit words) */ + nv_acc_cmd_dma(NV_IMAGE_PATTERN, NV_IMAGE_PATTERN_SETSHAPE, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000000; /* SetShape: 0 = 8x8, 1 = 64x1, 2 = 1x64 */ + nv_acc_cmd_dma(NV_IMAGE_PATTERN, NV_IMAGE_PATTERN_SETCOLOR0, 4); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xffffffff; /* SetColor0 */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xffffffff; /* SetColor1 */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xffffffff; /* SetPattern[0] */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xffffffff; /* SetPattern[1] */ + + /* tell the engine to fetch and execute all (new) commands in the DMA buffer */ + nv_start_dma(); + + return B_OK; +} + +static void nv_init_for_3D_dma(void) +{ + /* setup PGRAPH unknown registers and modify (pre-cleared) pipe stuff for 3D use */ + if (si->ps.card_arch >= NV10A) + { + /* setup unknown PGRAPH stuff */ + ACCW(PGWHAT_00, 0x00000000); + ACCW(PGWHAT_01, 0x00000000); + ACCW(PGWHAT_02, 0x00000000); + ACCW(PGWHAT_03, 0x00000000); + + ACCW(PGWHAT_04, 0x00001000); + ACCW(PGWHAT_05, 0x00001000); + ACCW(PGWHAT_06, 0x4003ff80); + + ACCW(PGWHAT_07, 0x00000000); + ACCW(PGWHAT_08, 0x00000000); + ACCW(PGWHAT_09, 0x00000000); + ACCW(PGWHAT_0A, 0x00000000); + ACCW(PGWHAT_0B, 0x00000000); + + ACCW(PGWHAT_0C, 0x00080008); + ACCW(PGWHAT_0D, 0x00080008); + + ACCW(PGWHAT_0E, 0x00000000); + ACCW(PGWHAT_0F, 0x00000000); + ACCW(PGWHAT_10, 0x00000000); + ACCW(PGWHAT_11, 0x00000000); + ACCW(PGWHAT_12, 0x00000000); + ACCW(PGWHAT_13, 0x00000000); + ACCW(PGWHAT_14, 0x00000000); + ACCW(PGWHAT_15, 0x00000000); + ACCW(PGWHAT_16, 0x00000000); + ACCW(PGWHAT_17, 0x00000000); + ACCW(PGWHAT_18, 0x00000000); + + ACCW(PGWHAT_19, 0x10000000); + + ACCW(PGWHAT_1A, 0x00000000); + ACCW(PGWHAT_1B, 0x00000000); + ACCW(PGWHAT_1C, 0x00000000); + ACCW(PGWHAT_1D, 0x00000000); + ACCW(PGWHAT_1E, 0x00000000); + ACCW(PGWHAT_1F, 0x00000000); + ACCW(PGWHAT_20, 0x00000000); + ACCW(PGWHAT_21, 0x00000000); + + ACCW(PGWHAT_22, 0x08000000); + + ACCW(PGWHAT_23, 0x00000000); + ACCW(PGWHAT_24, 0x00000000); + ACCW(PGWHAT_25, 0x00000000); + ACCW(PGWHAT_26, 0x00000000); + + ACCW(PGWHAT_27, 0x4b7fffff); + + ACCW(PGWHAT_28, 0x00000000); + ACCW(PGWHAT_29, 0x00000000); + ACCW(PGWHAT_2A, 0x00000000); + + /* setup window clipping */ + /* b0-11 = min; b16-27 = max. + * note: + * probably two's complement values, so setting to max range here: + * which would be -2048 upto/including +2047. */ + /* horizontal */ + ACCW(WINCLIP_H_0, 0x07ff0800); + ACCW(WINCLIP_H_1, 0x07ff0800); + ACCW(WINCLIP_H_2, 0x07ff0800); + ACCW(WINCLIP_H_3, 0x07ff0800); + ACCW(WINCLIP_H_4, 0x07ff0800); + ACCW(WINCLIP_H_5, 0x07ff0800); + ACCW(WINCLIP_H_6, 0x07ff0800); + ACCW(WINCLIP_H_7, 0x07ff0800); + /* vertical */ + ACCW(WINCLIP_V_0, 0x07ff0800); + ACCW(WINCLIP_V_1, 0x07ff0800); + ACCW(WINCLIP_V_2, 0x07ff0800); + ACCW(WINCLIP_V_3, 0x07ff0800); + ACCW(WINCLIP_V_4, 0x07ff0800); + ACCW(WINCLIP_V_5, 0x07ff0800); + ACCW(WINCLIP_V_6, 0x07ff0800); + ACCW(WINCLIP_V_7, 0x07ff0800); + + /* setup (initialize) pipe: + * needed to get valid 3D rendering on (at least) NV1x cards. Without this + * those cards produce rubbish instead of 3D, although the engine itself keeps + * running and 2D stays OK. */ + + /* set eyetype to local, lightning etc. is off */ + ACCW(NV10_XFMOD0, 0x10000000); + /* disable all lights */ + ACCW(NV10_XFMOD1, 0x00000000); + + /* note: upon writing data into the PIPEDAT register, the PIPEADR is + * probably auto-incremented! */ + /* (pipe adress = b2-16, pipe data = b0-31) */ + /* note: pipe adresses IGRAPH registers! */ + ACCW(NV10_PIPEADR, 0x00006740); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006750); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + ACCW(NV10_PIPEDAT, 0x40000000); + + ACCW(NV10_PIPEADR, 0x00006760); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006770); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006780); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000067a0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006ab0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006ac0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006c10); + ACCW(NV10_PIPEDAT, 0xbf800000); + + ACCW(NV10_PIPEADR, 0x00007030); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007040); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007050); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007060); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007070); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007080); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00007090); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x000070a0); + ACCW(NV10_PIPEDAT, 0x7149f2ca); + + ACCW(NV10_PIPEADR, 0x00006a80); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x00006aa0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + /* select primitive type that will be drawn (tri's) */ + ACCW(NV10_PIPEADR, 0x00000040); + ACCW(NV10_PIPEDAT, 0x00000005); + + ACCW(NV10_PIPEADR, 0x00006400); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x4b7fffff); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006410); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006420); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x00006430); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064c0); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x3f800000); + ACCW(NV10_PIPEDAT, 0x477fffff); + ACCW(NV10_PIPEDAT, 0x3f800000); + + ACCW(NV10_PIPEADR, 0x000064d0); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0xc5000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064e0); + ACCW(NV10_PIPEDAT, 0xc4fff000); + ACCW(NV10_PIPEDAT, 0xc4fff000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + ACCW(NV10_PIPEADR, 0x000064f0); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + ACCW(NV10_PIPEDAT, 0x00000000); + + /* turn lightning on */ + ACCW(NV10_XFMOD0, 0x30000000); + /* set light 1 to infinite type, other lights remain off */ + ACCW(NV10_XFMOD1, 0x00000004); + + /* Z-buffer state is: + * initialized, set to: 'fixed point' (integer?); Z-buffer; 16bits depth */ + /* note: + * other options possible are: floating point; 24bits depth; W-buffer */ + ACCW(GLOB_STAT_0, 0x10000000); + /* set DMA instance 2 and 3 to be invalid */ + ACCW(GLOB_STAT_1, 0x00000000); + } +} + +static void nv_start_dma(void) +{ + uint32 dummy; + + if (si->engine.dma.current != si->engine.dma.put) + { + si->engine.dma.put = si->engine.dma.current; + /* flush used caches so we know for sure the DMA cmd buffer received all data. */ + if (si->ps.card_arch < NV40A) + { + /* some CPU's support out-of-order processing (WinChip/Cyrix). Flush them. */ + __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory"); + /* read a non-cached adress to flush the cash */ + dummy = ACCR(STATUS); + } + else + { + /* dummy read the first adress of the framebuffer to flush MTRR-WC buffers */ + dummy = *((volatile uint32 *)(si->framebuffer)); + } + + /* actually start DMA to execute all commands now in buffer */ + /* note: + * it doesn't matter which FIFO channel's DMA registers we access, they are in + * fact all the same set. It also doesn't matter if the channel was assigned a + * command or not. */ + /* note also: + * NV_GENERAL_DMAPUT is a write-only register on some cards (confirmed NV11). */ + NV_REG32(NVACC_FIFO + NV_GENERAL_DMAPUT) = (si->engine.dma.put << 2); + } +} + +/* this routine does not check the engine's internal hardware FIFO, but the DMA + * command buffer. You can see this as a FIFO as well, that feeds the hardware FIFO. + * The hardware FIFO state is checked by the DMA hardware automatically. */ +static status_t nv_acc_fifofree_dma(uint16 cmd_size) +{ + uint32 dmaget; + + /* we'd better check for timeouts on the DMA engine as it's theoretically + * breakable by malfunctioning software */ + uint16 cnt = 0; + + /* check if the DMA buffer has enough room for the command. + * note: + * engine.dma.free is 'cached' */ + while ((si->engine.dma.free < cmd_size) && (cnt < 10000) && (err < 3)) + { + /* see where the engine is currently fetching from the buffer */ + /* note: + * read this only once in the code as accessing registers is relatively slow */ + /* note also: + * it doesn't matter which FIFO channel's DMA registers we access, they are in + * fact all the same set. It also doesn't matter if the channel was assigned a + * command or not. */ + dmaget = ((NV_REG32(NVACC_FIFO + NV_GENERAL_DMAGET)) >> 2); + + /* update timeout counter: on NV11 on a Pentium4 2.8Ghz max reached count + * using BeRoMeter 1.2.6 was about 600; so counting 10000 before generating + * a timeout should definately do it. Snooze()-ing cannot be done without a + * serious speed penalty, even if done for only 1 microSecond. */ + cnt++; + + /* where's the engine fetching viewed from us issuing? */ + if (si->engine.dma.put >= dmaget) + { + /* engine is fetching 'behind us', the last piece of the buffer is free */ + + /* note the 'updated' free space we have in the DMA buffer */ + si->engine.dma.free = si->engine.dma.max - si->engine.dma.current; + /* if it's enough after all we exit this routine immediately. Else: */ + if (si->engine.dma.free < cmd_size) + { + /* not enough room left, so instruct DMA engine to reset the buffer + * when it's reaching the end of it */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x20000000; + /* reset our buffer pointer, so new commands will be placed at the + * beginning of the buffer. */ + si->engine.dma.current = 0; + /* tell the engine to fetch the remaining command(s) in the DMA buffer + * that where not executed before. */ + nv_start_dma(); + + /* NOW the engine is fetching 'in front of us', so the first piece + * of the buffer is free */ + + /* note the updated current free space we have in the DMA buffer */ + si->engine.dma.free = dmaget - si->engine.dma.current; + /* mind this pittfall: + * Leave some room between where the engine is fetching and where we + * put new commands. Otherwise the engine will crash on heavy loads. + * A crash can be forced best in 640x480x32 mode with BeRoMeter 1.2.6. + * (confirmed on NV11 and NV43 with less than 256 words forced freespace.) + * Note: + * The engine is DMA triggered for fetching chunks every 128 bytes, + * maybe this is the reason for this behaviour. + * Note also: + * it looks like the space that needs to be kept free is coupled + * with the size of the DMA buffer. */ + if (si->engine.dma.free < 256) + si->engine.dma.free = 0; + else + si->engine.dma.free -= 256; + } + } + else + { + /* engine is fetching 'in front of us', so the first piece of the buffer + * is free */ + + /* note the updated current free space we have in the DMA buffer */ + si->engine.dma.free = dmaget - si->engine.dma.current; + /* mind this pittfall: + * Leave some room between where the engine is fetching and where we + * put new commands. Otherwise the engine will crash on heavy loads. + * A crash can be forced best in 640x480x32 mode with BeRoMeter 1.2.6. + * (confirmed on NV11 and NV43 with less than 256 words forced freespace.) + * Note: + * The engine is DMA triggered for fetching chunks every 128 bytes, + * maybe this is the reason for this behaviour. + * Note also: + * it looks like the space that needs to be kept free is coupled + * with the size of the DMA buffer. */ + if (si->engine.dma.free < 256) + si->engine.dma.free = 0; + else + si->engine.dma.free -= 256; + } + } + + /* log timeout if we had one */ + if (cnt == 10000) + { + if (err < 3) err++; + LOG(4,("ACC_DMA: fifofree; DMA timeout #%d, engine trouble!\n", err)); + } + + /* we must make the acceleration routines abort or the driver will hang! */ + if (err >= 3) return B_ERROR; + + return B_OK; +} + +static void nv_acc_cmd_dma(uint32 cmd, uint16 offset, uint16 size) +{ + /* NV_FIFO_DMA_OPCODE: set number of cmd words (b18 - 28); set FIFO offset for + * first cmd word (b2 - 15); set DMA opcode = method (b29 - 31). + * a 'NOP' is the opcode word $00000000. */ + /* note: + * possible DMA opcodes: + * b'000' is 'method' (execute cmd); + * b'001' is 'jump'; + * b'002' is 'noninc method' (execute buffer wrap-around); + * b'003' is 'call': return is executed by opcode word $00020000 (b17 = 1). */ + /* note also: + * this system uses auto-increments for the FIFO offset adresses. Make sure + * to set a new adress if a gap exists between the previous one and the new one. */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = ((size << 18) | + ((si->engine.fifo.ch_ptr[cmd] + offset) & 0x0000fffc)); + + /* space left after issuing the current command is the cmd AND it's arguments less */ + si->engine.dma.free -= (size + 1); +} + +static void nv_acc_set_ch_dma(uint16 ch, uint32 handle) +{ + /* issue FIFO channel assign cmd */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = ((1 << 18) | ch); + /* set new assignment */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = (0x80000000 | handle); + + /* space left after issuing the current command is the cmd AND it's arguments less */ + si->engine.dma.free -= 2; +} + +/* note: + * switching fifo channel assignments this way has no noticable slowdown: + * measured 0.2% with Quake2. */ +void nv_acc_assert_fifo_dma(void) +{ + /* does every engine cmd this accelerant needs have a FIFO channel? */ + //fixme: can probably be optimized for both speed and channel selection... + if (!si->engine.fifo.ch_ptr[NV_ROP5_SOLID] || + !si->engine.fifo.ch_ptr[NV_IMAGE_BLACK_RECTANGLE] || + !si->engine.fifo.ch_ptr[NV_IMAGE_PATTERN] || + !si->engine.fifo.ch_ptr[NV4_SURFACE] || + !si->engine.fifo.ch_ptr[NV_IMAGE_BLIT] || + !si->engine.fifo.ch_ptr[NV4_GDI_RECTANGLE_TEXT] || + !si->engine.fifo.ch_ptr[NV_SCALED_IMAGE_FROM_MEMORY]) + { + uint16 cnt; + + /* free the FIFO channels we want from the currently assigned cmd's */ + si->engine.fifo.ch_ptr[si->engine.fifo.handle[0]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[1]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[2]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[3]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[4]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[5]] = 0; + si->engine.fifo.ch_ptr[si->engine.fifo.handle[6]] = 0; + + /* set new object handles */ + si->engine.fifo.handle[0] = NV_ROP5_SOLID; + si->engine.fifo.handle[1] = NV_IMAGE_BLACK_RECTANGLE; + si->engine.fifo.handle[2] = NV_IMAGE_PATTERN; + si->engine.fifo.handle[3] = NV4_SURFACE; + si->engine.fifo.handle[4] = NV_IMAGE_BLIT; + si->engine.fifo.handle[5] = NV4_GDI_RECTANGLE_TEXT; + si->engine.fifo.handle[6] = NV_SCALED_IMAGE_FROM_MEMORY; + + /* set handle's pointers to their assigned FIFO channels */ + /* note: + * b0-1 aren't used as adressbits. Using b0 to indicate a valid pointer. */ + for (cnt = 0; cnt < 0x08; cnt++) + { + si->engine.fifo.ch_ptr[(si->engine.fifo.handle[cnt])] = + (0x00000001 + (cnt * 0x00002000)); + } + + /* wait for room in fifo for new FIFO assigment cmds if needed. */ + if (nv_acc_fifofree_dma(14) != B_OK) return; + + /* program new FIFO assignments */ + /* Raster OPeration: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH0, si->engine.fifo.handle[0]); + /* Clip: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH1, si->engine.fifo.handle[1]); + /* Pattern: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH2, si->engine.fifo.handle[2]); + /* 2D Surface: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH3, si->engine.fifo.handle[3]); + /* Blit: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH4, si->engine.fifo.handle[4]); + /* Bitmap: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH5, si->engine.fifo.handle[5]); + /* Scaled and fitered Blit: */ + nv_acc_set_ch_dma(NV_GENERAL_FIFO_CH6, si->engine.fifo.handle[6]); + + /* tell the engine to fetch and execute all (new) commands in the DMA buffer */ + nv_start_dma(); + } +} + +/* + note: + moved acceleration 'top-level' routines to be integrated in the engine: + it is costly to call the engine for every single function within a loop! + (measured with BeRoMeter 1.2.6: upto 15% speed increase on all CPU's.) + + note also: + splitting up each command list into sublists (see routines below) prevents + a lot more nested calls, further increasing the speed with upto 70%. + + finally: + sending the sublist to just one single engine command even further increases + speed with upto another 10%. This can't be done for blits though, as this engine- + command's hardware does not support multiple objects. +*/ + +/* screen to screen blit - i.e. move windows around and scroll within them. */ +void SCREEN_TO_SCREEN_BLIT_DMA(engine_token *et, blit_params *list, uint32 count) +{ + uint32 i = 0; + uint16 subcnt; + + /*** init acc engine for blit function ***/ + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP cmd if needed. */ + if (nv_acc_fifofree_dma(2) != B_OK) return; + /* now setup ROP (writing 2 32bit words) for GXcopy */ + nv_acc_cmd_dma(NV_ROP5_SOLID, NV_ROP5_SOLID_SETROP5, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xcc; /* SetRop5 */ + + /*** do each blit ***/ + /* Note: + * blit-copy direction is determined inside nvidia hardware: no setup needed */ + while (count) + { + /* break up the list in sublists to minimize calls, while making sure long + * lists still get executed without trouble */ + subcnt = 32; + if (count < 32) subcnt = count; + count -= subcnt; + + /* wait for room in fifo for blit cmd if needed. */ + if (nv_acc_fifofree_dma(4 * subcnt) != B_OK) return; + + while (subcnt--) + { + /* now setup blit (writing 4 32bit words) */ + nv_acc_cmd_dma(NV_IMAGE_BLIT, NV_IMAGE_BLIT_SOURCEORG, 3); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].src_top) << 16) | (list[i].src_left)); /* SourceOrg */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].dest_top) << 16) | (list[i].dest_left)); /* DestOrg */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((((list[i].height) + 1) << 16) | ((list[i].width) + 1)); /* HeightWidth */ + + i++; + } + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; +} + +/* scaled and filtered screen to screen blit - i.e. video playback without overlay */ +/* note: source and destination may not overlap. */ +//fixme? checkout NV5 and NV10 version of cmd: faster?? (or is 0x77 a 'autoselect' version?) +void SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT_DMA(engine_token *et, scaled_blit_params *list, uint32 count) +{ + uint32 i = 0; + uint16 subcnt; + uint32 cmd_depth; + uint8 bpp; + + /*** init acc engine for scaled filtered blit function ***/ + /* Set pixel width */ + switch(si->dm.space) + { + case B_RGB15_LITTLE: + cmd_depth = 0x00000002; + bpp = 2; + break; + case B_RGB16_LITTLE: + cmd_depth = 0x00000007; + bpp = 2; + break; + case B_RGB32_LITTLE: + case B_RGBA32_LITTLE: + cmd_depth = 0x00000004; + bpp = 4; + break; + /* fixme sometime: + * we could do the spaces below if this function would be modified to be able + * to use a source outside of the desktop, i.e. using offscreen bitmaps... */ + case B_YCbCr422: + cmd_depth = 0x00000005; + bpp = 2; + break; + case B_YUV422: + cmd_depth = 0x00000006; + bpp = 2; + break; + default: + /* note: this function does not support src or dest in the B_CMAP8 space! */ + //fixme: the NV10 version of this cmd supports B_CMAP8 src though... (checkout) + LOG(8,("ACC_DMA: scaled_filtered_blit, invalid bit depth\n")); + return; + } + + /* modify surface depth settings for 15-bit colorspace so command works as intended */ + if (si->dm.space == B_RGB15_LITTLE) + { + /* wait for room in fifo for surface setup cmd if needed */ + if (nv_acc_fifofree_dma(2) != B_OK) return; + /* now setup 2D surface (writing 1 32bit word) */ + nv_acc_cmd_dma(NV4_SURFACE, NV4_SURFACE_FORMAT, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000002; /* Format */ + } + + /* TNT1 has fixed operation mode 'SRCcopy' while the rest can be programmed: */ + if (si->ps.card_type != NV04) + { + /* wait for room in fifo for cmds if needed. */ + if (nv_acc_fifofree_dma(5) != B_OK) return; + /* now setup source bitmap colorspace */ + nv_acc_cmd_dma(NV_SCALED_IMAGE_FROM_MEMORY, NV_SCALED_IMAGE_FROM_MEMORY_SETCOLORFORMAT, 2); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = cmd_depth; /* SetColorFormat */ + /* now setup operation mode to SRCcopy */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000003; /* SetOperation */ + } + else + { + /* wait for room in fifo for cmd if needed. */ + if (nv_acc_fifofree_dma(4) != B_OK) return; + /* now setup source bitmap colorspace */ + nv_acc_cmd_dma(NV_SCALED_IMAGE_FROM_MEMORY, NV_SCALED_IMAGE_FROM_MEMORY_SETCOLORFORMAT, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = cmd_depth; /* SetColorFormat */ + /* TNT1 has fixed operation mode SRCcopy */ + } + /* now setup fill color (writing 2 32bit words) */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_COLOR1A, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000000; /* Color1A */ + + /*** do each blit ***/ + while (count) + { + /* break up the list in sublists to minimize calls, while making sure long + * lists still get executed without trouble */ + subcnt = 16; + if (count < 16) subcnt = count; + count -= subcnt; + + /* wait for room in fifo for blit cmd if needed. */ + if (nv_acc_fifofree_dma(12 * subcnt) != B_OK) return; + + while (subcnt--) + { + /* now setup blit (writing 12 32bit words) */ + nv_acc_cmd_dma(NV_SCALED_IMAGE_FROM_MEMORY, NV_SCALED_IMAGE_FROM_MEMORY_SOURCEORG, 6); + /* setup dest clipping ref for blit (not used) (b0-15 = left, b16-31 = top) */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0; /* SourceOrg */ + /* setup dest clipping size for blit */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].dest_height + 1) << 16) | (list[i].dest_width + 1)); /* SourceHeightWidth */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + /* setup destination location and size for blit */ + (((list[i].dest_top) << 16) | (list[i].dest_left)); /* DestOrg */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].dest_height + 1) << 16) | (list[i].dest_width + 1)); /* DestHeightWidth */ + //fixme: findout scaling limits... (although the current cmd interface doesn't support them.) + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].src_width + 1) << 20) / (list[i].dest_width + 1)); /* HorInvScale (in 12.20 format) */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].src_height + 1) << 20) / (list[i].dest_height + 1)); /* VerInvScale (in 12.20 format) */ + + nv_acc_cmd_dma(NV_SCALED_IMAGE_FROM_MEMORY, NV_SCALED_IMAGE_FROM_MEMORY_SOURCESIZE, 4); + /* setup horizontal and vertical source (fetching) ends. + * note: + * horizontal granularity is 2 pixels, vertical granularity is 1 pixel. + * look at Matrox or Neomagic bes engines code for usage example. */ + //fixme: tested 15, 16 and 32-bit RGB depth, verify other depths... + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].src_height + 1) << 16) | + (((list[i].src_width + 1) + 0x0001) & ~0x0001)); /* SourceHeightWidth */ + /* setup source pitch (b0-15). Set 'format origin center' (b16-17) and + * select 'format interpolator foh (bilinear filtering)' (b24). */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (si->fbc.bytes_per_row | (1 << 16) | (1 << 24)); /* SourcePitch */ + /* setup source surface location */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((uint32)((uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer)) + + (list[i].src_top * si->fbc.bytes_per_row) + (list[i].src_left * bpp); /* Offset */ + /* setup source start: first (sub)pixel contributing to output picture */ + /* note: + * clipping is not asked for. + * look at nVidia NV10+ bes engine code for useage example. */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + 0; /* SourceRef (b0-15 = hor, b16-31 = ver: both in 12.4 format) */ + + i++; + } + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* reset surface depth settings so the other engine commands works as intended */ + if (si->dm.space == B_RGB15_LITTLE) + { + /* wait for room in fifo for surface setup cmd if needed */ + if (nv_acc_fifofree_dma(2) != B_OK) return; + /* now setup 2D surface (writing 1 32bit word) */ + nv_acc_cmd_dma(NV4_SURFACE, NV4_SURFACE_FORMAT, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000004; /* Format */ + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; +} + +/* rectangle fill - i.e. workspace and window background color */ +void FILL_RECTANGLE_DMA(engine_token *et, uint32 colorIndex, fill_rect_params *list, uint32 count) +{ + uint32 i = 0; + uint16 subcnt; + + /*** init acc engine for fill function ***/ + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP and bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(4) != B_OK) return; + /* now setup ROP (writing 2 32bit words) for GXcopy */ + nv_acc_cmd_dma(NV_ROP5_SOLID, NV_ROP5_SOLID_SETROP5, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xcc; /* SetRop5 */ + /* now setup fill color (writing 2 32bit words) */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_COLOR1A, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = colorIndex; /* Color1A */ + + /*** draw each rectangle ***/ + while (count) + { + /* break up the list in sublists to minimize calls, while making sure long + * lists still get executed without trouble */ + subcnt = 32; + if (count < 32) subcnt = count; + count -= subcnt; + + /* wait for room in fifo for bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(1 + (2 * subcnt)) != B_OK) return; + + /* issue fill command once... */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_UCR0_LEFTTOP, (2 * subcnt)); + /* ... and send multiple rects (engine cmd supports 32 max) */ + while (subcnt--) + { + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].left) << 16) | ((list[i].top) & 0x0000ffff)); /* Unclipped Rect 0 LeftTop */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((((list[i].right)+1) - (list[i].left)) << 16) | + (((list[i].bottom-list[i].top)+1) & 0x0000ffff)); /* Unclipped Rect 0 WidthHeight */ + + i++; + } + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; +} + +/* span fill - i.e. (selected) menuitem background color (Dano) */ +void FILL_SPAN_DMA(engine_token *et, uint32 colorIndex, uint16 *list, uint32 count) +{ + uint32 i = 0; + uint16 subcnt; + + /*** init acc engine for fill function ***/ + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP and bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(4) != B_OK) return; + /* now setup ROP (writing 2 32bit words) for GXcopy */ + nv_acc_cmd_dma(NV_ROP5_SOLID, NV_ROP5_SOLID_SETROP5, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0xcc; /* SetRop5 */ + /* now setup fill color (writing 2 32bit words) */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_COLOR1A, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = colorIndex; /* Color1A */ + + /*** draw each span ***/ + while (count) + { + /* break up the list in sublists to minimize calls, while making sure long + * lists still get executed without trouble */ + subcnt = 32; + if (count < 32) subcnt = count; + count -= subcnt; + + /* wait for room in fifo for bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(1 + (2 * subcnt)) != B_OK) return; + + /* issue fill command once... */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_UCR0_LEFTTOP, (2 * subcnt)); + /* ... and send multiple rects (spans) (engine cmd supports 32 max) */ + while (subcnt--) + { + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i+1]) << 16) | ((list[i]) & 0x0000ffff)); /* Unclipped Rect 0 LeftTop */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + ((((list[i+2]+1) - (list[i+1])) << 16) | 0x00000001); /* Unclipped Rect 0 WidthHeight */ + + i+=3; + } + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; +} + +/* rectangle invert - i.e. text cursor and text selection */ +void INVERT_RECTANGLE_DMA(engine_token *et, fill_rect_params *list, uint32 count) +{ + uint32 i = 0; + uint16 subcnt; + + /*** init acc engine for invert function ***/ + /* ROP registers (Raster OPeration): + * wait for room in fifo for ROP and bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(4) != B_OK) return; + /* now setup ROP (writing 2 32bit words) for GXinvert */ + nv_acc_cmd_dma(NV_ROP5_SOLID, NV_ROP5_SOLID_SETROP5, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x55; /* SetRop5 */ + /* now reset fill color (writing 2 32bit words) */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_COLOR1A, 1); + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = 0x00000000; /* Color1A */ + + /*** invert each rectangle ***/ + while (count) + { + /* break up the list in sublists to minimize calls, while making sure long + * lists still get executed without trouble */ + subcnt = 32; + if (count < 32) subcnt = count; + count -= subcnt; + + /* wait for room in fifo for bitmap cmd if needed. */ + if (nv_acc_fifofree_dma(1 + (2 * subcnt)) != B_OK) return; + + /* issue fill command once... */ + nv_acc_cmd_dma(NV4_GDI_RECTANGLE_TEXT, NV4_GDI_RECTANGLE_TEXT_UCR0_LEFTTOP, (2 * subcnt)); + /* ... and send multiple rects (engine cmd supports 32 max) */ + while (subcnt--) + { + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((list[i].left) << 16) | ((list[i].top) & 0x0000ffff)); /* Unclipped Rect 0 LeftTop */ + ((uint32*)(si->dma_buffer))[si->engine.dma.current++] = + (((((list[i].right)+1) - (list[i].left)) << 16) | + (((list[i].bottom-list[i].top)+1) & 0x0000ffff)); /* Unclipped Rect 0 WidthHeight */ + + i++; + } + + /* tell the engine to fetch the commands in the DMA buffer that where not + * executed before. */ + nv_start_dma(); + } + + /* tell 3D add-ons that they should reload their rendering states and surfaces */ + si->engine.threeD.reload = 0xffffffff; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_agp.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_agp.c new file mode 100644 index 0000000000..3f6d0abd92 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_agp.c @@ -0,0 +1,222 @@ +/* Author: + Rudolf Cornelissen 6/2004-4/2006 +*/ + +#define MODULE_BIT 0x00000100 + +#include +#include "nv_std.h" + +static void nv_agp_list_info(agp_info ai); +static void nv_agp_list_active(uint32 cmd); + + +status_t +nv_agp_setup(bool enable_agp) +{ + nv_nth_agp_info nai; + nv_cmd_agp nca; + uint8 index; + agp_info nv_ai; + bool agp = false; + + /* preset we are running in PCI mode: so acc engine may not use AGP transfers */ + si->engine.agp_mode = false; + + /* first try to enable FW support on our card if user requested this + * ('unsupported' tweak!) + * This has no effect on PCI cards. */ + if (si->settings.unhide_fw) { + uint32 reg; + + LOG(4, ("AGP: STRAPINFO2 contains $%08x\n", NV_REG32(NV32_NVSTRAPINFO2))); + + LOG(4, ("AGP: attempting to enable fastwrite support..\n")); + /* 'force' FW support */ + reg = (NV_REG32(NV32_NVSTRAPINFO2) & ~0x00000800); + /* enable strapinfo overwrite */ + NV_REG32(NV32_NVSTRAPINFO2) = (reg | 0x80000000); + + LOG(4, ("AGP: STRAPINFO2 now contains $%08x\n", NV_REG32(NV32_NVSTRAPINFO2))); + } + + /* set the magic number so the nvidia kerneldriver knows we're for real */ + nca.magic = nai.magic = NV_PRIVATE_DATA_MAGIC; + + /* contact driver and get a pointer to the registers and shared data */ + for (index = 0; index < 8; index++) { + /* get nth AGP device info */ + nai.index = index; + ioctl(fd, NV_GET_NTH_AGP_INFO, &nai, sizeof(nai)); + + /* abort if no agp busmanager found */ + if (!nai.agp_bus) { + LOG(4,("AGP: no AGP busmanager found.\n")); + /* don't touch AGP command register, we don't know what has been setup: + * touching it anyway might 'hang' the graphics card! */ + + return B_ERROR; + } + + /* exit if we didn't get device info for this index */ + if (!nai.exist) { + if (index != 0) + LOG(4,("AGP: end of AGP capable devices list.\n")); + else + LOG(4,("AGP: no AGP capable devices found.\n")); + break; + } + + LOG(4,("AGP: AGP capable device #%d:\n", (index + 1))); + + /* see if we are this one */ + if (nai.agpi.device_id == si->device_id + && nai.agpi.vendor_id == si->vendor_id + && nai.agpi.bus == si->bus + && nai.agpi.device == si->device + && nai.agpi.function == si->function) { + LOG(4,("AGP: (this is the device this accelerant controls)\n")); + agp = true; + /* remember our info */ + nv_ai = nai.agpi; + } + + /* log capabilities */ + nv_agp_list_info(nai.agpi); + } + + /* if our card is not an AGP type, abort here */ + /* Note: + * We have to iterate through the capability list as specified in the PCI spec + * one way or the other, otherwise we cannot distinquish between nVidia PCI and + * AGP type cards as nVidia PCI cards still have AGP registers that pretend to + * support AGP. + * We rely on the AGP busmanager to iterate trough this list for us. */ + if (!agp) { + LOG(4,("AGP: the graphicscard this accelerant controls is PCI type.\n")); + + /* make sure card is set for PCI access */ + CFGW(AGPCMD, 0x00000000); + + return B_ERROR; + } + + if (si->settings.force_pci || !enable_agp) { + /* set PCI mode if specified by user in nv.settings */ + if (enable_agp) + LOG(4,("AGP: forcing PCI mode (specified in nv.settings)\n")); + else + LOG(4,("AGP: forcing PCI mode during coldstart (required)\n")); + + /* let the AGP busmanager setup PCI mode. + * (the AGP speed scheme is of no consequence now) */ + nca.cmd = 0x00000000; + ioctl(fd, NV_ENABLE_AGP, &nca, sizeof(nca)); + } else { + /* activate AGP mode */ + LOG(4,("AGP: activating AGP mode...\n")); + + /* let the AGP busmanager worry about what mode to set.. */ + nca.cmd = 0xfffffff7; + /* ..but we do need to select the right speed scheme fetched from our card */ + if (nv_ai.interface.status & AGP_3_MODE) + nca.cmd |= AGP_3_MODE; + ioctl(fd, NV_ENABLE_AGP, &nca, sizeof(nca)); + /* tell the engine in may use AGP transfers if AGP is up and running */ + if (nca.cmd & AGP_ENABLE) + si->engine.agp_mode = true; + } + + /* list mode now activated, + * make sure we have the correct speed scheme for logging */ + nv_agp_list_active(nca.cmd | (nv_ai.interface.status & AGP_3_MODE)); + + /* extra check */ + LOG(4,("AGP: graphics card AGPCMD register readback $%08x\n", CFGR(AGPCMD))); + return B_OK; +} + + +static void +nv_agp_list_info(agp_info ai) +{ + /* + list device + */ + if (ai.class_base == PCI_display) + LOG(4,("AGP: device is a graphicscard, subclass ID is $%02x\n", ai.class_sub)); + else + LOG(4,("AGP: device is a hostbridge, subclass ID is $%02x\n", ai.class_sub)); + LOG(4,("AGP: vendor ID $%04x\n", ai.vendor_id)); + LOG(4,("AGP: device ID $%04x\n", ai.device_id)); + LOG(4,("AGP: bus %d, device %d, function %d\n", ai.bus, ai.device, ai.function)); + + /* + list capabilities + */ + LOG(4,("AGP: this device supports AGP specification %d.%d;\n", + ((ai.interface.capability_id & AGP_REV_MAJOR) >> AGP_REV_MAJOR_SHIFT), + ((ai.interface.capability_id & AGP_REV_MINOR) >> AGP_REV_MINOR_SHIFT))); + + /* the AGP devices determine AGP speed scheme version used on power-up/reset */ + if (!(ai.interface.status & AGP_3_MODE)) { + /* AGP 2.0 scheme applies */ + if (ai.interface.status & AGP_2_1x) + LOG(4,("AGP: AGP 2.0 1x mode is available\n")); + if (ai.interface.status & AGP_2_2x) + LOG(4,("AGP: AGP 2.0 2x mode is available\n")); + if (ai.interface.status & AGP_2_4x) + LOG(4,("AGP: AGP 2.0 4x mode is available\n")); + } else { + /* AGP 3.0 scheme applies */ + if (ai.interface.status & AGP_3_4x) + LOG(4,("AGP: AGP 3.0 4x mode is available\n")); + if (ai.interface.status & AGP_3_8x) + LOG(4,("AGP: AGP 3.0 8x mode is available\n")); + } + if (ai.interface.status & AGP_FAST_WRITE) + LOG(4,("AGP: fastwrite transfers are supported\n")); + if (ai.interface.status & AGP_SBA) + LOG(4,("AGP: sideband adressing is supported\n")); + LOG(4,("AGP: %d queued AGP requests can be handled.\n", + (((ai.interface.status & AGP_REQUEST) >> AGP_REQUEST_SHIFT) + 1))); + + /* + list current settings, + make sure we have the correct speed scheme for logging + */ + nv_agp_list_active(ai.interface.command + | (ai.interface.status & AGP_3_MODE)); +} + + +static void +nv_agp_list_active(uint32 cmd) +{ + LOG(4,("AGP: listing settings now in use:\n")); + if (!(cmd & AGP_3_MODE)) { + /* AGP 2.0 scheme applies */ + if (cmd & AGP_2_1x) + LOG(4,("AGP: AGP 2.0 1x mode is set\n")); + if (cmd & AGP_2_2x) + LOG(4,("AGP: AGP 2.0 2x mode is set\n")); + if (cmd & AGP_2_4x) + LOG(4,("AGP: AGP 2.0 4x mode is set\n")); + } else { + /* AGP 3.0 scheme applies */ + if (cmd & AGP_3_4x) + LOG(4,("AGP: AGP 3.0 4x mode is set\n")); + if (cmd & AGP_3_8x) + LOG(4,("AGP: AGP 3.0 8x mode is set\n")); + } + if (cmd & AGP_FAST_WRITE) + LOG(4,("AGP: fastwrite transfers are enabled\n")); + if (cmd & AGP_SBA) + LOG(4,("AGP: sideband adressing is enabled\n")); + LOG(4,("AGP: max. AGP queued request depth is set to %d\n", + (((cmd & AGP_REQUEST) >> AGP_REQUEST_SHIFT) + 1))); + if (cmd & AGP_ENABLE) + LOG(4,("AGP: the AGP interface is enabled.\n")); + else + LOG(4,("AGP: the AGP interface is disabled.\n")); +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_bes.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_bes.c new file mode 100644 index 0000000000..7e8169561d --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_bes.c @@ -0,0 +1,876 @@ +/* Nvidia TNT and GeForce Back End Scaler functions */ +/* Written by Rudolf Cornelissen 05/2002-12/2005 */ + +#define MODULE_BIT 0x00000200 + +#include "nv_std.h" + +typedef struct move_overlay_info move_overlay_info; + +struct move_overlay_info +{ + uint32 hcoordv; /* left and right edges of video output window */ + uint32 vcoordv; /* top and bottom edges of video output window */ + uint32 hsrcstv; /* horizontal source start in source buffer (clipping) */ + uint32 v1srcstv; /* vertical source start in source buffer (clipping) */ + uint32 a1orgv; /* alternate source clipping via startadress of source buffer */ +}; + +static void nv_bes_calc_move_overlay(move_overlay_info *moi); +static void nv_bes_program_move_overlay(move_overlay_info moi); + +/* move the overlay output window in virtualscreens */ +/* Note: + * si->dm.h_display_start and si->dm.v_display_start determine where the new + * output window is located! */ +void nv_bes_move_overlay() +{ + move_overlay_info moi; + + /* abort if overlay is not active */ + if (!si->overlay.active) return; + + nv_bes_calc_move_overlay(&moi); + nv_bes_program_move_overlay(moi); +} + +static void nv_bes_calc_move_overlay(move_overlay_info *moi) +{ + /* misc used variables */ + uint16 temp1, temp2; + /* visible screen window in virtual workspaces */ + uint16 crtc_hstart, crtc_vstart, crtc_hend, crtc_vend; + + /* do 'overlay follow head' in dualhead modes on dualhead cards */ + if (si->ps.secondary_head) + { + switch (si->dm.flags & DUALHEAD_BITS) + { + case DUALHEAD_ON: + case DUALHEAD_SWITCH: + if ((si->overlay.ow.h_start + (si->overlay.ow.width / 2)) < + (si->dm.h_display_start + si->dm.timing.h_display)) + nv_bes_to_crtc(si->crtc_switch_mode); + else + nv_bes_to_crtc(!si->crtc_switch_mode); + break; + default: + nv_bes_to_crtc(si->crtc_switch_mode); + break; + } + } + + /* the BES does not respect virtual_workspaces, but adheres to CRTC + * constraints only */ + crtc_hstart = si->dm.h_display_start; + /* make dualhead stretch and switch mode work while we're at it.. */ + if (si->overlay.crtc) + { + crtc_hstart += si->dm.timing.h_display; + } + + /* horizontal end is the first position beyond the displayed range on the CRTC */ + crtc_hend = crtc_hstart + si->dm.timing.h_display; + crtc_vstart = si->dm.v_display_start; + /* vertical end is the first position beyond the displayed range on the CRTC */ + crtc_vend = crtc_vstart + si->dm.timing.v_display; + + + /**************************************** + *** setup all edges of output window *** + ****************************************/ + + /* setup left and right edges of output window */ + moi->hcoordv = 0; + /* left edge coordinate of output window, must be inside desktop */ + /* clipping on the left side */ + if (si->overlay.ow.h_start < crtc_hstart) + { + temp1 = 0; + } + else + { + /* clipping on the right side */ + if (si->overlay.ow.h_start >= (crtc_hend - 1)) + { + /* width < 2 is not allowed */ + temp1 = (crtc_hend - crtc_hstart - 2) & 0x7ff; + } + else + /* no clipping here */ + { + temp1 = (si->overlay.ow.h_start - crtc_hstart) & 0x7ff; + } + } + moi->hcoordv |= temp1 << 16; + /* right edge coordinate of output window, must be inside desktop */ + /* width < 2 is not allowed */ + if (si->overlay.ow.width < 2) + { + temp2 = (temp1 + 1) & 0x7ff; + } + else + { + /* clipping on the right side */ + if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) > (crtc_hend - 1)) + { + temp2 = (crtc_hend - crtc_hstart - 1) & 0x7ff; + } + else + { + /* clipping on the left side */ + if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) < (crtc_hstart + 1)) + { + /* width < 2 is not allowed */ + temp2 = 1; + } + else + /* no clipping here */ + { + temp2 = ((uint16)(si->overlay.ow.h_start + si->overlay.ow.width - crtc_hstart - 1)) & 0x7ff; + } + } + } + moi->hcoordv |= temp2 << 0; + LOG(4,("Overlay: CRTC left-edge output %d, right-edge output %d\n",temp1, temp2)); + + /* setup top and bottom edges of output window */ + moi->vcoordv = 0; + /* top edge coordinate of output window, must be inside desktop */ + /* clipping on the top side */ + if (si->overlay.ow.v_start < crtc_vstart) + { + temp1 = 0; + } + else + { + /* clipping on the bottom side */ + if (si->overlay.ow.v_start >= (crtc_vend - 1)) + { + /* height < 2 is not allowed */ + temp1 = (crtc_vend - crtc_vstart - 2) & 0x7ff; + } + else + /* no clipping here */ + { + temp1 = (si->overlay.ow.v_start - crtc_vstart) & 0x7ff; + } + } + moi->vcoordv |= temp1 << 16; + /* bottom edge coordinate of output window, must be inside desktop */ + /* height < 2 is not allowed */ + if (si->overlay.ow.height < 2) + { + temp2 = (temp1 + 1) & 0x7ff; + } + else + { + /* clipping on the bottom side */ + if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) > (crtc_vend - 1)) + { + temp2 = (crtc_vend - crtc_vstart - 1) & 0x7ff; + } + else + { + /* clipping on the top side */ + if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) < (crtc_vstart + 1)) + { + /* height < 2 is not allowed */ + temp2 = 1; + } + else + /* no clipping here */ + { + temp2 = ((uint16)(si->overlay.ow.v_start + si->overlay.ow.height - crtc_vstart - 1)) & 0x7ff; + } + } + } + moi->vcoordv |= temp2 << 0; + LOG(4,("Overlay: CRTC top-edge output %d, bottom-edge output %d\n",temp1, temp2)); + + + /********************************* + *** setup horizontal clipping *** + *********************************/ + + /* Setup horizontal source start: first (sub)pixel contributing to output picture */ + /* Note: + * The method is to calculate, based on 1:1 scaling, based on the output window. + * After this is done, include the scaling factor so you get a value based on the input bitmap. + * Then add the left starting position of the bitmap's view (zoom function) to get the final value needed. + * Note: The input bitmaps slopspace is automatically excluded from the calculations this way! */ + /* Note also: + * Even if the scaling factor is clamping we instruct the BES to use the correct source start pos.! */ + moi->hsrcstv = 0; + /* check for destination horizontal clipping at left side */ + if (si->overlay.ow.h_start < crtc_hstart) + { + /* check if entire destination picture is clipping left: + * (2 pixels will be clamped onscreen at least) */ + if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) < (crtc_hstart + 1)) + { + /* increase 'first contributing pixel' with 'fixed value': (total dest. width - 2) */ + moi->hsrcstv += (si->overlay.ow.width - 2); + } + else + { + /* increase 'first contributing pixel' with actual number of dest. clipping pixels */ + moi->hsrcstv += (crtc_hstart - si->overlay.ow.h_start); + } + LOG(4,("Overlay: clipping left...\n")); + + /* The calculated value is based on scaling = 1x. So we now compensate for scaling. + * Note that this also already takes care of aligning the value to the BES register! */ + moi->hsrcstv *= si->overlay.h_ifactor; + } + /* take zoom into account */ + moi->hsrcstv += ((uint32)si->overlay.my_ov.h_start) << 16; + /* AND below required by hardware */ + moi->hsrcstv &= 0x03fffffc; + LOG(4,("Overlay: first hor. (sub)pixel of input bitmap contributing %f\n", moi->hsrcstv / (float)65536)); + + + /******************************* + *** setup vertical clipping *** + *******************************/ + + /* calculate inputbitmap origin adress */ + moi->a1orgv = (uint32)((vuint32 *)si->overlay.ob.buffer); + moi->a1orgv -= (uint32)((vuint32 *)si->framebuffer); + LOG(4,("Overlay: topleft corner of input bitmap (cardRAM offset) $%08x\n", moi->a1orgv)); + + /* Setup vertical source start: first (sub)pixel contributing to output picture. */ + /* Note: + * The method is to calculate, based on 1:1 scaling, based on the output window. + * 'After' this is done, include the scaling factor so you get a value based on the input bitmap. + * Then add the top starting position of the bitmap's view (zoom function) to get the final value needed. */ + /* Note also: + * Even if the scaling factor is clamping we instruct the BES to use the correct source start pos.! */ + + moi->v1srcstv = 0; + /* check for destination vertical clipping at top side */ + if (si->overlay.ow.v_start < crtc_vstart) + { + /* check if entire destination picture is clipping at top: + * (2 pixels will be clamped onscreen at least) */ + if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) < (crtc_vstart + 1)) + { + /* increase 'number of clipping pixels' with 'fixed value': + * 'total height - 2' of dest. picture in pixels * inverse scaling factor */ + moi->v1srcstv = (si->overlay.ow.height - 2) * si->overlay.v_ifactor; + /* on pre-NV10 we need to do clipping in the source + * bitmap because no seperate clipping registers exist... */ + if (si->ps.card_arch < NV10A) + moi->a1orgv += ((moi->v1srcstv >> 16) * si->overlay.ob.bytes_per_row); + } + else + { + /* increase 'first contributing pixel' with: + * number of destination picture clipping pixels * inverse scaling factor */ + moi->v1srcstv = (crtc_vstart - si->overlay.ow.v_start) * si->overlay.v_ifactor; + /* on pre-NV10 we need to do clipping in the source + * bitmap because no seperate clipping registers exist... */ + if (si->ps.card_arch < NV10A) + moi->a1orgv += ((moi->v1srcstv >> 16) * si->overlay.ob.bytes_per_row); + } + LOG(4,("Overlay: clipping at top...\n")); + } + /* take zoom into account */ + moi->v1srcstv += (((uint32)si->overlay.my_ov.v_start) << 16); + if (si->ps.card_arch < NV10A) + { + moi->a1orgv += (si->overlay.my_ov.v_start * si->overlay.ob.bytes_per_row); + LOG(4,("Overlay: 'contributing part of buffer' origin is (cardRAM offset) $%08x\n", moi->a1orgv)); + } + LOG(4,("Overlay: first vert. (sub)pixel of input bitmap contributing %f\n", moi->v1srcstv / (float)65536)); + + /* AND below is probably required by hardware. */ + /* Buffer A topleft corner of field 1 (origin)(field 1 contains our full frames) */ + moi->a1orgv &= 0xfffffff0; +} + +static void nv_bes_program_move_overlay(move_overlay_info moi) +{ + /************************************* + *** sync to BES (Back End Scaler) *** + *************************************/ + + /* Done in card hardware: + * double buffered registers + trigger if programming complete feature. */ + + + /************************************** + *** actually program the registers *** + **************************************/ + + if (si->ps.card_arch < NV10A) + { + /* unknown, but needed (otherwise high-res distortions and only half the frames */ + BESW(NV04_OE_STATE, 0x00000000); + /* select buffer 0 as active (b16) */ + BESW(NV04_SU_STATE, 0x00000000); + /* unknown (no effect?) */ + BESW(NV04_RM_STATE, 0x00000000); + /* setup clipped(!) buffer startadress in RAM */ + /* RIVA128 - TNT bes doesn't have clipping registers, so no subpixelprecise clipping + * either. We do pixelprecise vertical and 'two pixel' precise horizontal clipping here. */ + /* (program both buffers to prevent sync distortions) */ + /* first include 'pixel precise' left clipping... (top clipping was already included) */ + moi.a1orgv += ((moi.hsrcstv >> 16) * 2); + /* we need to step in 4-byte (2 pixel) granularity due to the nature of yuy2 */ + BESW(NV04_0BUFADR, (moi.a1orgv & ~0x03)); + BESW(NV04_1BUFADR, (moi.a1orgv & ~0x03)); + /* setup output window position */ + BESW(NV04_DSTREF, ((moi.vcoordv & 0xffff0000) | ((moi.hcoordv & 0xffff0000) >> 16))); + /* setup output window size */ + BESW(NV04_DSTSIZE, ( + (((moi.vcoordv & 0x0000ffff) - ((moi.vcoordv & 0xffff0000) >> 16) + 1) << 16) | + ((moi.hcoordv & 0x0000ffff) - ((moi.hcoordv & 0xffff0000) >> 16) + 1) + )); + /* select buffer 1 as active (b16) */ + BESW(NV04_SU_STATE, 0x00010000); + } + else + { + /* >= NV10A */ + + /* setup buffer origin: GeForce uses subpixel precise clipping on left and top! (12.4 values) */ + BESW(NV10_0SRCREF, ((moi.v1srcstv << 4) & 0xffff0000) | ((moi.hsrcstv >> 12) & 0x0000ffff)); + /* setup output window position */ + BESW(NV10_0DSTREF, ((moi.vcoordv & 0xffff0000) | ((moi.hcoordv & 0xffff0000) >> 16))); + /* setup output window size */ + BESW(NV10_0DSTSIZE, ( + (((moi.vcoordv & 0x0000ffff) - ((moi.vcoordv & 0xffff0000) >> 16) + 1) << 16) | + ((moi.hcoordv & 0x0000ffff) - ((moi.hcoordv & 0xffff0000) >> 16) + 1) + )); + /* We only use buffer buffer 0: select it. (0x01 = buffer 0, 0x10 = buffer 1) */ + /* This also triggers activation of programmed values (double buffered registers feature) */ + BESW(NV10_BUFSEL, 0x00000001); + } +} + +status_t nv_bes_to_crtc(bool crtc) +{ + if (si->ps.secondary_head) + { + if (crtc) + { + LOG(4,("Overlay: switching overlay to CRTC2\n")); + /* switch overlay engine to CRTC2 */ + NV_REG32(NV32_FUNCSEL) &= ~0x00001000; + NV_REG32(NV32_2FUNCSEL) |= 0x00001000; + si->overlay.crtc = !si->crtc_switch_mode; + } + else + { + LOG(4,("Overlay: switching overlay to CRTC1\n")); + /* switch overlay engine to CRTC1 */ + NV_REG32(NV32_2FUNCSEL) &= ~0x00001000; + NV_REG32(NV32_FUNCSEL) |= 0x00001000; + si->overlay.crtc = si->crtc_switch_mode; + } + return B_OK; + } + else + { + return B_ERROR; + } +} + +status_t nv_bes_init() +{ + if (si->ps.card_arch < NV10A) + { + /* disable overlay ints (b0 = buffer 0, b4 = buffer 1) */ + BESW(NV04_INTE, 0x00000000); + + /* setup saturation to be 'neutral' */ + BESW(NV04_SAT, 0x00000000); + /* setup RGB brightness to be 'neutral' */ + BESW(NV04_RED_AMP, 0x00000069); + BESW(NV04_GRN_AMP, 0x0000003e); + BESW(NV04_BLU_AMP, 0x00000089); + + /* setup fifo for fetching data */ + BESW(NV04_FIFOBURL, 0x00000003); + BESW(NV04_FIFOTHRS, 0x00000038); + + /* unknown, but needed (registers only have b0 implemented) */ + /* (program both buffers to prevent sync distortions) */ + BESW(NV04_0OFFSET, 0x00000000); + BESW(NV04_1OFFSET, 0x00000000); + } + else + { + /* >= NV10A */ + + /* disable overlay ints (b0 = buffer 0, b4 = buffer 1) */ + BESW(NV10_INTE, 0x00000000); + /* shut off GeForce4MX MPEG2 decoder */ + BESW(DEC_GENCTRL, 0x00000000); + /* setup BES memory-range mask */ + BESW(NV10_0MEMMASK, (si->ps.memory_size - 1)); + /* unknown, but needed */ + BESW(NV10_0OFFSET, 0x00000000); + + /* setup brightness, contrast and saturation to be 'neutral' */ + BESW(NV10_0BRICON, ((0x1000 << 16) | 0x1000)); + BESW(NV10_0SAT, ((0x0000 << 16) | 0x1000)); + } + + /* make sure the engine is disabled. */ + nv_release_bes(); + + return B_OK; +} + +status_t nv_configure_bes + (const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov, int offset) +{ + /* yuy2 (4:2:2) colorspace calculations */ + + /* Note: + * in BeOS R5.0.3 and DANO: + * 'ow->offset_xxx' is always 0, so not used; + * 'ow->width' and 'ow->height' are the output window size: does not change + * if window is clipping; + * 'ow->h_start' and 'ow->v_start' are the left-top position of the output + * window. These values can be negative: this means the window is clipping + * at the left or the top of the display, respectively. */ + + /* 'ov' is the view in the source bitmap, so which part of the bitmap is actually + * displayed on screen. This is used for the 'hardware zoom' function. */ + + /* output window position and clipping info for source buffer */ + move_overlay_info moi; + /* calculated BES register values */ + uint32 hiscalv, viscalv; + /* interval representation, used for scaling calculations */ + uint16 intrep; + /* inverse scaling factor, used for source positioning */ + uint32 ifactor; + /* copy of overlay view which has checked valid values */ + overlay_view my_ov; + + + /************************************************************************************** + *** copy, check and limit if needed the user-specified view into the intput bitmap *** + **************************************************************************************/ + my_ov = *ov; + /* check for valid 'coordinates' */ + if (my_ov.width == 0) my_ov.width++; + if (my_ov.height == 0) my_ov.height++; + if (my_ov.h_start > ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1)) + my_ov.h_start = ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1); + if (((my_ov.h_start + my_ov.width) - 1) > ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1)) + my_ov.width = ((((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1) - my_ov.h_start) + 1); + if (my_ov.v_start > (ob->height - 1)) + my_ov.v_start = (ob->height - 1); + if (((my_ov.v_start + my_ov.height) - 1) > (ob->height - 1)) + my_ov.height = (((ob->height - 1) - my_ov.v_start) + 1); + + LOG(4,("Overlay: inputbuffer view (zoom) left %d, top %d, width %d, height %d\n", + my_ov.h_start, my_ov.v_start, my_ov.width, my_ov.height)); + + /* save for nv_bes_calc_move_overlay() */ + si->overlay.ow = *ow; + si->overlay.ob = *ob; + si->overlay.my_ov = my_ov; + + + /******************************** + *** setup horizontal scaling *** + ********************************/ + LOG(4,("Overlay: total input picture width = %d, height = %d\n", + (ob->width - si->overlay.myBufInfo[offset].slopspace), ob->height)); + LOG(4,("Overlay: output picture width = %d, height = %d\n", ow->width, ow->height)); + + /* determine interval representation value, taking zoom into account */ + if (ow->flags & B_OVERLAY_HORIZONTAL_FILTERING) + { + /* horizontal filtering is ON */ + if ((my_ov.width == ow->width) | (ow->width < 2)) + { + /* no horizontal scaling used, OR destination width < 2 */ + intrep = 0; + } + else + { + intrep = 1; + } + } + else + { + /* horizontal filtering is OFF */ + if ((ow->width < my_ov.width) & (ow->width >= 2)) + { + /* horizontal downscaling used AND destination width >= 2 */ + intrep = 1; + } + else + { + intrep = 0; + } + } + LOG(4,("Overlay: horizontal interval representation value is %d\n",intrep)); + + /* calculate inverse horizontal scaling factor, taking zoom into account */ + /* standard scaling formula: */ + ifactor = (((uint32)(my_ov.width - intrep)) << 16) / (ow->width - intrep); + + /* correct factor to prevent most-right visible 'line' from distorting */ + ifactor -= (1 << 2); + hiscalv = ifactor; + /* save for nv_bes_calc_move_overlay() */ + si->overlay.h_ifactor = ifactor; + LOG(4,("Overlay: horizontal scaling factor is %f\n", (float)65536 / ifactor)); + + /* check scaling factor (and modify if needed) to be within scaling limits */ + /* all cards have a upscaling limit of 8.0 (see official nVidia specsheets) */ + if (hiscalv < 0x00002000) + { + /* (non-inverse) factor too large, set factor to max. valid value */ + hiscalv = 0x00002000; + LOG(4,("Overlay: horizontal scaling factor too large, clamping at %f\n", (float)65536 / hiscalv)); + } + switch (si->ps.card_arch) + { + case NV04A: + /* Riva128-TNT2 series have a 'downscaling' limit of 1.000489 + * (16bit register with 0.11 format value) */ + if (hiscalv > 0x0000ffff) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + hiscalv = 0x0000ffff; + LOG(4,("Overlay: horizontal scaling factor too small, clamping at %f\n", (float)2048 / (hiscalv >> 5))); + } + break; + case NV30A: + case NV40A: + /* GeForceFX series and up have a downscaling limit of 0.5 (except NV31!) */ + if ((hiscalv > (2 << 16)) && (si->ps.card_type != NV31)) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + hiscalv = (2 << 16); + LOG(4,("Overlay: horizontal scaling factor too small, clamping at %f\n", (float)65536 / hiscalv)); + } + /* NV31 (confirmed GeForceFX 5600) has NV20A scaling limits! + * So let it fall through... */ + if (si->ps.card_type != NV31) break; + default: + /* the rest has a downscaling limit of 0.125 */ + if (hiscalv > (8 << 16)) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + hiscalv = (8 << 16); + LOG(4,("Overlay: horizontal scaling factor too small, clamping at %f\n", (float)65536 / hiscalv)); + } + break; + } + /* AND below is required by hardware */ + hiscalv &= 0x001ffffc; + + + /****************************** + *** setup vertical scaling *** + ******************************/ + + /* determine interval representation value, taking zoom into account */ + if (ow->flags & B_OVERLAY_VERTICAL_FILTERING) + { + /* vertical filtering is ON */ + if ((my_ov.height == ow->height) | (ow->height < 2)) + { + /* no vertical scaling used, OR destination height < 2 */ + intrep = 0; + } + else + { + intrep = 1; + } + } + else + { + /* vertical filtering is OFF */ + if ((ow->height < my_ov.height) & (ow->height >= 2)) + { + /* vertical downscaling used AND destination height >= 2 */ + intrep = 1; + } + else + { + intrep = 0; + } + } + LOG(4,("Overlay: vertical interval representation value is %d\n",intrep)); + + /* calculate inverse vertical scaling factor, taking zoom into account */ + /* standard scaling formula: */ + ifactor = (((uint32)(my_ov.height - intrep)) << 16) / (ow->height - intrep); + + /* correct factor to prevent lowest visible line from distorting */ + ifactor -= (1 << 2); + LOG(4,("Overlay: vertical scaling factor is %f\n", (float)65536 / ifactor)); + + /* preserve ifactor for source positioning calculations later on */ + viscalv = ifactor; + /* save for nv_bes_calc_move_overlay() */ + si->overlay.v_ifactor = ifactor; + + /* check scaling factor (and modify if needed) to be within scaling limits */ + /* all cards have a upscaling limit of 8.0 (see official nVidia specsheets) */ + if (viscalv < 0x00002000) + { + /* (non-inverse) factor too large, set factor to max. valid value */ + viscalv = 0x00002000; + LOG(4,("Overlay: vertical scaling factor too large, clamping at %f\n", (float)65536 / viscalv)); + } + switch (si->ps.card_arch) + { + case NV04A: + /* Riva128-TNT2 series have a 'downscaling' limit of 1.000489 + * (16bit register with 0.11 format value) */ + if (viscalv > 0x0000ffff) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + viscalv = 0x0000ffff; + LOG(4,("Overlay: vertical scaling factor too small, clamping at %f\n", (float)2048 / (viscalv >> 5))); + } + break; + case NV30A: + case NV40A: + /* GeForceFX series and up have a downscaling limit of 0.5 (except NV31!) */ + if ((viscalv > (2 << 16)) && (si->ps.card_type != NV31)) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + viscalv = (2 << 16); + LOG(4,("Overlay: vertical scaling factor too small, clamping at %f\n", (float)65536 / viscalv)); + } + /* NV31 (confirmed GeForceFX 5600) has NV20A scaling limits! + * So let it fall through... */ + if (si->ps.card_type != NV31) break; + default: + /* the rest has a downscaling limit of 0.125 */ + if (viscalv > (8 << 16)) + { + /* (non-inverse) factor too small, set factor to min. valid value */ + viscalv = (8 << 16); + LOG(4,("Overlay: vertical scaling factor too small, clamping at %f\n", (float)65536 / viscalv)); + } + break; + } + /* AND below is required by hardware */ + viscalv &= 0x001ffffc; + + + /******************************************************************************** + *** setup all edges of output window, setup horizontal and vertical clipping *** + ********************************************************************************/ + nv_bes_calc_move_overlay(&moi); + + + /***************************** + *** log color keying info *** + *****************************/ + + LOG(4,("Overlay: key_red %d, key_green %d, key_blue %d, key_alpha %d\n", + ow->red.value, ow->green.value, ow->blue.value, ow->alpha.value)); + LOG(4,("Overlay: mask_red %d, mask_green %d, mask_blue %d, mask_alpha %d\n", + ow->red.mask, ow->green.mask, ow->blue.mask, ow->alpha.mask)); + + + /***************** + *** log flags *** + *****************/ + + LOG(4,("Overlay: ow->flags is $%08x\n",ow->flags)); + /* BTW: horizontal and vertical filtering are fixed and turned on for GeForce overlay. */ + + + /************************************* + *** sync to BES (Back End Scaler) *** + *************************************/ + + /* Done in card hardware: + * double buffered registers + trigger if programming complete feature. */ + + + /************************************** + *** actually program the registers *** + **************************************/ + + if (si->ps.card_arch < NV10A) + { + /* unknown, but needed (otherwise high-res distortions and only half the frames */ + BESW(NV04_OE_STATE, 0x00000000); + /* select buffer 0 as active (b16) */ + BESW(NV04_SU_STATE, 0x00000000); + /* unknown (no effect?) */ + BESW(NV04_RM_STATE, 0x00000000); + /* setup clipped(!) buffer startadress in RAM */ + /* RIVA128 - TNT bes doesn't have clipping registers, so no subpixelprecise clipping + * either. We do pixelprecise vertical and 'two pixel' precise horizontal clipping here. */ + /* (program both buffers to prevent sync distortions) */ + /* first include 'pixel precise' left clipping... (top clipping was already included) */ + moi.a1orgv += ((moi.hsrcstv >> 16) * 2); + /* we need to step in 4-byte (2 pixel) granularity due to the nature of yuy2 */ + BESW(NV04_0BUFADR, (moi.a1orgv & ~0x03)); + BESW(NV04_1BUFADR, (moi.a1orgv & ~0x03)); + /* setup buffer source pitch including slopspace (in bytes). + * Note: + * source pitch granularity = 16 pixels on the RIVA128 - TNT (so pre-NV10) bes */ + /* (program both buffers to prevent sync distortions) */ + BESW(NV04_0SRCPTCH, (ob->width * 2)); + BESW(NV04_1SRCPTCH, (ob->width * 2)); + /* setup output window position */ + BESW(NV04_DSTREF, ((moi.vcoordv & 0xffff0000) | ((moi.hcoordv & 0xffff0000) >> 16))); + /* setup output window size */ + BESW(NV04_DSTSIZE, ( + (((moi.vcoordv & 0x0000ffff) - ((moi.vcoordv & 0xffff0000) >> 16) + 1) << 16) | + ((moi.hcoordv & 0x0000ffff) - ((moi.hcoordv & 0xffff0000) >> 16) + 1) + )); + /* setup horizontal and vertical scaling */ + BESW(NV04_ISCALVH, (((viscalv << 16) >> 5) | (hiscalv >> 5))); + /* enable vertical filtering (b0) */ + BESW(NV04_CTRL_V, 0x00000001); + /* enable horizontal filtering (no effect?) */ + BESW(NV04_CTRL_H, 0x00000111); + /* enable BES (b0), set colorkeying (b4), format yuy2 (b8: 0 = ccir) */ + if (ow->flags & B_OVERLAY_COLOR_KEY) + BESW(NV04_GENCTRL, 0x00000111); + else + BESW(NV04_GENCTRL, 0x00000101); + /* select buffer 1 as active (b16) */ + BESW(NV04_SU_STATE, 0x00010000); + + /************************** + *** setup color keying *** + **************************/ + + /* setup colorkeying */ + switch(si->dm.space) + { + case B_RGB15_LITTLE: + BESW(NV04_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 5) | + ((ow->red.value & ow->red.mask) << 10) | + ((ow->alpha.value & ow->alpha.mask) << 15) + )); + break; + case B_RGB16_LITTLE: + BESW(NV04_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 5) | + ((ow->red.value & ow->red.mask) << 11) + /* this space has no alpha bits */ + )); + break; + case B_CMAP8: + case B_RGB32_LITTLE: + default: + BESW(NV04_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 8) | + ((ow->red.value & ow->red.mask) << 16) | + ((ow->alpha.value & ow->alpha.mask) << 24) + )); + break; + } + } + else + { + /* >= NV10A */ + + /* setup buffer origin: GeForce uses subpixel precise clipping on left and top! (12.4 values) */ + BESW(NV10_0SRCREF, ((moi.v1srcstv << 4) & 0xffff0000) | ((moi.hsrcstv >> 12) & 0x0000ffff)); + /* setup buffersize */ + //fixme if needed: width must be even officially... + BESW(NV10_0SRCSIZE, ((ob->height << 16) | ob->width)); + /* setup source pitch including slopspace (in bytes), + * b16: select YUY2 (0 = YV12), b20: set colorkeying, b24: no iturbt_709 (do iturbt_601) */ + /* Note: + * source pitch granularity = 32 pixels on GeForce cards!! */ + if (ow->flags & B_OVERLAY_COLOR_KEY) + BESW(NV10_0SRCPTCH, (((ob->width * 2) & 0x0000ffff) | (1 << 16) | (1 << 20) | (0 << 24))); + else + BESW(NV10_0SRCPTCH, (((ob->width * 2) & 0x0000ffff) | (1 << 16) | (0 << 20) | (0 << 24))); + /* setup output window position */ + BESW(NV10_0DSTREF, ((moi.vcoordv & 0xffff0000) | ((moi.hcoordv & 0xffff0000) >> 16))); + /* setup output window size */ + BESW(NV10_0DSTSIZE, ( + (((moi.vcoordv & 0x0000ffff) - ((moi.vcoordv & 0xffff0000) >> 16) + 1) << 16) | + ((moi.hcoordv & 0x0000ffff) - ((moi.hcoordv & 0xffff0000) >> 16) + 1) + )); + /* setup horizontal scaling */ + BESW(NV10_0ISCALH, (hiscalv << 4)); + /* setup vertical scaling */ + BESW(NV10_0ISCALV, (viscalv << 4)); + /* setup (unclipped!) buffer startadress in RAM */ + BESW(NV10_0BUFADR, moi.a1orgv); + /* enable BES (b0 = 0) */ + BESW(NV10_GENCTRL, 0x00000000); + /* We only use buffer buffer 0: select it. (0x01 = buffer 0, 0x10 = buffer 1) */ + /* This also triggers activation of programmed values (double buffered registers feature) */ + BESW(NV10_BUFSEL, 0x00000001); + + /************************** + *** setup color keying *** + **************************/ + + /* setup colorkeying */ + switch(si->dm.space) + { + case B_RGB15_LITTLE: + BESW(NV10_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 5) | + ((ow->red.value & ow->red.mask) << 10) | + ((ow->alpha.value & ow->alpha.mask) << 15) + )); + break; + case B_RGB16_LITTLE: + BESW(NV10_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 5) | + ((ow->red.value & ow->red.mask) << 11) + /* this space has no alpha bits */ + )); + break; + case B_CMAP8: + case B_RGB32_LITTLE: + default: + BESW(NV10_COLKEY, ( + ((ow->blue.value & ow->blue.mask) << 0) | + ((ow->green.value & ow->green.mask) << 8) | + ((ow->red.value & ow->red.mask) << 16) | + ((ow->alpha.value & ow->alpha.mask) << 24) + )); + break; + } + } + + /* note that overlay is in use (for nv_bes_move_overlay()) */ + si->overlay.active = true; + + return B_OK; +} + +status_t nv_release_bes() +{ + if (si->ps.card_arch < NV10A) + { + /* setup BES control: disable scaler (b0 = 0) */ + BESW(NV04_GENCTRL, 0x00000000); + } + else + { + /* setup BES control: disable scaler (b0 = 1) */ + BESW(NV10_GENCTRL, 0x00000001); + } + + /* note that overlay is not in use (for nv_bes_move_overlay()) */ + si->overlay.active = false; + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_brooktreetv.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_brooktreetv.c new file mode 100644 index 0000000000..fa7bc12166 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_brooktreetv.c @@ -0,0 +1,1749 @@ +/* + Author: + Rudolf Cornelissen 4/2002-11/2005 +*/ + +#define MODULE_BIT 0x00100000 + +#include "nv_std.h" + +#define PRADR 0x88 +#define SCADR 0x8a +#define WR 0x00 +#define RD 0x01 + +enum +{ // TVoutput mode to set + NOT_SUPPORTED = 0, + NTSC640_TST, + NTSC640, + NTSC800, + PAL800_TST, + PAL640, + PAL800, + NTSC720, + PAL720, + NTSC640_OS, + PAL800_OS +}; + +/* Dirk Thierbach's Macro setup for registers 0xda-0xfe. + * (also see http://sourceforge.net/projects/nv-tv-out/) */ +static uint8 BtNtscMacro0 [] = { + 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, + 0xbd,0x67,0xb5,0x90,0xb2,0x7d,0x00,0x00}; +static uint8 BtNtscMacro1 [] = { + 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, + 0xbd,0x67,0xb5,0x90,0xb2,0x7d,0x63,0x00}; +static uint8 BtNtscMacro2 [] = { + 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, + 0xbd,0x6c,0x31,0x92,0x32,0xdd,0xe3,0x00}; +static uint8 BtNtscMacro3 [] = { + 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, + 0xbd,0x66,0xb5,0x90,0xb2,0x7d,0xe3,0x00}; + +static uint8 BtPalMacro0 [] = { + 0x05,0x57,0x20,0x40,0x6e,0x7e,0xf4,0x51,0x0f,0xf1,0x05, + 0xd3,0x78,0xa2,0x25,0x54,0xa5,0x00,0x00}; +static uint8 BtPalMacro1 [] = { + 0x05,0x57,0x20,0x40,0x6e,0x7e,0xf4,0x51,0x0f,0xf1,0x05, + 0xd3,0x78,0xa2,0x25,0x54,0xa5,0x63,0x00}; + +static uint8 BT_set_macro (int std, int mode) +{ + uint8 stat; + uint8 buffer[21]; + + LOG(4,("Brooktree: Setting Macro:\n")); + + if ((std < 0) | (std > 1) | (mode < 0) | (mode > 3)) + { + LOG(4,("Brooktree: Non existing mode or standard selected, aborting.\n")); + return 0x80; + } + + switch (std) + { + case 0: + /* NTSC */ + switch (mode) + { + case 0: + /* disabled */ + LOG(4,("Brooktree: NTSC, disabled\n")); + memcpy(&buffer[2], &BtNtscMacro0, 19); + break; + case 1: + /* enabled mode 1 */ + LOG(4,("Brooktree: NTSC, mode 1\n")); + memcpy(&buffer[2], &BtNtscMacro1, 19); + break; + case 2: + /* enabled mode 2 */ + LOG(4,("Brooktree: NTSC, mode 2\n")); + memcpy(&buffer[2], &BtNtscMacro2, 19); + break; + case 3: + /* enabled mode 3 */ + LOG(4,("Brooktree: NTSC, mode 3\n")); + memcpy(&buffer[2], &BtNtscMacro3, 19); + break; + } + break; + case 1: + /* PAL */ + switch (mode) + { + case 0: + /* disabled */ + LOG(4,("Brooktree: PAL, disabled\n")); + memcpy(&buffer[2], &BtPalMacro0, 19); + break; + case 1: + case 2: + case 3: + /* enabled */ + LOG(4,("Brooktree: PAL, enabled\n")); + memcpy(&buffer[2], &BtPalMacro1, 19); + break; + } + break; + } + + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select first register to write to */ + buffer[1] = 0xda; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting Macro\n")); + + return stat; +}//end BT_set_macro. + +/* + see if a (possible) BT/CX chip resides at the given adress. + Return zero if no errors occurred. +*/ +static uint8 BT_check (uint8 bus, uint8 adress) +{ + uint8 buffer[3]; + + buffer[0] = adress + WR; + /* set ESTATUS at b'00'; and enable bt chip-outputs + * WARNING: + * If bit0 = 0 is issued below (EN_OUT = disabled), the BT will lock SDA + * after writing adress $A0 (setting EN_XCLK)!!! + * Until a reboot the corresponding I2C bus will be inacessable then!!! */ + buffer[1] = 0xc4; + /* fixme: if testimage 'was' active txbuffer[3] should become 0x05... + * (currently this cannot be detected in a 'foolproof' way so don't touch...) */ + /* (ESTATUS b'0x' means: RX ID and VERSION info later..) */ + buffer[2] = 0x01; + + /* reset status */ + i2c_flag_error (-1); + + /* do check */ + i2c_bstart(bus); + i2c_writebuffer(bus, buffer, sizeof(buffer)); + i2c_bstop(bus); + return i2c_flag_error(0); +} + +/* identify chiptype */ +static uint8 BT_read_type (void) +{ + uint8 id, type, stat; + uint8 buffer[3]; + + /* Make sure a CX (Conexant) chip (if this turns out to be there) is set to + * BT-compatibility mode! (This command will do nothing on a BT chip...) */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select CX reg. for BT-compatible readback, video still off */ + buffer[1] = 0x6c; + /* set it up */ + buffer[2] = 0x02; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* abort on errors */ + stat = i2c_flag_error(0); + if (stat) return stat; + + /* Do actual readtype command */ + i2c_bstart(si->ps.tv_encoder.bus); + /* issue I2C read command */ + i2c_writebyte(si->ps.tv_encoder.bus, si->ps.tv_encoder.adress + RD); + /* receive 1 byte; + * ACK level to TX after last byte to RX should be 1 (= NACK) (see I2C spec). */ + /* note: + * While the BT's don't care, CX chips will block the SDA line if + * an ACK gets sent! */ + id = i2c_readbyte(si->ps.tv_encoder.bus, true); + i2c_bstop(si->ps.tv_encoder.bus); + /* abort on errors */ + stat = i2c_flag_error(0); + if (stat) return stat; + + /* check type to be supported one */ + type = (id & 0xe0) >> 5; + if (type > 3) + { + LOG(4,("Brooktree: Found unsupported encoder type %d, aborting.\n", type)); + return 0x80; + } + + /* inform driver about TV encoder found */ + si->ps.tvout = true; + si->ps.tv_encoder.type = BT868 + type; + si->ps.tv_encoder.version = id & 0x1f; + + return stat; +} + +bool BT_probe() +{ + uint8 bus; + bool btfound = false; + bool *i2c_bus = &(si->ps.i2c_bus0); + + LOG(4,("Brooktree: Checking wired I2C bus(ses) for first possible TV encoder...\n")); + for (bus = 0; bus < 3; bus++) + { + if (i2c_bus[bus] && !btfound) + { + /* try primary adress on bus */ + if (!BT_check(bus, PRADR)) + { + btfound = true; + si->ps.tv_encoder.adress = PRADR; + si->ps.tv_encoder.bus = bus; + } + else + { + /* try secondary adress on bus */ + if (!BT_check(bus, SCADR)) + { + btfound = true; + si->ps.tv_encoder.adress = SCADR; + si->ps.tv_encoder.bus = bus; + } + } + } + } + + /* identify exact TV encoder type */ + if (btfound) + { + /* if errors are found, retry */ + /* note: + * NACK: occurs on some ASUS V7700 GeForce cards! + * (apparantly the video-in chip or another chip resides at 'BT' adresses + * there..) */ + uint8 stat; + uint8 cnt = 0; + while ((stat = BT_read_type()) && (cnt < 3)) + { + /* don't retry on unsupported chiptype */ + if (stat == 0x80) + { + btfound = 0; + break; + } + cnt++; + } + if (stat & 0x7f) + { + LOG(4,("Brooktree: Too much errors occurred, aborting.\n")); + btfound = 0; + } + } + + if (btfound) + LOG(4,("Brooktree: Found TV encoder on bus %d, adress $%02x\n", + si->ps.tv_encoder.bus, si->ps.tv_encoder.adress)); + else + LOG(4,("Brooktree: No TV encoder Found\n")); + + return btfound; +} + +static uint8 BT_init_PAL640() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting PAL 640x480 desktop mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to + buffer[2] = 0x60; + buffer[3] = 0x80; + buffer[4] = 0x8a; + buffer[5] = 0xa6; + buffer[6] = 0x68; + buffer[7] = 0xc1; + buffer[8] = 0x2e; + buffer[9] = 0xf2; + buffer[10] = 0x27; + buffer[11] = 0x00; + buffer[12] = 0xb0; + buffer[13] = 0x0a; + buffer[14] = 0x0b; + buffer[15] = 0x71; + buffer[16] = 0x5a; + buffer[17] = 0xe0; + buffer[18] = 0x36; + buffer[19] = 0x00; + buffer[20] = 0x50; + buffer[21] = 0x72; + buffer[22] = 0x1c; + buffer[23] = 0x8d; //chip-pin CLKI is pixel clock (only non-default here!) + buffer[24] = 0x24; + buffer[25] = 0xf0; + buffer[26] = 0x58; + buffer[27] = 0x81; + buffer[28] = 0x49; + buffer[29] = 0x8c; + buffer[30] = 0x0c; + buffer[31] = 0x8c; + buffer[32] = 0x79; + buffer[33] = 0x26; + buffer[34] = 0x00; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode PAL640\n")); + + return stat; +}//end BT_init_PAL640. + +static uint8 BT_init_PAL800() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting PAL 800x600 desktop mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to + buffer[2] = 0x00; + buffer[3] = 0x20; + buffer[4] = 0xaa; + buffer[5] = 0xca; + buffer[6] = 0x9a; + buffer[7] = 0x0d; + buffer[8] = 0x29; + buffer[9] = 0xfc; + buffer[10] = 0x39; + buffer[11] = 0x00; + buffer[12] = 0xc0; + buffer[13] = 0x8c; + buffer[14] = 0x03; + buffer[15] = 0xee; + buffer[16] = 0x5f; + buffer[17] = 0x58; + buffer[18] = 0x3a; + buffer[19] = 0x66; + buffer[20] = 0x96; + buffer[21] = 0x00; + buffer[22] = 0x00; + buffer[23] = 0x90; //chip-pin CLKI is pixel clock (only non-default here!) + buffer[24] = 0x24; + buffer[25] = 0xf0; + buffer[26] = 0x57; + buffer[27] = 0x80; + buffer[28] = 0x48; + buffer[29] = 0x8c; + buffer[30] = 0x18; + buffer[31] = 0x28; + buffer[32] = 0x87; + buffer[33] = 0x1f; + buffer[34] = 0x00; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800\n")); + + return stat; +}//end BT_init_PAL800. + +static uint8 BT_init_NTSC640() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting NTSC 640x480 desktop mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to + buffer[2] = 0x00; + buffer[3] = 0x80; + buffer[4] = 0x84; + buffer[5] = 0x96; + buffer[6] = 0x60; + buffer[7] = 0x7d; + buffer[8] = 0x22; + buffer[9] = 0xd4; + buffer[10] = 0x27; + buffer[11] = 0x00; + buffer[12] = 0x10; + buffer[13] = 0x7e; + buffer[14] = 0x03; + buffer[15] = 0x58; + buffer[16] = 0x4b; + buffer[17] = 0xe0; + buffer[18] = 0x36; + buffer[19] = 0x92; + buffer[20] = 0x54; + buffer[21] = 0x0e; + buffer[22] = 0x88; + buffer[23] = 0x8c; //chip-pin CLKI is pixel clock (only non-default here!) + buffer[24] = 0x0a; + buffer[25] = 0xe5; + buffer[26] = 0x76; + buffer[27] = 0x79; + buffer[28] = 0x44; + buffer[29] = 0x85; + buffer[30] = 0x00; + buffer[31] = 0x00; + buffer[32] = 0x80; + buffer[33] = 0x20; + buffer[34] = 0x00; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC640\n")); + + return stat; +}//end BT_init_NTSC640. + +static uint8 BT_init_NTSC800() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting NTSC 800x600 desktop mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to + buffer[2] = 0xa0; + buffer[3] = 0x20; + buffer[4] = 0xb6; + buffer[5] = 0xce; + buffer[6] = 0x84; + buffer[7] = 0x55; + buffer[8] = 0x20; + buffer[9] = 0xd8; + buffer[10] = 0x39; + buffer[11] = 0x00; + buffer[12] = 0x70; + buffer[13] = 0x42; + buffer[14] = 0x03; + buffer[15] = 0xdf; + buffer[16] = 0x56; + buffer[17] = 0x58; + buffer[18] = 0x3a; + buffer[19] = 0xcd; + buffer[20] = 0x9c; + buffer[21] = 0x14; + buffer[22] = 0x3b; + buffer[23] = 0x91; //chip-pin CLKI is pixel clock (only non-default here!) + buffer[24] = 0x0a; + buffer[25] = 0xe5; + buffer[26] = 0x74; + buffer[27] = 0x77; + buffer[28] = 0x43; + buffer[29] = 0x85; + buffer[30] = 0xba; + buffer[31] = 0xe8; + buffer[32] = 0xa2; + buffer[33] = 0x17; + buffer[34] = 0x00; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800\n")); + + return stat; +}//end BT_init_NTSC800. + +static uint8 BT_init_PAL720() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting PAL 720x576 overscanning DVD mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to + buffer[2] = 0xf0; + buffer[3] = 0xd0; + buffer[4] = 0x82; + buffer[5] = 0x9c; + buffer[6] = 0x5a; + buffer[7] = 0x31; + buffer[8] = 0x16; + buffer[9] = 0x22; + buffer[10] = 0xa6; + buffer[11] = 0x00; + buffer[12] = 0x78; + buffer[13] = 0x93; + buffer[14] = 0x03; + buffer[15] = 0x71; + buffer[16] = 0x2a; + buffer[17] = 0x40; + buffer[18] = 0x0a; + buffer[19] = 0x00; + buffer[20] = 0x50; + buffer[21] = 0x55; + buffer[22] = 0x55; + buffer[23] = 0x8c; //chip-pin CLKI is pixel clock (only non-default here!) + buffer[24] = 0x24; + buffer[25] = 0xf0; + buffer[26] = 0x59; + buffer[27] = 0x82; + buffer[28] = 0x49; + buffer[29] = 0x8c; + buffer[30] = 0x8e; + buffer[31] = 0xb0; + buffer[32] = 0xe6; + buffer[33] = 0x28; + buffer[34] = 0x00; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode PAL720\n")); + + return stat; +}//end BT_init_PAL720. + +static uint8 BT_init_NTSC720() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting NTSC 720x480 overscanning DVD mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to. + buffer[2] = 0xf0; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) + buffer[3] = 0xd0; //lsb h_active: h_active = 720 pixels wide port + buffer[4] = 0x83; //scope: OK hsync_width: (hsync_width / h_clk_o) * 63.55556uS = 4.70uS for NTSC + buffer[5] = 0x98; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 63.55556uS = 5.3uS for NTSC + buffer[6] = 0x5e; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 63.55556uS = 7.94uS for NTSC + + //How to find the correct values for h_blank_o and v_blank_o: + // 1. Calculate h_blank_o according to initial setting guideline mentioned below; + // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while + // horizontal position is about OK; + // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); + // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); + // 5. If no vertical shivering occurs when image is centered, you're done. Else: + // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: + // 7. Make sure you update the Chrontel horizontal Phase setting also then! + + if (si->ps.tv_encoder.type >= CX25870)//set CX value + { + /* confirmed on NV11 using 4:3 TV and 16:9 TV */ + buffer[7] = 0x0c; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV + //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) + } + else //set BT value + { + /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ + buffer[7] = 0x28; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV + //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) + } + buffer[8] = 0x18; //try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') + + buffer[9] = 0xf2; //v_active_o: = (active output lines + 2) / field (on TV) + buffer[10] = 0x26; //lsn = msn h_clk_o; + //b4-5 = msbits h_active; + //b7 = b8 v_avtive_o. + buffer[11] = 0x00; //h_fract is always 0. + buffer[12] = 0x78; //lsb h_clk_i: h_clk_i is horizontal total = 888. + buffer[13] = 0x90; //try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! + buffer[14] = 0x03; //b2-0 = msn h_clk_i; + //try-out: b3 = msn h_blank_i; + //b4 = vblankdly is always 0. + buffer[15] = 0x0d; //lsb v_lines_i: v_lines_i = 525 + buffer[16] = 0x1a; //try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! + //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. + buffer[17] = 0xe0; //lsb v_active_i: v_active_i = 480 + buffer[18] = 0x36; //b1-0 = msn v_lines_i; + //b3-2 = msn v_active_i; + //b5-4 = ylpf = 3; + //b7-6 = clpf = 0. + buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 + buffer[20] = 0x50; //b5-0 = msn v_scale; + //scope: tuned. b7-6 = msn h_blank_o. + //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 60Hz) + buffer[21] = 0x98; //lsb PLL fract: PLL fract = 0x6e98 + buffer[22] = 0x6e; //msb PLL fract + buffer[23] = 0x8c; //b5-0 = PLL int: PLL int = 0x0c; + //b6 = by_pll: by_pll = 0; + //b7 = EN_XCLK: chip-pin CLKI is pixel clock. + buffer[24] = 0x0a; //b0 = ni_out is always 0; + //b1 = setup = 1 for NTSC; + //b2 = 625line = 0 for NTSC; + //b3 = vsync_dur = 1 for NTSC; + //b4 = dic_screset is always 0; + //b5 = pal_md = 0 for NTSC; + //b6 = eclip is always 0; + //b7 = reserved (en_scart) is always 0. + buffer[25] = 0xe5; //sync_amp $e5 for NTSC + buffer[26] = 0x75; //bst_amp $74-$76 for NTSC + buffer[27] = 0x78; //mcr: r-y $77-$79 for NTSC + buffer[28] = 0x44; //mcb: b-y $43-$44 for NTSC + buffer[29] = 0x85; //my: y $85 for NTSC + buffer[30] = 0x3c; //lsb msc: msc b31-0: NTSC formula: ((3579545 / pixelclk) * 2^32) = MSC + buffer[31] = 0x91; //msc = $20c2913c + buffer[32] = 0xc2; + buffer[33] = 0x20; //msb msc. + buffer[34] = 0x00; //phase_off always $00 + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC720\n")); + + return stat; +}//end BT_init_NTSC720. + +static uint8 BT_init_PAL800_OS() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting PAL 800x600 overscanning VCD mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to. + buffer[2] = 0x60; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) + buffer[3] = 0x20; //lsb h_active: h_active = 800 pixels wide port + buffer[4] = 0x8b; //scope: OK hsync_width: (hsync_width / h_clk_o) * 64.0uS = 4.70uS for PAL + buffer[5] = 0xa5; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 64.0uS = 5.6uS for PAL + buffer[6] = 0x6b; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 64.0uS = 7.97uS for PAL + + //How to find the correct values for h_blank_o and v_blank_o: + // 1. Calculate h_blank_o according to initial setting guideline mentioned below; + // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while + // horizontal position is about OK; + // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); + // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); + // 5. If no vertical shivering occurs when image is centered, you're done. Else: + // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: + // 7. Make sure you update the Chrontel horizontal Phase setting also then! + + if (si->ps.tv_encoder.type >= CX25870)//set CX value + { + /* confirmed on NV11 using 4:3 TV and 16:9 TV */ + buffer[7] = 0xf0; + buffer[8] = 0x17; + } + else //set BT value + { + /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ + buffer[7] = 0xd0;//scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV + //(guideline for initial setting: (h_blank_o / h_clk_0) * 64.0uS = 10.0uS for PAL) + buffer[8] = 0x18;//try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') + } + + buffer[9] = 0x2e; //v_active_o: = (active output lines + 2) / field (on TV) + buffer[10] = 0xb7; //lsn = msn h_clk_o; + //b4-5 = msbits h_active; + //b7 = b8 v_avtive_o. + buffer[11] = 0x00; //h_fract is always 0. + buffer[12] = 0xb0; //lsb h_clk_i: h_clk_i is horizontal total = 944. + + if (si->ps.tv_encoder.type >= CX25870)//set CX value + buffer[13] = 0x20; + else //set BT value + buffer[13] = 0x14;//try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! + + buffer[14] = 0x03; //b2-0 = msn h_clk_i; + //try-out: b3 = msn h_blank_i; + //b4 = vblankdly is always 0. + buffer[15] = 0x71; //lsb v_lines_i: v_lines_i = 625 + + if (si->ps.tv_encoder.type >= CX25870)//set CX value + buffer[16] = 0x08; + else //set BT value + buffer[16] = 0x2a;//try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! + //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. + + buffer[17] = 0x58; //lsb v_active_i: v_active_i = 600 + buffer[18] = 0x3a; //b1-0 = msn v_lines_i; + //b3-2 = msn v_active_i; + //b5-4 = ylpf = 3; + //b7-6 = clpf = 0. + buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 + buffer[20] = 0x10; //b5-0 = msn v_scale; + //scope: tuned. b7-6 = msn h_blank_o. + //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 50Hz) + buffer[21] = 0x72; //lsb PLL fract: PLL fract = 0x1c72 + buffer[22] = 0x1c; //msb PLL fract + buffer[23] = 0x8d; //b5-0 = PLL int: PLL int = 0x0d; + //b6 = by_pll: by_pll = 0; + //b7 = EN_XCLK: chip-pin CLKI is pixel clock. + buffer[24] = 0x24; //b0 = ni_out is always 0; + //b1 = setup = 0 for PAL; + //b2 = 625line = 1 for PAL; + //b3 = vsync_dur = 0 for PAL; + //b4 = dic_screset is always 0; + //b5 = pal_md = 1 for PAL; + //b6 = eclip is always 0; + //b7 = reserved (en_scart) is always 0. + buffer[25] = 0xf0; //sync_amp $f0 for PAL + buffer[26] = 0x57; //bst_amp $57-$58 for PAL + buffer[27] = 0x80; //mcr: r-y $80-$81 for PAL + buffer[28] = 0x48; //mcb: b-y $48-$49 for PAL + buffer[29] = 0x8c; //my: y $8c for PAL + buffer[30] = 0x31; //lsb msc: msc b31-0: PAL formula: ((4433619 / pixelclk) * 2^32) = MSC + buffer[31] = 0x8c; //msc = $26798c31 + buffer[32] = 0x79; + buffer[33] = 0x26; //msb msc. + buffer[34] = 0x00; //phase_off always $00 + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800 OS\n")); + + return stat; +}//end BT_init_PAL800_OS. + +static uint8 BT_init_NTSC640_OS() +{ + uint8 stat; + + uint8 buffer[35]; + + LOG(4,("Brooktree: Setting NTSC 640x480 overscanning VCD mode\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command + buffer[1] = 0x76; //select first bt register to write to. + buffer[2] = 0x20; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) + buffer[3] = 0x80; //lsb h_active: h_active = 640 pixels wide port + buffer[4] = 0x74; //scope: OK hsync_width: (hsync_width / h_clk_o) * 63.55556uS = 4.70uS for NTSC + buffer[5] = 0x83; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 63.55556uS = 5.3uS for NTSC + buffer[6] = 0x44; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 63.55556uS = 7.94uS for NTSC + + //How to find the correct values for h_blank_o and v_blank_o: + // 1. Calculate h_blank_o according to initial setting guideline mentioned below; + // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while + // horizontal position is about OK; + // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); + // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); + // 5. If no vertical shivering occurs when image is centered, you're done. Else: + // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: + // 7. Make sure you update the Chrontel horizontal Phase setting also then! + + buffer[7] = 0xf7; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV: + //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) + + if (si->ps.tv_encoder.type >= CX25870)//set CX value + buffer[8] = 0x1d; + else //set BT value + buffer[8] = 0x1c;//try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') + + buffer[9] = 0xf2; //v_active_o: = (active output lines + 2) / field (on TV) + buffer[10] = 0x26; //lsn = msn h_clk_o; + //b4-5 = msbits h_active; + //b7 = b8 v_avtive_o. + buffer[11] = 0x00; //h_fract is always 0. + buffer[12] = 0x10; //lsb h_clk_i: h_clk_i is horizontal total = 784. + buffer[13] = 0x14; //try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! + buffer[14] = 0x03; //b2-0 = msn h_clk_i; + //try-out: b3 = msn h_blank_i; + //b4 = vblankdly is always 0. + buffer[15] = 0x0d; //lsb v_lines_i: v_lines_i = 525 + buffer[16] = 0x18; //try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! + //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. + buffer[17] = 0xe0; //lsb v_active_i: v_active_i = 480 + buffer[18] = 0x36; //b1-0 = msn v_lines_i; + //b3-2 = msn v_active_i; + //b5-4 = ylpf = 3; + //b7-6 = clpf = 0. + buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 + buffer[20] = 0x10; //b5-0 = msn v_scale; + //scope: tuned. b7-6 = msn h_blank_o. + //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 60Hz) + buffer[21] = 0xdb; //lsb PLL fract: PLL fract = 0xf9db + buffer[22] = 0xf9; //msb PLL fract + buffer[23] = 0x8a; //b5-0 = PLL int: PLL int = 0x0a; + //b6 = by_pll: by_pll = 0; + //b7 = EN_XCLK: chip-pin CLKI is pixel clock. + buffer[24] = 0x0a; //b0 = ni_out is always 0; + //b1 = setup = 1 for NTSC; + //b2 = 625line = 0 for NTSC; + //b3 = vsync_dur = 1 for NTSC; + //b4 = dic_screset is always 0; + //b5 = pal_md = 0 for NTSC; + //b6 = eclip is always 0; + //b7 = reserved (en_scart) is always 0. + buffer[25] = 0xe5; //sync_amp $e5 for NTSC + buffer[26] = 0x75; //bst_amp $74-$76 for NTSC + buffer[27] = 0x78; //mcr: r-y $77-$79 for NTSC + buffer[28] = 0x44; //mcb: b-y $43-$44 for NTSC + buffer[29] = 0x85; //my: y $85 for NTSC + buffer[30] = 0x37; //lsb msc: msc b31-0: NTSC formula: ((3579545 / pixelclk) * 2^32) = MSC + buffer[31] = 0x12; //msc = $251b1237 + buffer[32] = 0x1b; + buffer[33] = 0x25; //msb msc. + buffer[34] = 0x00; //phase_off always $00 + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC640 OS\n")); + + return stat; +}//end BT_init_NTSC640_OS. + +static uint8 BT_testsignal(void) +{ + uint8 stat; + + uint8 buffer[3]; + + LOG(4,("Brooktree: Enabling testsignal\n")); + + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select bt register for enabling colorbars and outputs */ + buffer[1] = 0xc4; + /* issue the actual command */ + buffer[2] = 0x05; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting up flickerfilter and outputs\n")); + + return stat; +}//end BT_testsignal. + +static uint8 BT_setup_output(uint8 monstat, uint8 output, uint8 ffilter) +{ + uint8 stat; + + uint8 buffer[7]; + + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select first TV config register to write */ + buffer[1] = 0xc6; + /* input is 24bit mpx'd RGB, BLANK = out, sync = act. hi */ + buffer[2] = 0x98; + /* disable all filters, exept flicker filter */ + buffer[3] = 0x98; + if (!ffilter) + { + /* disable flicker filter */ + buffer[3] = 0xc0; + LOG(4,("Brooktree: Disabling flickerfilter\n")); + } + else + LOG(4,("Brooktree: Enabling flickerfilter\n")); + + /* (disable filters) */ + buffer[4] = 0xc0; + /* (disable filters) */ + buffer[5] = 0xc0; + switch (output) + /* Description of ELSA Erazor III hardware layout: + * (This is the default (recommended) layout by NVIDIA) + * DAC A = CVBS + * DAC B = C (chrominance) + * DAC C = Y (luminance) */ + + /* Description of Diamond VIPER550: + * DAC A = Not connected + * DAC B = C (chrominance) + * DAC C = Y (luminance) + * To be able to connect to CVBS TV's a special cable is supplied: + * This cable connects the Y (DAC C) output to the TV CVBS input. */ + { + case 1: + LOG(4,("Brooktree: Forcing both Y/C and CVBS signals where supported by hardware\n")); + buffer[6] = 0x18; // Y/C and CVBS out if all ports implemented + // in hardware, else only Y/C or CVBS out. + break; + case 2: + LOG(4,("Brooktree: Forcing CVBS signals on all outputs\n")); + buffer[6] = 0x00; // put CVBS on all outputs. Used for cards + break; // with only Y/C out and 'translation cable'. + default: + LOG(4,("Brooktree: Outputting signals according to autodetect status:\n")); + switch (monstat) // only 'autodetect' remains... + { + case 1: + LOG(4,("Brooktree: Only Y connected, outputting CVBS on all outputs\n")); + buffer[6] = 0x00; //only Y connected: must be CVBS! + break; + case 2: + LOG(4,("Brooktree: Only C connected, outputting CVBS on all outputs\n")); + buffer[6] = 0x00; //only C connected: must be CVBS! + break; //(though cable is wired wrong...) + case 5: + LOG(4,("Brooktree: CVBS and only Y connected, outputting CVBS on all outputs\n")); + buffer[6] = 0x00; //CVBS and only Y connected: 2x CVBS! + break; //(officially not supported...) + case 6: + LOG(4,("Brooktree: CVBS and only C connected, outputting CVBS on all outputs\n")); + buffer[6] = 0x00; //CVBS and only C connected: 2x CVBS! + break; //(officially not supported...) + default: + LOG(4,("Brooktree: Outputting both Y/C and CVBS where supported by hardware\n")); + buffer[6] = 0x18; //nothing, or + //Y/C only, or + //CVBS only (but on CVBS output), or + //Y/C and CVBS connected: + //So activate recommended signals. + } + } + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting up flickerfilter and outputs\n")); + + return stat; +}//end BT_setup_output. + +static uint8 BT_setup_hphase(uint8 mode) +{ + uint8 stat, hoffset; + + uint8 buffer[7]; + + LOG(4,("Brooktree: Tuning horizontal phase\n")); + + /* CX needs timing reset (advised on BT also), first 1mS delay needed! */ + snooze(1000); + + /* values below are all tested on TNT1, TNT2 and GeForce2MX */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select first TV output timing register to write */ + buffer[1] = 0x6c; + /* turn on active video & generate timing reset on CX chips! */ + buffer[2] = 0x86; + /* (set fail save values...) */ + buffer[3] = 0x00; //set default horizontal sync offset + buffer[4] = 0x02; //set default horizontal sync width + buffer[5] = 0x00; //set default vertical sync offset + + /* do specific timing setup for all chips and modes: */ + switch (si->ps.card_type) + { + case NV05: + case NV05M64: + case NV15: + /* confirmed TNT2, TNT2M64, GeForce2Ti. + * (8 pixels delayed hpos, so picture more to the right) */ + hoffset = 8; + break; + default: + /* confirmed TNT1, GeForce256, GeForce2MX. + * (std hpos) + * NOTE: It might be that GeForce needs TNT2 offset: + * for now CX chips get seperate extra offset, until sure. + * (CX is only found AFAIK on GeForce cards, no BT tested + * on GeForce yet. CH was tested on GeForce and seemed to + * indicate TNT1 offset was needed.) */ + hoffset = 0; + break; + } + + switch (mode) + { + case NTSC640_TST: + case NTSC640: + if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... + /* confirmed on TNT1 with BT869 using 4:3 TV and 16:9 TV */ + buffer[3] = (0x1e + hoffset); //set horizontal sync offset + break; + case NTSC800: + if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... + buffer[3] = (0xe1 + hoffset); //set horizontal sync offset + buffer[4] = 0xc2; + //Vsync offset reg. does not exist on CX: mode is checked and OK. + buffer[5] = 0x40; //set VSync offset (on BT's only) + break; + case PAL640: + if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... + buffer[3] = (0xa8 + hoffset); + break; + case PAL800_TST: + case PAL800: + if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... + buffer[3] = (0x2c + hoffset); + break; + case NTSC720: + if (si->ps.tv_encoder.type >= CX25870) + buffer[3] = (0xb2 + hoffset); //set horizontal sync offset CX + else + buffer[3] = (0xd0 + hoffset); //set horizontal sync offset BT + buffer[4] = 0xff; //hsync width = max: + break; //to prevent vertical image 'shivering'. + case PAL720: + buffer[3] = (0xd4 + hoffset); + buffer[4] = 0xff; + break; + case NTSC640_OS: + buffer[3] = (0xc8 + hoffset); + buffer[4] = 0xff; + break; + case PAL800_OS: + if (si->ps.tv_encoder.type >= CX25870) + buffer[3] = (0x78 + hoffset); //set horizontal sync offset CX + else + buffer[3] = (0xc4 + hoffset); //set horizontal sync offset BT + buffer[4] = 0xff; + break; + default: //nothing to be done here... + break; + } + + buffer[6] = 0x01; //set default vertical sync width + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + LOG(4,("Brooktree: I2C errors occurred while setting up h_phase\n")); + + return stat; +}//end BT_setup_hphase. + +static uint8 BT_read_monstat(uint8* monstat) +{ + uint8 stat; + uint8 buffer[3]; + + /* make sure we have the recommended failsafe selected */ + *monstat = 0; + + LOG(4,("Brooktree: Autodetecting connected output devices\n")); + + /* set BT to return connection status in ESTATUS on next read CMD: */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* set ESTATUS at b'01' (return conn.stat.) */ + buffer[1] = 0xc4; + /* and leave chip outputs on. */ + buffer[2] = 0x41; + + /* reset status */ + i2c_flag_error (-1); + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while reading connection status (1)\n")); + return stat; + } + + /* do actual read connection status: */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select register with CHECK_STAT CMD */ + buffer[1] = 0xba; + /* issue actual command. */ + buffer[2] = 0x40; + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while reading connection status (2)\n")); + return stat; + } + + /* CX: Wait 600uS for signals to stabilize (see datasheet) */ + /* warning, note: + * datasheet is in error! 60mS needed!! */ + snooze(60000); + + /* read back updated connection status: */ + buffer[0] = si->ps.tv_encoder.adress + RD; + + /* transmit 1 byte */ + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 1); + + /* receive 1 byte */ + /* ACK level to TX after last byte to RX should be 1 (= NACK) (see I2C spec) + * While the BT's don't care, CX chips will block the SDA line if an ACK gets sent! */ + buffer[0] = 1; + i2c_readbuffer(si->ps.tv_encoder.bus, buffer, 1); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while reading connection status (3)\n")); + return stat; + } + + *monstat = ((buffer[0] & 0xe0) >> 5); + LOG(4,("Brooktree: TV output monitor status = %d\n", *monstat)); + + /* instruct BT to go back to normal operation: */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select register with CHECK_STAT CMD */ + buffer[1] = 0xba; + /* issue actual command. */ + buffer[2] = 0x00; + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while reading connection status (4)\n")); + return stat; + } + + return stat; +}//end BT_read_monstat. + +static uint8 BT_killclk_blackout(void) +{ + uint8 stat; + + uint8 buffer[4]; + + LOG(4,("Brooktree: Killing clock and/or blacking out (blocking output signals)\n")); + + /* reset status */ + i2c_flag_error (-1); + + if (si->ps.tv_encoder.type <= BT869) //BT... + { + /* Only disable external pixelclock input on BT's. + * CX chips will lock the bus if you do this. + * (It looks like the external pixelclock is always OK as long as a valid + * mode is programmed for the TVout chip. This means that disabling the use + * of this clock is not needed anyway. + * If you do disable this input, this pixelclock will rise to about 60Mhz BTW..) */ + + /* disable use of external pixelclock source... */ + /* (should prevent BT for being 'overclocked' by RIVA in VGA-only mode...) */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select BT register for setting EN_XCLK */ + buffer[1] = 0xa0; + /* clear it */ + buffer[2] = 0x00; + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (1-BT)\n")); + return stat; + } + } + else //CX... + { + /* Disable CX video out (or wild output will be seen on TV..) */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select register in CX */ + buffer[1] = 0x6c; + /* disable active video out. */ + buffer[2] = 0x02; + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (1-CX)\n")); + return stat; + } + } + + /* black-out TVout while outputs are enabled... */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select first TV config register to write */ + buffer[1] = 0xc4; + /* disable testimage while outputs remain enabled */ + buffer[2] = 0x01; + /* input is 24bit mpx'd RGB, BLANK = in, sync = act. hi */ + buffer[3] = 0x18; + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (2)\n")); + return stat; + } + + return stat; +}//end BT_killclk_blackout. + +uint8 BT_dpms(bool display) +{ + uint8 stat; + + uint8 buffer[3]; + + LOG(4,("Brooktree: setting DPMS: ")); + + /* reset status */ + i2c_flag_error (-1); + + /* shutdown all analog electronics... */ + buffer[0] = si->ps.tv_encoder.adress + WR; + /* select first TV config register to write */ + buffer[1] = 0xba; + if (display) + { + /* enable all DACs */ + buffer[2] = 0x00; + LOG(4,("display on\n")); + } + else + { + /* shutdown all DACs */ + buffer[2] = 0x10; + LOG(4,("display off\n")); + } + + i2c_bstart(si->ps.tv_encoder.bus); + i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); + i2c_bstop(si->ps.tv_encoder.bus); + /* log on errors */ + stat = i2c_flag_error(0); + if (stat) + { + LOG(4,("Brooktree: I2C errors occurred while setting DPMS\n")); + return stat; + } + + return stat; +}//end BT_dpms. + +uint8 BT_check_tvmode(display_mode target) +{ + uint8 status = NOT_SUPPORTED; + uint32 mode = ((target.timing.h_display) | ((target.timing.v_display) << 16)); + + switch (mode) + { + case (640 | (480 << 16)): + if (((target.flags & TV_BITS) == TV_PAL) && (!(target.flags & TV_VIDEO))) + status = PAL640; + if ((target.flags & TV_BITS) == TV_NTSC) + { + if (!(target.flags & TV_VIDEO)) status = NTSC640; + else status = NTSC640_OS; + } + break; + case (768 | (576 << 16)): + if (((target.flags & TV_BITS) == TV_PAL) && (target.flags & TV_VIDEO)) + status = PAL800_OS; + break; + case (800 | (600 << 16)): + if (((target.flags & TV_BITS) == TV_PAL) && (!(target.flags & TV_VIDEO))) + status = PAL800; + if (((target.flags & TV_BITS) == TV_NTSC) && (!(target.flags & TV_VIDEO))) + status = NTSC800; + break; + case (720 | (480 << 16)): + if (((target.flags & TV_BITS) == TV_NTSC) && (target.flags & TV_VIDEO)) + status = NTSC720; + break; + case (720 | (576 << 16)): + if (((target.flags & TV_BITS) == TV_PAL) && (target.flags & TV_VIDEO)) + status = PAL720; + break; + } + + return status; +}//end BT_check_tvmode. + + +/* +//BeTVOut's SwitchRIVAtoTV(vtot) timing formula: (note: vtot = (v_total - 2)) +//----------------------------------------------------------------------------------- +//HORIZONTAL: +//----------- +h_sync_start = h_display; + +//fixme, note, checkout: +//feels like in fact TNT2-M64 nv_crtc.c registerprogramming should be adapted... +if (TNT2-M64) +{ + h_sync_end = h_display + 8; + h_total = h_display + 56; +} +else //TNT1, TNT2, Geforce2... (so default) +{ + h_sync_end = h_display + 16; + h_total = h_display + 48; +} + +//fixme, note, checkout: +//BeTVOut uses two 'tweaks': +// - on TNT2-M64 only: +// register h_blank_e is increased with 1 (so should be done in nv_crtc.c here) +// - 'all cards': +// register h_blank_e b6 = 0 (only influences TNT2-M64 in modes NTSC800 and PAL800). +//----------------------------------------------------------------------------------- +//VERTICAL: +//--------- +v_sync_start = v_display; +v_total = vtot + 2; +v_sync_end = v_total - 1; //(This takes care of the 'cursor trash' on TNT1's...) +//----------------------------------------------------------------------------------- +*/ +static status_t BT_update_mode_for_gpu(display_mode *target, uint8 tvmode) +{ + //fixme if needed: + //pixelclock is not actually pgm'd because PLL is pgm' earlier during setmode... + switch (tvmode) + { + case NTSC640: + case NTSC640_TST: + target->timing.h_display = 640; + target->timing.h_sync_start = 640; + if (si->ps.card_type == NV05M64) + { + target->timing.h_sync_end = 648; + target->timing.h_total = 696; + } + else + { + //fixme if possible: + //see if tweaking h_sync_end can shift picture 8 pixels right to fix + //ws tv's tuning fault (always going for max. compatibility :) + target->timing.h_sync_end = 656; + target->timing.h_total = 688; + } + target->timing.v_display = 480; + target->timing.v_sync_start = 480; + target->timing.v_sync_end = 555; //This prevents 'cursor trash' on TNT1's + target->timing.v_total = 556; //Above 525 because mode scales down + if (si->ps.card_type == NV05M64) + target->timing.pixel_clock = ((696 * 556 * 60) / 1000); + else + target->timing.pixel_clock = ((688 * 556 * 60) / 1000); + break; + case NTSC800: + target->timing.h_display = 800; + target->timing.h_sync_start = 800; + if (si->ps.card_type == NV05M64) + { + target->timing.h_sync_end = 808; + target->timing.h_total = 856; + } + else + { + target->timing.h_sync_end = 816; + target->timing.h_total = 848; + } + target->timing.v_display = 600; + target->timing.v_sync_start = 600; + target->timing.v_sync_end = 685; //This prevents 'cursor trash' on TNT1's + target->timing.v_total = 686; //Above 525 because mode scales down + if (si->ps.card_type == NV05M64) + target->timing.pixel_clock = ((856 * 686 * 60) / 1000); + else + target->timing.pixel_clock = ((848 * 686 * 60) / 1000); + break; + case PAL640: + target->timing.h_display = 640; + target->timing.h_sync_start = 640; + if (si->ps.card_type == NV05M64) + { + target->timing.h_sync_end = 648; + target->timing.h_total = 696; + } + else + { + target->timing.h_sync_end = 656; + target->timing.h_total = 688; + } + target->timing.v_display = 480; + target->timing.v_sync_start = 480; + target->timing.v_sync_end = 570; //This prevents 'cursor trash' on TNT1's + target->timing.v_total = 571; //Below 625 because mode scales up + if (si->ps.card_type == NV05M64) + target->timing.pixel_clock = ((696 * 571 * 50) / 1000); + else + target->timing.pixel_clock = ((688 * 571 * 50) / 1000); + break; + case PAL800: + case PAL800_TST: + target->timing.h_display = 800; + target->timing.h_sync_start = 800; + if (si->ps.card_type == NV05M64) + { + target->timing.h_sync_end = 808; + target->timing.h_total = 856; + } + else + { + target->timing.h_sync_end = 816; + target->timing.h_total = 848; + } + target->timing.v_display = 600; + target->timing.v_sync_start = 600; + target->timing.v_sync_end = 695; //This prevents 'cursor trash' on TNT1's + target->timing.v_total = 696; //Above 625 because mode scales down + if (si->ps.card_type == NV05M64) + target->timing.pixel_clock = ((856 * 696 * 50) / 1000); + else + target->timing.pixel_clock = ((848 * 696 * 50) / 1000); + break; + case NTSC640_OS: + target->timing.h_display = 640; //BT H_ACTIVE + target->timing.h_sync_start = 744; //set for CH/BT compatible TV output + target->timing.h_sync_end = 744+20; //delta is BT H_BLANKI + target->timing.h_total = 784; //BT H_CLKI + target->timing.v_display = 480; //BT V_ACTIVEI + target->timing.v_sync_start = 490; //set for centered sync pulse + target->timing.v_sync_end = 490+25; //delta is BT V_BLANKI + target->timing.v_total = 525; //BT V_LINESI (== 525: 1:1 scaled mode) + target->timing.pixel_clock = ((784 * 525 * 60) / 1000); //refresh + break; + case PAL800_OS: + target->timing.h_display = 768; //H_ACTIVE + if (si->ps.tv_encoder.type <= BT869) + { + /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ + target->timing.h_sync_start = 856; //set for centered TV output + target->timing.h_sync_end = 856+20; //delta is BT H_BLANKI + } + else + { + /* confirmed on NV11 using 4:3 TV and 16:9 TV */ + target->timing.h_sync_start = 848; //set for centered TV output + target->timing.h_sync_end = 848+20; //delta is BT H_BLANKI + } + target->timing.h_total = 944; //BT H_CLKI + target->timing.v_display = 576; //V_ACTIVEI + target->timing.v_sync_start = 579; //set for centered sync pulse + target->timing.v_sync_end = 579+42; //delta is BT V_BLANKI + target->timing.v_total = 625; //BT V_LINESI (== 625: 1:1 scaled mode) + target->timing.pixel_clock = ((944 * 625 * 50) / 1000); //refresh + break; + case NTSC720: + /* (tested on TNT2 with BT869) */ + target->timing.h_display = 720; //H_ACTIVE + if (si->ps.tv_encoder.type <= BT869) + { + /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ + target->timing.h_sync_start = 744; //do not change! + target->timing.h_sync_end = 744+144; //delta is H_sync_pulse + } + else + { + /* confirmed on NV11 using 4:3 TV and 16:9 TV */ + target->timing.h_sync_start = 728; //do not change! + target->timing.h_sync_end = 728+160; //delta is H_sync_pulse + } + target->timing.h_total = 888; //BT H_TOTAL + target->timing.v_display = 480; //V_ACTIVEI + target->timing.v_sync_start = 490; //set for centered sync pulse + target->timing.v_sync_end = 490+26; //delta is V_sync_pulse + target->timing.v_total = 525; //CH V_TOTAL (== 525: 1:1 scaled mode) + target->timing.pixel_clock = ((888 * 525 * 60) / 1000); //refresh + break; + case PAL720: + target->timing.h_display = 720; //BT H_ACTIVE + target->timing.h_sync_start = 744; //set for centered sync pulse + target->timing.h_sync_end = 744+140; //delta is BT H_BLANKI + target->timing.h_total = 888; //BT H_CLKI + target->timing.v_display = 576; //BT V_ACTIVEI + target->timing.v_sync_start = 579; //set for centered sync pulse + target->timing.v_sync_end = 579+42; //delta is BT V_BLANKI + target->timing.v_total = 625; //BT V_LINESI (== 625: 1:1 scaled mode) + target->timing.pixel_clock = ((888 * 625 * 50) / 1000); //refresh + break; + default: + return B_ERROR; + } + + return B_OK; +}//end BT_update_mode_for_gpu. + +/* note: + * tested on ELSA Erazor III 32Mb AGP (TNT2/BT869), + * Diamond Viper V550 16Mb PCI (TNT1/BT869), + * and ASUS V7100 GeForce2 MX200 AGP/32Mb (CH7007). */ +static status_t BT_start_tvout(display_mode tv_target) +{ + /* TV_PRIMARY tells us that the head to be used with TVout is the head that's + * actually assigned as being the primary head at powerup: + * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ + if (tv_target.flags & TV_PRIMARY) + { + if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head1_start_tvout(); + else + head2_start_tvout(); + } + else + { + if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head2_start_tvout(); + else + head1_start_tvout(); + } + + return B_OK; +}//end BT_start_tvout. + +/* note: + * tested on ELSA Erazor III 32Mb AGP (TNT2/BT869), + * Diamond Viper V550 16Mb PCI (TNT1/BT869), + * and ASUS V7100 GeForce2 MX200 AGP/32Mb (CH7007). */ +status_t BT_stop_tvout(void) +{ + /* prevent BT from being overclocked by VGA-only modes & black-out TV-out */ + BT_killclk_blackout(); + + /* TV_PRIMARY tells us that the head to be used with TVout is the head that's + * actually assigned as being the primary head at powerup: + * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ + if (si->dm.flags & TV_PRIMARY) + { + if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head1_stop_tvout(); + else + head2_stop_tvout(); + } + else + { + if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head2_stop_tvout(); + else + head1_stop_tvout(); + } + + /* fixme if needed: + * a full encoder chip reset could be done here (so after decoupling crtc)... */ + /* (but: beware of the 'locked SDA' syndrome then!) */ + + /* fixme if needed: we _could_ setup a TVout mode and apply the testsignal here... */ + if (0) + { + //set mode (selecting PAL/NTSC according to board wiring for example) etc, then: + BT_testsignal(); + } + + return B_OK; +}//end BT_stop_tvout. + +status_t BT_setmode(display_mode target) +{ + uint8 tvmode, monstat; + /* enable flickerfilter in desktop modes, disable it in video modes. */ + uint8 ffilter = 0; + + /* use a display_mode copy because we might tune it for TVout compatibility */ + display_mode tv_target = target; + + /* preset new TVout mode */ + tvmode = BT_check_tvmode(tv_target); + if (!tvmode) return B_ERROR; + + /* read current output devices connection status */ + BT_read_monstat(&monstat); + + /* (pre)set TV mode */ + /* note: + * Manual config is non-dependent of the state of the PAL hardware input pin; + * Also SDA lockups occur when setting EN_XCLK after autoconfig! + * Make sure PAL_MD=0 for NTSC and PAL_MD = 1 for PAL... */ + switch (tvmode) + { + case NTSC640: + case NTSC640_TST: + ffilter = 1; + BT_init_NTSC640(); + break; + case NTSC800: + ffilter = 1; + BT_init_NTSC800(); + break; + case PAL640: + ffilter = 1; + BT_init_PAL640(); + break; + case PAL800: + case PAL800_TST: + ffilter = 1; + BT_init_PAL800(); + break; + case NTSC640_OS: + BT_init_NTSC640_OS(); + break; + case PAL800_OS: + BT_init_PAL800_OS(); + break; + case NTSC720: + BT_init_NTSC720(); + break; + case PAL720: + BT_init_PAL720(); + break; + } + + /* modify BT Hphase signal to center TV image... */ + BT_setup_hphase(tvmode); + + /* disable Macro mode */ + switch (tvmode) + { + case NTSC640: + case NTSC640_TST: + case NTSC800: + case NTSC640_OS: + case NTSC720: + /* NTSC */ + BT_set_macro (0, 0); + break; + default: + /* PAL */ + BT_set_macro (1, 0); + break; + } + + /* setup output signal routing and flickerfilter */ + BT_setup_output(monstat, (uint8)(si->settings.tv_output), ffilter); + + /* update the GPU CRTC timing for the requested mode */ + BT_update_mode_for_gpu(&tv_target, tvmode); + + /* setup GPU CRTC timing */ + /* TV_PRIMARY tells us that the head to be used with TVout is the head that's + * actually assigned as being the primary head at powerup: + * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ + if (tv_target.flags & TV_PRIMARY) + { + if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head1_set_timing(tv_target); + else + head2_set_timing(tv_target); + } + else + { + if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) + head2_set_timing(tv_target); + else + head1_set_timing(tv_target); + } + + /* now set GPU CRTC to slave mode */ + BT_start_tvout(tv_target); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc.c new file mode 100644 index 0000000000..02a5abed3c --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc.c @@ -0,0 +1,1114 @@ +/* CTRC functionality */ +/* Author: + Rudolf Cornelissen 11/2002-2/2006 +*/ + +#define MODULE_BIT 0x00040000 + +#include "nv_std.h" + +/* + Enable/Disable interrupts. Just a wrapper around the + ioctl() to the kernel driver. +*/ +status_t nv_crtc_interrupt_enable(bool flag) +{ + status_t result = B_OK; + nv_set_vblank_int svi; + + if (si->ps.int_assigned) + { + /* set the magic number so the driver knows we're for real */ + svi.magic = NV_PRIVATE_DATA_MAGIC; + svi.crtc = 0; + svi.do_it = flag; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, NV_RUN_INTERRUPTS, &svi, sizeof(svi)); + } + + return result; +} + +/* doing general fail-safe default setup here */ +//fixme: this is a _very_ basic setup, and it's preliminary... +status_t nv_crtc_update_fifo() +{ + uint8 bytes_per_pixel = 1; + uint32 drain; + + /* we are only using this on >>coldstarted<< cards which really need this */ + //fixme: re-enable or remove after general user confirmation of behaviour... + if (/*(si->settings.usebios) ||*/ (si->ps.card_type != NV05M64)) return B_OK; + + /* enable access to primary head */ + set_crtc_owner(0); + + /* set CRTC FIFO low watermark according to memory drain */ + switch(si->dm.space) + { + case B_CMAP8: + bytes_per_pixel = 1; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + bytes_per_pixel = 2; + break; + case B_RGB24_LITTLE: + bytes_per_pixel = 3; + break; + case B_RGB32_LITTLE: + bytes_per_pixel = 4; + break; + } + /* fixme: + * - I should probably include the refreshrate as well; + * - and the memory clocking speed, core clocking speed, RAM buswidth.. */ + drain = si->dm.timing.h_display * si->dm.timing.v_display * bytes_per_pixel; + + /* Doesn't work for other than 32bit space (yet?) */ + if (si->dm.space != B_RGB32_LITTLE) + { + /* BIOS defaults */ + CRTCW(FIFO, 0x03); + CRTCW(FIFO_LWM, 0x20); + LOG(4,("CRTC: FIFO low-watermark set to $20, burst size 256 (BIOS defaults)\n")); + return B_OK; + } + + if (drain > (((uint32)1280) * 1024 * 4)) + { + /* set CRTC FIFO burst size for 'smaller' bursts */ + CRTCW(FIFO, 0x01); + /* Instruct CRTC to fetch new data 'earlier' */ + CRTCW(FIFO_LWM, 0x40); + LOG(4,("CRTC: FIFO low-watermark set to $40, burst size 64\n")); + } + else + { + if (drain > (((uint32)1024) * 768 * 4)) + { + /* BIOS default */ + CRTCW(FIFO, 0x02); + /* Instruct CRTC to fetch new data 'earlier' */ + CRTCW(FIFO_LWM, 0x40); + LOG(4,("CRTC: FIFO low-watermark set to $40, burst size 128\n")); + } + else + { + /* BIOS defaults */ + CRTCW(FIFO, 0x03); + CRTCW(FIFO_LWM, 0x20); + LOG(4,("CRTC: FIFO low-watermark set to $20, burst size 256 (BIOS defaults)\n")); + } + } + + return B_OK; +} + +/* Adjust passed parameters to a valid mode line */ +status_t nv_crtc_validate_timing( + uint16 *hd_e,uint16 *hs_s,uint16 *hs_e,uint16 *ht, + uint16 *vd_e,uint16 *vs_s,uint16 *vs_e,uint16 *vt +) +{ +/* horizontal */ + /* make all parameters multiples of 8 */ + *hd_e &= 0xfff8; + *hs_s &= 0xfff8; + *hs_e &= 0xfff8; + *ht &= 0xfff8; + + /* confine to required number of bits, taking logic into account */ + if (*hd_e > ((0x01ff - 2) << 3)) *hd_e = ((0x01ff - 2) << 3); + if (*hs_s > ((0x01ff - 1) << 3)) *hs_s = ((0x01ff - 1) << 3); + if (*hs_e > ( 0x01ff << 3)) *hs_e = ( 0x01ff << 3); + if (*ht > ((0x01ff + 5) << 3)) *ht = ((0x01ff + 5) << 3); + + /* NOTE: keep horizontal timing at multiples of 8! */ + /* confine to a reasonable width */ + if (*hd_e < 640) *hd_e = 640; + if (si->ps.card_type > NV04) + { + if (*hd_e > 2048) *hd_e = 2048; + } + else + { + if (*hd_e > 1920) *hd_e = 1920; + } + + /* if hor. total does not leave room for a sensible sync pulse, increase it! */ + if (*ht < (*hd_e + 80)) *ht = (*hd_e + 80); + + /* if hor. total does not adhere to max. blanking pulse width, decrease it! */ + if (*ht > (*hd_e + 0x3f8)) *ht = (*hd_e + 0x3f8); + + /* make sure sync pulse is not during display */ + if (*hs_e > (*ht - 8)) *hs_e = (*ht - 8); + if (*hs_s < (*hd_e + 8)) *hs_s = (*hd_e + 8); + + /* correct sync pulse if it is too long: + * there are only 5 bits available to save this in the card registers! */ + if (*hs_e > (*hs_s + 0xf8)) *hs_e = (*hs_s + 0xf8); + +/*vertical*/ + /* confine to required number of bits, taking logic into account */ + //fixme if needed: on GeForce cards there are 12 instead of 11 bits... + if (*vd_e > (0x7ff - 2)) *vd_e = (0x7ff - 2); + if (*vs_s > (0x7ff - 1)) *vs_s = (0x7ff - 1); + if (*vs_e > 0x7ff ) *vs_e = 0x7ff ; + if (*vt > (0x7ff + 2)) *vt = (0x7ff + 2); + + /* confine to a reasonable height */ + if (*vd_e < 480) *vd_e = 480; + if (si->ps.card_type > NV04) + { + if (*vd_e > 1536) *vd_e = 1536; + } + else + { + if (*vd_e > 1440) *vd_e = 1440; + } + + /*if vertical total does not leave room for a sync pulse, increase it!*/ + if (*vt < (*vd_e + 3)) *vt = (*vd_e + 3); + + /* if vert. total does not adhere to max. blanking pulse width, decrease it! */ + if (*vt > (*vd_e + 0xff)) *vt = (*vd_e + 0xff); + + /* make sure sync pulse is not during display */ + if (*vs_e > (*vt - 1)) *vs_e = (*vt - 1); + if (*vs_s < (*vd_e + 1)) *vs_s = (*vd_e + 1); + + /* correct sync pulse if it is too long: + * there are only 4 bits available to save this in the card registers! */ + if (*vs_e > (*vs_s + 0x0f)) *vs_e = (*vs_s + 0x0f); + + return B_OK; +} + +/*set a mode line - inputs are in pixels*/ +status_t nv_crtc_set_timing(display_mode target) +{ + uint8 temp; + + uint32 htotal; /*total horizontal total VCLKs*/ + uint32 hdisp_e; /*end of horizontal display (begins at 0)*/ + uint32 hsync_s; /*begin of horizontal sync pulse*/ + uint32 hsync_e; /*end of horizontal sync pulse*/ + uint32 hblnk_s; /*begin horizontal blanking*/ + uint32 hblnk_e; /*end horizontal blanking*/ + + uint32 vtotal; /*total vertical total scanlines*/ + uint32 vdisp_e; /*end of vertical display*/ + uint32 vsync_s; /*begin of vertical sync pulse*/ + uint32 vsync_e; /*end of vertical sync pulse*/ + uint32 vblnk_s; /*begin vertical blanking*/ + uint32 vblnk_e; /*end vertical blanking*/ + + uint32 linecomp; /*split screen and vdisp_e interrupt*/ + + LOG(4,("CRTC: setting timing\n")); + + /* setup tuned internal modeline for flatpanel if connected and active */ + /* notes: + * - the CRTC modeline must end earlier than the panel modeline to keep correct + * sync going; + * - if the CRTC modeline ends too soon, pixelnoise will occur in 8 (or so) pixel + * wide horizontal stripes. This can be observed earliest on fullscreen overlay, + * and if it gets worse, also normal desktop output will suffer. The stripes + * are mainly visible at the left of the screen, over the entire screen height. */ + if (si->ps.tmds1_active) + { + LOG(2,("CRTC: DFP active: tuning modeline\n")); + + /* horizontal timing */ + target.timing.h_sync_start = + ((uint16)((si->ps.p1_timing.h_sync_start / ((float)si->ps.p1_timing.h_display)) * + target.timing.h_display)) & 0xfff8; + + target.timing.h_sync_end = + ((uint16)((si->ps.p1_timing.h_sync_end / ((float)si->ps.p1_timing.h_display)) * + target.timing.h_display)) & 0xfff8; + + target.timing.h_total = + (((uint16)((si->ps.p1_timing.h_total / ((float)si->ps.p1_timing.h_display)) * + target.timing.h_display)) & 0xfff8) - 8; + + /* in native mode the CRTC needs some extra time to keep synced correctly; + * OTOH the overlay unit distorts if we reserve too much time! */ + if (target.timing.h_display == si->ps.p1_timing.h_display) + { + /* NV11 timing has different constraints than later cards */ + if (si->ps.card_type == NV11) + target.timing.h_total -= 56; + else + /* confirmed NV34 with 1680x1050 panel */ + target.timing.h_total -= 32; + } + + if (target.timing.h_sync_start == target.timing.h_display) + target.timing.h_sync_start += 8; + if (target.timing.h_sync_end == target.timing.h_total) + target.timing.h_sync_end -= 8; + + /* vertical timing */ + target.timing.v_sync_start = + ((uint16)((si->ps.p1_timing.v_sync_start / ((float)si->ps.p1_timing.v_display)) * + target.timing.v_display)); + + target.timing.v_sync_end = + ((uint16)((si->ps.p1_timing.v_sync_end / ((float)si->ps.p1_timing.v_display)) * + target.timing.v_display)); + + target.timing.v_total = + ((uint16)((si->ps.p1_timing.v_total / ((float)si->ps.p1_timing.v_display)) * + target.timing.v_display)) - 1; + + if (target.timing.v_sync_start == target.timing.v_display) + target.timing.v_sync_start += 1; + if (target.timing.v_sync_end == target.timing.v_total) + target.timing.v_sync_end -= 1; + + /* disable GPU scaling testmode so automatic scaling will be done */ + DACW(FP_DEBUG1, 0); + } + + /* Modify parameters as required by standard VGA */ + htotal = ((target.timing.h_total >> 3) - 5); + hdisp_e = ((target.timing.h_display >> 3) - 1); + hblnk_s = hdisp_e; + hblnk_e = (htotal + 4); + hsync_s = (target.timing.h_sync_start >> 3); + hsync_e = (target.timing.h_sync_end >> 3); + + vtotal = target.timing.v_total - 2; + vdisp_e = target.timing.v_display - 1; + vblnk_s = vdisp_e; + vblnk_e = (vtotal + 1); + vsync_s = target.timing.v_sync_start; + vsync_e = target.timing.v_sync_end; + + /* prevent memory adress counter from being reset (linecomp may not occur) */ + linecomp = target.timing.v_display; + + /* enable access to primary head */ + set_crtc_owner(0); + + /* Note for laptop and DVI flatpanels: + * CRTC timing has a seperate set of registers from flatpanel timing. + * The flatpanel timing registers have scaling registers that are used to match + * these two modelines. */ + { + LOG(4,("CRTC: Setting full timing...\n")); + + /* log the mode that will be set */ + LOG(2,("CRTC:\n\tHTOT:%x\n\tHDISPEND:%x\n\tHBLNKS:%x\n\tHBLNKE:%x\n\tHSYNCS:%x\n\tHSYNCE:%x\n\t",htotal,hdisp_e,hblnk_s,hblnk_e,hsync_s,hsync_e)); + LOG(2,("VTOT:%x\n\tVDISPEND:%x\n\tVBLNKS:%x\n\tVBLNKE:%x\n\tVSYNCS:%x\n\tVSYNCE:%x\n",vtotal,vdisp_e,vblnk_s,vblnk_e,vsync_s,vsync_e)); + + /* actually program the card! */ + /* unlock CRTC registers at index 0-7 */ + CRTCW(VSYNCE, (CRTCR(VSYNCE) & 0x7f)); + /* horizontal standard VGA regs */ + CRTCW(HTOTAL, (htotal & 0xff)); + CRTCW(HDISPE, (hdisp_e & 0xff)); + CRTCW(HBLANKS, (hblnk_s & 0xff)); + /* also unlock vertical retrace registers in advance */ + CRTCW(HBLANKE, ((hblnk_e & 0x1f) | 0x80)); + CRTCW(HSYNCS, (hsync_s & 0xff)); + CRTCW(HSYNCE, ((hsync_e & 0x1f) | ((hblnk_e & 0x20) << 2))); + + /* vertical standard VGA regs */ + CRTCW(VTOTAL, (vtotal & 0xff)); + CRTCW(OVERFLOW, + ( + ((vtotal & 0x100) >> (8 - 0)) | ((vtotal & 0x200) >> (9 - 5)) | + ((vdisp_e & 0x100) >> (8 - 1)) | ((vdisp_e & 0x200) >> (9 - 6)) | + ((vsync_s & 0x100) >> (8 - 2)) | ((vsync_s & 0x200) >> (9 - 7)) | + ((vblnk_s & 0x100) >> (8 - 3)) | ((linecomp & 0x100) >> (8 - 4)) + )); + CRTCW(PRROWSCN, 0x00); /* not used */ + CRTCW(MAXSCLIN, (((vblnk_s & 0x200) >> (9 - 5)) | ((linecomp & 0x200) >> (9 - 6)))); + CRTCW(VSYNCS, (vsync_s & 0xff)); + CRTCW(VSYNCE, ((CRTCR(VSYNCE) & 0xf0) | (vsync_e & 0x0f))); + CRTCW(VDISPE, (vdisp_e & 0xff)); + CRTCW(VBLANKS, (vblnk_s & 0xff)); + CRTCW(VBLANKE, (vblnk_e & 0xff)); + CRTCW(LINECOMP, (linecomp & 0xff)); + + /* horizontal extended regs */ + //fixme: we reset bit4. is this correct?? + CRTCW(HEB, (CRTCR(HEB) & 0xe0) | + ( + ((htotal & 0x100) >> (8 - 0)) | + ((hdisp_e & 0x100) >> (8 - 1)) | + ((hblnk_s & 0x100) >> (8 - 2)) | + ((hsync_s & 0x100) >> (8 - 3)) + )); + + /* (mostly) vertical extended regs */ + CRTCW(LSR, + ( + ((vtotal & 0x400) >> (10 - 0)) | + ((vdisp_e & 0x400) >> (10 - 1)) | + ((vsync_s & 0x400) >> (10 - 2)) | + ((vblnk_s & 0x400) >> (10 - 3)) | + ((hblnk_e & 0x040) >> (6 - 4)) + //fixme: we still miss one linecomp bit!?! is this it?? + //| ((linecomp & 0x400) >> 3) + )); + + /* more vertical extended regs (on GeForce cards only) */ + if (si->ps.card_arch >= NV10A) + { + CRTCW(EXTRA, + ( + ((vtotal & 0x800) >> (11 - 0)) | + ((vdisp_e & 0x800) >> (11 - 2)) | + ((vsync_s & 0x800) >> (11 - 4)) | + ((vblnk_s & 0x800) >> (11 - 6)) + //fixme: do we miss another linecomp bit!?! + )); + } + + /* setup 'large screen' mode */ + if (target.timing.h_display >= 1280) + CRTCW(REPAINT1, (CRTCR(REPAINT1) & 0xfb)); + else + CRTCW(REPAINT1, (CRTCR(REPAINT1) | 0x04)); + + /* setup HSYNC & VSYNC polarity */ + LOG(2,("CRTC: sync polarity: ")); + temp = NV_REG8(NV8_MISCR); + if (target.timing.flags & B_POSITIVE_HSYNC) + { + LOG(2,("H:pos ")); + temp &= ~0x40; + } + else + { + LOG(2,("H:neg ")); + temp |= 0x40; + } + if (target.timing.flags & B_POSITIVE_VSYNC) + { + LOG(2,("V:pos ")); + temp &= ~0x80; + } + else + { + LOG(2,("V:neg ")); + temp |= 0x80; + } + NV_REG8(NV8_MISCW) = temp; + + LOG(2,(", MISC reg readback: $%02x\n", NV_REG8(NV8_MISCR))); + } + + /* always disable interlaced operation */ + /* (interlace is supported on upto and including NV10, NV15, and NV30 and up) */ + CRTCW(INTERLACE, 0xff); + + /* disable CRTC slaved mode unless a panel is in use */ + // fixme: this kills TVout when it was in use... + if (!si->ps.tmds1_active) CRTCW(PIXEL, (CRTCR(PIXEL) & 0x7f)); + + /* setup flatpanel if connected and active */ + if (si->ps.tmds1_active) + { + uint32 iscale_x, iscale_y; + + /* calculate inverse scaling factors used by hardware in 20.12 format */ + iscale_x = (((1 << 12) * target.timing.h_display) / si->ps.p1_timing.h_display); + iscale_y = (((1 << 12) * target.timing.v_display) / si->ps.p1_timing.v_display); + + /* unblock flatpanel timing programming (or something like that..) */ + CRTCW(FP_HTIMING, 0); + CRTCW(FP_VTIMING, 0); + LOG(2,("CRTC: FP_HTIMING reg readback: $%02x\n", CRTCR(FP_HTIMING))); + LOG(2,("CRTC: FP_VTIMING reg readback: $%02x\n", CRTCR(FP_VTIMING))); + + /* enable full width visibility on flatpanel */ + DACW(FP_HVALID_S, 0); + DACW(FP_HVALID_E, (si->ps.p1_timing.h_display - 1)); + /* enable full height visibility on flatpanel */ + DACW(FP_VVALID_S, 0); + DACW(FP_VVALID_E, (si->ps.p1_timing.v_display - 1)); + + /* nVidia cards support upscaling except on ??? */ + /* NV11 cards can upscale after all! */ + if (0)//si->ps.card_type == NV11) + { + /* disable last fetched line limiting */ + DACW(FP_DEBUG2, 0x00000000); + /* inform panel to scale if needed */ + if ((iscale_x != (1 << 12)) || (iscale_y != (1 << 12))) + { + LOG(2,("CRTC: DFP needs to do scaling\n")); + DACW(FP_TG_CTRL, (DACR(FP_TG_CTRL) | 0x00000100)); + } + else + { + LOG(2,("CRTC: no scaling for DFP needed\n")); + DACW(FP_TG_CTRL, (DACR(FP_TG_CTRL) & 0xfffffeff)); + } + } + else + { + float dm_aspect; + + LOG(2,("CRTC: GPU scales for DFP if needed\n")); + + /* calculate display mode aspect */ + dm_aspect = (target.timing.h_display / ((float)target.timing.v_display)); + + /* limit last fetched line if vertical scaling is done */ + if (iscale_y != (1 << 12)) + DACW(FP_DEBUG2, ((1 << 28) | ((target.timing.v_display - 1) << 16))); + else + DACW(FP_DEBUG2, 0x00000000); + + /* inform panel not to scale */ + DACW(FP_TG_CTRL, (DACR(FP_TG_CTRL) & 0xfffffeff)); + + /* GPU scaling is automatically setup by hardware, so only modify this + * scalingfactor for non 4:3 (1.33) aspect panels; + * let's consider 1280x1024 1:33 aspect (it's 1.25 aspect actually!) */ + + /* correct for widescreen panels relative to mode... + * (so if panel is more widescreen than mode being set) */ + /* BTW: known widescreen panels: + * 1280 x 800 (1.60), + * 1440 x 900 (1.60), + * 1680 x 1050 (1.60), + * 1920 x 1200 (1.60). */ + /* known 4:3 aspect non-standard resolution panels: + * 1400 x 1050 (1.33). */ + /* NOTE: + * allow 0.10 difference so 1280x1024 panels will be used fullscreen! */ + if ((iscale_x != (1 << 12)) && (si->ps.panel1_aspect > (dm_aspect + 0.10))) + { + uint16 diff; + + LOG(2,("CRTC: (relative) widescreen panel: tuning horizontal scaling\n")); + + /* X-scaling should be the same as Y-scaling */ + iscale_x = iscale_y; + /* enable testmode (b12) and program modified X-scaling factor */ + DACW(FP_DEBUG1, (((iscale_x >> 1) & 0x00000fff) | (1 << 12))); + /* center/cut-off left and right side of screen */ + diff = ((si->ps.p1_timing.h_display - + ((target.timing.h_display * (1 << 12)) / iscale_x)) + / 2); + DACW(FP_HVALID_S, diff); + DACW(FP_HVALID_E, ((si->ps.p1_timing.h_display - diff) - 1)); + } + /* correct for portrait panels... */ + /* NOTE: + * allow 0.10 difference so 1280x1024 panels will be used fullscreen! */ + if ((iscale_y != (1 << 12)) && (si->ps.panel1_aspect < (dm_aspect - 0.10))) + { + LOG(2,("CRTC: (relative) portrait panel: should tune vertical scaling\n")); + /* fixme: implement if this kind of portrait panels exist on nVidia... */ + } + } + + /* do some logging.. */ + LOG(2,("CRTC: FP_HVALID_S reg readback: $%08x\n", DACR(FP_HVALID_S))); + LOG(2,("CRTC: FP_HVALID_E reg readback: $%08x\n", DACR(FP_HVALID_E))); + LOG(2,("CRTC: FP_VVALID_S reg readback: $%08x\n", DACR(FP_VVALID_S))); + LOG(2,("CRTC: FP_VVALID_E reg readback: $%08x\n", DACR(FP_VVALID_E))); + LOG(2,("CRTC: FP_DEBUG0 reg readback: $%08x\n", DACR(FP_DEBUG0))); + LOG(2,("CRTC: FP_DEBUG1 reg readback: $%08x\n", DACR(FP_DEBUG1))); + LOG(2,("CRTC: FP_DEBUG2 reg readback: $%08x\n", DACR(FP_DEBUG2))); + LOG(2,("CRTC: FP_DEBUG3 reg readback: $%08x\n", DACR(FP_DEBUG3))); + LOG(2,("CRTC: FP_TG_CTRL reg readback: $%08x\n", DACR(FP_TG_CTRL))); + } + + return B_OK; +} + +status_t nv_crtc_depth(int mode) +{ + uint8 viddelay = 0; + uint32 genctrl = 0; + + /* set VCLK scaling */ + switch(mode) + { + case BPP8: + viddelay = 0x01; + /* genctrl b4 & b5 reset: 'direct mode' */ + genctrl = 0x00101100; + break; + case BPP15: + viddelay = 0x02; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00100130; + break; + case BPP16: + viddelay = 0x02; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00101130; + break; + case BPP24: + viddelay = 0x03; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00100130; + break; + case BPP32: + viddelay = 0x03; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00101130; + break; + } + /* enable access to primary head */ + set_crtc_owner(0); + + CRTCW(PIXEL, ((CRTCR(PIXEL) & 0xfc) | viddelay)); + DACW(GENCTRL, genctrl); + + return B_OK; +} + +status_t nv_crtc_dpms(bool display, bool h, bool v, bool do_panel) +{ + uint8 temp; + char msg[100]; + + sprintf(msg, "CRTC: setting DPMS: "); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* start synchronous reset: required before turning screen off! */ + SEQW(RESET, 0x01); + + temp = SEQR(CLKMODE); + if (display) + { + /* turn screen on */ + SEQW(CLKMODE, (temp & ~0x20)); + + /* end synchronous reset because display should be enabled */ + SEQW(RESET, 0x03); + + if (do_panel && si->ps.tmds1_active) + { + if (!si->ps.laptop) + { + /* restore original panelsync and panel-enable */ + uint32 panelsync = 0x00000000; + if(si->ps.p1_timing.flags & B_POSITIVE_VSYNC) panelsync |= 0x00000001; + if(si->ps.p1_timing.flags & B_POSITIVE_HSYNC) panelsync |= 0x00000010; + /* display enable polarity (not an official flag) */ + if(si->ps.p1_timing.flags & B_BLANK_PEDESTAL) panelsync |= 0x10000000; + DACW(FP_TG_CTRL, ((DACR(FP_TG_CTRL) & 0xcfffffcc) | panelsync)); + + //fixme?: looks like we don't need this after all: + /* powerup both LVDS (laptop panellink) and TMDS (DVI panellink) + * internal transmitters... */ + /* note: + * the powerbits in this register are hardwired to the DVI connectors, + * instead of to the DACs! (confirmed NV34) */ + //fixme... + //DACW(FP_DEBUG0, (DACR(FP_DEBUG0) & 0xcfffffff)); + /* ... and powerup external TMDS transmitter if it exists */ + /* (confirmed OK on NV28 and NV34) */ + //CRTCW(0x59, (CRTCR(0x59) | 0x01)); + + sprintf(msg, "%s(panel-)", msg); + } + else + { + //fixme? linux only does this on dualhead cards... + //fixme: see if LVDS head can be determined with two panels there... + if (!si->ps.tmds2_active && (si->ps.card_type != NV11)) + { + /* b2 = 0 = enable laptop panel backlight */ + /* note: this seems to be a write-only register. */ + NV_REG32(NV32_LVDS_PWR) = 0x00000003; + + sprintf(msg, "%s(panel-)", msg); + } + } + } + + sprintf(msg, "%sdisplay on, ", msg); + } + else + { + /* turn screen off */ + SEQW(CLKMODE, (temp | 0x20)); + + if (do_panel && si->ps.tmds1_active) + { + if (!si->ps.laptop) + { + /* shutoff panelsync and disable panel */ + DACW(FP_TG_CTRL, ((DACR(FP_TG_CTRL) & 0xcfffffcc) | 0x20000022)); + + //fixme?: looks like we don't need this after all: + /* powerdown both LVDS (laptop panellink) and TMDS (DVI panellink) + * internal transmitters... */ + /* note: + * the powerbits in this register are hardwired to the DVI connectors, + * instead of to the DACs! (confirmed NV34) */ + //fixme... + //DACW(FP_DEBUG0, (DACR(FP_DEBUG0) | 0x30000000)); + /* ... and powerdown external TMDS transmitter if it exists */ + /* (confirmed OK on NV28 and NV34) */ + //CRTCW(0x59, (CRTCR(0x59) & 0xfe)); + + sprintf(msg, "%s(panel-)", msg); + } + else + { + //fixme? linux only does this on dualhead cards... + //fixme: see if LVDS head can be determined with two panels there... + if (!si->ps.tmds2_active && (si->ps.card_type != NV11)) + { + /* b2 = 1 = disable laptop panel backlight */ + /* note: this seems to be a write-only register. */ + NV_REG32(NV32_LVDS_PWR) = 0x00000007; + + sprintf(msg, "%s(panel-)", msg); + } + } + } + + sprintf(msg, "%sdisplay off, ", msg); + } + + if (h) + { + CRTCW(REPAINT1, (CRTCR(REPAINT1) & 0x7f)); + sprintf(msg, "%shsync enabled, ", msg); + } + else + { + CRTCW(REPAINT1, (CRTCR(REPAINT1) | 0x80)); + sprintf(msg, "%shsync disabled, ", msg); + } + if (v) + { + CRTCW(REPAINT1, (CRTCR(REPAINT1) & 0xbf)); + sprintf(msg, "%svsync enabled\n", msg); + } + else + { + CRTCW(REPAINT1, (CRTCR(REPAINT1) | 0x40)); + sprintf(msg, "%svsync disabled\n", msg); + } + + LOG(4, (msg)); + + return B_OK; +} + +status_t nv_crtc_set_display_pitch() +{ + uint32 offset; + + LOG(4,("CRTC: setting card pitch (offset between lines)\n")); + + /* figure out offset value hardware needs */ + offset = si->fbc.bytes_per_row / 8; + + LOG(2,("CRTC: offset register set to: $%04x\n", offset)); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* program the card */ + CRTCW(PITCHL, (offset & 0x00ff)); + CRTCW(REPAINT0, ((CRTCR(REPAINT0) & 0x1f) | ((offset & 0x0700) >> 3))); + + return B_OK; +} + +status_t nv_crtc_set_display_start(uint32 startadd,uint8 bpp) +{ + uint8 temp; + uint32 timeout = 0; + + LOG(4,("CRTC: setting card RAM to be displayed bpp %d\n", bpp)); + + LOG(2,("CRTC: startadd: $%08x\n", startadd)); + LOG(2,("CRTC: frameRAM: $%08x\n", si->framebuffer)); + LOG(2,("CRTC: framebuffer: $%08x\n", si->fbc.frame_buffer)); + + /* we might have no retraces during setmode! */ + /* wait 25mS max. for retrace to occur (refresh > 40Hz) */ + while (((NV_REG32(NV32_RASTER) & 0x000007ff) < si->dm.timing.v_display) && + (timeout < (25000/10))) + { + /* don't snooze much longer or retrace might get missed! */ + snooze(10); + timeout++; + } + + /* enable access to primary head */ + set_crtc_owner(0); + + if (si->ps.card_arch == NV04A) + { + /* upto 32Mb RAM adressing: must be used this way on pre-NV10! */ + + /* set standard registers */ + /* (NVidia: startadress in 32bit words (b2 - b17) */ + CRTCW(FBSTADDL, ((startadd & 0x000003fc) >> 2)); + CRTCW(FBSTADDH, ((startadd & 0x0003fc00) >> 10)); + + /* set extended registers */ + /* NV4 extended bits: (b18-22) */ + temp = (CRTCR(REPAINT0) & 0xe0); + CRTCW(REPAINT0, (temp | ((startadd & 0x007c0000) >> 18))); + /* NV4 extended bits: (b23-24) */ + temp = (CRTCR(HEB) & 0x9f); + CRTCW(HEB, (temp | ((startadd & 0x01800000) >> 18))); + } + else + { + /* upto 4Gb RAM adressing: must be used on NV10 and later! */ + /* NOTE: + * While this register also exists on pre-NV10 cards, it will + * wrap-around at 16Mb boundaries!! */ + + /* 30bit adress in 32bit words */ + NV_REG32(NV32_NV10FBSTADD32) = (startadd & 0xfffffffc); + } + + /* set NV4/NV10 byte adress: (b0 - 1) */ + ATBW(HORPIXPAN, ((startadd & 0x00000003) << 1)); + + return B_OK; +} + +status_t nv_crtc_cursor_init() +{ + int i; + vuint32 * fb; + /* cursor bitmap will be stored at the start of the framebuffer */ + const uint32 curadd = 0; + + /* enable access to primary head */ + set_crtc_owner(0); + + /* set cursor bitmap adress ... */ + if ((si->ps.card_arch == NV04A) || (si->ps.laptop)) + { + /* must be used this way on pre-NV10 and on all 'Go' cards! */ + + /* cursorbitmap must start on 2Kbyte boundary: */ + /* set adress bit11-16, and set 'no doublescan' (registerbit 1 = 0) */ + CRTCW(CURCTL0, ((curadd & 0x0001f800) >> 9)); + /* set adress bit17-23, and set graphics mode cursor(?) (registerbit 7 = 1) */ + CRTCW(CURCTL1, (((curadd & 0x00fe0000) >> 17) | 0x80)); + /* set adress bit24-31 */ + CRTCW(CURCTL2, ((curadd & 0xff000000) >> 24)); + } + else + { + /* upto 4Gb RAM adressing: + * can be used on NV10 and later (except for 'Go' cards)! */ + /* NOTE: + * This register does not exist on pre-NV10 and 'Go' cards. */ + + /* cursorbitmap must still start on 2Kbyte boundary: */ + NV_REG32(NV32_NV10CURADD32) = (curadd & 0xfffff800); + } + + /* set cursor colour: not needed because of direct nature of cursor bitmap. */ + + /*clear cursor*/ + fb = (vuint32 *) si->framebuffer + curadd; + for (i=0;i<(2048/4);i++) + { + fb[i]=0; + } + + /* select 32x32 pixel, 16bit color cursorbitmap, no doublescan */ + NV_REG32(NV32_CURCONF) = 0x02000100; + + /* activate hardware-sync between cursor updates and vertical retrace where + * available */ + if (si->ps.card_arch >= NV10A) + DACW(NV10_CURSYNC, (DACR(NV10_CURSYNC) | 0x02000000)); + + /* activate hardware cursor */ + nv_crtc_cursor_show(); + + return B_OK; +} + +status_t nv_crtc_cursor_show() +{ + LOG(4,("CRTC: enabling cursor\n")); + + /* enable access to CRTC1 on dualhead cards */ + set_crtc_owner(0); + + /* b0 = 1 enables cursor */ + CRTCW(CURCTL0, (CRTCR(CURCTL0) | 0x01)); + + /* workaround for hardware bug confirmed existing on NV43: + * Cursor visibility is not updated without a position update if its hardware + * retrace sync is enabled. */ + if (si->ps.card_arch == NV40A) DACW(CURPOS, (DACR(CURPOS))); + + return B_OK; +} + +status_t nv_crtc_cursor_hide() +{ + LOG(4,("CRTC: disabling cursor\n")); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* b0 = 0 disables cursor */ + CRTCW(CURCTL0, (CRTCR(CURCTL0) & 0xfe)); + + /* workaround for hardware bug confirmed existing on NV43: + * Cursor visibility is not updated without a position update if its hardware + * retrace sync is enabled. */ + if (si->ps.card_arch == NV40A) DACW(CURPOS, (DACR(CURPOS))); + + return B_OK; +} + +/*set up cursor shape*/ +status_t nv_crtc_cursor_define(uint8* andMask,uint8* xorMask) +{ + int x, y; + uint8 b; + vuint16 *cursor; + uint16 pixel; + + /* get a pointer to the cursor */ + cursor = (vuint16*) si->framebuffer; + + /* draw the cursor */ + /* (Nvidia cards have a RGB15 direct color cursor bitmap, bit #16 is transparancy) */ + for (y = 0; y < 16; y++) + { + b = 0x80; + for (x = 0; x < 8; x++) + { + /* preset transparant */ + pixel = 0x0000; + /* set white if requested */ + if ((!(*andMask & b)) && (!(*xorMask & b))) pixel = 0xffff; + /* set black if requested */ + if ((!(*andMask & b)) && (*xorMask & b)) pixel = 0x8000; + /* set invert if requested */ + if ( (*andMask & b) && (*xorMask & b)) pixel = 0x7fff; + /* place the pixel in the bitmap */ + cursor[x + (y * 32)] = pixel; + b >>= 1; + } + xorMask++; + andMask++; + b = 0x80; + for (; x < 16; x++) + { + /* preset transparant */ + pixel = 0x0000; + /* set white if requested */ + if ((!(*andMask & b)) && (!(*xorMask & b))) pixel = 0xffff; + /* set black if requested */ + if ((!(*andMask & b)) && (*xorMask & b)) pixel = 0x8000; + /* set invert if requested */ + if ( (*andMask & b) && (*xorMask & b)) pixel = 0x7fff; + /* place the pixel in the bitmap */ + cursor[x + (y * 32)] = pixel; + b >>= 1; + } + xorMask++; + andMask++; + } + + return B_OK; +} + +/* position the cursor */ +status_t nv_crtc_cursor_position(uint16 x, uint16 y) +{ + /* the cursor position is updated during retrace by card hardware except for + * pre-GeForce cards */ + if (si->ps.card_arch < NV10A) + { + uint16 yhigh; + uint32 timeout = 0; + + /* make sure we are beyond the first line of the cursorbitmap being drawn during + * updating the position to prevent distortions: no double buffering feature */ + /* Note: + * we need to return as quick as possible or some apps will exhibit lagging.. */ + + /* read the old cursor Y position */ + yhigh = ((DACR(CURPOS) & 0x0fff0000) >> 16); + /* make sure we will wait until we are below both the old and new Y position: + * visible cursorbitmap drawing needs to be done at least... */ + if (y > yhigh) yhigh = y; + + if (yhigh < (si->dm.timing.v_display - 16)) + { + /* we have vertical lines below old and new cursorposition to spare. So we + * update the cursor postion 'mid-screen', but below that area. */ + /* wait 25mS max. (refresh > 40Hz) */ + while ((((uint16)(NV_REG32(NV32_RASTER) & 0x000007ff)) < (yhigh + 16)) && + (timeout < (25000/10))) + { + snooze(10); + timeout++; + } + } + else + { + timeout = 0; + /* no room to spare, just wait for retrace (is relatively slow) */ + /* wait 25mS max. (refresh > 40Hz) */ + while (((NV_REG32(NV32_RASTER) & 0x000007ff) < si->dm.timing.v_display) && + (timeout < (25000/10))) + { + /* don't snooze much longer or retrace might get missed! */ + snooze(10); + timeout++; + } + } + } + + /* update cursorposition */ + DACW(CURPOS, ((x & 0x0fff) | ((y & 0x0fff) << 16))); + + return B_OK; +} + +status_t nv_crtc_stop_tvout(void) +{ + uint16 cnt; + + LOG(4,("CRTC: stopping TV output\n")); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* just to be sure Vsync is _really_ enabled */ + CRTCW(REPAINT1, (CRTCR(REPAINT1) & 0xbf)); + + /* wait for one image to be generated to make sure VGA has kicked in and is + * running OK before continuing... + * (Kicking in will fail often if we do not wait here) */ + /* Note: + * The used CRTC's Vsync is required to be enabled here. The DPMS state + * programming in the driver makes sure this is the case. + * (except for driver startup: see nv_general.c.) */ + + /* make sure we are 'in' active VGA picture: wait with timeout! */ + cnt = 1; + while ((NV_REG8(NV8_INSTAT1) & 0x08) && cnt) + { + snooze(1); + cnt++; + } + /* wait for next vertical retrace start on VGA: wait with timeout! */ + cnt = 1; + while ((!(NV_REG8(NV8_INSTAT1) & 0x08)) && cnt) + { + snooze(1); + cnt++; + } + /* now wait until we are 'in' active VGA picture again: wait with timeout! */ + cnt = 1; + while ((NV_REG8(NV8_INSTAT1) & 0x08) && cnt) + { + snooze(1); + cnt++; + } + + /* set CRTC to master mode (b7 = 0) if it wasn't slaved for a panel before */ + if (!(si->ps.slaved_tmds1)) CRTCW(PIXEL, (CRTCR(PIXEL) & 0x03)); + + /* CAUTION: + * On old cards, PLLSEL (and TV_SETUP?) cannot be read (sometimes?), but + * write actions do succeed ... + * This is confirmed for both ISA and PCI access, on NV04 and NV11. */ + + /* setup TVencoder connection */ + /* b1-0 = %00: encoder type is SLAVE; + * b24 = 1: VIP datapos is b0-7 */ + //fixme if needed: setup completely instead of relying on pre-init by BIOS.. + //(it seems to work OK on NV04 and NV11 although read reg. doesn't seem to work) + DACW(TV_SETUP, ((DACR(TV_SETUP) & ~0x00000003) | 0x01000000)); + + /* tell GPU to use pixelclock from internal source instead of using TVencoder */ + if (si->ps.secondary_head) + DACW(PLLSEL, 0x30000f00); + else + DACW(PLLSEL, 0x10000700); + + /* HTOTAL, VTOTAL and OVERFLOW return their default CRTC use, instead of + * H, V-low and V-high 'shadow' counters(?)(b0, 4 and 6 = 0) (b7 use = unknown) */ + CRTCW(TREG, 0x00); + + /* select panel encoder, not TV encoder if needed (b0 = 1). + * Note: + * Both are devices (often) using the CRTC in slaved mode. */ + if (si->ps.slaved_tmds1) CRTCW(LCD, (CRTCR(LCD) | 0x01)); + + return B_OK; +} + +status_t nv_crtc_start_tvout(void) +{ + LOG(4,("CRTC: starting TV output\n")); + + if (si->ps.secondary_head) + { + /* switch TV encoder to CRTC1 */ + NV_REG32(NV32_2FUNCSEL) &= ~0x00000100; + NV_REG32(NV32_FUNCSEL) |= 0x00000100; + } + + /* enable access to primary head */ + set_crtc_owner(0); + + /* CAUTION: + * On old cards, PLLSEL (and TV_SETUP?) cannot be read (sometimes?), but + * write actions do succeed ... + * This is confirmed for both ISA and PCI access, on NV04 and NV11. */ + + /* setup TVencoder connection */ + /* b1-0 = %01: encoder type is MASTER; + * b24 = 1: VIP datapos is b0-7 */ + //fixme if needed: setup completely instead of relying on pre-init by BIOS.. + //(it seems to work OK on NV04 and NV11 although read reg. doesn't seem to work) + DACW(TV_SETUP, ((DACR(TV_SETUP) & ~0x00000002) | 0x01000001)); + + /* tell GPU to use pixelclock from TVencoder instead of using internal source */ + /* (nessecary or display will 'shiver' on both TV and VGA.) */ + if (si->ps.secondary_head) + DACW(PLLSEL, 0x20030f00); + else + DACW(PLLSEL, 0x00030700); + + /* Set overscan color to 'black' */ + /* note: + * Change this instruction for a visible overscan color if you're trying to + * center the output on TV. Use it as a guide-'line' then ;-) */ + ATBW(OSCANCOLOR, 0x00); + + /* set CRTC to slaved mode (b7 = 1) and clear TVadjust (b3-5 = %000) */ + CRTCW(PIXEL, ((CRTCR(PIXEL) & 0xc7) | 0x80)); + /* select TV encoder, not panel encoder (b0 = 0). + * Note: + * Both are devices (often) using the CRTC in slaved mode. */ + CRTCW(LCD, (CRTCR(LCD) & 0xfe)); + + /* HTOTAL, VTOTAL and OVERFLOW return their default CRTC use, instead of + * H, V-low and V-high 'shadow' counters(?)(b0, 4 and 6 = 0) (b7 use = unknown) */ + CRTCW(TREG, 0x80); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc2.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc2.c new file mode 100644 index 0000000000..70871502b9 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_crtc2.c @@ -0,0 +1,1020 @@ +/* second CTRC functionality for GeForce cards */ +/* Author: + Rudolf Cornelissen 11/2002-2/2006 +*/ + +#define MODULE_BIT 0x00020000 + +#include "nv_std.h" + +/* + Enable/Disable interrupts. Just a wrapper around the + ioctl() to the kernel driver. +*/ +status_t nv_crtc2_interrupt_enable(bool flag) +{ + status_t result = B_OK; + nv_set_vblank_int svi; + + if (si->ps.int_assigned) + { + /* set the magic number so the driver knows we're for real */ + svi.magic = NV_PRIVATE_DATA_MAGIC; + svi.crtc = 1; + svi.do_it = flag; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, NV_RUN_INTERRUPTS, &svi, sizeof(svi)); + } + + return result; +} + +/* doing general fail-safe default setup here */ +//fixme: this is a _very_ basic setup, and it's preliminary... +status_t nv_crtc2_update_fifo() +{ + uint8 bytes_per_pixel = 1; + uint32 drain; + + /* we are only using this on >>coldstarted<< cards which really need this */ + //fixme: re-enable or remove after general user confirmation of behaviour... + if (/*(si->settings.usebios) ||*/ (si->ps.card_type != NV11)) return B_OK; + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* set CRTC FIFO low watermark according to memory drain */ + switch(si->dm.space) + { + case B_CMAP8: + bytes_per_pixel = 1; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + bytes_per_pixel = 2; + break; + case B_RGB24_LITTLE: + bytes_per_pixel = 3; + break; + case B_RGB32_LITTLE: + bytes_per_pixel = 4; + break; + } + /* fixme: + * - I should probably include the refreshrate as well; + * - and the memory clocking speed, core clocking speed, RAM buswidth.. */ + drain = si->dm.timing.h_display * si->dm.timing.v_display * bytes_per_pixel; + + /* Doesn't work for other than 32bit space (yet?) */ + if (si->dm.space != B_RGB32_LITTLE) + { + /* BIOS defaults */ + CRTC2W(FIFO, 0x03); + CRTC2W(FIFO_LWM, 0x20); + LOG(4,("CRTC2: FIFO low-watermark set to $20, burst size 256 (BIOS defaults)\n")); + return B_OK; + } + + if (drain > (((uint32)1280) * 1024 * 4)) + { + /* set CRTC FIFO burst size for 'smaller' bursts */ + CRTC2W(FIFO, 0x01); + /* Instruct CRTC to fetch new data 'earlier' */ + CRTC2W(FIFO_LWM, 0x40); + LOG(4,("CRTC2: FIFO low-watermark set to $40, burst size 64\n")); + } + else + { + if (drain > (((uint32)1024) * 768 * 4)) + { + /* BIOS default */ + CRTC2W(FIFO, 0x02); + /* Instruct CRTC to fetch new data 'earlier' */ + CRTC2W(FIFO_LWM, 0x40); + LOG(4,("CRTC2: FIFO low-watermark set to $40, burst size 128\n")); + } + else + { + /* BIOS defaults */ + CRTC2W(FIFO, 0x03); + CRTC2W(FIFO_LWM, 0x20); + LOG(4,("CRTC2: FIFO low-watermark set to $20, burst size 256 (BIOS defaults)\n")); + } + } + + return B_OK; +} + +/* Adjust passed parameters to a valid mode line */ +status_t nv_crtc2_validate_timing( + uint16 *hd_e,uint16 *hs_s,uint16 *hs_e,uint16 *ht, + uint16 *vd_e,uint16 *vs_s,uint16 *vs_e,uint16 *vt +) +{ +/* horizontal */ + /* make all parameters multiples of 8 */ + *hd_e &= 0xfff8; + *hs_s &= 0xfff8; + *hs_e &= 0xfff8; + *ht &= 0xfff8; + + /* confine to required number of bits, taking logic into account */ + if (*hd_e > ((0x01ff - 2) << 3)) *hd_e = ((0x01ff - 2) << 3); + if (*hs_s > ((0x01ff - 1) << 3)) *hs_s = ((0x01ff - 1) << 3); + if (*hs_e > ( 0x01ff << 3)) *hs_e = ( 0x01ff << 3); + if (*ht > ((0x01ff + 5) << 3)) *ht = ((0x01ff + 5) << 3); + + /* NOTE: keep horizontal timing at multiples of 8! */ + /* confine to a reasonable width */ + if (*hd_e < 640) *hd_e = 640; + if (*hd_e > 2048) *hd_e = 2048; + + /* if hor. total does not leave room for a sensible sync pulse, increase it! */ + if (*ht < (*hd_e + 80)) *ht = (*hd_e + 80); + + /* if hor. total does not adhere to max. blanking pulse width, decrease it! */ + if (*ht > (*hd_e + 0x3f8)) *ht = (*hd_e + 0x3f8); + + /* make sure sync pulse is not during display */ + if (*hs_e > (*ht - 8)) *hs_e = (*ht - 8); + if (*hs_s < (*hd_e + 8)) *hs_s = (*hd_e + 8); + + /* correct sync pulse if it is too long: + * there are only 5 bits available to save this in the card registers! */ + if (*hs_e > (*hs_s + 0xf8)) *hs_e = (*hs_s + 0xf8); + +/*vertical*/ + /* confine to required number of bits, taking logic into account */ + //fixme if needed: on GeForce cards there are 12 instead of 11 bits... + if (*vd_e > (0x7ff - 2)) *vd_e = (0x7ff - 2); + if (*vs_s > (0x7ff - 1)) *vs_s = (0x7ff - 1); + if (*vs_e > 0x7ff ) *vs_e = 0x7ff ; + if (*vt > (0x7ff + 2)) *vt = (0x7ff + 2); + + /* confine to a reasonable height */ + if (*vd_e < 480) *vd_e = 480; + if (*vd_e > 1536) *vd_e = 1536; + + /*if vertical total does not leave room for a sync pulse, increase it!*/ + if (*vt < (*vd_e + 3)) *vt = (*vd_e + 3); + + /* if vert. total does not adhere to max. blanking pulse width, decrease it! */ + if (*vt > (*vd_e + 0xff)) *vt = (*vd_e + 0xff); + + /* make sure sync pulse is not during display */ + if (*vs_e > (*vt - 1)) *vs_e = (*vt - 1); + if (*vs_s < (*vd_e + 1)) *vs_s = (*vd_e + 1); + + /* correct sync pulse if it is too long: + * there are only 4 bits available to save this in the card registers! */ + if (*vs_e > (*vs_s + 0x0f)) *vs_e = (*vs_s + 0x0f); + + return B_OK; +} + +/*set a mode line - inputs are in pixels*/ +status_t nv_crtc2_set_timing(display_mode target) +{ + uint8 temp; + + uint32 htotal; /*total horizontal total VCLKs*/ + uint32 hdisp_e; /*end of horizontal display (begins at 0)*/ + uint32 hsync_s; /*begin of horizontal sync pulse*/ + uint32 hsync_e; /*end of horizontal sync pulse*/ + uint32 hblnk_s; /*begin horizontal blanking*/ + uint32 hblnk_e; /*end horizontal blanking*/ + + uint32 vtotal; /*total vertical total scanlines*/ + uint32 vdisp_e; /*end of vertical display*/ + uint32 vsync_s; /*begin of vertical sync pulse*/ + uint32 vsync_e; /*end of vertical sync pulse*/ + uint32 vblnk_s; /*begin vertical blanking*/ + uint32 vblnk_e; /*end vertical blanking*/ + + uint32 linecomp; /*split screen and vdisp_e interrupt*/ + + LOG(4,("CRTC2: setting timing\n")); + + /* setup tuned internal modeline for flatpanel if connected and active */ + /* notes: + * - the CRTC modeline must end earlier than the panel modeline to keep correct + * sync going; + * - if the CRTC modeline ends too soon, pixelnoise will occur in 8 (or so) pixel + * wide horizontal stripes. This can be observed earliest on fullscreen overlay, + * and if it gets worse, also normal desktop output will suffer. The stripes + * are mainly visible at the left of the screen, over the entire screen height. */ + if (si->ps.tmds2_active) + { + LOG(2,("CRTC2: DFP active: tuning modeline\n")); + + /* horizontal timing */ + target.timing.h_sync_start = + ((uint16)((si->ps.p2_timing.h_sync_start / ((float)si->ps.p2_timing.h_display)) * + target.timing.h_display)) & 0xfff8; + + target.timing.h_sync_end = + ((uint16)((si->ps.p2_timing.h_sync_end / ((float)si->ps.p2_timing.h_display)) * + target.timing.h_display)) & 0xfff8; + + target.timing.h_total = + (((uint16)((si->ps.p2_timing.h_total / ((float)si->ps.p2_timing.h_display)) * + target.timing.h_display)) & 0xfff8) - 8; + + /* in native mode the CRTC needs some extra time to keep synced correctly; + * OTOH the overlay unit distorts if we reserve too much time! */ + if (target.timing.h_display == si->ps.p2_timing.h_display) + { + /* NV11 timing has different constraints than later cards */ + if (si->ps.card_type == NV11) + target.timing.h_total -= 56; + else + /* confirmed NV34 with 1680x1050 panel */ + target.timing.h_total -= 32; + } + + if (target.timing.h_sync_start == target.timing.h_display) + target.timing.h_sync_start += 8; + if (target.timing.h_sync_end == target.timing.h_total) + target.timing.h_sync_end -= 8; + + /* vertical timing */ + target.timing.v_sync_start = + ((uint16)((si->ps.p2_timing.v_sync_start / ((float)si->ps.p2_timing.v_display)) * + target.timing.v_display)); + + target.timing.v_sync_end = + ((uint16)((si->ps.p2_timing.v_sync_end / ((float)si->ps.p2_timing.v_display)) * + target.timing.v_display)); + + target.timing.v_total = + ((uint16)((si->ps.p2_timing.v_total / ((float)si->ps.p2_timing.v_display)) * + target.timing.v_display)) - 1; + + if (target.timing.v_sync_start == target.timing.v_display) + target.timing.v_sync_start += 1; + if (target.timing.v_sync_end == target.timing.v_total) + target.timing.v_sync_end -= 1; + + /* disable GPU scaling testmode so automatic scaling will be done */ + DAC2W(FP_DEBUG1, 0); + } + + /* Modify parameters as required by standard VGA */ + htotal = ((target.timing.h_total >> 3) - 5); + hdisp_e = ((target.timing.h_display >> 3) - 1); + hblnk_s = hdisp_e; + hblnk_e = (htotal + 4); + hsync_s = (target.timing.h_sync_start >> 3); + hsync_e = (target.timing.h_sync_end >> 3); + + vtotal = target.timing.v_total - 2; + vdisp_e = target.timing.v_display - 1; + vblnk_s = vdisp_e; + vblnk_e = (vtotal + 1); + vsync_s = target.timing.v_sync_start; + vsync_e = target.timing.v_sync_end; + + /* prevent memory adress counter from being reset (linecomp may not occur) */ + linecomp = target.timing.v_display; + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* Note for laptop and DVI flatpanels: + * CRTC timing has a seperate set of registers from flatpanel timing. + * The flatpanel timing registers have scaling registers that are used to match + * these two modelines. */ + { + LOG(4,("CRTC2: Setting full timing...\n")); + + /* log the mode that will be set */ + LOG(2,("CRTC2:\n\tHTOT:%x\n\tHDISPEND:%x\n\tHBLNKS:%x\n\tHBLNKE:%x\n\tHSYNCS:%x\n\tHSYNCE:%x\n\t",htotal,hdisp_e,hblnk_s,hblnk_e,hsync_s,hsync_e)); + LOG(2,("VTOT:%x\n\tVDISPEND:%x\n\tVBLNKS:%x\n\tVBLNKE:%x\n\tVSYNCS:%x\n\tVSYNCE:%x\n",vtotal,vdisp_e,vblnk_s,vblnk_e,vsync_s,vsync_e)); + + /* actually program the card! */ + /* unlock CRTC registers at index 0-7 */ + CRTC2W(VSYNCE, (CRTC2R(VSYNCE) & 0x7f)); + /* horizontal standard VGA regs */ + CRTC2W(HTOTAL, (htotal & 0xff)); + CRTC2W(HDISPE, (hdisp_e & 0xff)); + CRTC2W(HBLANKS, (hblnk_s & 0xff)); + /* also unlock vertical retrace registers in advance */ + CRTC2W(HBLANKE, ((hblnk_e & 0x1f) | 0x80)); + CRTC2W(HSYNCS, (hsync_s & 0xff)); + CRTC2W(HSYNCE, ((hsync_e & 0x1f) | ((hblnk_e & 0x20) << 2))); + + /* vertical standard VGA regs */ + CRTC2W(VTOTAL, (vtotal & 0xff)); + CRTC2W(OVERFLOW, + ( + ((vtotal & 0x100) >> (8 - 0)) | ((vtotal & 0x200) >> (9 - 5)) | + ((vdisp_e & 0x100) >> (8 - 1)) | ((vdisp_e & 0x200) >> (9 - 6)) | + ((vsync_s & 0x100) >> (8 - 2)) | ((vsync_s & 0x200) >> (9 - 7)) | + ((vblnk_s & 0x100) >> (8 - 3)) | ((linecomp & 0x100) >> (8 - 4)) + )); + CRTC2W(PRROWSCN, 0x00); /* not used */ + CRTC2W(MAXSCLIN, (((vblnk_s & 0x200) >> (9 - 5)) | ((linecomp & 0x200) >> (9 - 6)))); + CRTC2W(VSYNCS, (vsync_s & 0xff)); + CRTC2W(VSYNCE, ((CRTC2R(VSYNCE) & 0xf0) | (vsync_e & 0x0f))); + CRTC2W(VDISPE, (vdisp_e & 0xff)); + CRTC2W(VBLANKS, (vblnk_s & 0xff)); + CRTC2W(VBLANKE, (vblnk_e & 0xff)); + CRTC2W(LINECOMP, (linecomp & 0xff)); + + /* horizontal extended regs */ + //fixme: we reset bit4. is this correct?? + CRTC2W(HEB, (CRTC2R(HEB) & 0xe0) | + ( + ((htotal & 0x100) >> (8 - 0)) | + ((hdisp_e & 0x100) >> (8 - 1)) | + ((hblnk_s & 0x100) >> (8 - 2)) | + ((hsync_s & 0x100) >> (8 - 3)) + )); + + /* (mostly) vertical extended regs */ + CRTC2W(LSR, + ( + ((vtotal & 0x400) >> (10 - 0)) | + ((vdisp_e & 0x400) >> (10 - 1)) | + ((vsync_s & 0x400) >> (10 - 2)) | + ((vblnk_s & 0x400) >> (10 - 3)) | + ((hblnk_e & 0x040) >> (6 - 4)) + //fixme: we still miss one linecomp bit!?! is this it?? + //| ((linecomp & 0x400) >> 3) + )); + + /* more vertical extended regs */ + CRTC2W(EXTRA, + ( + ((vtotal & 0x800) >> (11 - 0)) | + ((vdisp_e & 0x800) >> (11 - 2)) | + ((vsync_s & 0x800) >> (11 - 4)) | + ((vblnk_s & 0x800) >> (11 - 6)) + //fixme: do we miss another linecomp bit!?! + )); + + /* setup 'large screen' mode */ + if (target.timing.h_display >= 1280) + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) & 0xfb)); + else + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) | 0x04)); + + /* setup HSYNC & VSYNC polarity */ + LOG(2,("CRTC2: sync polarity: ")); + temp = NV_REG8(NV8_MISCR); + if (target.timing.flags & B_POSITIVE_HSYNC) + { + LOG(2,("H:pos ")); + temp &= ~0x40; + } + else + { + LOG(2,("H:neg ")); + temp |= 0x40; + } + if (target.timing.flags & B_POSITIVE_VSYNC) + { + LOG(2,("V:pos ")); + temp &= ~0x80; + } + else + { + LOG(2,("V:neg ")); + temp |= 0x80; + } + NV_REG8(NV8_MISCW) = temp; + + LOG(2,(", MISC reg readback: $%02x\n", NV_REG8(NV8_MISCR))); + } + + /* always disable interlaced operation */ + /* (interlace is supported on upto and including NV10, NV15, and NV30 and up) */ + CRTC2W(INTERLACE, 0xff); + + /* disable CRTC slaved mode unless a panel is in use */ + // fixme: this kills TVout when it was in use... + if (!si->ps.tmds2_active) CRTC2W(PIXEL, (CRTC2R(PIXEL) & 0x7f)); + + /* setup flatpanel if connected and active */ + if (si->ps.tmds2_active) + { + uint32 iscale_x, iscale_y; + + /* calculate inverse scaling factors used by hardware in 20.12 format */ + iscale_x = (((1 << 12) * target.timing.h_display) / si->ps.p2_timing.h_display); + iscale_y = (((1 << 12) * target.timing.v_display) / si->ps.p2_timing.v_display); + + /* unblock flatpanel timing programming (or something like that..) */ + CRTC2W(FP_HTIMING, 0); + CRTC2W(FP_VTIMING, 0); + LOG(2,("CRTC2: FP_HTIMING reg readback: $%02x\n", CRTC2R(FP_HTIMING))); + LOG(2,("CRTC2: FP_VTIMING reg readback: $%02x\n", CRTC2R(FP_VTIMING))); + + /* enable full width visibility on flatpanel */ + DAC2W(FP_HVALID_S, 0); + DAC2W(FP_HVALID_E, (si->ps.p2_timing.h_display - 1)); + /* enable full height visibility on flatpanel */ + DAC2W(FP_VVALID_S, 0); + DAC2W(FP_VVALID_E, (si->ps.p2_timing.v_display - 1)); + + /* nVidia cards support upscaling except on ??? */ + /* NV11 cards can upscale after all! */ + if (0)//si->ps.card_type == NV11) + { + /* disable last fetched line limiting */ + DAC2W(FP_DEBUG2, 0x00000000); + /* inform panel to scale if needed */ + if ((iscale_x != (1 << 12)) || (iscale_y != (1 << 12))) + { + LOG(2,("CRTC2: DFP needs to do scaling\n")); + DAC2W(FP_TG_CTRL, (DAC2R(FP_TG_CTRL) | 0x00000100)); + } + else + { + LOG(2,("CRTC2: no scaling for DFP needed\n")); + DAC2W(FP_TG_CTRL, (DAC2R(FP_TG_CTRL) & 0xfffffeff)); + } + } + else + { + float dm_aspect; + + LOG(2,("CRTC2: GPU scales for DFP if needed\n")); + + /* calculate display mode aspect */ + dm_aspect = (target.timing.h_display / ((float)target.timing.v_display)); + + /* limit last fetched line if vertical scaling is done */ + if (iscale_y != (1 << 12)) + DAC2W(FP_DEBUG2, ((1 << 28) | ((target.timing.v_display - 1) << 16))); + else + DAC2W(FP_DEBUG2, 0x00000000); + + /* inform panel not to scale */ + DAC2W(FP_TG_CTRL, (DAC2R(FP_TG_CTRL) & 0xfffffeff)); + + /* GPU scaling is automatically setup by hardware, so only modify this + * scalingfactor for non 4:3 (1.33) aspect panels; + * let's consider 1280x1024 1:33 aspect (it's 1.25 aspect actually!) */ + + /* correct for widescreen panels relative to mode... + * (so if panel is more widescreen than mode being set) */ + /* BTW: known widescreen panels: + * 1280 x 800 (1.60), + * 1440 x 900 (1.60), + * 1680 x 1050 (1.60), + * 1920 x 1200 (1.60). */ + /* known 4:3 aspect non-standard resolution panels: + * 1400 x 1050 (1.33). */ + /* NOTE: + * allow 0.10 difference so 1280x1024 panels will be used fullscreen! */ + if ((iscale_x != (1 << 12)) && (si->ps.panel2_aspect > (dm_aspect + 0.10))) + { + uint16 diff; + + LOG(2,("CRTC2: (relative) widescreen panel: tuning horizontal scaling\n")); + + /* X-scaling should be the same as Y-scaling */ + iscale_x = iscale_y; + /* enable testmode (b12) and program new X-scaling factor */ + DAC2W(FP_DEBUG1, (((iscale_x >> 1) & 0x00000fff) | (1 << 12))); + /* center/cut-off left and right side of screen */ + diff = ((si->ps.p2_timing.h_display - + ((target.timing.h_display * (1 << 12)) / iscale_x)) + / 2); + DAC2W(FP_HVALID_S, diff); + DAC2W(FP_HVALID_E, ((si->ps.p2_timing.h_display - diff) - 1)); + } + /* correct for portrait panels... */ + /* NOTE: + * allow 0.10 difference so 1280x1024 panels will be used fullscreen! */ + if ((iscale_y != (1 << 12)) && (si->ps.panel2_aspect < (dm_aspect - 0.10))) + { + LOG(2,("CRTC2: (relative) portrait panel: should tune vertical scaling\n")); + /* fixme: implement if this kind of portrait panels exist on nVidia... */ + } + } + + /* do some logging.. */ + LOG(2,("CRTC2: FP_HVALID_S reg readback: $%08x\n", DAC2R(FP_HVALID_S))); + LOG(2,("CRTC2: FP_HVALID_E reg readback: $%08x\n", DAC2R(FP_HVALID_E))); + LOG(2,("CRTC2: FP_VVALID_S reg readback: $%08x\n", DAC2R(FP_VVALID_S))); + LOG(2,("CRTC2: FP_VVALID_E reg readback: $%08x\n", DAC2R(FP_VVALID_E))); + LOG(2,("CRTC2: FP_DEBUG0 reg readback: $%08x\n", DAC2R(FP_DEBUG0))); + LOG(2,("CRTC2: FP_DEBUG1 reg readback: $%08x\n", DAC2R(FP_DEBUG1))); + LOG(2,("CRTC2: FP_DEBUG2 reg readback: $%08x\n", DAC2R(FP_DEBUG2))); + LOG(2,("CRTC2: FP_DEBUG3 reg readback: $%08x\n", DAC2R(FP_DEBUG3))); + LOG(2,("CRTC2: FP_TG_CTRL reg readback: $%08x\n", DAC2R(FP_TG_CTRL))); + } + + return B_OK; +} + +status_t nv_crtc2_depth(int mode) +{ + uint8 viddelay = 0; + uint32 genctrl = 0; + + /* set VCLK scaling */ + switch(mode) + { + case BPP8: + viddelay = 0x01; + /* genctrl b4 & b5 reset: 'direct mode' */ + genctrl = 0x00101100; + break; + case BPP15: + viddelay = 0x02; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00100130; + break; + case BPP16: + viddelay = 0x02; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00101130; + break; + case BPP24: + viddelay = 0x03; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00100130; + break; + case BPP32: + viddelay = 0x03; + /* genctrl b4 & b5 set: 'indirect mode' (via colorpalette) */ + genctrl = 0x00101130; + break; + } + /* enable access to secondary head */ + set_crtc_owner(1); + + CRTC2W(PIXEL, ((CRTC2R(PIXEL) & 0xfc) | viddelay)); + DAC2W(GENCTRL, genctrl); + + return B_OK; +} + +status_t nv_crtc2_dpms(bool display, bool h, bool v, bool do_panel) +{ + uint8 temp; + char msg[100]; + + sprintf(msg, "CRTC2: setting DPMS: "); + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* start synchronous reset: required before turning screen off! */ + SEQW(RESET, 0x01); + + temp = SEQR(CLKMODE); + if (display) + { + /* turn screen on */ + SEQW(CLKMODE, (temp & ~0x20)); + + /* end synchronous reset because display should be enabled */ + SEQW(RESET, 0x03); + + if (do_panel && si->ps.tmds2_active) + { + if (!si->ps.laptop) + { + /* restore original panelsync and panel-enable */ + uint32 panelsync = 0x00000000; + if(si->ps.p2_timing.flags & B_POSITIVE_VSYNC) panelsync |= 0x00000001; + if(si->ps.p2_timing.flags & B_POSITIVE_HSYNC) panelsync |= 0x00000010; + /* display enable polarity (not an official flag) */ + if(si->ps.p2_timing.flags & B_BLANK_PEDESTAL) panelsync |= 0x10000000; + DAC2W(FP_TG_CTRL, ((DAC2R(FP_TG_CTRL) & 0xcfffffcc) | panelsync)); + + //fixme?: looks like we don't need this after all: + /* powerup both LVDS (laptop panellink) and TMDS (DVI panellink) + * internal transmitters... */ + /* note: + * the powerbits in this register are hardwired to the DVI connectors, + * instead of to the DACs! (confirmed NV34) */ + //fixme... + //DAC2W(FP_DEBUG0, (DAC2R(FP_DEBUG0) & 0xcfffffff)); + /* ... and powerup external TMDS transmitter if it exists */ + /* (confirmed OK on NV28 and NV34) */ + //CRTC2W(0x59, (CRTC2R(0x59) | 0x01)); + + sprintf(msg, "%s(panel-)", msg); + } + else + { + //fixme: see if LVDS head can be determined with two panels there... + if (!si->ps.tmds1_active && (si->ps.card_type != NV11)) + { + /* b2 = 0 = enable laptop panel backlight */ + /* note: this seems to be a write-only register. */ + NV_REG32(NV32_LVDS_PWR) = 0x00000003; + + sprintf(msg, "%s(panel-)", msg); + } + } + } + + sprintf(msg, "%sdisplay on, ", msg); + } + else + { + /* turn screen off */ + SEQW(CLKMODE, (temp | 0x20)); + + if (do_panel && si->ps.tmds2_active) + { + if (!si->ps.laptop) + { + /* shutoff panelsync and disable panel */ + DAC2W(FP_TG_CTRL, ((DAC2R(FP_TG_CTRL) & 0xcfffffcc) | 0x20000022)); + + //fixme?: looks like we don't need this after all: + /* powerdown both LVDS (laptop panellink) and TMDS (DVI panellink) + * internal transmitters... */ + /* note: + * the powerbits in this register are hardwired to the DVI connectors, + * instead of to the DACs! (confirmed NV34) */ + //fixme... + //DAC2W(FP_DEBUG0, (DAC2R(FP_DEBUG0) | 0x30000000)); + /* ... and powerdown external TMDS transmitter if it exists */ + /* (confirmed OK on NV28 and NV34) */ + //CRTC2W(0x59, (CRTC2R(0x59) & 0xfe)); + + sprintf(msg, "%s(panel-)", msg); + } + else + { + //fixme: see if LVDS head can be determined with two panels there... + if (!si->ps.tmds1_active && (si->ps.card_type != NV11)) + { + /* b2 = 1 = disable laptop panel backlight */ + /* note: this seems to be a write-only register. */ + NV_REG32(NV32_LVDS_PWR) = 0x00000007; + + sprintf(msg, "%s(panel-)", msg); + } + } + } + + sprintf(msg, "%sdisplay off, ", msg); + } + + if (h) + { + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) & 0x7f)); + sprintf(msg, "%shsync enabled, ", msg); + } + else + { + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) | 0x80)); + sprintf(msg, "%shsync disabled, ", msg); + } + if (v) + { + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) & 0xbf)); + sprintf(msg, "%svsync enabled\n", msg); + } + else + { + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) | 0x40)); + sprintf(msg, "%svsync disabled\n", msg); + } + + LOG(4, (msg)); + + return B_OK; +} + +status_t nv_crtc2_set_display_pitch() +{ + uint32 offset; + + LOG(4,("CRTC2: setting card pitch (offset between lines)\n")); + + /* figure out offset value hardware needs */ + offset = si->fbc.bytes_per_row / 8; + + LOG(2,("CRTC2: offset register set to: $%04x\n", offset)); + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* program the card */ + CRTC2W(PITCHL, (offset & 0x00ff)); + CRTC2W(REPAINT0, ((CRTC2R(REPAINT0) & 0x1f) | ((offset & 0x0700) >> 3))); + + return B_OK; +} + +status_t nv_crtc2_set_display_start(uint32 startadd,uint8 bpp) +{ + uint32 timeout = 0; + + LOG(4,("CRTC2: setting card RAM to be displayed bpp %d\n", bpp)); + + LOG(2,("CRTC2: startadd: $%08x\n", startadd)); + LOG(2,("CRTC2: frameRAM: $%08x\n", si->framebuffer)); + LOG(2,("CRTC2: framebuffer: $%08x\n", si->fbc.frame_buffer)); + + /* we might have no retraces during setmode! */ + /* wait 25mS max. for retrace to occur (refresh > 40Hz) */ + while (((NV_REG32(NV32_RASTER2) & 0x000007ff) < si->dm.timing.v_display) && + (timeout < (25000/10))) + { + /* don't snooze much longer or retrace might get missed! */ + snooze(10); + timeout++; + } + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* upto 4Gb RAM adressing: must be used on NV10 and later! */ + /* NOTE: + * While this register also exists on pre-NV10 cards, it will + * wrap-around at 16Mb boundaries!! */ + + /* 30bit adress in 32bit words */ + NV_REG32(NV32_NV10FB2STADD32) = (startadd & 0xfffffffc); + + /* set byte adress: (b0 - 1) */ + ATB2W(HORPIXPAN, ((startadd & 0x00000003) << 1)); + + return B_OK; +} + +status_t nv_crtc2_cursor_init() +{ + int i; + vuint32 * fb; + /* cursor bitmap will be stored at the start of the framebuffer */ + const uint32 curadd = 0; + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* set cursor bitmap adress ... */ + if (si->ps.laptop) + { + /* must be used this way on pre-NV10 and on all 'Go' cards! */ + + /* cursorbitmap must start on 2Kbyte boundary: */ + /* set adress bit11-16, and set 'no doublescan' (registerbit 1 = 0) */ + CRTC2W(CURCTL0, ((curadd & 0x0001f800) >> 9)); + /* set adress bit17-23, and set graphics mode cursor(?) (registerbit 7 = 1) */ + CRTC2W(CURCTL1, (((curadd & 0x00fe0000) >> 17) | 0x80)); + /* set adress bit24-31 */ + CRTC2W(CURCTL2, ((curadd & 0xff000000) >> 24)); + } + else + { + /* upto 4Gb RAM adressing: + * can be used on NV10 and later (except for 'Go' cards)! */ + /* NOTE: + * This register does not exist on pre-NV10 and 'Go' cards. */ + + /* cursorbitmap must still start on 2Kbyte boundary: */ + NV_REG32(NV32_NV10CUR2ADD32) = (curadd & 0xfffff800); + } + + /* set cursor colour: not needed because of direct nature of cursor bitmap. */ + + /*clear cursor*/ + fb = (vuint32 *) si->framebuffer + curadd; + for (i=0;i<(2048/4);i++) + { + fb[i]=0; + } + + /* select 32x32 pixel, 16bit color cursorbitmap, no doublescan */ + NV_REG32(NV32_2CURCONF) = 0x02000100; + + /* activate hardware-sync between cursor updates and vertical retrace */ + DAC2W(NV10_CURSYNC, (DAC2R(NV10_CURSYNC) | 0x02000000)); + + /* activate hardware cursor */ + nv_crtc2_cursor_show(); + + return B_OK; +} + +status_t nv_crtc2_cursor_show() +{ + LOG(4,("CRTC2: enabling cursor\n")); + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* b0 = 1 enables cursor */ + CRTC2W(CURCTL0, (CRTC2R(CURCTL0) | 0x01)); + + /* workaround for hardware bug confirmed existing on NV43: + * Cursor visibility is not updated without a position update if its hardware + * retrace sync is enabled. */ + if (si->ps.card_arch == NV40A) DAC2W(CURPOS, (DAC2R(CURPOS))); + + return B_OK; +} + +status_t nv_crtc2_cursor_hide() +{ + LOG(4,("CRTC2: disabling cursor\n")); + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* b0 = 0 disables cursor */ + CRTC2W(CURCTL0, (CRTC2R(CURCTL0) & 0xfe)); + + /* workaround for hardware bug confirmed existing on NV43: + * Cursor visibility is not updated without a position update if its hardware + * retrace sync is enabled. */ + if (si->ps.card_arch == NV40A) DAC2W(CURPOS, (DAC2R(CURPOS))); + + return B_OK; +} + +/*set up cursor shape*/ +status_t nv_crtc2_cursor_define(uint8* andMask,uint8* xorMask) +{ + int x, y; + uint8 b; + vuint16 *cursor; + uint16 pixel; + + /* get a pointer to the cursor */ + cursor = (vuint16*) si->framebuffer; + + /* draw the cursor */ + /* (Nvidia cards have a RGB15 direct color cursor bitmap, bit #16 is transparancy) */ + for (y = 0; y < 16; y++) + { + b = 0x80; + for (x = 0; x < 8; x++) + { + /* preset transparant */ + pixel = 0x0000; + /* set white if requested */ + if ((!(*andMask & b)) && (!(*xorMask & b))) pixel = 0xffff; + /* set black if requested */ + if ((!(*andMask & b)) && (*xorMask & b)) pixel = 0x8000; + /* set invert if requested */ + if ( (*andMask & b) && (*xorMask & b)) pixel = 0x7fff; + /* place the pixel in the bitmap */ + cursor[x + (y * 32)] = pixel; + b >>= 1; + } + xorMask++; + andMask++; + b = 0x80; + for (; x < 16; x++) + { + /* preset transparant */ + pixel = 0x0000; + /* set white if requested */ + if ((!(*andMask & b)) && (!(*xorMask & b))) pixel = 0xffff; + /* set black if requested */ + if ((!(*andMask & b)) && (*xorMask & b)) pixel = 0x8000; + /* set invert if requested */ + if ( (*andMask & b) && (*xorMask & b)) pixel = 0x7fff; + /* place the pixel in the bitmap */ + cursor[x + (y * 32)] = pixel; + b >>= 1; + } + xorMask++; + andMask++; + } + + return B_OK; +} + +/* position the cursor */ +status_t nv_crtc2_cursor_position(uint16 x, uint16 y) +{ + /* the cursor position is updated during retrace by card hardware */ + + /* update cursorposition */ + DAC2W(CURPOS, ((x & 0x0fff) | ((y & 0x0fff) << 16))); + + return B_OK; +} + +status_t nv_crtc2_stop_tvout(void) +{ + uint16 cnt; + + LOG(4,("CRTC2: stopping TV output\n")); + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* just to be sure Vsync is _really_ enabled */ + CRTC2W(REPAINT1, (CRTC2R(REPAINT1) & 0xbf)); + + /* wait for one image to be generated to make sure VGA has kicked in and is + * running OK before continuing... + * (Kicking in will fail often if we do not wait here) */ + /* Note: + * The used CRTC's Vsync is required to be enabled here. The DPMS state + * programming in the driver makes sure this is the case. + * (except for driver startup: see nv_general.c.) */ + + /* make sure we are 'in' active VGA picture: wait with timeout! */ + cnt = 1; + while ((NV_REG8(NV8_INSTAT1) & 0x08) && cnt) + { + snooze(1); + cnt++; + } + /* wait for next vertical retrace start on VGA: wait with timeout! */ + cnt = 1; + while ((!(NV_REG8(NV8_INSTAT1) & 0x08)) && cnt) + { + snooze(1); + cnt++; + } + /* now wait until we are 'in' active VGA picture again: wait with timeout! */ + cnt = 1; + while ((NV_REG8(NV8_INSTAT1) & 0x08) && cnt) + { + snooze(1); + cnt++; + } + + /* set CRTC to master mode (b7 = 0) if it wasn't slaved for a panel before */ + if (!(si->ps.slaved_tmds2)) CRTC2W(PIXEL, (CRTC2R(PIXEL) & 0x03)); + + /* CAUTION: + * On old cards, PLLSEL (and TV_SETUP?) cannot be read (sometimes?), but + * write actions do succeed ... + * This is confirmed for both ISA and PCI access, on NV04 and NV11. */ + + /* setup TVencoder connection */ + /* b1-0 = %00: encoder type is SLAVE; + * b24 = 1: VIP datapos is b0-7 */ + //fixme if needed: setup completely instead of relying on pre-init by BIOS.. + //(it seems to work OK on NV04 and NV11 although read reg. doesn't seem to work) + DAC2W(TV_SETUP, ((DAC2R(TV_SETUP) & ~0x00000003) | 0x01000000)); + + /* tell GPU to use pixelclock from internal source instead of using TVencoder */ + DACW(PLLSEL, 0x30000f00); + + /* HTOTAL, VTOTAL and OVERFLOW return their default CRTC use, instead of + * H, V-low and V-high 'shadow' counters(?)(b0, 4 and 6 = 0) (b7 use = unknown) */ + CRTC2W(TREG, 0x00); + + /* select panel encoder, not TV encoder if needed (b0 = 1). + * Note: + * Both are devices (often) using the CRTC in slaved mode. */ + if (si->ps.slaved_tmds2) CRTC2W(LCD, (CRTC2R(LCD) | 0x01)); + + return B_OK; +} + +status_t nv_crtc2_start_tvout(void) +{ + LOG(4,("CRTC2: starting TV output\n")); + + /* switch TV encoder to CRTC2 */ + NV_REG32(NV32_FUNCSEL) &= ~0x00000100; + NV_REG32(NV32_2FUNCSEL) |= 0x00000100; + + /* enable access to secondary head */ + set_crtc_owner(1); + + /* CAUTION: + * On old cards, PLLSEL (and TV_SETUP?) cannot be read (sometimes?), but + * write actions do succeed ... + * This is confirmed for both ISA and PCI access, on NV04 and NV11. */ + + /* setup TVencoder connection */ + /* b1-0 = %01: encoder type is MASTER; + * b24 = 1: VIP datapos is b0-7 */ + //fixme if needed: setup completely instead of relying on pre-init by BIOS.. + //(it seems to work OK on NV04 and NV11 although read reg. doesn't seem to work) + DAC2W(TV_SETUP, ((DAC2R(TV_SETUP) & ~0x00000002) | 0x01000001)); + + /* tell GPU to use pixelclock from TVencoder instead of using internal source */ + /* (nessecary or display will 'shiver' on both TV and VGA.) */ + DACW(PLLSEL, 0x100c0f00); + + /* Set overscan color to 'black' */ + /* note: + * Change this instruction for a visible overscan color if you're trying to + * center the output on TV. Use it as a guide-'line' then ;-) */ + ATB2W(OSCANCOLOR, 0x00); + + /* set CRTC to slaved mode (b7 = 1) and clear TVadjust (b3-5 = %000) */ + CRTC2W(PIXEL, ((CRTC2R(PIXEL) & 0xc7) | 0x80)); + /* select TV encoder, not panel encoder (b0 = 0). + * Note: + * Both are devices (often) using the CRTC in slaved mode. */ + CRTC2W(LCD, (CRTC2R(LCD) & 0xfe)); + + /* HTOTAL, VTOTAL and OVERFLOW return their default CRTC use, instead of + * H, V-low and V-high 'shadow' counters(?)(b0, 4 and 6 = 0) (b7 use = unknown) */ + CRTC2W(TREG, 0x80); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac.c new file mode 100644 index 0000000000..7ac741ef68 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac.c @@ -0,0 +1,573 @@ +/* program the DAC */ +/* Author: + Rudolf Cornelissen 12/2003-10/2004 +*/ + +#define MODULE_BIT 0x00010000 + +#include "nv_std.h" + +static status_t nv4_nv10_nv20_dac_pix_pll_find( + display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test); + +/* see if an analog VGA monitor is connected to connector #1 */ +bool nv_dac_crt_connected(void) +{ + uint32 output, dac; + bool present; + + /* save output connector setting */ + output = DACR(OUTPUT); + /* save DAC state */ + dac = DACR(TSTCTRL); + + /* turn on DAC */ + DACW(TSTCTRL, (DACR(TSTCTRL) & 0xfffeffff)); + if (si->ps.secondary_head) + { + /* select primary CRTC (head) and turn off CRT (and DVI?) outputs */ + DACW(OUTPUT, (output & 0x0000feee)); + } + else + { + /* turn off CRT (and DVI?) outputs */ + /* note: + * Don't touch the CRTC (head) assignment bit, as that would have undefined + * results. Confirmed NV15 cards getting into lasting RAM access trouble + * otherwise!! (goes for both system gfx RAM access and CRTC/DAC RAM access.) */ + DACW(OUTPUT, (output & 0x0000ffee)); + } + /* wait for signal lines to stabilize */ + snooze(1000); + /* re-enable CRT output */ + DACW(OUTPUT, (DACR(OUTPUT) | 0x00000001)); + + /* setup RGB test signal levels to approx 30% of DAC range and enable them */ + DACW(TSTDATA, ((0x2 << 30) | (0x140 << 20) | (0x140 << 10) | (0x140 << 0))); + /* route test signals to output */ + DACW(TSTCTRL, (DACR(TSTCTRL) | 0x00001000)); + /* wait for signal lines to stabilize */ + snooze(1000); + + /* do actual detection: all signals paths high == CRT connected */ + if (DACR(TSTCTRL) & 0x10000000) + { + present = true; + LOG(4,("DAC: CRT detected on connector #1\n")); + } + else + { + present = false; + LOG(4,("DAC: no CRT detected on connector #1\n")); + } + + /* kill test signal routing */ + DACW(TSTCTRL, (DACR(TSTCTRL) & 0xffffefff)); + + /* restore output connector setting */ + DACW(OUTPUT, output); + /* restore DAC state */ + DACW(TSTCTRL, dac); + + return present; +} + +/*set the mode, brightness is a value from 0->2 (where 1 is equivalent to direct)*/ +status_t nv_dac_mode(int mode,float brightness) +{ + uint8 *r,*g,*b; + int i, ri; + + /*set colour arrays to point to space reserved in shared info*/ + r = si->color_data; + g = r + 256; + b = g + 256; + + LOG(4,("DAC: Setting screen mode %d brightness %f\n", mode, brightness)); + /* init the palette for brightness specified */ + /* (Nvidia cards always use MSbits from screenbuffer as index for PAL) */ + for (i = 0; i < 256; i++) + { + ri = i * brightness; + if (ri > 255) ri = 255; + b[i] = g[i] = r[i] = ri; + } + + if (nv_dac_palette(r,g,b) != B_OK) return B_ERROR; + + /* disable palette RAM adressing mask */ + NV_REG8(NV8_PALMASK) = 0xff; + LOG(2,("DAC: PAL pixrdmsk readback $%02x\n", NV_REG8(NV8_PALMASK))); + + return B_OK; +} + +/*program the DAC palette using the given r,g,b values*/ +status_t nv_dac_palette(uint8 r[256],uint8 g[256],uint8 b[256]) +{ + int i; + + LOG(4,("DAC: setting palette\n")); + + /* select first PAL adress before starting programming */ + NV_REG8(NV8_PALINDW) = 0x00; + + /* loop through all 256 to program DAC */ + for (i = 0; i < 256; i++) + { + /* the 6 implemented bits are on b0-b5 of the bus */ + NV_REG8(NV8_PALDATA) = r[i]; + NV_REG8(NV8_PALDATA) = g[i]; + NV_REG8(NV8_PALDATA) = b[i]; + } + if (NV_REG8(NV8_PALINDW) != 0x00) + { + LOG(8,("DAC: PAL write index incorrect after programming\n")); + return B_ERROR; + } +if (1) + {//reread LUT + uint8 R, G, B; + + /* select first PAL adress to read (modulo 3 counter) */ + NV_REG8(NV8_PALINDR) = 0x00; + for (i = 0; i < 256; i++) + { + R = NV_REG8(NV8_PALDATA); + G = NV_REG8(NV8_PALDATA); + B = NV_REG8(NV8_PALDATA); + if ((r[i] != R) || (g[i] != G) || (b[i] != B)) + LOG(1,("DAC palette %d: w %x %x %x, r %x %x %x\n", i, r[i], g[i], b[i], R, G, B)); // apsed + } + } + + return B_OK; +} + +/*program the pixpll - frequency in kHz*/ +status_t nv_dac_set_pix_pll(display_mode target) +{ + uint8 m=0,n=0,p=0; +// uint time = 0; + + float pix_setting, req_pclk; + status_t result; + + /* we offer this option because some panels have very tight restrictions, + * and there's no overlapping settings range that makes them all work. + * note: + * this assumes the cards BIOS correctly programmed the panel (is likely) */ + //fixme: when VESA DDC EDID stuff is implemented, this option can be deleted... + if (si->ps.tmds1_active && !si->settings.pgm_panel) + { + LOG(4,("DAC: Not programming DFP refresh (specified in nv.settings)\n")); + return B_OK; + } + + /* fix a DVI or laptop flatpanel to 60Hz refresh! */ + /* Note: + * The pixelclock drives the flatpanel modeline, not the CRTC modeline. */ + if (si->ps.tmds1_active) + { + LOG(4,("DAC: Fixing DFP refresh to 60Hz!\n")); + + /* use the panel's modeline to determine the needed pixelclock */ + target.timing.pixel_clock = si->ps.p1_timing.pixel_clock; + } + + req_pclk = (target.timing.pixel_clock)/1000.0; + LOG(4,("DAC: Setting PIX PLL for pixelclock %f\n", req_pclk)); + + /* signal that we actually want to set the mode */ + result = nv_dac_pix_pll_find(target,&pix_setting,&m,&n,&p, 1); + if (result != B_OK) + { + return result; + } + + /*reprogram (disable,select,wait for stability,enable)*/ +// DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0F)|0x04); /*disable the PIXPLL*/ +// DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0C)|0x01); /*select the PIXPLL*/ + + /* program new frequency */ + DACW(PIXPLLC, ((p << 16) | (n << 8) | m)); + + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ + if (si->ps.ext_pll) DACW(PIXPLLC2, 0x80000401); + + /* Wait for the PIXPLL frequency to lock until timeout occurs */ +//fixme: do NV cards have a LOCK indication bit?? +/* while((!(DXIR(PIXPLLSTAT)&0x40)) & (time <= 2000)) + { + time++; + snooze(1); + } + + if (time > 2000) + LOG(2,("DAC: PIX PLL frequency not locked!\n")); + else + LOG(2,("DAC: PIX PLL frequency locked\n")); + DXIW(PIXCLKCTRL,DXIR(PIXCLKCTRL)&0x0B); //enable the PIXPLL +*/ + +//for now: + /* Give the PIXPLL frequency some time to lock... */ + snooze(1000); + LOG(2,("DAC: PIX PLL frequency should be locked now...\n")); + + return B_OK; +} + +/* find nearest valid pix pll */ +status_t nv_dac_pix_pll_find + (display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test) +{ + switch (si->ps.card_type) { + default: return nv4_nv10_nv20_dac_pix_pll_find(target, calc_pclk, m_result, n_result, p_result, test); + } + return B_ERROR; +} + +/* find nearest valid pixel PLL setting */ +static status_t nv4_nv10_nv20_dac_pix_pll_find( + display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test) +{ + int m = 0, n = 0, p = 0/*, m_max*/; + float error, error_best = 999999999; + int best[3]; + float f_vco, max_pclk; + float req_pclk = target.timing.pixel_clock/1000.0; + + /* determine the max. reference-frequency postscaler setting for the + * current card (see G100, G200 and G400 specs). */ +/* switch(si->ps.card_type) + { + case G100: + LOG(4,("DAC: G100 restrictions apply\n")); + m_max = 7; + break; + case G200: + LOG(4,("DAC: G200 restrictions apply\n")); + m_max = 7; + break; + default: + LOG(4,("DAC: G400/G400MAX restrictions apply\n")); + m_max = 32; + break; + } +*/ + LOG(4,("DAC: NV4/NV10/NV20 restrictions apply\n")); + + /* determine the max. pixelclock for the current videomode */ + switch (target.space) + { + case B_CMAP8: + max_pclk = si->ps.max_dac1_clock_8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_pclk = si->ps.max_dac1_clock_16; + break; + case B_RGB24_LITTLE: + max_pclk = si->ps.max_dac1_clock_24; + break; + case B_RGB32_LITTLE: + max_pclk = si->ps.max_dac1_clock_32; + break; + default: + /* use fail-safe value */ + max_pclk = si->ps.max_dac1_clock_32; + break; + } + /* if some dualhead mode is active, an extra restriction might apply */ + if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE)) + max_pclk = si->ps.max_dac1_clock_32dh; + + /* Make sure the requested pixelclock is within the PLL's operational limits */ + /* lower limit is min_pixel_vco divided by highest postscaler-factor */ + if (req_pclk < (si->ps.min_pixel_vco / 16.0)) + { + LOG(4,("DAC: clamping pixclock: requested %fMHz, set to %fMHz\n", + req_pclk, (float)(si->ps.min_pixel_vco / 16.0))); + req_pclk = (si->ps.min_pixel_vco / 16.0); + } + /* upper limit is given by pins in combination with current active mode */ + if (req_pclk > max_pclk) + { + LOG(4,("DAC: clamping pixclock: requested %fMHz, set to %fMHz\n", + req_pclk, (float)max_pclk)); + req_pclk = max_pclk; + } + + /* iterate through all valid PLL postscaler settings */ + for (p=0x01; p < 0x20; p = p<<1) + { + /* calculate the needed VCO frequency for this postscaler setting */ + f_vco = req_pclk * p; + + /* check if this is within range of the VCO specs */ + if ((f_vco >= si->ps.min_pixel_vco) && (f_vco <= si->ps.max_pixel_vco)) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco /= 4; + + /* iterate trough all valid reference-frequency postscaler settings */ + for (m = 7; m <= 14; m++) + { + /* check if phase-discriminator will be within operational limits */ + //fixme: PLL calcs will be resetup/splitup/updated... + if (si->ps.card_type == NV36) + { + if (((si->ps.f_ref / m) < 3.2) || ((si->ps.f_ref / m) > 6.4)) continue; + } + else + { + if (((si->ps.f_ref / m) < 1.0) || ((si->ps.f_ref / m) > 2.0)) continue; + } + + /* calculate VCO postscaler setting for current setup.. */ + n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5); + + /* ..and check for validity */ + if ((n < 1) || (n > 255)) continue; + + /* find error in frequency this setting gives */ + if (si->ps.ext_pll) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + error = fabs((req_pclk / 4) - (((si->ps.f_ref / m) * n) / p)); + } + else + error = fabs(req_pclk - (((si->ps.f_ref / m) * n) / p)); + + /* note the setting if best yet */ + if (error < error_best) + { + error_best = error; + best[0]=m; + best[1]=n; + best[2]=p; + } + } + } + } + + /* setup the scalers programming values for found optimum setting */ + m = best[0]; + n = best[1]; + p = best[2]; + + /* log the VCO frequency found */ + f_vco = ((si->ps.f_ref / m) * n); + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco *= 4; + + LOG(2,("DAC: pix VCO frequency found %fMhz\n", f_vco)); + + /* return the results */ + *calc_pclk = (f_vco / p); + *m_result = m; + *n_result = n; + switch(p) + { + case 1: + p = 0x00; + break; + case 2: + p = 0x01; + break; + case 4: + p = 0x02; + break; + case 8: + p = 0x03; + break; + case 16: + p = 0x04; + break; + } + *p_result = p; + + /* display the found pixelclock values */ + LOG(2,("DAC: pix PLL check: requested %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n", + req_pclk, *calc_pclk, *m_result, *n_result, *p_result)); + + return B_OK; +} + +/* find nearest valid system PLL setting */ +status_t nv_dac_sys_pll_find( + float req_sclk, float* calc_sclk, uint8* m_result, uint8* n_result, uint8* p_result, uint8 test) +{ + int m = 0, n = 0, p = 0, m_max, p_max; + float error, error_best = 999999999; + int best[3]; + float f_vco, discr_low, discr_high; + + /* determine the max. reference-frequency postscaler setting for the + * current requested clock */ + switch (si->ps.card_arch) + { + case NV04A: + LOG(4,("DAC: NV04 restrictions apply\n")); + /* set phase-discriminator frequency range (Mhz) (verified) */ + discr_low = 1.0; + discr_high = 2.0; + /* set max. useable reference frequency postscaler divider factor */ + m_max = 14; + /* set max. useable VCO output postscaler divider factor */ + p_max = 16; + break; + default: + switch (si->ps.card_type) + { + case NV28: + //fixme: how about some other cards??? + LOG(4,("DAC: NV28 restrictions apply\n")); + /* set max. useable reference frequency postscaler divider factor; + * apparantly we would get distortions on high PLL output frequencies if + * we use the phase-discriminator at low frequencies */ + if (req_sclk > 340.0) m_max = 2; /* Fpll > 340Mhz */ + else if (req_sclk > 200.0) m_max = 4; /* 200Mhz < Fpll <= 340Mhz */ + else if (req_sclk > 150.0) m_max = 6; /* 150Mhz < Fpll <= 200Mhz */ + else m_max = 14; /* Fpll < 150Mhz */ + + /* set max. useable VCO output postscaler divider factor */ + p_max = 32; + /* set phase-discriminator frequency range (Mhz) (verified) */ + discr_low = 1.0; + discr_high = 27.0; + break; + default: + LOG(4,("DAC: NV10/NV20/NV30 restrictions apply\n")); + /* set max. useable reference frequency postscaler divider factor; + * apparantly we would get distortions on high PLL output frequencies if + * we use the phase-discriminator at low frequencies */ + if (req_sclk > 340.0) m_max = 2; /* Fpll > 340Mhz */ + else if (req_sclk > 250.0) m_max = 6; /* 250Mhz < Fpll <= 340Mhz */ + else m_max = 14; /* Fpll < 250Mhz */ + + /* set max. useable VCO output postscaler divider factor */ + p_max = 16; + /* set phase-discriminator frequency range (Mhz) (verified) */ + if (si->ps.card_type == NV36) discr_low = 3.2; + else discr_low = 1.0; + /* (high discriminator spec is failsafe) */ + discr_high = 14.0; + break; + } + break; + } + + LOG(4,("DAC: PLL reference frequency postscaler divider range is 1 - %d\n", m_max)); + LOG(4,("DAC: PLL VCO output postscaler divider range is 1 - %d\n", p_max)); + LOG(4,("DAC: PLL discriminator input frequency range is %2.2fMhz - %2.2fMhz\n", + discr_low, discr_high)); + + /* Make sure the requested clock is within the PLL's operational limits */ + /* lower limit is min_system_vco divided by highest postscaler-factor */ + if (req_sclk < (si->ps.min_system_vco / ((float)p_max))) + { + LOG(4,("DAC: clamping sysclock: requested %fMHz, set to %fMHz\n", + req_sclk, (si->ps.min_system_vco / ((float)p_max)))); + req_sclk = (si->ps.min_system_vco / ((float)p_max)); + } + /* upper limit is given by pins */ + if (req_sclk > si->ps.max_system_vco) + { + LOG(4,("DAC: clamping sysclock: requested %fMHz, set to %fMHz\n", + req_sclk, (float)si->ps.max_system_vco)); + req_sclk = si->ps.max_system_vco; + } + + /* iterate through all valid PLL postscaler settings */ + for (p=0x01; p <= p_max; p = p<<1) + { + /* calculate the needed VCO frequency for this postscaler setting */ + f_vco = req_sclk * p; + + /* check if this is within range of the VCO specs */ + if ((f_vco >= si->ps.min_system_vco) && (f_vco <= si->ps.max_system_vco)) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco /= 4; + + /* iterate trough all valid reference-frequency postscaler settings */ + for (m = 1; m <= m_max; m++) + { + /* check if phase-discriminator will be within operational limits */ + if (((si->ps.f_ref / m) < discr_low) || ((si->ps.f_ref / m) > discr_high)) + continue; + + /* calculate VCO postscaler setting for current setup.. */ + n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5); + + /* ..and check for validity */ + if ((n < 1) || (n > 255)) continue; + + /* find error in frequency this setting gives */ + if (si->ps.ext_pll) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + error = fabs((req_sclk / 4) - (((si->ps.f_ref / m) * n) / p)); + } + else + error = fabs(req_sclk - (((si->ps.f_ref / m) * n) / p)); + + /* note the setting if best yet */ + if (error < error_best) + { + error_best = error; + best[0]=m; + best[1]=n; + best[2]=p; + } + } + } + } + + /* setup the scalers programming values for found optimum setting */ + m = best[0]; + n = best[1]; + p = best[2]; + + /* log the VCO frequency found */ + f_vco = ((si->ps.f_ref / m) * n); + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco *= 4; + + LOG(2,("DAC: sys VCO frequency found %fMhz\n", f_vco)); + + /* return the results */ + *calc_sclk = (f_vco / p); + *m_result = m; + *n_result = n; + switch(p) + { + case 1: + p = 0x00; + break; + case 2: + p = 0x01; + break; + case 4: + p = 0x02; + break; + case 8: + p = 0x03; + break; + case 16: + p = 0x04; + break; + case 32: + p = 0x05; + break; + } + *p_result = p; + + /* display the found pixelclock values */ + LOG(2,("DAC: sys PLL check: requested %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n", + req_sclk, *calc_sclk, *m_result, *n_result, *p_result)); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac2.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac2.c new file mode 100644 index 0000000000..72df7b6c3e --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_dac2.c @@ -0,0 +1,392 @@ +/* program the secondary DAC */ +/* Author: + Rudolf Cornelissen 12/2003-9/2004 +*/ + +#define MODULE_BIT 0x00001000 + +#include "nv_std.h" + +static status_t nv10_nv20_dac2_pix_pll_find( + display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test); + +/* see if an analog VGA monitor is connected to connector #2 */ +//fixme if possible: on NV40 arch (confirmed NV43) this routine always find a monitor! +bool nv_dac2_crt_connected() +{ + uint32 output, dac; + bool present; + + /* NOTE: + * NV11 can't do this: It will report DAC1 status instead because it HAS no + * actual secondary DAC function. */ + /* (It DOES have a secondary palette RAM and pixelclock PLL though.) */ + + /* save output connector setting */ + output = DAC2R(OUTPUT); + /* save DAC state */ + dac = DAC2R(TSTCTRL); + + /* turn on DAC2 */ + DAC2W(TSTCTRL, (DAC2R(TSTCTRL) & 0xfffeffff)); + /* select primary CRTC (head) and turn off CRT (and DVI?) outputs */ + DAC2W(OUTPUT, (output & 0x0000feee)); + /* wait for signal lines to stabilize */ + snooze(1000); + /* re-enable CRT output */ + DAC2W(OUTPUT, (DAC2R(OUTPUT) | 0x00000001)); + + /* setup RGB test signal levels to approx 30% of DAC range and enable them + * (NOTE: testsignal function block resides in DAC1 only (!)) */ + DACW(TSTDATA, ((0x2 << 30) | (0x140 << 20) | (0x140 << 10) | (0x140 << 0))); + /* route test signals to output + * (NOTE: testsignal function block resides in DAC1 only (!)) */ + DACW(TSTCTRL, (DACR(TSTCTRL) | 0x00001000)); + /* wait for signal lines to stabilize */ + snooze(1000); + + /* do actual detection: all signals paths high == CRT connected */ + if (DAC2R(TSTCTRL) & 0x10000000) + { + present = true; + LOG(4,("DAC2: CRT detected on connector #2\n")); + } + else + { + present = false; + LOG(4,("DAC2: no CRT detected on connector #2\n")); + } + + /* kill test signal routing + * (NOTE: testsignal function block resides in DAC1 only (!)) */ + DACW(TSTCTRL, (DACR(TSTCTRL) & 0xffffefff)); + + /* restore output connector setting */ + DAC2W(OUTPUT, output); + /* restore DAC state */ + DAC2W(TSTCTRL, dac); + + return present; +} + +/*set the mode, brightness is a value from 0->2 (where 1 is equivalent to direct)*/ +status_t nv_dac2_mode(int mode,float brightness) +{ + uint8 *r,*g,*b; + int i, ri; + + /*set colour arrays to point to space reserved in shared info*/ + r = si->color_data; + g = r + 256; + b = g + 256; + + LOG(4,("DAC2: Setting screen mode %d brightness %f\n", mode, brightness)); + /* init the palette for brightness specified */ + /* (Nvidia cards always use MSbits from screenbuffer as index for PAL) */ + for (i = 0; i < 256; i++) + { + ri = i * brightness; + if (ri > 255) ri = 255; + b[i] = g[i] = r[i] = ri; + } + + if (nv_dac2_palette(r,g,b) != B_OK) return B_ERROR; + + /* disable palette RAM adressing mask */ + NV_REG8(NV8_PAL2MASK) = 0xff; + LOG(2,("DAC2: PAL pixrdmsk readback $%02x\n", NV_REG8(NV8_PAL2MASK))); + + return B_OK; +} + +/*program the DAC palette using the given r,g,b values*/ +status_t nv_dac2_palette(uint8 r[256],uint8 g[256],uint8 b[256]) +{ + int i; + + LOG(4,("DAC2: setting palette\n")); + + /* select first PAL adress before starting programming */ + NV_REG8(NV8_PAL2INDW) = 0x00; + + /* loop through all 256 to program DAC */ + for (i = 0; i < 256; i++) + { + /* the 6 implemented bits are on b0-b5 of the bus */ + NV_REG8(NV8_PAL2DATA) = r[i]; + NV_REG8(NV8_PAL2DATA) = g[i]; + NV_REG8(NV8_PAL2DATA) = b[i]; + } + if (NV_REG8(NV8_PAL2INDW) != 0x00) + { + LOG(8,("DAC2: PAL write index incorrect after programming\n")); + return B_ERROR; + } +if (1) + {//reread LUT + uint8 R, G, B; + + /* select first PAL adress to read (modulo 3 counter) */ + NV_REG8(NV8_PAL2INDR) = 0x00; + for (i = 0; i < 256; i++) + { + R = NV_REG8(NV8_PAL2DATA); + G = NV_REG8(NV8_PAL2DATA); + B = NV_REG8(NV8_PAL2DATA); + if ((r[i] != R) || (g[i] != G) || (b[i] != B)) + LOG(1,("DAC2 palette %d: w %x %x %x, r %x %x %x\n", i, r[i], g[i], b[i], R, G, B)); // apsed + } + } + + return B_OK; +} + +/*program the pixpll - frequency in kHz*/ +status_t nv_dac2_set_pix_pll(display_mode target) +{ + uint8 m=0,n=0,p=0; +// uint time = 0; + + float pix_setting, req_pclk; + status_t result; + + /* we offer this option because some panels have very tight restrictions, + * and there's no overlapping settings range that makes them all work. + * note: + * this assumes the cards BIOS correctly programmed the panel (is likely) */ + //fixme: when VESA DDC EDID stuff is implemented, this option can be deleted... + if (si->ps.tmds2_active && !si->settings.pgm_panel) + { + LOG(4,("DAC2: Not programming DFP refresh (specified in nv.settings)\n")); + return B_OK; + } + + /* fix a DVI or laptop flatpanel to 60Hz refresh! */ + /* Note: + * The pixelclock drives the flatpanel modeline, not the CRTC modeline. */ + if (si->ps.tmds2_active) + { + LOG(4,("DAC2: Fixing DFP refresh to 60Hz!\n")); + + /* use the panel's modeline to determine the needed pixelclock */ + target.timing.pixel_clock = si->ps.p2_timing.pixel_clock; + } + + req_pclk = (target.timing.pixel_clock)/1000.0; + LOG(4,("DAC2: Setting PIX PLL for pixelclock %f\n", req_pclk)); + + /* signal that we actually want to set the mode */ + result = nv_dac2_pix_pll_find(target,&pix_setting,&m,&n,&p, 1); + if (result != B_OK) + { + return result; + } + + /*reprogram (disable,select,wait for stability,enable)*/ +// DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0F)|0x04); /*disable the PIXPLL*/ +// DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0C)|0x01); /*select the PIXPLL*/ + + /* program new frequency */ + DAC2W(PIXPLLC, ((p << 16) | (n << 8) | m)); + + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ + if (si->ps.ext_pll) DAC2W(PIXPLLC2, 0x80000401); + + /* Wait for the PIXPLL frequency to lock until timeout occurs */ +//fixme: do NV cards have a LOCK indication bit?? +/* while((!(DXIR(PIXPLLSTAT)&0x40)) & (time <= 2000)) + { + time++; + snooze(1); + } + + if (time > 2000) + LOG(2,("DAC: PIX PLL frequency not locked!\n")); + else + LOG(2,("DAC: PIX PLL frequency locked\n")); + DXIW(PIXCLKCTRL,DXIR(PIXCLKCTRL)&0x0B); //enable the PIXPLL +*/ + +//for now: + /* Give the PIXPLL frequency some time to lock... */ + snooze(1000); + LOG(2,("DAC2: PIX PLL frequency should be locked now...\n")); + + return B_OK; +} + +/* find nearest valid pix pll */ +status_t nv_dac2_pix_pll_find + (display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test) +{ + switch (si->ps.card_type) { + default: return nv10_nv20_dac2_pix_pll_find(target, calc_pclk, m_result, n_result, p_result, test); + } + return B_ERROR; +} + +/* find nearest valid pixel PLL setting */ +static status_t nv10_nv20_dac2_pix_pll_find( + display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test) +{ + int m = 0, n = 0, p = 0/*, m_max*/; + float error, error_best = 999999999; + int best[3]; + float f_vco, max_pclk; + float req_pclk = target.timing.pixel_clock/1000.0; + + /* determine the max. reference-frequency postscaler setting for the + * current card (see G100, G200 and G400 specs). */ +/* switch(si->ps.card_type) + { + case G100: + LOG(4,("DAC: G100 restrictions apply\n")); + m_max = 7; + break; + case G200: + LOG(4,("DAC: G200 restrictions apply\n")); + m_max = 7; + break; + default: + LOG(4,("DAC: G400/G400MAX restrictions apply\n")); + m_max = 32; + break; + } +*/ + LOG(4,("DAC2: NV10/NV20 restrictions apply\n")); + + /* determine the max. pixelclock for the current videomode */ + switch (target.space) + { + case B_CMAP8: + max_pclk = si->ps.max_dac2_clock_8; + break; + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + max_pclk = si->ps.max_dac2_clock_16; + break; + case B_RGB24_LITTLE: + max_pclk = si->ps.max_dac2_clock_24; + break; + case B_RGB32_LITTLE: + max_pclk = si->ps.max_dac2_clock_32; + break; + default: + /* use fail-safe value */ + max_pclk = si->ps.max_dac2_clock_32; + break; + } + /* if some dualhead mode is active, an extra restriction might apply */ + if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE)) + max_pclk = si->ps.max_dac2_clock_32dh; + + /* Make sure the requested pixelclock is within the PLL's operational limits */ + /* lower limit is min_pixel_vco divided by highest postscaler-factor */ + if (req_pclk < (si->ps.min_video_vco / 16.0)) + { + LOG(4,("DAC2: clamping pixclock: requested %fMHz, set to %fMHz\n", + req_pclk, (float)(si->ps.min_video_vco / 16.0))); + req_pclk = (si->ps.min_video_vco / 16.0); + } + /* upper limit is given by pins in combination with current active mode */ + if (req_pclk > max_pclk) + { + LOG(4,("DAC2: clamping pixclock: requested %fMHz, set to %fMHz\n", + req_pclk, (float)max_pclk)); + req_pclk = max_pclk; + } + + /* iterate through all valid PLL postscaler settings */ + for (p=0x01; p < 0x20; p = p<<1) + { + /* calculate the needed VCO frequency for this postscaler setting */ + f_vco = req_pclk * p; + + /* check if this is within range of the VCO specs */ + if ((f_vco >= si->ps.min_video_vco) && (f_vco <= si->ps.max_video_vco)) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco /= 4; + + /* iterate trough all valid reference-frequency postscaler settings */ + for (m = 7; m <= 14; m++) + { + /* check if phase-discriminator will be within operational limits */ + //fixme: PLL calcs will be resetup/splitup/updated... + if (si->ps.card_type == NV36) + { + if (((si->ps.f_ref / m) < 3.2) || ((si->ps.f_ref / m) > 6.4)) continue; + } + else + { + if (((si->ps.f_ref / m) < 1.0) || ((si->ps.f_ref / m) > 2.0)) continue; + } + + /* calculate VCO postscaler setting for current setup.. */ + n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5); + /* ..and check for validity */ + if ((n < 1) || (n > 255)) continue; + + /* find error in frequency this setting gives */ + if (si->ps.ext_pll) + { + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + error = fabs((req_pclk / 4) - (((si->ps.f_ref / m) * n) / p)); + } + else + error = fabs(req_pclk - (((si->ps.f_ref / m) * n) / p)); + + /* note the setting if best yet */ + if (error < error_best) + { + error_best = error; + best[0]=m; + best[1]=n; + best[2]=p; + } + } + } + } + + /* setup the scalers programming values for found optimum setting */ + m = best[0]; + n = best[1]; + p = best[2]; + + /* log the VCO frequency found */ + f_vco = ((si->ps.f_ref / m) * n); + /* FX5600 and FX5700 tweak for 2nd set N and M scalers */ + if (si->ps.ext_pll) f_vco *= 4; + + LOG(2,("DAC2: pix VCO frequency found %fMhz\n", f_vco)); + + /* return the results */ + *calc_pclk = (f_vco / p); + *m_result = m; + *n_result = n; + switch(p) + { + case 1: + p = 0x00; + break; + case 2: + p = 0x01; + break; + case 4: + p = 0x02; + break; + case 8: + p = 0x03; + break; + case 16: + p = 0x04; + break; + } + *p_result = p; + + /* display the found pixelclock values */ + LOG(2,("DAC2: pix PLL check: requested %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n", + req_pclk, *calc_pclk, *m_result, *n_result, *p_result)); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_general.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_general.c new file mode 100644 index 0000000000..2a21350eff --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_general.c @@ -0,0 +1,961 @@ +/* Authors: + Mark Watson 12/1999, + Apsed, + Rudolf Cornelissen 10/2002-6/2008 +*/ + +#define MODULE_BIT 0x00008000 + +#include "nv_std.h" + +static status_t test_ram(void); +static status_t nvxx_general_powerup (void); +static void unlock_card(void); +static status_t nv_general_bios_to_powergraphics(void); + +static void nv_dump_configuration_space (void) +{ +#define DUMP_CFG(reg, type) if (si->ps.card_type >= type) do { \ + uint32 value = CFGR(reg); \ + MSG(("configuration_space 0x%02x %20s 0x%08x\n", \ + NVCFG_##reg, #reg, value)); \ +} while (0) + DUMP_CFG (DEVID, 0); + DUMP_CFG (DEVCTRL, 0); + DUMP_CFG (CLASS, 0); + DUMP_CFG (HEADER, 0); + DUMP_CFG (BASE1REGS,0); + DUMP_CFG (BASE2FB, 0); + DUMP_CFG (BASE3, 0); + DUMP_CFG (BASE4, 0); + DUMP_CFG (BASE5, 0); + DUMP_CFG (BASE6, 0); + DUMP_CFG (BASE7, 0); + DUMP_CFG (SUBSYSID1,0); + DUMP_CFG (ROMBASE, 0); + DUMP_CFG (CAPPTR, 0); + DUMP_CFG (CFG_1, 0); + DUMP_CFG (INTERRUPT,0); + DUMP_CFG (SUBSYSID2,0); + DUMP_CFG (AGPREF, 0); + DUMP_CFG (AGPSTAT, 0); + DUMP_CFG (AGPCMD, 0); + DUMP_CFG (ROMSHADOW,0); + DUMP_CFG (VGA, 0); + DUMP_CFG (SCHRATCH, 0); + DUMP_CFG (CFG_10, 0); + DUMP_CFG (CFG_11, 0); + DUMP_CFG (CFG_12, 0); + DUMP_CFG (CFG_13, 0); + DUMP_CFG (CFG_14, 0); + DUMP_CFG (CFG_15, 0); + DUMP_CFG (CFG_16, 0); + DUMP_CFG (PCIEREF, 0); + DUMP_CFG (PCIEDCAP, 0); + DUMP_CFG (PCIEDCTST,0); + DUMP_CFG (PCIELCAP, 0); + DUMP_CFG (PCIELCTST,0); + DUMP_CFG (CFG_22, 0); + DUMP_CFG (CFG_23, 0); + DUMP_CFG (CFG_24, 0); + DUMP_CFG (CFG_25, 0); + DUMP_CFG (CFG_26, 0); + DUMP_CFG (CFG_27, 0); + DUMP_CFG (CFG_28, 0); + DUMP_CFG (CFG_29, 0); + DUMP_CFG (CFG_30, 0); + DUMP_CFG (CFG_31, 0); + DUMP_CFG (CFG_32, 0); + DUMP_CFG (CFG_33, 0); + DUMP_CFG (CFG_34, 0); + DUMP_CFG (CFG_35, 0); + DUMP_CFG (CFG_36, 0); + DUMP_CFG (CFG_37, 0); + DUMP_CFG (CFG_38, 0); + DUMP_CFG (CFG_39, 0); + DUMP_CFG (CFG_40, 0); + DUMP_CFG (CFG_41, 0); + DUMP_CFG (CFG_42, 0); + DUMP_CFG (CFG_43, 0); + DUMP_CFG (CFG_44, 0); + DUMP_CFG (CFG_45, 0); + DUMP_CFG (CFG_46, 0); + DUMP_CFG (CFG_47, 0); + DUMP_CFG (CFG_48, 0); + DUMP_CFG (CFG_49, 0); + DUMP_CFG (CFG_50, 0); +#undef DUMP_CFG +} + +status_t nv_general_powerup() +{ + status_t status; + + LOG(1,("POWERUP: Haiku nVidia-gpgpu Accelerant 0.00 running.\n")); + + /* log VBLANK INT usability status */ + if (si->ps.int_assigned) + LOG(4,("POWERUP: Usable INT assigned to HW; Vblank semaphore enabled\n")); + else + LOG(4,("POWERUP: No (usable) INT assigned to HW; Vblank semaphore disabled\n")); + + /* preset no laptop */ + si->ps.laptop = false; + + /* WARNING: + * _adi.name_ and _adi.chipset_ can contain 31 readable characters max.!!! */ + + /* detect card type and power it up */ + switch(CFGR(DEVID)) + { + /* Vendor Nvidia */ + case 0x019110de: /* Nvidia GeForce 8800 GTX */ + case 0x019310de: /* Nvidia GeForce 8800 GTS */ + si->ps.card_type = G80; + si->ps.card_arch = NV50A; + sprintf(si->adi.name, "Nvidia GeForce 8800"); + sprintf(si->adi.chipset, "G80"); + status = nvxx_general_powerup(); + break; + case 0x040010de: /* Nvidia GeForce 8600 GTS */ + case 0x040210de: /* Nvidia GeForce 8600 GT */ + si->ps.card_type = G84; + si->ps.card_arch = NV50A; + sprintf(si->adi.name, "Nvidia GeForce 8600"); + sprintf(si->adi.chipset, "G84"); + status = nvxx_general_powerup(); + break; + case 0x040710de: /* Nvidia GeForce 8600M GT */ + si->ps.card_type = G86; + si->ps.card_arch = NV50A; + si->ps.laptop = true; + sprintf(si->adi.name, "Nvidia GeForce 8600M GT"); + sprintf(si->adi.chipset, "G86"); + status = nvxx_general_powerup(); + break; + case 0x042110de: /* Nvidia GeForce 8500 GT */ + si->ps.card_type = G86; + si->ps.card_arch = NV50A; + sprintf(si->adi.name, "Nvidia GeForce 8500 GT"); + sprintf(si->adi.chipset, "G86"); + status = nvxx_general_powerup(); + break; + case 0x042210de: /* Nvidia GeForce 8400 GS */ + si->ps.card_type = G86; + si->ps.card_arch = NV50A; + sprintf(si->adi.name, "Nvidia GeForce 8400 GS"); + sprintf(si->adi.chipset, "G86"); + status = nvxx_general_powerup(); + break; + case 0x042310de: /* Nvidia GeForce 8300 GS */ + si->ps.card_type = G86; + si->ps.card_arch = NV50A; + sprintf(si->adi.name, "Nvidia GeForce 8300 GS"); + sprintf(si->adi.chipset, "G86"); + status = nvxx_general_powerup(); + break; + default: + LOG(8,("POWERUP: Failed to detect valid card 0x%08x\n",CFGR(DEVID))); + return B_ERROR; + } + + return status; +} + +static status_t test_ram() +{ + uint32 value, offset; + status_t result = B_OK; + + /* make sure we don't corrupt the hardware cursor by using fbc.frame_buffer. */ + if (si->fbc.frame_buffer == NULL) + { + LOG(8,("INIT: test_ram detected NULL pointer.\n")); + return B_ERROR; + } + + for (offset = 0, value = 0x55aa55aa; offset < 256; offset++) + { + /* write testpattern to cardRAM */ + ((uint32 *)si->fbc.frame_buffer)[offset] = value; + /* toggle testpattern */ + value = 0xffffffff - value; + } + + for (offset = 0, value = 0x55aa55aa; offset < 256; offset++) + { + /* readback and verify testpattern from cardRAM */ + if (((uint32 *)si->fbc.frame_buffer)[offset] != value) result = B_ERROR; + /* toggle testpattern */ + value = 0xffffffff - value; + } + return result; +} + +/* NOTE: + * This routine *has* to be done *after* SetDispplayMode has been executed, + * or test results will not be representative! + * (CAS latency is dependant on NV setup on some (DRAM) boards) */ +status_t nv_set_cas_latency() +{ + status_t result = B_ERROR; + uint8 latency = 0; + + /* check current RAM access to see if we need to change anything */ + if (test_ram() == B_OK) + { + LOG(4,("INIT: RAM access OK.\n")); + return B_OK; + } + + /* check if we read PINS at starttime so we have valid registersettings at our disposal */ + if (si->ps.pins_status != B_OK) + { + LOG(4,("INIT: RAM access errors; not fixable: PINS was not read from cardBIOS.\n")); + return B_ERROR; + } + + /* OK. We might have a problem, try to fix it now.. */ + LOG(4,("INIT: RAM access errors; tuning CAS latency if prudent...\n")); + + switch(si->ps.card_type) + { + default: + LOG(4,("INIT: RAM CAS tuning not implemented for this card, aborting.\n")); + return B_OK; + break; + } + if (result == B_OK) + LOG(4,("INIT: RAM access OK. CAS latency set to %d cycles.\n", latency)); + else + LOG(4,("INIT: RAM access not fixable. CAS latency set to %d cycles.\n", latency)); + + return result; +} + +void setup_virtualized_heads(bool cross) +{ + if (cross) + { + head1_interrupt_enable = (crtc_interrupt_enable) nv_crtc2_interrupt_enable; + head1_update_fifo = (crtc_update_fifo) nv_crtc2_update_fifo; + head1_validate_timing = (crtc_validate_timing) nv_crtc2_validate_timing; + head1_set_timing = (crtc_set_timing) nv_crtc2_set_timing; + head1_depth = (crtc_depth) nv_crtc2_depth; + head1_dpms = (crtc_dpms) nv_crtc2_dpms; + head1_set_display_pitch = (crtc_set_display_pitch) nv_crtc2_set_display_pitch; + head1_set_display_start = (crtc_set_display_start) nv_crtc2_set_display_start; + head1_cursor_init = (crtc_cursor_init) nv_crtc2_cursor_init; + head1_cursor_show = (crtc_cursor_show) nv_crtc2_cursor_show; + head1_cursor_hide = (crtc_cursor_hide) nv_crtc2_cursor_hide; + head1_cursor_define = (crtc_cursor_define) nv_crtc2_cursor_define; + head1_cursor_position = (crtc_cursor_position) nv_crtc2_cursor_position; + head1_stop_tvout = (crtc_stop_tvout) nv_crtc2_stop_tvout; + head1_start_tvout = (crtc_start_tvout) nv_crtc2_start_tvout; + + head1_mode = (dac_mode) nv_dac2_mode; + head1_palette = (dac_palette) nv_dac2_palette; + head1_set_pix_pll = (dac_set_pix_pll) nv_dac2_set_pix_pll; + head1_pix_pll_find = (dac_pix_pll_find) nv_dac2_pix_pll_find; + + head2_interrupt_enable = (crtc_interrupt_enable) nv_crtc_interrupt_enable; + head2_update_fifo = (crtc_update_fifo) nv_crtc_update_fifo; + head2_validate_timing = (crtc_validate_timing) nv_crtc_validate_timing; + head2_set_timing = (crtc_set_timing) nv_crtc_set_timing; + head2_depth = (crtc_depth) nv_crtc_depth; + head2_dpms = (crtc_dpms) nv_crtc_dpms; + head2_set_display_pitch = (crtc_set_display_pitch) nv_crtc_set_display_pitch; + head2_set_display_start = (crtc_set_display_start) nv_crtc_set_display_start; + head2_cursor_init = (crtc_cursor_init) nv_crtc_cursor_init; + head2_cursor_show = (crtc_cursor_show) nv_crtc_cursor_show; + head2_cursor_hide = (crtc_cursor_hide) nv_crtc_cursor_hide; + head2_cursor_define = (crtc_cursor_define) nv_crtc_cursor_define; + head2_cursor_position = (crtc_cursor_position) nv_crtc_cursor_position; + head2_stop_tvout = (crtc_stop_tvout) nv_crtc_stop_tvout; + head2_start_tvout = (crtc_start_tvout) nv_crtc_start_tvout; + + head2_mode = (dac_mode) nv_dac_mode; + head2_palette = (dac_palette) nv_dac_palette; + head2_set_pix_pll = (dac_set_pix_pll) nv_dac_set_pix_pll; + head2_pix_pll_find = (dac_pix_pll_find) nv_dac_pix_pll_find; + } + else + { + head1_interrupt_enable = (crtc_interrupt_enable) nv_crtc_interrupt_enable; + head1_update_fifo = (crtc_update_fifo) nv_crtc_update_fifo; + head1_validate_timing = (crtc_validate_timing) nv_crtc_validate_timing; + head1_set_timing = (crtc_set_timing) nv_crtc_set_timing; + head1_depth = (crtc_depth) nv_crtc_depth; + head1_dpms = (crtc_dpms) nv_crtc_dpms; + head1_set_display_pitch = (crtc_set_display_pitch) nv_crtc_set_display_pitch; + head1_set_display_start = (crtc_set_display_start) nv_crtc_set_display_start; + head1_cursor_init = (crtc_cursor_init) nv_crtc_cursor_init; + head1_cursor_show = (crtc_cursor_show) nv_crtc_cursor_show; + head1_cursor_hide = (crtc_cursor_hide) nv_crtc_cursor_hide; + head1_cursor_define = (crtc_cursor_define) nv_crtc_cursor_define; + head1_cursor_position = (crtc_cursor_position) nv_crtc_cursor_position; + head1_stop_tvout = (crtc_stop_tvout) nv_crtc_stop_tvout; + head1_start_tvout = (crtc_start_tvout) nv_crtc_start_tvout; + + head1_mode = (dac_mode) nv_dac_mode; + head1_palette = (dac_palette) nv_dac_palette; + head1_set_pix_pll = (dac_set_pix_pll) nv_dac_set_pix_pll; + head1_pix_pll_find = (dac_pix_pll_find) nv_dac_pix_pll_find; + + head2_interrupt_enable = (crtc_interrupt_enable) nv_crtc2_interrupt_enable; + head2_update_fifo = (crtc_update_fifo) nv_crtc2_update_fifo; + head2_validate_timing = (crtc_validate_timing) nv_crtc2_validate_timing; + head2_set_timing = (crtc_set_timing) nv_crtc2_set_timing; + head2_depth = (crtc_depth) nv_crtc2_depth; + head2_dpms = (crtc_dpms) nv_crtc2_dpms; + head2_set_display_pitch = (crtc_set_display_pitch) nv_crtc2_set_display_pitch; + head2_set_display_start = (crtc_set_display_start) nv_crtc2_set_display_start; + head2_cursor_init = (crtc_cursor_init) nv_crtc2_cursor_init; + head2_cursor_show = (crtc_cursor_show) nv_crtc2_cursor_show; + head2_cursor_hide = (crtc_cursor_hide) nv_crtc2_cursor_hide; + head2_cursor_define = (crtc_cursor_define) nv_crtc2_cursor_define; + head2_cursor_position = (crtc_cursor_position) nv_crtc2_cursor_position; + head2_stop_tvout = (crtc_stop_tvout) nv_crtc2_stop_tvout; + head2_start_tvout = (crtc_start_tvout) nv_crtc2_start_tvout; + + head2_mode = (dac_mode) nv_dac2_mode; + head2_palette = (dac_palette) nv_dac2_palette; + head2_set_pix_pll = (dac_set_pix_pll) nv_dac2_set_pix_pll; + head2_pix_pll_find = (dac_pix_pll_find) nv_dac2_pix_pll_find; + } +} + +void set_crtc_owner(bool head) +{ + if (si->ps.secondary_head) + { + if (!head) + { + /* note: 'OWNER' is a non-standard register in behaviour(!) on NV11's, + * while non-NV11 cards behave normally. + * + * Double-write action needed on those strange NV11 cards: */ + /* RESET: needed on NV11 */ + CRTCW(OWNER, 0xff); + /* enable access to CRTC1, SEQ1, GRPH1, ATB1, ??? */ + CRTCW(OWNER, 0x00); + } + else + { + /* note: 'OWNER' is a non-standard register in behaviour(!) on NV11's, + * while non-NV11 cards behave normally. + * + * Double-write action needed on those strange NV11 cards: */ + /* RESET: needed on NV11 */ + CRTC2W(OWNER, 0xff); + /* enable access to CRTC2, SEQ2, GRPH2, ATB2, ??? */ + CRTC2W(OWNER, 0x03); + } + } +} + +static status_t nvxx_general_powerup() +{ + LOG(4, ("INIT: NV powerup\n")); + LOG(4,("POWERUP: Detected %s (%s)\n", si->adi.name, si->adi.chipset)); + +//for now keeping it (need to get a system up and running to test.. + if (si->ps.card_arch >= NV50A) + { + LOG(8,("POWERUP: G80 and higher support not implemented: different architecture!\n")); + return B_ERROR; + } + + /* setup cardspecs */ + /* note: + * this MUST be done before the driver attempts a card coldstart */ + set_specs(); + + /* only process BIOS for finetuning specs and coldstarting card if requested + * by the user; + * note: + * this in fact frees the driver from relying on the BIOS to be executed + * at system power-up POST time. */ + if (!si->settings.usebios) + { + LOG(2, ("INIT: Attempting card coldstart!\n")); + /* update the cardspecs in the shared_info PINS struct according to reported + * specs as much as is possible; + * this also coldstarts the card if possible (executes BIOS CMD script(s)) */ +// parse_pins(); + } + else + { + LOG(2, ("INIT: Skipping card coldstart!\n")); + } + +// unlock_card(); + + /* get RAM size, detect TV encoder and do fake panel startup (panel init code + * is still missing). */ +// fake_panel_start(); + + /* log the final card specifications */ + dump_pins(); + + /* dump config space as it is after a possible coldstart attempt */ + if (si->settings.logmask & 0x80000000) nv_dump_configuration_space(); + + /* setup CRTC and DAC functions access: determined in fake_panel_start */ + setup_virtualized_heads(si->ps.crtc2_prim); + + /* do powerup needed from pre-inited card state as done by system POST cardBIOS + * execution or driver coldstart above */ + return nv_general_bios_to_powergraphics(); +} + +/* this routine switches the CRTC/DAC sets to 'connectors', but only for analog + * outputs. We need this to make sure the analog 'switch' is set in the same way the + * digital 'switch' is set by the BIOS or we might not be able to use dualhead. */ +status_t nv_general_output_select(bool cross) +{ + /* make sure this call is warranted */ + if (si->ps.secondary_head) + { + /* NV11 cards can't switch heads (confirmed) */ + if (si->ps.card_type != NV11) + { + if (cross) + { + LOG(4,("INIT: switching analog outputs to be cross-connected\n")); + + /* enable head 2 on connector 1 */ + /* (b8 = select CRTC (head) for output, + * b4 = ??? (confirmed not to be a FP switch), + * b0 = enable CRT) */ + DACW(OUTPUT, 0x00000101); + /* enable head 1 on connector 2 */ + DAC2W(OUTPUT, 0x00000001); + } + else + { + LOG(4,("INIT: switching analog outputs to be straight-through\n")); + + /* enable head 1 on connector 1 */ + DACW(OUTPUT, 0x00000001); + /* enable head 2 on connector 2 */ + DAC2W(OUTPUT, 0x00000101); + } + } + else + { + LOG(4,("INIT: NV11 analog outputs are hardwired to be straight-through\n")); + } + return B_OK; + } + else + { + return B_ERROR; + } +} + +/* this routine switches CRTC/DAC set use. We need this because it's unknown howto + * switch digital panels to/from a specific CRTC/DAC set. */ +status_t nv_general_head_select(bool cross) +{ + /* make sure this call is warranted */ + if (si->ps.secondary_head) + { + /* invert CRTC/DAC use to do switching */ + if (cross) + { + LOG(4,("INIT: switching CRTC/DAC use to be cross-connected\n")); + si->crtc_switch_mode = !si->ps.crtc2_prim; + } + else + { + LOG(4,("INIT: switching CRTC/DAC use to be straight-through\n")); + si->crtc_switch_mode = si->ps.crtc2_prim; + } + /* update CRTC and DAC functions access */ + setup_virtualized_heads(si->crtc_switch_mode); + + return B_OK; + } + else + { + return B_ERROR; + } +} + +static void unlock_card(void) +{ + /* power-up all nvidia hardware function blocks */ + /* bit 28: OVERLAY ENGINE (BES), + * bit 25: CRTC2, (> NV04A) + * bit 24: CRTC1, + * bit 20: framebuffer, + * bit 16: PPMI, + * bit 12: PGRAPH, + * bit 8: PFIFO, + * bit 4: PMEDIA, + * bit 0: TVOUT. (> NV04A) */ + NV_REG32(NV32_PWRUPCTRL) = 0x13111111; + + /* select colormode CRTC registers base adresses */ + NV_REG8(NV8_MISCW) = 0xcb; + + /* enable access to primary head */ + set_crtc_owner(0); + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + } +} + +/* basic change of card state from VGA to enhanced mode: + * Should work from VGA BIOS POST init state. */ +static status_t nv_general_bios_to_powergraphics() +{ +return B_OK; + /* let acc engine make power off/power on cycle to start 'fresh' */ + NV_REG32(NV32_PWRUPCTRL) = 0x13110011; + snooze(1000); + NV_REG32(NV32_PWRUPCTRL) = 0x13111111; + + unlock_card(); + + /* turn off both displays and the hardcursors (also disables transfers) */ + head1_dpms(false, false, false, true); + head1_cursor_hide(); + if (si->ps.secondary_head) + { + head2_dpms(false, false, false, true); + head2_cursor_hide(); + } + + if (si->ps.secondary_head) + { + /* switch overlay engine and TV encoder to CRTC1 */ + /* bit 17: GPU FP port #1 (confirmed NV25, NV28, confirmed not on NV34), + * bit 16: GPU FP port #2 (confirmed NV25, NV28, NV34), + * bit 12: overlay engine (all cards), + * bit 9: TVout chip #2 (confirmed on NV18, NV25, NV28), + * bit 8: TVout chip #1 (all cards), + * bit 4: both I2C busses (all cards) */ + NV_REG32(NV32_2FUNCSEL) &= ~0x00001100; + NV_REG32(NV32_FUNCSEL) |= 0x00001100; + } + si->overlay.crtc = false; + + /* enable 'enhanced' mode on primary head: */ + /* enable access to primary head */ + set_crtc_owner(0); + /* note: 'BUFFER' is a non-standard register in behaviour(!) on most + * NV11's like the GeForce2 MX200, while the MX400 and non-NV11 cards + * behave normally. + * Also readback is not nessesarily what was written before! + * + * Double-write action needed on those strange NV11 cards: */ + /* RESET: don't doublebuffer CRTC access: set programmed values immediately... */ + CRTCW(BUFFER, 0xff); + /* ... and use fine pitched CRTC granularity on > NV4 cards (b2 = 0) */ + /* note: this has no effect on possible bandwidth issues. */ + CRTCW(BUFFER, 0xfb); + /* select VGA mode (old VGA register) */ + CRTCW(MODECTL, 0xc3); + /* select graphics mode (old VGA register) */ + SEQW(MEMMODE, 0x0e); + /* select 8 dots character clocks (old VGA register) */ + SEQW(CLKMODE, 0x21); + /* select VGA mode (old VGA register) */ + GRPHW(MODE, 0x00); + /* select graphics mode (old VGA register) */ + GRPHW(MISC, 0x01); + /* select graphics mode (old VGA register) */ + ATBW(MODECTL, 0x01); + /* enable 'enhanced mode', enable Vsync & Hsync, + * set DAC palette to 8-bit width, disable large screen */ + CRTCW(REPAINT1, 0x04); + + /* enable 'enhanced' mode on secondary head: */ + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* select colormode CRTC2 registers base adresses */ + NV_REG8(NV8_MISCW) = 0xcb; + /* note: 'BUFFER' is a non-standard register in behaviour(!) on most + * NV11's like the GeForce2 MX200, while the MX400 and non-NV11 cards + * behave normally. + * Also readback is not nessesarily what was written before! + * + * Double-write action needed on those strange NV11 cards: */ + /* RESET: don't doublebuffer CRTC2 access: set programmed values immediately... */ + CRTC2W(BUFFER, 0xff); + /* ... and use fine pitched CRTC granularity on > NV4 cards (b2 = 0) */ + /* note: this has no effect on possible bandwidth issues. */ + CRTC2W(BUFFER, 0xfb); + /* select VGA mode (old VGA register) */ + CRTC2W(MODECTL, 0xc3); + /* select graphics mode (old VGA register) */ + SEQW(MEMMODE, 0x0e); + /* select 8 dots character clocks (old VGA register) */ + SEQW(CLKMODE, 0x21); + /* select VGA mode (old VGA register) */ + GRPHW(MODE, 0x00); + /* select graphics mode (old VGA register) */ + GRPHW(MISC, 0x01); + /* select graphics mode (old VGA register) */ + ATB2W(MODECTL, 0x01); + /* enable 'enhanced mode', enable Vsync & Hsync, + * set DAC palette to 8-bit width, disable large screen */ + CRTC2W(REPAINT1, 0x04); + } + + /* enable palettes */ + DACW(GENCTRL, 0x00100100); + if (si->ps.secondary_head) DAC2W(GENCTRL, 0x00100100); + + /* enable programmable PLLs */ + /* (confirmed PLLSEL to be a write-only register on NV04 and NV11!) */ + if (si->ps.secondary_head) + DACW(PLLSEL, 0x30000f00); + else + DACW(PLLSEL, 0x10000700); + + /* turn on DAC and make sure detection testsignal routing is disabled + * (b16 = disable DAC, + * b12 = enable testsignal output */ + //fixme note: b20 ('DACTM_TEST') when set apparantly blocks a DAC's video output + //(confirmed NV43), while it's timing remains operational (black screen). + //It feels like in some screen configurations it can move the output to the other + //output connector as well... + DACW(TSTCTRL, (DACR(TSTCTRL) & 0xfffeefff)); + /* turn on DAC2 if it exists + * (NOTE: testsignal function block resides in DAC1 only (!)) */ + if (si->ps.secondary_head) DAC2W(TSTCTRL, (DAC2R(TSTCTRL) & 0xfffeefff)); + + /* NV40 and NV45 need a 'tweak' to make sure the CRTC FIFO's/shiftregisters get + * their data in time (otherwise momentarily ghost images of windows or such + * may appear on heavy acceleration engine use for instance, especially in 32-bit + * colordepth) */ + if ((si->ps.card_type == NV40) || (si->ps.card_type == NV45)) + { + /* clear b15: some framebuffer config item (unknown) */ + NV_REG32(NV32_PFB_CLS_PAGE2) &= 0xffff7fff; + } + + /* tweak card GPU-core and RAM speeds if requested (hoping we'll survive)... */ + if (si->settings.gpu_clk) + { + LOG(2,("INIT: tweaking GPU clock!\n")); + + set_pll(NV32_COREPLL, si->settings.gpu_clk); + snooze(1000); + } + if (si->settings.ram_clk) + { + LOG(2,("INIT: tweaking cardRAM clock!\n")); + + set_pll(NV32_MEMPLL, si->settings.ram_clk); + snooze(1000); + } + + return B_OK; +} + +/* Check if mode virtual_size adheres to the cards _maximum_ contraints, and modify + * virtual_size to the nearest valid maximum for the mode on the card if not so. + * Also: check if virtual_width adheres to the cards granularity constraints, and + * create mode slopspace if not so. + * We use acc or crtc granularity constraints based on the 'worst case' scenario. + * + * Mode slopspace is reflected in fbc->bytes_per_row BTW. */ +status_t nv_general_validate_pic_size (display_mode *target, uint32 *bytes_per_row, bool *acc_mode) +{ + uint32 video_pitch; + uint32 acc_mask, crtc_mask; + uint32 max_crtc_width, max_acc_width; + uint8 depth = 8; + + /* determine pixel multiple based on acceleration engine constraints */ + /* note: + * because of the seemingly 'random' variations in these constraints we take + * a reasonable 'lowest common denominator' instead of always true constraints. */ + switch (si->ps.card_arch) + { + case NV04A: + /* confirmed for: + * TNT1 (NV04), TNT2 (NV05), TNT2-M64 (NV05M64), GeForce2 MX400 (NV11), + * GeForce4 MX440 (NV18), GeForceFX 5200 (NV34) in PIO acc mode; + * confirmed for: + * TNT1 (NV04), TNT2 (NV05), TNT2-M64 (NV05M64), GeForce4 Ti4200 (NV28), + * GeForceFX 5200 (NV34) in DMA acc mode. */ + switch (target->space) + { + case B_CMAP8: acc_mask = 0x0f; depth = 8; break; + case B_RGB15: acc_mask = 0x07; depth = 16; break; + case B_RGB16: acc_mask = 0x07; depth = 16; break; + case B_RGB24: acc_mask = 0x0f; depth = 24; break; + case B_RGB32: acc_mask = 0x03; depth = 32; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; + default: + /* confirmed for: + * GeForce4 Ti4200 (NV28), GeForceFX 5600 (NV31) in PIO acc mode; + * confirmed for: + * GeForce2 MX400 (NV11), GeForce4 MX440 (NV18), GeForcePCX 5750 (NV36), + * GeForcePCX 6600 GT (NV43) in DMA acc mode. */ + switch (target->space) + { + case B_CMAP8: acc_mask = 0x3f; depth = 8; break; + case B_RGB15: acc_mask = 0x1f; depth = 16; break; + case B_RGB16: acc_mask = 0x1f; depth = 16; break; + case B_RGB24: acc_mask = 0x3f; depth = 24; break; + case B_RGB32: acc_mask = 0x0f; depth = 32; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; + } + + /* determine pixel multiple based on CRTC memory pitch constraints: + * -> all NV cards have same granularity constraints on CRTC1 and CRTC2, + * provided that the CRTC1 and CRTC2 BUFFER register b2 = 0; + * + * (Note: Don't mix this up with CRTC timing contraints! Those are + * multiples of 8 for horizontal, 1 for vertical timing.) */ + switch (si->ps.card_type) + { + default: +// case NV04: + /* confirmed for: + * TNT1 always; + * TNT2, TNT2-M64, GeForce2 MX400, GeForce4 MX440, GeForce4 Ti4200, + * GeForceFX 5200: if the CRTC1 (and CRTC2) BUFFER register b2 = 0 */ + /* NOTE: + * Unfortunately older cards have a hardware fault that prevents use. + * We need doubled granularity on those to prevent the single top line + * from shifting to the left! + * This is confirmed for TNT2, GeForce2 MX200, GeForce2 MX400. + * Confirmed OK are: + * GeForce4 MX440, GeForce4 Ti4200, GeForceFX 5200. */ + switch (target->space) + { + case B_CMAP8: crtc_mask = 0x0f; break; /* 0x07 */ + case B_RGB15: crtc_mask = 0x07; break; /* 0x03 */ + case B_RGB16: crtc_mask = 0x07; break; /* 0x03 */ + case B_RGB24: crtc_mask = 0x0f; break; /* 0x07 */ + case B_RGB32: crtc_mask = 0x03; break; /* 0x01 */ + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; +// default: + /* confirmed for: + * TNT2, TNT2-M64, GeForce2 MX400, GeForce4 MX440, GeForce4 Ti4200, + * GeForceFX 5200: if the CRTC1 (and CRTC2) BUFFER register b2 = 1 */ +/* switch (target->space) + { + case B_CMAP8: crtc_mask = 0x1f; break; + case B_RGB15: crtc_mask = 0x0f; break; + case B_RGB16: crtc_mask = 0x0f; break; + case B_RGB24: crtc_mask = 0x1f; break; + case B_RGB32: crtc_mask = 0x07; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; +*/ } + + /* set virtual_width limit for accelerated modes */ + /* note: + * because of the seemingly 'random' variations in these constraints we take + * a reasonable 'lowest common denominator' instead of always true constraints. */ + switch (si->ps.card_arch) + { + case NV04A: + /* confirmed for: + * TNT1 (NV04), TNT2 (NV05), TNT2-M64 (NV05M64) in both PIO and DMA acc mode. */ + switch(target->space) + { + case B_CMAP8: max_acc_width = 8176; break; + case B_RGB15: max_acc_width = 4088; break; + case B_RGB16: max_acc_width = 4088; break; + case B_RGB24: max_acc_width = 2720; break; + case B_RGB32: max_acc_width = 2044; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; + default: + /* confirmed for: + * GeForce4 Ti4200 (NV28), GeForceFX 5600 (NV31) in PIO acc mode; + * GeForce2 MX400 (NV11), GeForce4 MX440 (NV18), GeForceFX 5200 (NV34) can do + * 16368/8184/8184/5456/4092, so a bit better in PIO acc mode; + * confirmed for: + * GeForce2 MX400 (NV11), GeForce4 MX440 (NV18), GeForcePCX 5750 (NV36), + * GeForcePCX 6600 GT (NV43) in DMA acc mode; + * GeForce4 Ti4200 (NV28), GeForceFX 5200 (NV34) can do + * 16368/8184/8184/5456/4092, so a bit better in DMA acc mode. */ + switch(target->space) + { + case B_CMAP8: max_acc_width = 16320; break; + case B_RGB15: max_acc_width = 8160; break; + case B_RGB16: max_acc_width = 8160; break; + case B_RGB24: max_acc_width = 5440; break; + case B_RGB32: max_acc_width = 4080; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; + } + + /* set virtual_width limit for unaccelerated modes */ + switch (si->ps.card_type) + { + default: +// case NV04: + /* confirmed for: + * TNT1 always; + * TNT2, TNT2-M64, GeForce2 MX400, GeForce4 MX440, GeForce4 Ti4200, + * GeForceFX 5200: if the CRTC1 (and CRTC2) BUFFER register b2 = 0 */ + /* NOTE: + * Unfortunately older cards have a hardware fault that prevents use. + * We need doubled granularity on those to prevent the single top line + * from shifting to the left! + * This is confirmed for TNT2, GeForce2 MX200, GeForce2 MX400. + * Confirmed OK are: + * GeForce4 MX440, GeForce4 Ti4200, GeForceFX 5200. */ + switch(target->space) + { + case B_CMAP8: max_crtc_width = 16368; break; /* 16376 */ + case B_RGB15: max_crtc_width = 8184; break; /* 8188 */ + case B_RGB16: max_crtc_width = 8184; break; /* 8188 */ + case B_RGB24: max_crtc_width = 5456; break; /* 5456 */ + case B_RGB32: max_crtc_width = 4092; break; /* 4094 */ + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; +// default: + /* confirmed for: + * TNT2, TNT2-M64, GeForce2 MX400, GeForce4 MX440, GeForce4 Ti4200, + * GeForceFX 5200: if the CRTC1 (and CRTC2) BUFFER register b2 = 1 */ +/* switch(target->space) + { + case B_CMAP8: max_crtc_width = 16352; break; + case B_RGB15: max_crtc_width = 8176; break; + case B_RGB16: max_crtc_width = 8176; break; + case B_RGB24: max_crtc_width = 5440; break; + case B_RGB32: max_crtc_width = 4088; break; + default: + LOG(8,("INIT: unknown color space: 0x%08x\n", target->space)); + return B_ERROR; + } + break; +*/ } + + /* check for acc capability, and adjust mode to adhere to hardware constraints */ + if (max_acc_width <= max_crtc_width) + { + /* check if we can setup this mode with acceleration */ + *acc_mode = true; + //no acc support for G8x yet! + if (si->ps.card_arch >= NV50A) *acc_mode = false; + + /* virtual_width */ + if (target->virtual_width > max_acc_width) *acc_mode = false; + /* virtual_height */ + /* (NV cards can even do more than this(?)... + * but 4096 is confirmed on all cards at max. accelerated width.) */ + if (target->virtual_height > 4096) *acc_mode = false; + + /* now check virtual_size based on CRTC constraints */ + if (target->virtual_width > max_crtc_width) target->virtual_width = max_crtc_width; + /* virtual_height: The only constraint here is the cards memory size which is + * checked later on in ProposeMode: virtual_height is adjusted then if needed. + * 'Limiting here' to the variable size that's at least available (uint16). */ + if (target->virtual_height > 65535) target->virtual_height = 65535; + + /* OK, now we know that virtual_width is valid, and it's needing no slopspace if + * it was confined above, so we can finally calculate safely if we need slopspace + * for this mode... */ + if (*acc_mode) + { + /* the mode needs to adhere to the largest granularity imposed... */ + if (acc_mask < crtc_mask) + video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask); + else + video_pitch = ((target->virtual_width + acc_mask) & ~acc_mask); + } + else /* unaccelerated mode */ + video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask); + } + else /* max_acc_width > max_crtc_width */ + { + /* check if we can setup this mode with acceleration */ + *acc_mode = true; + //no acc support for G8x yet! + if (si->ps.card_arch >= NV50A) *acc_mode = false; + /* (we already know virtual_width will be no problem) */ + /* virtual_height */ + /* (NV cards can even do more than this(?)... + * but 4096 is confirmed on all cards at max. accelerated width.) */ + if (target->virtual_height > 4096) *acc_mode = false; + + /* now check virtual_size based on CRTC constraints */ + if (*acc_mode) + { + /* note that max_crtc_width already adheres to crtc_mask */ + if (target->virtual_width > (max_crtc_width & ~acc_mask)) + target->virtual_width = (max_crtc_width & ~acc_mask); + } + else /* unaccelerated mode */ + { + if (target->virtual_width > max_crtc_width) + target->virtual_width = max_crtc_width; + } + /* virtual_height: The only constraint here is the cards memory size which is + * checked later on in ProposeMode: virtual_height is adjusted then if needed. + * 'Limiting here' to the variable size that's at least available (uint16). */ + if (target->virtual_height > 65535) target->virtual_height = 65535; + + /* OK, now we know that virtual_width is valid, and it's needing no slopspace if + * it was confined above, so we can finally calculate safely if we need slopspace + * for this mode... */ + if (*acc_mode) + { + /* the mode needs to adhere to the largest granularity imposed... */ + if (acc_mask < crtc_mask) + video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask); + else + video_pitch = ((target->virtual_width + acc_mask) & ~acc_mask); + } + else /* unaccelerated mode */ + video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask); + } + + LOG(2,("INIT: memory pitch will be set to %d pixels for colorspace 0x%08x\n", + video_pitch, target->space)); + if (target->virtual_width != video_pitch) + LOG(2,("INIT: effective mode slopspace is %d pixels\n", + (video_pitch - target->virtual_width))); + + /* now calculate bytes_per_row for this mode */ + *bytes_per_row = video_pitch * (depth >> 3); + + return B_OK; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.c new file mode 100644 index 0000000000..18905a9aab --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.c @@ -0,0 +1,36 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + Other authors: + Mark Watson, + Rudolf Cornelissen 8/2004-5/2005 +*/ + +#include "nv_std.h" + +int fd; +shared_info *si; +area_id shared_info_area; +area_id dma_cmd_buf_area; +vuint32 *regs; +area_id regs_area; +display_mode *my_mode_list; +area_id my_mode_list_area; +int accelerantIsClone; + +nv_get_set_pci nv_pci_access= + { + NV_PRIVATE_DATA_MAGIC, + 0, + 4, + 0 + }; + +nv_in_out_isa nv_isa_access= + { + NV_PRIVATE_DATA_MAGIC, + 0, + 1, + 0 + }; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.h b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.h new file mode 100644 index 0000000000..c31f202c12 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_globals.h @@ -0,0 +1,75 @@ +extern int fd; +extern shared_info *si; +extern area_id shared_info_area; +extern area_id dma_cmd_buf_area; +extern area_id regs_area; +extern vuint32 *regs; +extern display_mode *my_mode_list; +extern area_id my_mode_list_area; +extern int accelerantIsClone; + +extern nv_get_set_pci nv_pci_access; +extern nv_in_out_isa nv_isa_access; + +typedef status_t (*crtc_interrupt_enable)(bool); +typedef status_t (*crtc_update_fifo)(void); +typedef status_t (*crtc_validate_timing)(uint16*, uint16*, uint16*, uint16*, uint16*, uint16*, uint16*, uint16*); +typedef status_t (*crtc_set_timing)(display_mode); +typedef status_t (*crtc_depth)(int); +typedef status_t (*crtc_dpms)(bool, bool, bool, bool); +typedef status_t (*crtc_set_display_pitch)(void); +typedef status_t (*crtc_set_display_start)(uint32, uint8); +typedef status_t (*crtc_cursor_init)(void); +typedef status_t (*crtc_cursor_show)(void); +typedef status_t (*crtc_cursor_hide)(void); +typedef status_t (*crtc_cursor_define)(uint8*, uint8*); +typedef status_t (*crtc_cursor_position)(uint16, uint16); +typedef status_t (*crtc_stop_tvout)(void); +typedef status_t (*crtc_start_tvout)(void); + +typedef status_t (*dac_mode)(int, float); +typedef status_t (*dac_palette)(uint8[256], uint8[256], uint8[256]); +typedef status_t (*dac_set_pix_pll)(display_mode); +typedef status_t (*dac_pix_pll_find)(display_mode, float*, uint8*, uint8*, uint8*, uint8); + +crtc_interrupt_enable head1_interrupt_enable; +crtc_update_fifo head1_update_fifo; +crtc_validate_timing head1_validate_timing; +crtc_set_timing head1_set_timing; +crtc_depth head1_depth; +crtc_dpms head1_dpms; +crtc_set_display_pitch head1_set_display_pitch; +crtc_set_display_start head1_set_display_start; +crtc_cursor_init head1_cursor_init; +crtc_cursor_show head1_cursor_show; +crtc_cursor_hide head1_cursor_hide; +crtc_cursor_define head1_cursor_define; +crtc_cursor_position head1_cursor_position; +crtc_stop_tvout head1_stop_tvout; +crtc_start_tvout head1_start_tvout; + +crtc_interrupt_enable head2_interrupt_enable; +crtc_update_fifo head2_update_fifo; +crtc_validate_timing head2_validate_timing; +crtc_set_timing head2_set_timing; +crtc_depth head2_depth; +crtc_dpms head2_dpms; +crtc_set_display_pitch head2_set_display_pitch; +crtc_set_display_start head2_set_display_start; +crtc_cursor_init head2_cursor_init; +crtc_cursor_show head2_cursor_show; +crtc_cursor_hide head2_cursor_hide; +crtc_cursor_define head2_cursor_define; +crtc_cursor_position head2_cursor_position; +crtc_stop_tvout head2_stop_tvout; +crtc_start_tvout head2_start_tvout; + +dac_mode head1_mode; +dac_palette head1_palette; +dac_set_pix_pll head1_set_pix_pll; +dac_pix_pll_find head1_pix_pll_find; + +dac_mode head2_mode; +dac_palette head2_palette; +dac_set_pix_pll head2_set_pix_pll; +dac_pix_pll_find head2_pix_pll_find; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_i2c.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_i2c.c new file mode 100644 index 0000000000..1106604377 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_i2c.c @@ -0,0 +1,344 @@ +/* + * i2c interface. + * Bus should be run at max. 100kHz: see original Philips I2C specification + * + * Rudolf Cornelissen 12/2002-10/2005 + */ + +#define MODULE_BIT 0x00004000 + +#include "nv_std.h" + +char i2c_flag_error (char ErrNo) +//error code list: +//0 - OK status +//1 - SCL locked low by device (bus is still busy) +//2 - SDA locked low by device (bus is still busy) +//3 - No Acknowledge from device (no handshake) +//4 - SDA not released for master to generate STOP bit +{ + static char I2CError = 0; + + if (!I2CError) I2CError = ErrNo; + if (ErrNo == -1) I2CError = 0; + return I2CError; +} + +static void i2c_select_bus_set(bool set) +{ + /* I/O pins set selection is only valid on dualhead cards */ + if (!si->ps.secondary_head) return; + + /* select GPU I/O pins set to connect to I2C 'registers' */ + if (set) + { + NV_REG32(NV32_FUNCSEL) &= ~0x00000010; + NV_REG32(NV32_2FUNCSEL) |= 0x00000010; + } + else + { + NV_REG32(NV32_2FUNCSEL) &= ~0x00000010; + NV_REG32(NV32_FUNCSEL) |= 0x00000010; + } +} + +static void OutSCL(uint8 BusNR, bool Bit) +{ + uint8 data; + + if (BusNR & 0x01) + { + data = (CRTCR(WR_I2CBUS_1) & 0xf0) | 0x01; + if (Bit) + CRTCW(WR_I2CBUS_1, (data | 0x20)); + else + CRTCW(WR_I2CBUS_1, (data & ~0x20)); + } + else + { + data = (CRTCR(WR_I2CBUS_0) & 0xf0) | 0x01; + if (Bit) + CRTCW(WR_I2CBUS_0, (data | 0x20)); + else + CRTCW(WR_I2CBUS_0, (data & ~0x20)); + } +} + +static void OutSDA(uint8 BusNR, bool Bit) +{ + uint8 data; + + if (BusNR & 0x01) + { + data = (CRTCR(WR_I2CBUS_1) & 0xf0) | 0x01; + if (Bit) + CRTCW(WR_I2CBUS_1, (data | 0x10)); + else + CRTCW(WR_I2CBUS_1, (data & ~0x10)); + } + else + { + data = (CRTCR(WR_I2CBUS_0) & 0xf0) | 0x01; + if (Bit) + CRTCW(WR_I2CBUS_0, (data | 0x10)); + else + CRTCW(WR_I2CBUS_0, (data & ~0x10)); + } +} + +static bool InSCL(uint8 BusNR) +{ + if (BusNR & 0x01) + { + if ((CRTCR(RD_I2CBUS_1) & 0x04)) return true; + } + else + { + if ((CRTCR(RD_I2CBUS_0) & 0x04)) return true; + } + + return false; +} + +static bool InSDA(uint8 BusNR) +{ + if (BusNR & 0x01) + { + if ((CRTCR(RD_I2CBUS_1) & 0x08)) return true; + } + else + { + if ((CRTCR(RD_I2CBUS_0) & 0x08)) return true; + } + + return false; +} + +static void TXBit (uint8 BusNR, bool Bit) +{ + /* send out databit */ + if (Bit) + { + OutSDA(BusNR, true); + snooze(3); + if (!InSDA(BusNR)) i2c_flag_error (2); + } + else + { + OutSDA(BusNR, false); + } + /* generate clock pulse */ + snooze(6); + OutSCL(BusNR, true); + snooze(3); + if (!InSCL(BusNR)) i2c_flag_error (1); + snooze(6); + OutSCL(BusNR, false); + snooze(6); +} + +static uint8 RXBit (uint8 BusNR) +{ + uint8 Bit = 0; + + /* set SDA so input is possible */ + OutSDA(BusNR, true); + /* generate clock pulse */ + snooze(6); + OutSCL(BusNR, true); + snooze(3); + if (!InSCL(BusNR)) i2c_flag_error (1); + snooze(3); + /* read databit */ + if (InSDA(BusNR)) Bit = 1; + /* finish clockpulse */ + OutSCL(BusNR, false); + snooze(6); + + return Bit; +} + +void i2c_bstart (uint8 BusNR) +{ + /* select GPU I/O pins set */ + i2c_select_bus_set(BusNR & 0x02); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* make sure SDA is high */ + OutSDA(BusNR, true); + snooze(3); + OutSCL(BusNR, true); + snooze(3); + if (!InSCL(BusNR)) i2c_flag_error (1); + snooze(6); + /* clear SDA while SCL set (bus-start condition) */ + OutSDA(BusNR, false); + snooze(6); + OutSCL(BusNR, false); + snooze(6); + + LOG(4,("I2C: START condition generated on bus %d; status is %d\n", + BusNR, i2c_flag_error (0))); +} + +void i2c_bstop (uint8 BusNR) +{ + /* select GPU I/O pins set */ + i2c_select_bus_set(BusNR & 0x02); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* make sure SDA is low */ + OutSDA(BusNR, false); + snooze(3); + OutSCL(BusNR, true); + snooze(3); + if (!InSCL(BusNR)) i2c_flag_error (1); + snooze(6); + /* set SDA while SCL set (bus-stop condition) */ + OutSDA(BusNR, true); + snooze(3); + if (!InSDA(BusNR)) i2c_flag_error (4); + snooze(3); + + LOG(4,("I2C: STOP condition generated on bus %d; status is %d\n", + BusNR, i2c_flag_error (0))); +} + +uint8 i2c_readbyte(uint8 BusNR, bool Ack) +{ + uint8 cnt, bit, byte = 0; + + /* select GPU I/O pins set */ + i2c_select_bus_set(BusNR & 0x02); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* read data */ + for (cnt = 8; cnt > 0; cnt--) + { + byte <<= 1; + bit = RXBit (BusNR); + byte += bit; + } + /* send acknowledge */ + TXBit (BusNR, Ack); + + LOG(4,("I2C: read byte ($%02x) from bus #%d; status is %d\n", + byte, BusNR, i2c_flag_error(0))); + + return byte; +} + +bool i2c_writebyte (uint8 BusNR, uint8 byte) +{ + uint8 cnt; + bool bit; + uint8 tmp = byte; + + /* select GPU I/O pins set */ + i2c_select_bus_set(BusNR & 0x02); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* write data */ + for (cnt = 8; cnt > 0; cnt--) + { + bit = (tmp & 0x80); + TXBit (BusNR, bit); + tmp <<= 1; + } + /* read acknowledge */ + bit = RXBit (BusNR); + if (bit) i2c_flag_error (3); + + LOG(4,("I2C: written byte ($%02x) to bus #%d; status is %d\n", + byte, BusNR, i2c_flag_error(0))); + + return bit; +} + +void i2c_readbuffer (uint8 BusNR, uint8* buf, uint8 size) +{ + uint8 cnt; + + for (cnt = 0; cnt < size; cnt++) + { + buf[cnt] = i2c_readbyte(BusNR, buf[cnt]); + } +} + +void i2c_writebuffer (uint8 BusNR, uint8* buf, uint8 size) +{ + uint8 cnt; + + for (cnt = 0; cnt < size; cnt++) + { + i2c_writebyte(BusNR, buf[cnt]); + } +} + +status_t i2c_init(void) +{ + uint8 bus, buses; + bool *i2c_bus = &(si->ps.i2c_bus0); + status_t result = B_ERROR; + + LOG(4,("I2C: searching for wired I2C buses...\n")); + + /* enable access to primary head */ + set_crtc_owner(0); + + /* preset no board wired buses */ + si->ps.i2c_bus0 = false; + si->ps.i2c_bus1 = false; + si->ps.i2c_bus2 = false; + si->ps.i2c_bus3 = false; + + /* set number of buses to test for */ + buses = 2; + if (si->ps.secondary_head) buses = 4; + + /* find existing buses */ + for (bus = 0; bus < buses; bus++) + { + /* reset status */ + i2c_flag_error (-1); + snooze(6); + /* init and/or stop I2C bus */ + i2c_bstop(bus); + /* check for hardware coupling of SCL and SDA -out and -in lines */ + snooze(6); + OutSCL(bus, false); + OutSDA(bus, true); + snooze(3); + if (InSCL(bus) || !InSDA(bus)) continue; + snooze(3); + OutSCL(bus, true); + OutSDA(bus, false); + snooze(3); + if (!InSCL(bus) || InSDA(bus)) continue; + i2c_bus[bus] = true; + snooze(3); + /* re-init bus */ + i2c_bstop(bus); + } + + for (bus = 0; bus < buses; bus++) + { + if (i2c_bus[bus]) + { + LOG(4,("I2C: bus #%d wiring check: passed\n", bus)); + result = B_OK; + } + else + LOG(4,("I2C: bus #%d wiring check: failed\n", bus)); + } + + return result; +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_info.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_info.c new file mode 100644 index 0000000000..17e18a6d7d --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_info.c @@ -0,0 +1,3285 @@ +/* Read initialisation information from card */ +/* some bits are hacks, where PINS is not known */ +/* Author: + Rudolf Cornelissen 7/2003-3/2006 +*/ + +#define MODULE_BIT 0x00002000 + +#include "nv_std.h" + +/* pins V5.16 and up ROM infoblock stuff */ +typedef struct { + uint16 InitScriptTablePtr; /* ptr to list of ptrs to scripts to exec */ + uint16 MacroIndexTablePtr; /* ptr to list with indexes and sizes of items in MacroTable */ + uint16 MacroTablePtr; /* ptr to list with items containing multiple 32bit reg writes */ + uint16 ConditionTablePtr; /* ptr to list of PCI regs and bits to tst for exec mode */ + uint16 IOConditionTablePtr; /* ptr to list of ISA regs and bits to tst for exec mode */ + uint16 IOFlagConditionTablePtr;/* ptr to list of ISA regs and bits to tst, ref'd to a matrix, for exec mode */ + uint16 InitFunctionTablePtr; /* ptr to list of startadresses of fixed ROM init routines */ +} PinsTables; + +static void detect_panels(void); +static void setup_output_matrix(void); +static void pinsnv4_fake(void); +static void pinsnv5_nv5m64_fake(void); +static void pinsnv6_fake(void); +static void pinsnv10_arch_fake(void); +static void pinsnv20_arch_fake(void); +static void pinsnv30_arch_fake(void); +static void getRAMsize_arch_nv4(void); +static void getstrap_arch_nv4(void); +static void getRAMsize_arch_nv10_20_30_40(void); +static void getstrap_arch_nv10_20_30_40(void); +static status_t pins2_read(uint8 *rom, uint32 offset); +static status_t pins3_5_read(uint8 *rom, uint32 offset); +static status_t coldstart_card(uint8* rom, uint16 init1, uint16 init2, uint16 init_size, uint16 ram_tab); +static status_t coldstart_card_516_up(uint8* rom, PinsTables tabs, uint16 ram_tab); +static status_t exec_type1_script(uint8* rom, uint16 adress, int16* size, uint16 ram_tab); +static status_t exec_type2_script(uint8* rom, uint16 adress, int16* size, PinsTables tabs, uint16 ram_tab); +static status_t exec_type2_script_mode(uint8* rom, uint16* adress, int16* size, PinsTables tabs, uint16 ram_tab, bool* exec); +static void exec_cmd_39_type2(uint8* rom, uint32 data, PinsTables tabs, bool* exec); +static void log_pll(uint32 reg, uint32 freq); +static void setup_ram_config(uint8* rom, uint16 ram_tab); +static void setup_ram_config_nv10_up(uint8* rom); +static void setup_ram_config_nv28(uint8* rom); +static status_t translate_ISA_PCI(uint32* reg); +static status_t nv_crtc_setup_fifo(void); + +/* Parse the BIOS PINS structure if there */ +status_t parse_pins () +{ + uint8 *rom; + uint8 chksum = 0; + int i; + uint32 offset; + status_t result = B_ERROR; + + /* preset PINS read status to failed */ + si->ps.pins_status = B_ERROR; + + /* check the validity of PINS */ + LOG(2,("INFO: Reading PINS info\n")); + rom = (uint8 *) si->rom_mirror; + /* check BIOS signature - this is defined in the PCI standard */ + if (rom[0]!=0x55 || rom[1]!=0xaa) + { + LOG(8,("INFO: BIOS signature not found\n")); + return B_ERROR; + } + LOG(2,("INFO: BIOS signature $AA55 found OK\n")); + + /* find the PINS struct adress */ + for (offset = 0; offset < 65536; offset++) + { + if (rom[offset ] != 0xff) continue; + if (rom[offset + 1] != 0x7f) continue; + if (rom[offset + 2] != 0x4e) continue; /* N */ + if (rom[offset + 3] != 0x56) continue; /* V */ + if (rom[offset + 4] != 0x00) continue; + + LOG(8,("INFO: PINS signature found\n")); + break; + } + + if (offset > 65535) + { + LOG(8,("INFO: PINS signature not found\n")); + return B_ERROR; + } + + /* verify PINS checksum */ + for (i = 0; i < 8; i++) + { + chksum += rom[offset + i]; + } + if (chksum) + { + LOG(8,("INFO: PINS checksum error\n")); + return B_ERROR; + } + + /* checkout PINS struct version */ + LOG(2,("INFO: PINS checksum is OK; PINS version is %d.%d\n", + rom[offset + 5], rom[offset + 6])); + + /* update the si->ps struct as far as is possible and coldstart card */ + //fixme: NV40 and up(?) nolonger use this system... + switch (rom[offset + 5]) + { + case 2: + result = pins2_read(rom, offset); + break; + case 3: + case 4: + case 5: + result = pins3_5_read(rom, offset); + break; + default: + LOG(8,("INFO: unknown PINS version\n")); + return B_ERROR; + break; + } + + /* check PINS read result */ + if (result == B_ERROR) + { + LOG(8,("INFO: PINS read/decode/execute error\n")); + return B_ERROR; + } + /* PINS scan succeeded */ + si->ps.pins_status = B_OK; + LOG(2,("INFO: PINS scan completed succesfully\n")); + return B_OK; +} + +static status_t pins2_read(uint8 *rom, uint32 offset) +{ + uint16 init1 = rom[offset + 18] + (rom[offset + 19] * 256); + uint16 init2 = rom[offset + 20] + (rom[offset + 21] * 256); + uint16 init_size = rom[offset + 22] + (rom[offset + 23] * 256) + 1; + /* confirmed by comparing cards */ + uint16 ram_tab = init1 - 0x0010; + /* fixme: PPC BIOSes (might) return NULL pointers for messages here */ + char* signon_msg = &(rom[(rom[offset + 24] + (rom[offset + 25] * 256))]); + char* vendor_name = &(rom[(rom[offset + 40] + (rom[offset + 41] * 256))]); + char* product_name = &(rom[(rom[offset + 42] + (rom[offset + 43] * 256))]); + char* product_rev = &(rom[(rom[offset + 44] + (rom[offset + 45] * 256))]); + + LOG(8,("INFO: cmdlist 1: $%04x, 2: $%04x, max. size $%04x\n", init1, init2, init_size)); + LOG(8,("INFO: signon msg:\n%s\n", signon_msg)); + LOG(8,("INFO: vendor name: %s\n", vendor_name)); + LOG(8,("INFO: product name: %s\n", product_name)); + LOG(8,("INFO: product rev: %s\n", product_rev)); + + return coldstart_card(rom, init1, init2, init_size, ram_tab); +} + +static status_t pins3_5_read(uint8 *rom, uint32 offset) +{ + uint16 init1 = rom[offset + 18] + (rom[offset + 19] * 256); + uint16 init2 = rom[offset + 20] + (rom[offset + 21] * 256); + uint16 init_size = rom[offset + 22] + (rom[offset + 23] * 256) + 1; + /* confirmed on a TNT2-M64 with pins V5.1 */ + uint16 ram_tab = rom[offset + 24] + (rom[offset + 25] * 256); + /* fixme: PPC BIOSes (might) return NULL pointers for messages here */ + char* signon_msg = &(rom[(rom[offset + 30] + (rom[offset + 31] * 256))]); + char* vendor_name = &(rom[(rom[offset + 46] + (rom[offset + 47] * 256))]); + char* product_name = &(rom[(rom[offset + 48] + (rom[offset + 49] * 256))]); + char* product_rev = &(rom[(rom[offset + 50] + (rom[offset + 51] * 256))]); + + LOG(8,("INFO: pre PINS 5.16 cmdlist 1: $%04x, 2: $%04x, max. size $%04x\n", init1, init2, init_size)); + LOG(8,("INFO: signon msg:\n%s\n", signon_msg)); + LOG(8,("INFO: vendor name: %s\n", vendor_name)); + LOG(8,("INFO: product name: %s\n", product_name)); + LOG(8,("INFO: product rev: %s\n", product_rev)); + + /* pins 5.06 and higher has VCO range info */ + if (((rom[offset + 5]) == 5) && ((rom[offset + 6]) >= 0x06)) + { + /* get PLL VCO range info */ + uint32 fvco_max = *((uint32*)(&(rom[offset + 67]))); + uint32 fvco_min = *((uint32*)(&(rom[offset + 71]))); + + LOG(8,("INFO: PLL VCO range is %dkHz - %dkHz\n", fvco_min, fvco_max)); + + /* modify presets to reflect card capability */ + si->ps.min_system_vco = fvco_min / 1000; + si->ps.max_system_vco = fvco_max / 1000; + //fixme: enable and modify PLL code... + //si->ps.min_pixel_vco = fvco_min / 1000; + //si->ps.max_pixel_vco = fvco_max / 1000; + //si->ps.min_video_vco = fvco_min / 1000; + //si->ps.max_video_vco = fvco_max / 1000; + } + + //fixme: add 'parsing scripts while not actually executing' as warmstart method, + // instead of not parsing at all: this will update the driver's speeds + // as below, while logging the scripts as well (for our learning pleasure :) + + /* pins 5.16 and higher is more extensive, and works differently from before */ + if (((rom[offset + 5]) == 5) && ((rom[offset + 6]) >= 0x10)) + { + /* pins 5.16 and up have a more extensive command list table, and have more + * commands to choose from as well. */ + PinsTables tabs; + tabs.InitScriptTablePtr = rom[offset + 75] + (rom[offset + 76] * 256); + tabs.MacroIndexTablePtr = rom[offset + 77] + (rom[offset + 78] * 256); + tabs.MacroTablePtr = rom[offset + 79] + (rom[offset + 80] * 256); + tabs.ConditionTablePtr = rom[offset + 81] + (rom[offset + 82] * 256); + tabs.IOConditionTablePtr = rom[offset + 83] + (rom[offset + 84] * 256); + tabs.IOFlagConditionTablePtr = rom[offset + 85] + (rom[offset + 86] * 256); + tabs.InitFunctionTablePtr = rom[offset + 87] + (rom[offset + 88] * 256); + + LOG(8,("INFO: PINS 5.16 and later cmdlist pointers:\n")); + LOG(8,("INFO: InitScriptTablePtr: $%04x\n", tabs.InitScriptTablePtr)); + LOG(8,("INFO: MacroIndexTablePtr: $%04x\n", tabs.MacroIndexTablePtr)); + LOG(8,("INFO: MacroTablePtr: $%04x\n", tabs.MacroTablePtr)); + LOG(8,("INFO: ConditionTablePtr: $%04x\n", tabs.ConditionTablePtr)); + LOG(8,("INFO: IOConditionTablePtr: $%04x\n", tabs.IOConditionTablePtr)); + LOG(8,("INFO: IOFlagConditionTablePtr: $%04x\n", tabs.IOFlagConditionTablePtr)); + LOG(8,("INFO: InitFunctionTablePtr: $%04x\n", tabs.InitFunctionTablePtr)); + + return coldstart_card_516_up(rom, tabs, ram_tab); + } + else + { + /* pre 'pins 5.16' still uses the 'old' method in which the command list + * table always has two entries. */ + return coldstart_card(rom, init1, init2, init_size, ram_tab); + } +} + +static status_t coldstart_card(uint8* rom, uint16 init1, uint16 init2, uint16 init_size, uint16 ram_tab) +{ + status_t result = B_OK; + int16 size = init_size; + + LOG(8,("INFO: now executing coldstart...\n")); + + /* select colormode CRTC registers base adresses */ + NV_REG8(NV8_MISCW) = 0xcb; + + /* unknown.. */ + NV_REG8(NV8_VSE2) = 0x01; + + /* enable access to primary head */ + set_crtc_owner(0); + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + /* disable RMA as it's not used */ + /* (RMA is the cmd register for the 32bit port in the GPU to access 32bit registers + * and framebuffer via legacy ISA I/O space.) */ + CRTCW(RMA, 0x00); + + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + } + + /* turn off both displays and the hardcursors (also disables transfers) */ + nv_crtc_dpms(false, false, false, true); + nv_crtc_cursor_hide(); + if (si->ps.secondary_head) + { + nv_crtc2_dpms(false, false, false, true); + nv_crtc2_cursor_hide(); + } + + /* execute BIOS coldstart script(s) */ + if (init1 || init2) + { + if (init1) + if (exec_type1_script(rom, init1, &size, ram_tab) != B_OK) result = B_ERROR; + if (init2 && (result == B_OK)) + if (exec_type1_script(rom, init2, &size, ram_tab) != B_OK) result = B_ERROR; + + /* now enable ROM shadow or the card will remain shut-off! */ + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) |= 0x00000001)); + + //temporary: should be called from setmode probably.. + nv_crtc_setup_fifo(); + } + else + { + result = B_ERROR; + } + + if (result != B_OK) + LOG(8,("INFO: coldstart failed.\n")); + else + LOG(8,("INFO: coldstart execution completed OK.\n")); + + return result; +} + +static status_t coldstart_card_516_up(uint8* rom, PinsTables tabs, uint16 ram_tab) +{ + status_t result = B_OK; + uint16 adress; + uint32 fb_mrs1 = 0; + uint32 fb_mrs2 = 0; + + LOG(8,("INFO: now executing coldstart...\n")); + + /* get some strapinfo(?) for NV28 framebuffer access */ + //fixme?: works on at least one NV28... how about other cards? + if (si->ps.card_type == NV28) + { + fb_mrs2 = NV_REG32(NV32_FB_MRS2); + fb_mrs1 = NV_REG32(NV32_FB_MRS1); + } + + /* select colormode CRTC registers base adresses */ + NV_REG8(NV8_MISCW) = 0xcb; + + /* unknown.. */ + NV_REG8(NV8_VSE2) = 0x01; + + /* enable access to primary head */ + set_crtc_owner(0); + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + /* disable RMA as it's not used */ + /* (RMA is the cmd register for the 32bit port in the GPU to access 32bit registers + * and framebuffer via legacy ISA I/O space.) */ + CRTCW(RMA, 0x00); + + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + } + + /* turn off both displays and the hardcursors (also disables transfers) */ + nv_crtc_dpms(false, false, false, true); + nv_crtc_cursor_hide(); + if (si->ps.secondary_head) + { + nv_crtc2_dpms(false, false, false, true); + nv_crtc2_cursor_hide(); + } + + /* execute all BIOS coldstart script(s) */ + if (tabs.InitScriptTablePtr) + { + /* size is nolonger used, keeping it anyway for testing purposes :) */ + int16 size = 32767; + uint16 index = tabs.InitScriptTablePtr; + + adress = *((uint16*)(&(rom[index]))); + if (!adress) + { + LOG(8,("INFO: no cmdlist found!\n")); + result = B_ERROR; + } + + while (adress && (result == B_OK)) + { + result = exec_type2_script(rom, adress, &size, tabs, ram_tab); + /* next command script, please */ + index += 2; + adress = *((uint16*)(&(rom[index]))); + } + + /* do some NV28 specific extra stuff */ + //fixme: NV28 only?? + if (si->ps.card_type == NV28) + { + /* setup PTIMER */ + ACCW(PT_NUMERATOR, (si->ps.std_engine_clock * 20)); + ACCW(PT_DENOMINATR, 0x00000271); + + /* get NV28 RAM access up and running */ + //fixme?: works on at least one NV28... how about other cards? + NV_REG32(NV32_FB_MRS2) = fb_mrs2; + NV_REG32(NV32_FB_MRS1) = fb_mrs1; + } + + /* now enable ROM shadow or the card will remain shut-off! */ + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) |= 0x00000001)); + + //temporary: should be called from setmode probably.. + nv_crtc_setup_fifo(); + } + else + { + result = B_ERROR; + } + + if (result != B_OK) + LOG(8,("INFO: coldstart failed.\n")); + else + LOG(8,("INFO: coldstart execution completed OK.\n")); + + return result; +} + +/* This routine is complete, and is used for pre-NV10 cards. It's tested on a Elsa + * Erazor III with TNT2 (NV05) and on two no-name TNT2-M64's. All cards coldstart + * perfectly. */ +static status_t exec_type1_script(uint8* rom, uint16 adress, int16* size, uint16 ram_tab) +{ + status_t result = B_OK; + bool end = false; + bool exec = true; + uint8 index, byte; + uint32 reg, data, data2, and_out, or_in; + + LOG(8,("\nINFO: executing type1 script at adress $%04x...\n", adress)); + LOG(8,("INFO: ---Executing following command(s):\n")); + + while (!end) + { + LOG(8,("INFO: $%04x ($%02x); ", adress, rom[adress])); + + switch (rom[adress]) + { + case 0x59: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + data2 = *((uint16*)(&(rom[data]))); + LOG(8,("cmd 'calculate indirect and set PLL 32bit reg $%08x for %.3fMHz'\n", + reg, ((float)data2))); + if (exec) + { + float calced_clk; + uint8 m, n, p; + nv_dac_sys_pll_find(((float)data2), &calced_clk, &m, &n, &p, 0); + NV_REG32(reg) = ((p << 16) | (n << 8) | m); + } + log_pll(reg, data2); + break; + case 0x5a: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + data2 = *((uint32*)(&(rom[data]))); + LOG(8,("cmd 'WR indirect 32bit reg' $%08x = $%08x\n", reg, data2)); + if (exec) NV_REG32(reg) = data2; + break; + case 0x63: + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + LOG(8,("cmd 'setup RAM config' (always done)\n")); + /* always done */ + setup_ram_config(rom, ram_tab); + break; + case 0x65: + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint32*)(&(rom[adress]))); + adress += 4; + data2 = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'WR 32bit reg $%08x = $%08x, then = $%08x' (always done)\n", + reg, data, data2)); + /* always done */ + NV_REG32(reg) = data; + NV_REG32(reg) = data2; + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) & 0xfffffffe)); + break; + case 0x69: + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint16*)(&(rom[adress]))); + adress += 2; + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + or_in = *((uint8*)(&(rom[adress]))); + adress += 1; + LOG(8,("cmd 'RD 8bit ISA reg $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + reg, and_out, or_in)); + if (exec) + { + translate_ISA_PCI(®); + byte = NV_REG8(reg); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + NV_REG8(reg) = byte; + } + break; + case 0x6d: + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = NV_REG32(NV32_NV4STRAPINFO); + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + byte = *((uint8*)(&(rom[adress]))); + adress += 1; + data &= (uint32)and_out; + LOG(8,("cmd 'CHK bits AND-out $%02x RAMCFG for $%02x'\n", + and_out, byte)); + if (((uint8)data) != byte) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x6e: + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + and_out = *((uint32*)(&(rom[adress]))); + adress += 4; + or_in = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, OR-in = $%08x, WR-bk'\n", + reg, and_out, or_in)); + if (exec) + { + data = NV_REG32(reg); + data &= and_out; + data |= or_in; + NV_REG32(reg) = data; + } + break; + case 0x71: + LOG(8,("cmd 'END', execution completed.\n\n")); + end = true; + + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + break; + case 0x72: + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + adress += 1; + LOG(8,("cmd 'PGM commands'\n")); + LOG(8,("INFO: ---Executing following command(s):\n")); + exec = true; + break; + case 0x73: + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = NV_REG32(NV32_NVSTRAPINFO2); + and_out = *((uint32*)(&(rom[adress]))); + adress += 4; + data2 = *((uint32*)(&(rom[adress]))); + adress += 4; + data &= and_out; + LOG(8,("cmd 'CHK bits AND-out $%08x STRAPCFG2 for $%08x'\n", + and_out, data2)); + if (data != data2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x74: + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'SNOOZE for %d ($%04x) microSeconds' (always done)\n", data, data)); + /* always done */ + snooze(data); + break; + case 0x77: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x (b31-16 = '0', b15-0 = data)\n", + reg, data)); + if (exec) NV_REG32(reg) = data; + break; + case 0x78: + *size -= 6; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint16*)(&(rom[adress]))); + adress += 2; + index = *((uint8*)(&(rom[adress]))); + adress += 1; + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + or_in = *((uint8*)(&(rom[adress]))); + adress += 1; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + index, reg, and_out, or_in)); + if (exec) + { + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + NV_REG8(reg + 1) = byte; + } + break; + case 0x79: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'calculate and set PLL 32bit reg $%08x for %.3fMHz'\n", reg, (data / 100.0))); + if (exec) + { + float calced_clk; + uint8 m, n, p; + nv_dac_sys_pll_find((data / 100.0), &calced_clk, &m, &n, &p, 0); + NV_REG32(reg) = ((p << 16) | (n << 8) | m); + } + log_pll(reg, (data / 100)); + break; + case 0x7a: + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x\n", reg, data)); + if (exec) NV_REG32(reg) = data; + break; + default: + LOG(8,("unknown cmd, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + } + + return result; +} + +static void log_pll(uint32 reg, uint32 freq) +{ + if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) + LOG(8,("INFO: ---WARNING: check/update PLL programming script code!!!\n")); + switch (reg) + { + case NV32_MEMPLL: + LOG(8,("INFO: ---Memory PLL accessed.\n")); + /* update the card's specs */ + si->ps.std_memory_clock = freq; + break; + case NV32_COREPLL: + LOG(8,("INFO: ---Core PLL accessed.\n")); + /* update the card's specs */ + si->ps.std_engine_clock = freq; + break; + case NVDAC_PIXPLLC: + LOG(8,("INFO: ---DAC1 PLL accessed.\n")); + break; + case NVDAC2_PIXPLLC: + LOG(8,("INFO: ---DAC2 PLL accessed.\n")); + break; + /* unexpected cases, here for learning goals... */ + case NV32_MEMPLL2: + LOG(8,("INFO: ---NV31/NV36 extension to memory PLL accessed only!\n")); + break; + case NV32_COREPLL2: + LOG(8,("INFO: ---NV31/NV36 extension to core PLL accessed only!\n")); + break; + case NVDAC_PIXPLLC2: + LOG(8,("INFO: ---NV31/NV36 extension to DAC1 PLL accessed only!\n")); + break; + case NVDAC2_PIXPLLC2: + LOG(8,("INFO: ---NV31/NV36 extension to DAC2 PLL accessed only!\n")); + break; + default: + LOG(8,("INFO: ---Unknown PLL accessed!\n")); + break; + } +} + +static void setup_ram_config(uint8* rom, uint16 ram_tab) +{ + uint32 ram_cfg, data; + uint8 cnt; + + /* set MRS = 256 */ + NV_REG32(NV32_PFB_DEBUG_0) &= 0xffffffef; + /* read RAM config hardware(?) strap */ + ram_cfg = ((NV_REG32(NV32_NVSTRAPINFO2) >> 2) & 0x0000000f); + LOG(8,("INFO: ---RAM config strap is $%01x\n", ram_cfg)); + /* use it as a pointer in a BIOS table for prerecorded RAM configurations */ + ram_cfg = *((uint16*)(&(rom[(ram_tab + (ram_cfg * 2))]))); + /* log info */ + switch (ram_cfg & 0x00000003) + { + case 0: + LOG(8,("INFO: ---32Mb RAM should be connected\n")); + break; + case 1: + LOG(8,("INFO: ---4Mb RAM should be connected\n")); + break; + case 2: + LOG(8,("INFO: ---8Mb RAM should be connected\n")); + break; + case 3: + LOG(8,("INFO: ---16Mb RAM should be connected\n")); + break; + } + if (ram_cfg & 0x00000004) + LOG(8,("INFO: ---RAM should be 128bits wide\n")); + else + LOG(8,("INFO: ---RAM should be 64bits wide\n")); + switch ((ram_cfg & 0x00000038) >> 3) + { + case 0: + LOG(8,("INFO: ---RAM type: 8Mbit SGRAM\n")); + break; + case 1: + LOG(8,("INFO: ---RAM type: 16Mbit SGRAM\n")); + break; + case 2: + LOG(8,("INFO: ---RAM type: 4 banks of 16Mbit SGRAM\n")); + break; + case 3: + LOG(8,("INFO: ---RAM type: 16Mbit SDRAM\n")); + break; + case 4: + LOG(8,("INFO: ---RAM type: 64Mbit SDRAM\n")); + break; + case 5: + LOG(8,("INFO: ---RAM type: 64Mbit x16 SDRAM\n")); + break; + } + /* set RAM amount, width and type */ + data = (NV_REG32(NV32_NV4STRAPINFO) & 0xffffffc0); + NV_REG32(NV32_NV4STRAPINFO) = (data | (ram_cfg & 0x0000003f)); + /* setup write to read delay (?) */ + data = (NV_REG32(NV32_PFB_CONFIG_1) & 0xff8ffffe); + data |= ((ram_cfg & 0x00000700) << 12); + /* force update via b0 = 0... */ + NV_REG32(NV32_PFB_CONFIG_1) = data; + /* ... followed by b0 = 1(?) */ + NV_REG32(NV32_PFB_CONFIG_1) = (data | 0x00000001); + + /* do RAM width test to confirm RAM width set to be correct */ + /* write testpattern to first 128 bits of graphics memory... */ + data = 0x4e563541; + for (cnt = 0; cnt < 4; cnt++) + ((volatile uint32 *)si->framebuffer)[cnt] = data; + /* ... if second 64 bits does not contain the testpattern we are apparantly + * set to 128bits width while we should be set to 64bits width, so correct. */ + if (((volatile uint32 *)si->framebuffer)[3] != data) + { + LOG(8,("INFO: ---RAM width tested: width is 64bits, correcting settings.\n")); + NV_REG32(NV32_NV4STRAPINFO) &= ~0x00000004; + } + else + { + LOG(8,("INFO: ---RAM width tested: access is OK.\n")); + } + + /* do RAM size test to confirm RAM size set to be correct */ + ram_cfg = (NV_REG32(NV32_NV4STRAPINFO) & 0x00000003); + data = 0x4e563542; + /* first check for 32Mb... */ + if (!ram_cfg) + { + /* write testpattern to just above the 16Mb boundary */ + ((volatile uint32 *)si->framebuffer)[(16 * 1024 * 1024) >> 2] = data; + /* check if pattern reads back */ + if (((volatile uint32 *)si->framebuffer)[(16 * 1024 * 1024) >> 2] == data) + { + /* write second testpattern to base adress */ + data = 0x4135564e; + ((volatile uint32 *)si->framebuffer)[0] = data; + if (((volatile uint32 *)si->framebuffer)[0] == data) + { + LOG(8,("INFO: ---RAM size tested: size was set OK (32Mb).\n")); + return; + } + } + /* one of the two tests for 32Mb failed, we must have 16Mb */ + ram_cfg = 0x00000003; + LOG(8,("INFO: ---RAM size tested: size is 16Mb, correcting settings.\n")); + NV_REG32(NV32_NV4STRAPINFO) = + (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); + return; + } + /* ... now check for 16Mb... */ + if (ram_cfg == 0x00000003) + { + /* increment testpattern */ + data++; + /* write testpattern to just above the 8Mb boundary */ + ((volatile uint32 *)si->framebuffer)[(8 * 1024 * 1024) >> 2] = data; + /* check if pattern reads back */ + if (((volatile uint32 *)si->framebuffer)[(8 * 1024 * 1024) >> 2] == data) + { + LOG(8,("INFO: ---RAM size tested: size was set OK (16Mb).\n")); + return; + } + else + { + /* assuming 8Mb: retesting below! */ + ram_cfg = 0x00000002; + LOG(8,("INFO: ---RAM size tested: size is NOT 16Mb, testing for 8Mb...\n")); + NV_REG32(NV32_NV4STRAPINFO) = + (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); + } + } + /* ... and now check for 8Mb! (ram_cfg will be 'pre'set to 4Mb or 8Mb here) */ + { + /* increment testpattern (again) */ + data++; + /* write testpattern to just above the 4Mb boundary */ + ((volatile uint32 *)si->framebuffer)[(4 * 1024 * 1024) >> 2] = data; + /* check if pattern reads back */ + if (((volatile uint32 *)si->framebuffer)[(4 * 1024 * 1024) >> 2] == data) + { + /* we have 8Mb, make sure this is set. */ + ram_cfg = 0x00000002; + LOG(8,("INFO: ---RAM size tested: size is 8Mb, setting 8Mb.\n")); + /* fixme? assuming this should be done here! */ + NV_REG32(NV32_NV4STRAPINFO) = + (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); + return; + } + else + { + /* we must have 4Mb, make sure this is set. */ + ram_cfg = 0x00000001; + LOG(8,("INFO: ---RAM size tested: size is 4Mb, setting 4Mb.\n")); + NV_REG32(NV32_NV4STRAPINFO) = + (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); + return; + } + } +} + +/* this routine is used for NV10 and later */ +static status_t exec_type2_script(uint8* rom, uint16 adress, int16* size, PinsTables tabs, uint16 ram_tab) +{ + bool exec = true; + + LOG(8,("\nINFO: executing type2 script at adress $%04x...\n", adress)); + LOG(8,("INFO: ---Executing following command(s):\n")); + + return exec_type2_script_mode(rom, &adress, size, tabs, ram_tab, &exec); +} + +/* this routine is used for NV10 and later. It's tested on a GeForce2 MX400 (NV11), + * GeForce4 MX440 (NV18), GeForce4 Ti4200 (NV28) and a GeForceFX 5200 (NV34). + * These cards coldstart perfectly. */ +static status_t exec_type2_script_mode(uint8* rom, uint16* adress, int16* size, PinsTables tabs, uint16 ram_tab, bool* exec) +{ + status_t result = B_OK; + bool end = false; + uint8 index, byte, byte2, shift; + uint32 reg, reg2, data, data2, and_out, and_out2, or_in, or_in2, safe32, offset32, size32; + + while (!end) + { + LOG(8,("INFO: $%04x ($%02x); ", *adress, rom[*adress])); + + /* all commands are here (verified NV11 and NV28) */ + switch (rom[*adress]) + { + case 0x31: /* new */ + *size -= (15 + ((*((uint8*)(&(rom[(*adress + 10)])))) << 2)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + shift = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 2); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, shift-right = $%02x,\n", + reg, and_out, shift)); + LOG(8,("INFO: (cont.) RD 32bit data from subtable with size $%04x, at offset (result << 2),\n", + size32)); + LOG(8,("INFO: (cont.) then WR result data to 32bit reg $%08x'\n", reg2)); + if (*exec && reg2) + { + data = NV_REG32(reg); + data &= and_out; + data >>= shift; + data2 = *((uint32*)(&(rom[(*adress + (data << 2))]))); + NV_REG32(reg2) = data2; + } + *adress += size32; + break; + case 0x32: /* new */ + *size -= (11 + ((*((uint8*)(&(rom[(*adress + 6)])))) << 2)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + byte2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 2); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, shift-right = $%02x,\n", + index, reg, and_out, byte2)); + LOG(8,("INFO: (cont.) RD 32bit data from subtable with size $%04x, at offset (result << 2),\n", + size32)); + LOG(8,("INFO: (cont.) then WR result data to 32bit reg $%08x'\n", reg2)); + if (*exec && reg2) + { + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + byte >>= byte2; + offset32 = (byte << 2); + data = *((uint32*)(&(rom[(*adress + offset32)]))); + NV_REG32(reg2) = data; + } + *adress += size32; + break; + case 0x33: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + size32 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + /* executed 1-256 times */ + if (!size32) size32 = 256; + /* remember where to start each time */ + safe32 = *adress; + LOG(8,("cmd 'execute following part of this script $%03x times' (always done)\n", size32)); + for (offset32 = 0; offset32 < size32; offset32++) + { + LOG(8,("\nINFO: (#$%02x) executing part of type2 script at adress $%04x...\n", + offset32, *adress)); + LOG(8,("INFO: ---Not touching 'execution' mode at this time:\n")); + *adress = safe32; + result = exec_type2_script_mode(rom, adress, size, tabs, ram_tab, exec); + } + LOG(8,("INFO: ---Continuing script:\n")); + break; + case 0x34: /* new */ + *size -= (12 + ((*((uint8*)(&(rom[(*adress + 7)])))) << 1)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + shift = *((uint8*)(&(rom[*adress]))); + *adress += 1; + offset32 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 1); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, shift-right = $%02x,\n", + index, reg, and_out, shift)); + LOG(8,("INFO: (cont.) RD 16bit PLL frequency to pgm from subtable with size $%04x, at offset (result << 1),\n", + size32)); + LOG(8,("INFO: (cont.) RD table-index ($%02x) for cmd $39'\n", + offset32)); + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + data = (byte >> shift); + data <<= 1; + data2 = *((uint16*)(&(rom[(*adress + data)]))); + if (offset32 < 0x80) + { + bool double_f = true; + LOG(8,("INFO: Do subcmd ($39); ")); + exec_cmd_39_type2(rom, offset32, tabs, &double_f); + LOG(8,("INFO: (cont. cmd $34) Doubling PLL frequency to be set for cmd $34.\n")); + if (double_f) data2 <<= 1; + LOG(8,("INFO: ---Reverting to pre-subcmd ($39) 'execution' mode.\n")); + } + else + { + LOG(8,("INFO: table index is negative, not executing subcmd ($39).\n")); + } + LOG(8,("INFO: (cont.) 'calc and set PLL 32bit reg $%08x for %.3fMHz'\n", + reg2, (data2 / 100.0))); + if (*exec && reg2) + { + float calced_clk; + uint8 m, n, p; + nv_dac_sys_pll_find((data2 / 100.0), &calced_clk, &m, &n, &p, 0); + /* programming the PLL needs to be done in steps! (confirmed NV28) */ + data = NV_REG32(reg2); + NV_REG32(reg2) = ((data & 0xffff0000) | (n << 8) | m); + data = NV_REG32(reg2); + NV_REG32(reg2) = ((p << 16) | (n << 8) | m); +//fixme? + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ +// if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) +// DACW(PIXPLLC2, 0x80000401); + } + log_pll(reg2, (data2 / 100)); + *adress += size32; + break; + case 0x35: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + offset32 = (byte << 1); + offset32 += tabs.InitFunctionTablePtr; + LOG(8,("cmd 'execute fixed VGA BIOS routine #$%02x at adress $%04x'\n", + byte, offset32)); + /* note: + * This command is BIOS/'pins' version specific. Confirmed a NV28 having NO + * entries at all in InitFunctionTable! + * (BIOS version 4.28.20.05.11; 'pins' version 5.21) */ + //fixme: impl. if it turns out this cmd is used.. (didn't see that yet) + if (*exec) + { + //fixme: add BIOS/'pins' version dependancy... + switch(byte) + { + default: + LOG(8,("\n\nINFO: WARNING: function not implemented, skipping!\n\n")); + break; + } + } + break; + case 0x37: /* new */ + *size -= 11; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + byte2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + reg2 = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD 32bit reg $%08x, shift-right = $%02x, AND-out lsb = $%02x,\n", + reg, byte2, and_out)); + LOG(8,("INFO: (cont.) RD 8bit ISA reg $%02x via $%04x, AND-out = $%02x, OR-in lsb result 32bit, WR-bk'\n", + index, reg2, and_out2)); + if (*exec) + { + data = NV_REG32(reg); + if (byte2 < 0x80) + { + data >>= byte2; + } + else + { + data <<= (0x0100 - byte2); + } + data &= and_out; + translate_ISA_PCI(®2); + NV_REG8(reg2) = index; + byte = NV_REG8(reg2 + 1); + byte &= (uint8)and_out2; + byte |= (uint8)data; + NV_REG8(reg2 + 1) = byte; + } + break; + case 0x38: /* new */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'invert current mode'\n")); + *exec = !(*exec); + if (*exec) + LOG(8,("INFO: ---Executing following command(s):\n")); + else + LOG(8,("INFO: ---Not executing following command(s):\n")); + break; + case 0x39: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + exec_cmd_39_type2(rom, data, tabs, exec); + break; + case 0x49: /* new */ + size32 = *((uint8*)(&(rom[*adress + 17]))); + if (!size32) size32 = 256; + *size -= (18 + (size32 << 1)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + or_in = *((uint32*)(&(rom[*adress]))); + *adress += 4; + size32 = *((uint8*)(&(rom[*adress]))); + if (!size32) size32 = 256; + *adress += 1; + LOG(8,("cmd 'do following cmd structure $%03x time(s)':\n", size32)); + for (offset32 = 0; offset32 < size32; offset32++) + { + or_in2 = *((uint8*)(&(rom[(*adress + (offset32 << 1))]))); + data2 = *((uint8*)(&(rom[(*adress + (offset32 << 1) + 1)]))); + LOG(8,("INFO (cont.) (#$%02x) cmd 'WR 32bit reg $%08x = $%08x, RD 32bit reg $%08x,\n", + offset32, reg2, data2, reg)); + LOG(8,("INFO (cont.) AND-out $%08x, OR-in $%08x, OR-in $%08x, WR-bk'\n", + and_out, or_in, or_in2)); + } + if (*exec) + { + for (index = 0; index < size32; index++) + { + or_in2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + NV_REG32(reg2) = data2; + data = NV_REG32(reg); + data &= and_out; + data |= or_in; + data |= or_in2; + NV_REG32(reg) = data; + } + } + else + { + *adress += (size32 << 1); + } + break; + case 0x61: /* new */ + *size -= 4; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'WR ISA reg $%04x = $%02x'\n", reg, byte)); + if (*exec) + { + translate_ISA_PCI(®); + NV_REG8(reg) = byte; + } + break; + case 0x62: /* new */ + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'WR idx ISA reg $%02x via $%04x = $%02x'\n", index, reg, byte)); + if (*exec) + { + translate_ISA_PCI(®); + NV_REG16(reg) = ((((uint16)byte) << 8) | index); + } + break; + case 0x63: /* new setup compared to pre-NV10 version */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'setup RAM config' (always done)\n")); + /* always done */ + switch (si->ps.card_type) + { + case NV28: + setup_ram_config_nv28(rom); + break; + default: + setup_ram_config_nv10_up(rom); + break; + } + break; + case 0x65: /* identical to type1 */ + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'WR 32bit reg $%08x = $%08x, then = $%08x' (always done)\n", + reg, data, data2)); + /* always done */ + NV_REG32(reg) = data; + NV_REG32(reg) = data2; + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) & 0xfffffffe)); + break; + case 0x69: /* identical to type1 */ + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + or_in = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD 8bit ISA reg $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + reg, and_out, or_in)); + if (*exec) + { + translate_ISA_PCI(®); + byte = NV_REG8(reg); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + NV_REG8(reg) = byte; + } + break; + case 0x6a: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint16*)(&(rom[(tabs.InitScriptTablePtr + (data << 1))]))); + LOG(8,("cmd 'jump to script #$%02x at adress $%04x'\n", data, data2)); + if (*exec) + { + *adress = data2; + LOG(8,("INFO: ---Jumping; not touching 'execution' mode.\n")); + } + break; + case 0x6b: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint16*)(&(rom[(tabs.InitScriptTablePtr + (data << 1))]))); + LOG(8,("cmd 'gosub script #$%02x at adress $%04x'\n", data, data2)); + if (*exec && data2) + { + result = exec_type2_script(rom, data2, size, tabs, ram_tab); + LOG(8,("INFO: ---Reverting to pre-gosub 'execution' mode.\n")); + } + break; + case 0x6e: /* identical to type1 */ + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + or_in = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, OR-in = $%08x, WR-bk'\n", + reg, and_out, or_in)); + if (*exec) + { + data = NV_REG32(reg); + data &= and_out; + data |= or_in; + NV_REG32(reg) = data; + } + break; + case 0x6f: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data = tabs.MacroIndexTablePtr + (byte << 1); + offset32 = (*((uint8*)(&(rom[data]))) << 3); + size32 = *((uint8*)(&(rom[(data + 1)]))); + offset32 += tabs.MacroTablePtr; + /* note: min 1, max 255 commands can be requested */ + LOG(8,("cmd 'do $%02x time(s) a 32bit reg WR with 32bit data' (MacroIndexTable idx = $%02x):\n", + size32, byte)); + safe32 = 0; + while (safe32 < size32) + { + reg2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3))]))); + data2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3) + 4)]))); + LOG(8,("INFO: (cont.) (#$%02x) cmd 'WR 32bit reg' $%08x = $%08x\n", + safe32, reg2, data2)); + safe32++; + } + if (*exec) + { + safe32 = 0; + while (safe32 < size32) + { + reg2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3))]))); + data2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3) + 4)]))); + NV_REG32(reg2) = data2; + safe32++; + } + } + break; + case 0x36: /* new */ + case 0x66: /* new */ + case 0x67: /* new */ + case 0x68: /* new */ + case 0x6c: /* new */ + case 0x71: /* identical to type1 */ + LOG(8,("cmd 'END', execution completed.\n\n")); + end = true; + + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; /* needed to make cmd #$33 work correctly! */ + break; + case 0x72: /* identical to type1 */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'PGM commands'\n")); + LOG(8,("INFO: ---Executing following command(s):\n")); + *exec = true; + break; + case 0x74: /* identical to type1 */ + //fixme? on at least NV28 this cmd hammers the CRTC PCI-timeout register + //'data' number of times instead of snoozing. + //Couldn't see any diff in behaviour though! + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint16*)(&(rom[*adress]))); + *adress += 2; + LOG(8,("cmd 'SNOOZE for %d ($%04x) microSeconds' (always done)\n", data, data)); + /* always done */ + snooze(data); + break; + case 0x75: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data *= 12; + data += tabs.ConditionTablePtr; + reg = *((uint32*)(&(rom[data]))); + and_out = *((uint32*)(&(rom[(data + 4)]))); + data2 = *((uint32*)(&(rom[(data + 8)]))); + data = NV_REG32(reg); + data &= and_out; + LOG(8,("cmd 'CHK bits AND-out $%08x reg $%08x for $%08x'\n", + and_out, reg, data2)); + if (data != data2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x76: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data *= 5; + data += tabs.IOConditionTablePtr; + reg = *((uint16*)(&(rom[data]))); + index = *((uint8*)(&(rom[(data + 2)]))); + and_out = *((uint8*)(&(rom[(data + 3)]))); + byte2 = *((uint8*)(&(rom[(data + 4)]))); + LOG(8,("cmd 'CHK bits AND-out $%02x idx ISA reg $%02x via $%04x for $%02x'\n", + and_out, index, reg, byte2)); + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + if (byte != byte2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x78: /* identical to type1 */ + *size -= 6; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + or_in = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + index, reg, and_out, or_in)); + if (*exec) + { + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + NV_REG8(reg + 1) = byte; + } + break; + case 0x79: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint16*)(&(rom[*adress]))); + *adress += 2; + LOG(8,("cmd 'calculate and set PLL 32bit reg $%08x for %.3fMHz'\n", reg, (data / 100.0))); + if (*exec) + { + float calced_clk; + uint8 m, n, p; + nv_dac_sys_pll_find((data / 100.0), &calced_clk, &m, &n, &p, 0); + /* programming the PLL needs to be done in steps! (confirmed NV28) */ + data2 = NV_REG32(reg); + NV_REG32(reg) = ((data2 & 0xffff0000) | (n << 8) | m); + data2 = NV_REG32(reg); + NV_REG32(reg) = ((p << 16) | (n << 8) | m); +//fixme? + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ +// if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) +// DACW(PIXPLLC2, 0x80000401); + } + log_pll(reg, (data / 100)); + break; + case 0x7a: /* identical to type1 */ + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x\n", reg, data)); + if (*exec) NV_REG32(reg) = data; + break; + default: + LOG(8,("unknown cmd, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + } + + return result; +} + +static void exec_cmd_39_type2(uint8* rom, uint32 data, PinsTables tabs, bool* exec) +{ + uint8 index, byte, byte2, safe, shift; + uint32 reg, and_out, and_out2, offset32; + + data *= 9; + data += tabs.IOFlagConditionTablePtr; + reg = *((uint16*)(&(rom[data]))); + index = *((uint8*)(&(rom[(data + 2)]))); + and_out = *((uint8*)(&(rom[(data + 3)]))); + shift = *((uint8*)(&(rom[(data + 4)]))); + offset32 = *((uint16*)(&(rom[data + 5]))); + and_out2 = *((uint8*)(&(rom[(data + 7)]))); + byte2 = *((uint8*)(&(rom[(data + 8)]))); + LOG(8,("cmd 'AND-out bits $%02x idx ISA reg $%02x via $%04x, shift-right = $%02x,\n", + and_out, index, reg, shift)); + translate_ISA_PCI(®); + NV_REG8(reg) = index; + byte = NV_REG8(reg + 1); + byte &= (uint8)and_out; + offset32 += (byte >> shift); + safe = byte = *((uint8*)(&(rom[offset32]))); + byte &= (uint8)and_out2; + LOG(8,("INFO: (cont.) use result as index in table to get data $%02x,\n", + safe)); + LOG(8,("INFO: (cont.) then chk bits AND-out $%02x of data for $%02x'\n", + and_out2, byte2)); + if (byte != byte2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } +} + +static void setup_ram_config_nv10_up(uint8* rom) +{ + /* note: + * After writing data to RAM a snooze is required to make the test work. + * Confirmed a NV11: without snooze it worked OK on a low-voltage AGP2.0 slot, + * but on a higher-voltage AGP 1.0 slot it failed to identify errors correctly!! + * Adding the snooze fixed that. */ + + uint32 data, dummy; + uint8 cnt = 0; + status_t stat = B_ERROR; + + /* set 'refctrl is valid' */ + NV_REG32(NV32_PFB_REFCTRL) = 0x80000000; + + /* check RAM for 256bits buswidth(?) */ + while ((cnt < 4) && (stat != B_OK)) + { + /* reset RAM bits at offset 224-255 bits four times */ + ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; + snooze(10); + ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; + snooze(10); + ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; + snooze(10); + ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; + snooze(10); + /* write testpattern */ + ((volatile uint32 *)si->framebuffer)[0x07] = 0x4e563131; + snooze(10); + /* reset RAM bits at offset 480-511 bits */ + ((volatile uint32 *)si->framebuffer)[0x0f] = 0x00000000; + snooze(10); + /* check testpattern to have survived */ + if (((volatile uint32 *)si->framebuffer)[0x07] == 0x4e563131) stat = B_OK; + cnt++; + } + + /* if pattern did not hold modify RAM-type setup */ + if (stat != B_OK) + { + LOG(8,("INFO: ---RAM test #1 done: access errors, modified setup.\n")); + data = NV_REG32(NV32_PFB_CONFIG_0); + if (data & 0x00000010) + { + data &= 0xffffffcf; + } + else + { + data &= 0xffffffcf; + data |= 0x00000020; + } + NV_REG32(NV32_PFB_CONFIG_0) = data; + } + else + { + LOG(8,("INFO: ---RAM test #1 done: access is OK.\n")); + } + + /* check RAM bankswitching stuff(?) */ + cnt = 0; + stat = B_ERROR; + while ((cnt < 4) && (stat != B_OK)) + { + /* read RAM size */ + data = NV_REG32(NV32_NV10STRAPINFO); + /* subtract 1MB */ + data -= 0x00100000; + /* write testpattern at generated RAM adress */ + ((volatile uint32 *)si->framebuffer)[(data >> 2)] = 0x4e564441; + snooze(10); + /* reset first RAM adress */ + ((volatile uint32 *)si->framebuffer)[0x00] = 0x00000000; + snooze(10); + /* dummyread first RAM adress four times */ + dummy = ((volatile uint32 *)si->framebuffer)[0x00]; + dummy = ((volatile uint32 *)si->framebuffer)[0x00]; + dummy = ((volatile uint32 *)si->framebuffer)[0x00]; + dummy = ((volatile uint32 *)si->framebuffer)[0x00]; + /* check testpattern to have survived */ + if (((volatile uint32 *)si->framebuffer)[(data >> 2)] == 0x4e564441) stat = B_OK; + cnt++; + } + + /* if pattern did not hold modify RAM-type setup */ + if (stat != B_OK) + { + LOG(8,("INFO: ---RAM test #2 done: access errors, modified setup.\n")); + NV_REG32(NV32_PFB_CONFIG_0) &= 0xffffefff; + } + else + { + LOG(8,("INFO: ---RAM test #2 done: access is OK.\n")); + } +} + +/* Note: this routine assumes at least 128Mb was mapped to memory (kerneldriver). + * It doesn't matter if the card actually _has_ this amount of RAM or not(!) */ +static void setup_ram_config_nv28(uint8* rom) +{ + /* note: + * After writing data to RAM a snooze is required to make the test work. + * Confirmed a NV11: without snooze it worked OK on a low-voltage AGP2.0 slot, + * but on a higher-voltage AGP 1.0 slot it failed to identify errors correctly!! + * Adding the snooze fixed that. */ + + uint32 dummy; + uint8 cnt = 0; + status_t stat = B_ERROR; + + /* set 'refctrl is valid' */ + NV_REG32(NV32_PFB_REFCTRL) = 0x80000000; + + /* check RAM */ + while ((cnt < 4) && (stat != B_OK)) + { + /* set bit 11: 'pulse' something into a new setting? */ + NV_REG32(NV32_PFB_CONFIG_0) |= 0x00000800; + /* write testpattern to RAM adress 127Mb */ + ((volatile uint32 *)si->framebuffer)[0x01fc0000] = 0x4e564441; + snooze(10); + /* reset first RAM adress */ + ((volatile uint32 *)si->framebuffer)[0x00000000] = 0x00000000; + snooze(10); + /* dummyread first RAM adress four times */ + dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("INFO: (#%d) dummy1 = $%08x, ", cnt, dummy)); + dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy2 = $%08x, ", dummy)); + dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy3 = $%08x, ", dummy)); + dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy4 = $%08x\n", dummy)); + /* check testpattern to have survived */ + if (((volatile uint32 *)si->framebuffer)[0x01fc0000] == 0x4e564441) stat = B_OK; + cnt++; + } + + /* clear bit 11: set normal mode */ + NV_REG32(NV32_PFB_CONFIG_0) &= ~0x00000800; + + if (stat == B_OK) + LOG(8,("INFO: ---RAM test done: access was OK within %d iteration(s).\n", cnt)); + else + LOG(8,("INFO: ---RAM test done: access was still not OK after 4 iterations.\n")); +} + +static status_t translate_ISA_PCI(uint32* reg) +{ + switch (*reg) + { + case 0x03c0: + *reg = NV8_ATTRDATW; + break; + case 0x03c1: + *reg = NV8_ATTRDATR; + break; + case 0x03c2: + *reg = NV8_MISCW; + break; + case 0x03c4: + *reg = NV8_SEQIND; + break; + case 0x03c5: + *reg = NV8_SEQDAT; + break; + case 0x03c6: + *reg = NV8_PALMASK; + break; + case 0x03c7: + *reg = NV8_PALINDR; + break; + case 0x03c8: + *reg = NV8_PALINDW; + break; + case 0x03c9: + *reg = NV8_PALDATA; + break; + case 0x03cc: + *reg = NV8_MISCR; + break; + case 0x03ce: + *reg = NV8_GRPHIND; + break; + case 0x03cf: + *reg = NV8_GRPHDAT; + break; + case 0x03d4: + *reg = NV8_CRTCIND; + break; + case 0x03d5: + *reg = NV8_CRTCDAT; + break; + case 0x03da: + *reg = NV8_INSTAT1; + break; + default: + LOG(8,("\n\nINFO: WARNING: ISA->PCI register adress translation failed!\n\n")); + return B_ERROR; + break; + } + + return B_OK; +} + +void set_pll(uint32 reg, uint32 req_clk) +{ + uint32 data; + float calced_clk; + uint8 m, n, p; + nv_dac_sys_pll_find(req_clk, &calced_clk, &m, &n, &p, 0); + /* programming the PLL needs to be done in steps! (confirmed NV28) */ + data = NV_REG32(reg); + NV_REG32(reg) = ((data & 0xffff0000) | (n << 8) | m); + data = NV_REG32(reg); + NV_REG32(reg) = ((p << 16) | (n << 8) | m); + +//fixme? + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ + if (si->ps.ext_pll) + { + if (reg == NV32_COREPLL) NV_REG32(NV32_COREPLL2) = 0x80000401; + if (reg == NV32_MEMPLL) NV_REG32(NV32_MEMPLL2) = 0x80000401; + } + + log_pll(reg, req_clk); +} + +/* doing general fail-safe default setup here */ +static status_t nv_crtc_setup_fifo() +{ + /* enable access to primary head */ + set_crtc_owner(0); + + /* set CRTC FIFO burst size to 256 */ + CRTCW(FIFO, 0x03); + + /* set CRTC FIFO low watermark to 32 */ + CRTCW(FIFO_LWM, 0x20); + + return B_OK; +} + +/* (pre)set 'fixed' card specifications */ +void set_specs(void) +{ + LOG(8,("INFO: setting up card specifications\n")); + + /* set failsave speeds */ + switch (si->ps.card_type) + { + case NV04: + pinsnv4_fake(); + break; + case NV05: + case NV05M64: + pinsnv5_nv5m64_fake(); + break; + case NV06: + pinsnv6_fake(); + break; + default: + switch (si->ps.card_arch) + { + case NV10A: + pinsnv10_arch_fake(); + break; + case NV20A: + pinsnv20_arch_fake(); + break; + case NV30A: + case NV40A: + pinsnv30_arch_fake(); + break; + default: + /* 'failsafe' values... */ + pinsnv10_arch_fake(); + break; + } + break; + } + + /* detect reference crystal frequency and dualhead */ + switch (si->ps.card_arch) + { + case NV04A: + getstrap_arch_nv4(); + break; + default: + getstrap_arch_nv10_20_30_40(); + break; + } +} + +/* this routine presumes the card was coldstarted by the card's BIOS for panel stuff */ +void fake_panel_start(void) +{ + LOG(8,("INFO: detecting RAM size\n")); + + /* detect RAM amount */ + switch (si->ps.card_arch) + { + case NV04A: + getRAMsize_arch_nv4(); + break; + default: + getRAMsize_arch_nv10_20_30_40(); + break; + } + + /* override memory detection if requested by user */ + if (si->settings.memory != 0) + { + LOG(2,("INFO: forcing memory size (specified in settings file)\n")); + si->ps.memory_size = si->settings.memory * 1024 * 1024; + } + + /* find out if the card has a tvout chip */ + si->ps.tvout = false; + si->ps.tv_encoder.type = NONE; + si->ps.tv_encoder.version = 0; + i2c_init(); + //fixme: add support for more encoders... + BT_probe(); + + LOG(8,("INFO: faking panel startup\n")); + + /* find out the BIOS preprogrammed panel use status... */ + detect_panels(); + + /* determine and setup output devices and heads */ + setup_output_matrix(); + + /* select other CRTC for primary head use if specified by user in settings file */ + if (si->ps.secondary_head && si->settings.switchhead) + { + LOG(2,("INFO: inverting head use (specified in settings file)\n")); + si->ps.crtc2_prim = !si->ps.crtc2_prim; + } +} + +static void detect_panels() +{ + /* detect if the BIOS enabled LCD's (internal panels or DVI) or TVout */ + + /* both external TMDS transmitters (used for LCD/DVI) and external TVencoders + * (can) use the CRTC's in slaved mode. */ + /* Note: + * DFP's are programmed with standard VESA modelines by the card's BIOS! */ + bool slaved_for_dev1 = false, slaved_for_dev2 = false; + bool tvout1 = false, tvout2 = false; + + /* check primary head: */ + /* enable access to primary head */ + set_crtc_owner(0); + + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + + LOG(2,("INFO: Dumping flatpanel related CRTC registers:\n")); + /* related info PIXEL register: + * b7: 1 = slaved mode (all cards). */ + LOG(2,("CRTC1: PIXEL register: $%02x\n", CRTCR(PIXEL))); + /* info LCD register: + * b7: 1 = stereo view (shutter glasses use) (all cards), + * b5: 1 = power ext. TMDS (or something)/0 = TVout use (?) (confirmed NV17, NV28), + * b4: 1 = power ext. TMDS (or something)/0 = TVout use (?) (confirmed NV34), + * b3: 1 = ??? (not panel related probably!) (confirmed NV34), + * b1: 1 = power ext. TMDS (or something) (?) (confirmed NV05?, NV17), + * b0: 1 = select panel encoder / 0 = select TVout encoder (all cards). */ + LOG(2,("CRTC1: LCD register: $%02x\n", CRTCR(LCD))); + /* info 0x59 register: + * b0: 1 = enable ext. TMDS clock (DPMS) (confirmed NV28, NV34). */ + LOG(2,("CRTC1: register $59: $%02x\n", CRTCR(0x59))); + /* info 0x9f register: + * b4: 0 = TVout use (?). */ + LOG(2,("CRTC1: register $9f: $%02x\n", CRTCR(0x9f))); + + /* detect active slave device (if any) */ + slaved_for_dev1 = (CRTCR(PIXEL) & 0x80); + if (slaved_for_dev1) + { + /* if the panel isn't selected, tvout is.. */ + tvout1 = !(CRTCR(LCD) & 0x01); + } + + if (si->ps.secondary_head) + { + /* check secondary head: */ + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTC2R(VSYNCE) & 0x7f)); + + LOG(2,("CRTC2: PIXEL register: $%02x\n", CRTC2R(PIXEL))); + LOG(2,("CRTC2: LCD register: $%02x\n", CRTC2R(LCD))); + LOG(2,("CRTC2: register $59: $%02x\n", CRTC2R(0x59))); + LOG(2,("CRTC2: register $9f: $%02x\n", CRTC2R(0x9f))); + + /* detect active slave device (if any) */ + slaved_for_dev2 = (CRTC2R(PIXEL) & 0x80); + if (slaved_for_dev2) + { + /* if the panel isn't selected, tvout is.. */ + tvout2 = !(CRTC2R(LCD) & 0x01); + } + } + + LOG(2,("INFO: End flatpanel related CRTC registers dump.\n")); + + /* do some presets */ + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + si->ps.panel1_aspect = 0; + si->ps.p2_timing.h_display = 0; + si->ps.p2_timing.v_display = 0; + si->ps.panel2_aspect = 0; + si->ps.slaved_tmds1 = false; + si->ps.slaved_tmds2 = false; + si->ps.master_tmds1 = false; + si->ps.master_tmds2 = false; + si->ps.tmds1_active = false; + si->ps.tmds2_active = false; + /* determine the situation we are in... (regarding flatpanels) */ + /* fixme: add VESA DDC EDID stuff one day... */ + /* fixme: find out how to program those transmitters one day instead of + * relying on the cards BIOS to do it. This adds TVout options where panels + * are used! + * Currently we'd loose the panel setup while not being able to restore it. */ + + /* note: (facts) + * -> NV11 and NV17 laptops have LVDS panels, programmed in both sets registers; + * -> NV34 laptops have TMDS panels, programmed in only one set of registers; + * -> NV11, NV25 and NV34 DVI cards, so external panels (TMDS) are programmed + * in only one set of registers; + * -> a register-set's FP_TG_CTRL register, bit 31 tells you if a LVDS panel is + * connected to the primary head (0), or to the secondary head (1) except + * on some NV11's if this bit is '0' there; + * -> for LVDS panels both registersets are programmed identically by the card's + * BIOSes; + * -> the programmed set of registers tells you where a TMDS (DVI) panel is + * connected; + * -> On all cards a CRTC is used in slaved mode when a panel is connected, + * except on NV11: here master mode is (might be?) detected. */ + /* note also: + * external TMDS encoders are only used for logic-level translation: it's + * modeline registers are not used. Instead the GPU's internal modeline registers + * are used. The external encoder is not connected to a I2C bus (confirmed NV34). */ + if (slaved_for_dev1 && !tvout1) + { + uint16 width = ((DACR(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DACR(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.slaved_tmds1 = true; + si->ps.tmds1_active = true; + si->ps.p1_timing.h_display = width; + si->ps.p1_timing.v_display = height; + } + } + + if (si->ps.secondary_head && slaved_for_dev2 && !tvout2) + { + uint16 width = ((DAC2R(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DAC2R(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.slaved_tmds2 = true; + si->ps.tmds2_active = true; + si->ps.p2_timing.h_display = width; + si->ps.p2_timing.v_display = height; + } + } + + if ((si->ps.card_type == NV11) && + !si->ps.slaved_tmds1 && !tvout1) + { + uint16 width = ((DACR(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DACR(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.master_tmds1 = true; + si->ps.tmds1_active = true; + si->ps.p1_timing.h_display = width; + si->ps.p1_timing.v_display = height; + } + } + + if ((si->ps.card_type == NV11) && + si->ps.secondary_head && !si->ps.slaved_tmds2 && !tvout2) + { + uint16 width = ((DAC2R(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DAC2R(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.master_tmds2 = true; + si->ps.tmds2_active = true; + si->ps.p2_timing.h_display = width; + si->ps.p2_timing.v_display = height; + } + } + + //fixme...: + //we are assuming that no DVI is used as external monitor on laptops; + //otherwise we probably get into trouble here if the checked specs match. + if (si->ps.laptop && si->ps.tmds1_active && si->ps.tmds2_active && + ((DACR(FP_TG_CTRL) & 0x80000000) == (DAC2R(FP_TG_CTRL) & 0x80000000)) && + (si->ps.p1_timing.h_display == si->ps.p2_timing.h_display) && + (si->ps.p1_timing.v_display == si->ps.p2_timing.v_display)) + { + LOG(2,("INFO: correcting double detection of single panel!\n")); + + if (si->ps.card_type == NV11) + { + /* LVDS panel is _always_ on CRTC2, so clear false primary detection */ + si->ps.slaved_tmds1 = false; + si->ps.master_tmds1 = false; + si->ps.tmds1_active = false; + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + } + else + { + if (DACR(FP_TG_CTRL) & 0x80000000) + { + /* LVDS panel is on CRTC2, so clear false primary detection */ + si->ps.slaved_tmds1 = false; + si->ps.master_tmds1 = false; + si->ps.tmds1_active = false; + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + } + else + { + /* LVDS panel is on CRTC1, so clear false secondary detection */ + si->ps.slaved_tmds2 = false; + si->ps.master_tmds2 = false; + si->ps.tmds2_active = false; + si->ps.p2_timing.h_display = 0; + si->ps.p2_timing.v_display = 0; + } + } + } + + /* fetch panel(s) modeline(s) */ + if (si->ps.tmds1_active) + { + /* determine panel aspect ratio */ + si->ps.panel1_aspect = + (si->ps.p1_timing.h_display / ((float)si->ps.p1_timing.v_display)); + /* force widescreen type if requested */ + if (si->settings.force_ws) si->ps.panel1_aspect = 1.60; + /* horizontal timing */ + si->ps.p1_timing.h_sync_start = (DACR(FP_HSYNC_S) & 0x0000ffff) + 1; + si->ps.p1_timing.h_sync_end = (DACR(FP_HSYNC_E) & 0x0000ffff) + 1; + si->ps.p1_timing.h_total = (DACR(FP_HTOTAL) & 0x0000ffff) + 1; + /* vertical timing */ + si->ps.p1_timing.v_sync_start = (DACR(FP_VSYNC_S) & 0x0000ffff) + 1; + si->ps.p1_timing.v_sync_end = (DACR(FP_VSYNC_E) & 0x0000ffff) + 1; + si->ps.p1_timing.v_total = (DACR(FP_VTOTAL) & 0x0000ffff) + 1; + /* sync polarity */ + si->ps.p1_timing.flags = 0; + if (DACR(FP_TG_CTRL) & 0x00000001) si->ps.p1_timing.flags |= B_POSITIVE_VSYNC; + if (DACR(FP_TG_CTRL) & 0x00000010) si->ps.p1_timing.flags |= B_POSITIVE_HSYNC; + /* display enable polarity (not an official flag) */ + if (DACR(FP_TG_CTRL) & 0x10000000) si->ps.p1_timing.flags |= B_BLANK_PEDESTAL; + /* refreshrate: + * fix a DVI or laptop flatpanel to 60Hz refresh! */ + si->ps.p1_timing.pixel_clock = + (si->ps.p1_timing.h_total * si->ps.p1_timing.v_total * 60) / 1000; + } + if (si->ps.tmds2_active) + { + /* determine panel aspect ratio */ + si->ps.panel2_aspect = + (si->ps.p2_timing.h_display / ((float)si->ps.p2_timing.v_display)); + /* force widescreen type if requested */ + if (si->settings.force_ws) si->ps.panel2_aspect = 1.60; + /* horizontal timing */ + si->ps.p2_timing.h_sync_start = (DAC2R(FP_HSYNC_S) & 0x0000ffff) + 1; + si->ps.p2_timing.h_sync_end = (DAC2R(FP_HSYNC_E) & 0x0000ffff) + 1; + si->ps.p2_timing.h_total = (DAC2R(FP_HTOTAL) & 0x0000ffff) + 1; + /* vertical timing */ + si->ps.p2_timing.v_sync_start = (DAC2R(FP_VSYNC_S) & 0x0000ffff) + 1; + si->ps.p2_timing.v_sync_end = (DAC2R(FP_VSYNC_E) & 0x0000ffff) + 1; + si->ps.p2_timing.v_total = (DAC2R(FP_VTOTAL) & 0x0000ffff) + 1; + /* sync polarity */ + si->ps.p2_timing.flags = 0; + if (DAC2R(FP_TG_CTRL) & 0x00000001) si->ps.p2_timing.flags |= B_POSITIVE_VSYNC; + if (DAC2R(FP_TG_CTRL) & 0x00000010) si->ps.p2_timing.flags |= B_POSITIVE_HSYNC; + /* display enable polarity (not an official flag) */ + if (DAC2R(FP_TG_CTRL) & 0x10000000) si->ps.p2_timing.flags |= B_BLANK_PEDESTAL; + /* refreshrate: + * fix a DVI or laptop flatpanel to 60Hz refresh! */ + si->ps.p2_timing.pixel_clock = + (si->ps.p2_timing.h_total * si->ps.p2_timing.v_total * 60) / 1000; + } + + /* dump some panel configuration registers... */ + LOG(2,("INFO: Dumping flatpanel registers:\n")); + LOG(2,("DUALHEAD_CTRL: $%08x\n", NV_REG32(NV32_DUALHEAD_CTRL))); + LOG(2,("DAC1: FP_HDISPEND: %d\n", DACR(FP_HDISPEND))); + LOG(2,("DAC1: FP_HTOTAL: %d\n", DACR(FP_HTOTAL))); + LOG(2,("DAC1: FP_HCRTC: %d\n", DACR(FP_HCRTC))); + LOG(2,("DAC1: FP_HSYNC_S: %d\n", DACR(FP_HSYNC_S))); + LOG(2,("DAC1: FP_HSYNC_E: %d\n", DACR(FP_HSYNC_E))); + LOG(2,("DAC1: FP_HVALID_S: %d\n", DACR(FP_HVALID_S))); + LOG(2,("DAC1: FP_HVALID_E: %d\n", DACR(FP_HVALID_E))); + + LOG(2,("DAC1: FP_VDISPEND: %d\n", DACR(FP_VDISPEND))); + LOG(2,("DAC1: FP_VTOTAL: %d\n", DACR(FP_VTOTAL))); + LOG(2,("DAC1: FP_VCRTC: %d\n", DACR(FP_VCRTC))); + LOG(2,("DAC1: FP_VSYNC_S: %d\n", DACR(FP_VSYNC_S))); + LOG(2,("DAC1: FP_VSYNC_E: %d\n", DACR(FP_VSYNC_E))); + LOG(2,("DAC1: FP_VVALID_S: %d\n", DACR(FP_VVALID_S))); + LOG(2,("DAC1: FP_VVALID_E: %d\n", DACR(FP_VVALID_E))); + + LOG(2,("DAC1: FP_CHKSUM: $%08x = (dec) %d\n", DACR(FP_CHKSUM),DACR(FP_CHKSUM))); + LOG(2,("DAC1: FP_TST_CTRL: $%08x\n", DACR(FP_TST_CTRL))); + LOG(2,("DAC1: FP_TG_CTRL: $%08x\n", DACR(FP_TG_CTRL))); + LOG(2,("DAC1: FP_DEBUG0: $%08x\n", DACR(FP_DEBUG0))); + LOG(2,("DAC1: FP_DEBUG1: $%08x\n", DACR(FP_DEBUG1))); + LOG(2,("DAC1: FP_DEBUG2: $%08x\n", DACR(FP_DEBUG2))); + LOG(2,("DAC1: FP_DEBUG3: $%08x\n", DACR(FP_DEBUG3))); + + LOG(2,("DAC1: FUNCSEL: $%08x\n", NV_REG32(NV32_FUNCSEL))); + LOG(2,("DAC1: PANEL_PWR: $%08x\n", NV_REG32(NV32_PANEL_PWR))); + + if(si->ps.secondary_head) + { + LOG(2,("DAC2: FP_HDISPEND: %d\n", DAC2R(FP_HDISPEND))); + LOG(2,("DAC2: FP_HTOTAL: %d\n", DAC2R(FP_HTOTAL))); + LOG(2,("DAC2: FP_HCRTC: %d\n", DAC2R(FP_HCRTC))); + LOG(2,("DAC2: FP_HSYNC_S: %d\n", DAC2R(FP_HSYNC_S))); + LOG(2,("DAC2: FP_HSYNC_E: %d\n", DAC2R(FP_HSYNC_E))); + LOG(2,("DAC2: FP_HVALID_S:%d\n", DAC2R(FP_HVALID_S))); + LOG(2,("DAC2: FP_HVALID_E: %d\n", DAC2R(FP_HVALID_E))); + + LOG(2,("DAC2: FP_VDISPEND: %d\n", DAC2R(FP_VDISPEND))); + LOG(2,("DAC2: FP_VTOTAL: %d\n", DAC2R(FP_VTOTAL))); + LOG(2,("DAC2: FP_VCRTC: %d\n", DAC2R(FP_VCRTC))); + LOG(2,("DAC2: FP_VSYNC_S: %d\n", DAC2R(FP_VSYNC_S))); + LOG(2,("DAC2: FP_VSYNC_E: %d\n", DAC2R(FP_VSYNC_E))); + LOG(2,("DAC2: FP_VVALID_S: %d\n", DAC2R(FP_VVALID_S))); + LOG(2,("DAC2: FP_VVALID_E: %d\n", DAC2R(FP_VVALID_E))); + + LOG(2,("DAC2: FP_CHKSUM: $%08x = (dec) %d\n", DAC2R(FP_CHKSUM),DAC2R(FP_CHKSUM))); + LOG(2,("DAC2: FP_TST_CTRL: $%08x\n", DAC2R(FP_TST_CTRL))); + LOG(2,("DAC2: FP_TG_CTRL: $%08x\n", DAC2R(FP_TG_CTRL))); + LOG(2,("DAC2: FP_DEBUG0: $%08x\n", DAC2R(FP_DEBUG0))); + LOG(2,("DAC2: FP_DEBUG1: $%08x\n", DAC2R(FP_DEBUG1))); + LOG(2,("DAC2: FP_DEBUG2: $%08x\n", DAC2R(FP_DEBUG2))); + LOG(2,("DAC2: FP_DEBUG3: $%08x\n", DAC2R(FP_DEBUG3))); + + LOG(2,("DAC2: FUNCSEL: $%08x\n", NV_REG32(NV32_2FUNCSEL))); + LOG(2,("DAC2: PANEL_PWR: $%08x\n", NV_REG32(NV32_2PANEL_PWR))); + } + + /* determine flatpanel type(s) */ + /* note: + * on NV11 accessing registerset(s) hangs card. */ + //fixme: how about NV11's with panels? + //fixme?: linux checks on (only and) all dualhead cards, and only on DAC1... +//fixme: testing... + if (/*si->ps.tmds1_active && */(si->ps.card_type != NV11)) + { + /* Read a indexed register to see if it indicates LVDS or TMDS panel presence. + * b0-7 = adress, b16 = 1 = write_enable */ + DACW(FP_TMDS_CTRL, ((1 << 16) | 0x04)); + /* (b0-7 = data) */ + if (DACR(FP_TMDS_DATA) & 0x01) + LOG(2,("INFO: Flatpanel on head 1 is LVDS type\n")); + else + LOG(2,("INFO: Flatpanel on head 1 is TMDS type\n")); + } +//fixme: testing... +// if (si->ps.tmds2_active && (si->ps.card_type != NV11)) + if (si->ps.secondary_head && (si->ps.card_type != NV11)) + { + /* Read a indexed register to see if it indicates LVDS or TMDS panel presence. + * b0-7 = adress, b16 = 1 = write_enable */ + DAC2W(FP_TMDS_CTRL, ((1 << 16) | 0x04)); + /* (b0-7 = data) */ + if (DAC2R(FP_TMDS_DATA) & 0x01) + LOG(2,("INFO: Flatpanel on head 2 is LVDS type\n")); + else + LOG(2,("INFO: Flatpanel on head 2 is TMDS type\n")); + } + + LOG(2,("INFO: End flatpanel registers dump.\n")); +} + +static void setup_output_matrix() +{ + /* setup defaults: */ + /* no monitors (output devices) detected */ + si->ps.monitors = 0x00; + /* head 1 will be the primary head */ + si->ps.crtc2_prim = false; + + /* setup output devices and heads */ + if (si->ps.secondary_head) + { + if (si->ps.card_type != NV11) + { + /* setup defaults: */ + /* connect analog outputs straight through */ + nv_general_output_select(false); + + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + if (si->ps.tmds2_active) si->ps.monitors |= 0x10; + /* detect analog monitors (confirmed working OK on NV18, NV28 and NV34): */ + /* sense analog monitor on primary connector */ + if (nv_dac_crt_connected()) si->ps.monitors |= 0x02; + /* sense analog monitor on secondary connector */ + if (nv_dac2_crt_connected()) si->ps.monitors |= 0x20; + + /* setup correct output and head use */ + //fixme? add TVout (only, so no CRT(s) connected) support... + switch (si->ps.monitors) + { + case 0x00: /* no monitor found at all */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x01: /* digital panel on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x02: /* analog panel or CRT on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x03: /* both types on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel AND an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: correcting...\n")); + /* cross connect analog outputs so analog panel or CRT gets head 2 */ + nv_general_output_select(true); + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x10: /* nothing on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x20: /* nothing on head 1, analog panel or CRT on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x30: /* nothing on head 1, both types on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel AND an analog panel or CRT:\n")); + LOG(2,("INFO: correcting...\n")); + /* cross connect analog outputs so analog panel or CRT gets head 1 */ + nv_general_output_select(true); + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x11: /* digital panels on both heads */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x12: /* analog panel or CRT on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x21: /* digital panel on head 1, analog panel or CRT on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x22: /* analog panel(s) or CRT(s) on both heads */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x32: /* more than two monitors connected to just two outputs: illegal! */ + //general fixme: + //NV40 architecture contains (an) additional switch(es) to + //connect a CRTC/DAC combination to a connector. We can't work as + //usual (yet) because this interferes via BIOS card pre-programming. + // + //Also: it looks as if each pixelclock PLL can select different CRTC's + //as well now via a new register: one PLL can be driving both CRTC's + //and there's nothing we can do about that (yet). (DVI/dualhead trouble) + if (si->ps.card_arch < NV40A) + { + LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); + /* head 2 takes precedence because it has a digital panel while + * head 1 has not. */ + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + } + default: /* more than two monitors connected to just two outputs: illegal! */ + LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + } + } + else /* dualhead NV11 cards */ + { + /* confirmed no analog output switch-options for NV11 */ + LOG(2,("INFO: NV11 outputs are hardwired to be straight-through\n")); + + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + if (si->ps.tmds2_active) si->ps.monitors |= 0x10; + /* detect analog monitor (confirmed working OK on NV11): */ + /* sense analog monitor on primary connector */ + if (nv_dac_crt_connected()) si->ps.monitors |= 0x02; + /* (sense analog monitor on secondary connector is impossible on NV11) */ + + /* setup correct output and head use */ + //fixme? add TVout (only, so no CRT(s) connected) support... + switch (si->ps.monitors) + { + case 0x00: /* no monitor found at all */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x01: /* digital panel on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x02: /* analog panel or CRT on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x03: /* both types on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel AND an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: correction not possible...\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x10: /* nothing on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x11: /* digital panels on both heads */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x12: /* analog panel or CRT on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + default: /* more than two monitors connected to just two outputs: illegal! */ + LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + } + } + } + else /* singlehead cards */ + { + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + /* detect analog monitor (confirmed working OK on all cards): */ + /* sense analog monitor on primary connector */ + if (nv_dac_crt_connected()) si->ps.monitors |= 0x02; + + //fixme? add TVout (only, so no CRT connected) support... + } +} + +void get_panel_modes(display_mode *p1, display_mode *p2, bool *pan1, bool *pan2) +{ + if (si->ps.tmds1_active) + { + /* timing ('modeline') */ + p1->timing = si->ps.p1_timing; + /* setup the rest */ + p1->space = B_CMAP8; + p1->virtual_width = p1->timing.h_display; + p1->virtual_height = p1->timing.v_display; + p1->h_display_start = 0; + p1->v_display_start = 0; + p1->flags = 0; + *pan1 = true; + } + else + *pan1 = false; + + if (si->ps.tmds2_active) + { + /* timing ('modeline') */ + p2->timing = si->ps.p2_timing; + /* setup the rest */ + p2->space = B_CMAP8; + p2->virtual_width = p2->timing.h_display; + p2->virtual_height = p2->timing.v_display; + p2->h_display_start = 0; + p2->v_display_start = 0; + p2->flags = 0; + *pan2 = true; + } + else + *pan2 = false; +} + +static void pinsnv4_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 256; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 256; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 250; + si->ps.max_dac1_clock_8 = 250; + si->ps.max_dac1_clock_16 = 250; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 220; + si->ps.max_dac1_clock_32 = 180; + si->ps.max_dac1_clock_32dh = 180; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 90; + si->ps.std_memory_clock = 110; +} + +static void pinsnv5_nv5m64_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 300; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 300; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 300; + si->ps.max_dac1_clock_8 = 300; + si->ps.max_dac1_clock_16 = 300; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 270; + si->ps.max_dac1_clock_32 = 230; + si->ps.max_dac1_clock_32dh = 230; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 125; + si->ps.std_memory_clock = 150; +} + +static void pinsnv6_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 300; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 300; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 300; + si->ps.max_dac1_clock_8 = 300; + si->ps.max_dac1_clock_16 = 300; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 270; + si->ps.max_dac1_clock_32 = 230; + si->ps.max_dac1_clock_32dh = 230; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 100; + si->ps.std_memory_clock = 125; +} + +static void pinsnv10_arch_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + if (si->ps.card_type < NV17) + { + /* if a GeForce2 has analog VGA dualhead capability, + * it uses an external secondary DAC probably with limited capability. */ + /* (called twinview technology) */ + si->ps.max_dac2_clock = 200; + si->ps.max_dac2_clock_8 = 200; + si->ps.max_dac2_clock_16 = 200; + si->ps.max_dac2_clock_24 = 200; + si->ps.max_dac2_clock_32 = 200; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 180; + } + else + { + /* GeForce4 cards have dual integrated DACs with identical capaability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + } + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 120; + si->ps.std_memory_clock = 150; +} + +static void pinsnv20_arch_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + /* GeForce4 cards have dual integrated DACs with identical capaability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 175; + si->ps.std_memory_clock = 200; +} + +static void pinsnv30_arch_fake(void) +{ + /* determine PLL type */ + if ((si->ps.card_type == NV31) || + (si->ps.card_type == NV36) || + (si->ps.card_type >= NV40)) + { + /* we have a extended PLL */ + si->ps.ext_pll = true; + } + else + { + /* we have a standard PLL */ + si->ps.ext_pll = false; + } + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + if (si->ps.ext_pll) + { + si->ps.max_pixel_vco = 600; + si->ps.min_pixel_vco = 220; + si->ps.max_video_vco = 600; + si->ps.min_video_vco = 220; + } + else + { + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + } + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + /* GeForceFX cards have dual integrated DACs with identical capability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + //fixme: primary & secondary_dvi should be overrule-able via nv.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 190; + si->ps.std_memory_clock = 190; +} + +static void getRAMsize_arch_nv4(void) +{ + uint32 strapinfo = NV_REG32(NV32_NV4STRAPINFO); + + if (strapinfo & 0x00000100) + { + /* Unified memory architecture used */ + si->ps.memory_size = 1024 * 1024 * + ((((strapinfo & 0x0000f000) >> 12) * 2) + 2); + + LOG(8,("INFO: NV4 architecture chip with UMA detected\n")); + } + else + { + /* private memory architecture used */ + switch (strapinfo & 0x00000003) + { + case 0: + si->ps.memory_size = 32 * 1024 * 1024; + break; + case 1: + si->ps.memory_size = 4 * 1024 * 1024; + break; + case 2: + si->ps.memory_size = 8 * 1024 * 1024; + break; + case 3: + si->ps.memory_size = 16 * 1024 * 1024; + break; + } + } +} + +static void getstrap_arch_nv4(void) +{ + uint32 strapinfo = NV_REG32(NV32_NVSTRAPINFO2); + + /* determine PLL reference crystal frequency */ + if (strapinfo & 0x00000040) + si->ps.f_ref = 14.31818; + else + si->ps.f_ref = 13.50000; + + /* these cards are always singlehead */ + si->ps.secondary_head = false; +} + +static void getRAMsize_arch_nv10_20_30_40(void) +{ + uint32 dev_manID = CFGR(DEVID); + uint32 strapinfo = NV_REG32(NV32_NV10STRAPINFO); + + switch (dev_manID) + { + case 0x01a010de: /* Nvidia GeForce2 Integrated GPU */ + case 0x01f010de: /* Nvidia GeForce4 MX Integrated GPU */ + /* the kerneldriver already determined the amount of RAM these cards have at + * their disposal (UMA, values read from PCI config space in other device) */ + LOG(8,("INFO: nVidia GPU with UMA detected\n")); + break; + default: + LOG(8,("INFO: (Memory detection) Strapinfo value is: $%08x\n", strapinfo)); + + switch ((strapinfo & 0x3ff00000) >> 20) + { + case 2: + si->ps.memory_size = 2 * 1024 * 1024; + break; + case 4: + si->ps.memory_size = 4 * 1024 * 1024; + break; + case 8: + si->ps.memory_size = 8 * 1024 * 1024; + break; + case 16: + si->ps.memory_size = 16 * 1024 * 1024; + break; + case 32: + si->ps.memory_size = 32 * 1024 * 1024; + break; + case 64: + si->ps.memory_size = 64 * 1024 * 1024; + break; + case 128: + si->ps.memory_size = 128 * 1024 * 1024; + break; + case 256: + si->ps.memory_size = 256 * 1024 * 1024; + break; + case 512: + si->ps.memory_size = 512 * 1024 * 1024; + break; + default: + si->ps.memory_size = 16 * 1024 * 1024; + + LOG(8,("INFO: NV10/20/30 architecture chip with unknown RAM amount detected;\n")); + LOG(8,("INFO: Setting 16Mb\n")); + break; + } + } +} + +static void getstrap_arch_nv10_20_30_40(void) +{ + uint32 dev_manID = CFGR(DEVID); + uint32 strapinfo = NV_REG32(NV32_NVSTRAPINFO2); + + /* determine if we have a dualhead card */ + si->ps.secondary_head = false; + switch (si->ps.card_type) + { + case NV04: + case NV05: + case NV05M64: + case NV06: + case NV10: + case NV15: + case NV20: + break; + default: + if ((dev_manID & 0xfff0ffff) == 0x01a010de) + { + /* this is a singlehead NV11! */ + } + else + { + si->ps.secondary_head = true; + } + } + + /* determine PLL reference crystal frequency: three types are used... */ + if (strapinfo & 0x00000040) + si->ps.f_ref = 14.31818; + else + si->ps.f_ref = 13.50000; + + if ((si->ps.secondary_head) && (si->ps.card_type != NV11)) + { + if (strapinfo & 0x00400000) si->ps.f_ref = 27.00000; + } +} + +void dump_pins(void) +{ + char *msg = ""; + + LOG(2,("INFO: pinsdump follows:\n")); + LOG(2,("PLL type: ")); + if (si->ps.ext_pll) LOG(2,("extended\n")); else LOG(2,("standard\n")); + LOG(2,("f_ref: %fMhz\n", si->ps.f_ref)); + LOG(2,("max_system_vco: %dMhz\n", si->ps.max_system_vco)); + LOG(2,("min_system_vco: %dMhz\n", si->ps.min_system_vco)); + LOG(2,("max_pixel_vco: %dMhz\n", si->ps.max_pixel_vco)); + LOG(2,("min_pixel_vco: %dMhz\n", si->ps.min_pixel_vco)); + LOG(2,("max_video_vco: %dMhz\n", si->ps.max_video_vco)); + LOG(2,("min_video_vco: %dMhz\n", si->ps.min_video_vco)); + LOG(2,("std_engine_clock: %dMhz\n", si->ps.std_engine_clock)); + LOG(2,("std_memory_clock: %dMhz\n", si->ps.std_memory_clock)); + LOG(2,("max_dac1_clock: %dMhz\n", si->ps.max_dac1_clock)); + LOG(2,("max_dac1_clock_8: %dMhz\n", si->ps.max_dac1_clock_8)); + LOG(2,("max_dac1_clock_16: %dMhz\n", si->ps.max_dac1_clock_16)); + LOG(2,("max_dac1_clock_24: %dMhz\n", si->ps.max_dac1_clock_24)); + LOG(2,("max_dac1_clock_32: %dMhz\n", si->ps.max_dac1_clock_32)); + LOG(2,("max_dac1_clock_32dh: %dMhz\n", si->ps.max_dac1_clock_32dh)); + LOG(2,("max_dac2_clock: %dMhz\n", si->ps.max_dac2_clock)); + LOG(2,("max_dac2_clock_8: %dMhz\n", si->ps.max_dac2_clock_8)); + LOG(2,("max_dac2_clock_16: %dMhz\n", si->ps.max_dac2_clock_16)); + LOG(2,("max_dac2_clock_24: %dMhz\n", si->ps.max_dac2_clock_24)); + LOG(2,("max_dac2_clock_32: %dMhz\n", si->ps.max_dac2_clock_32)); + LOG(2,("max_dac2_clock_32dh: %dMhz\n", si->ps.max_dac2_clock_32dh)); + LOG(2,("secondary_head: ")); + if (si->ps.secondary_head) LOG(2,("present\n")); else LOG(2,("absent\n")); + LOG(2,("tvout: ")); + if (si->ps.tvout) LOG(2,("present\n")); else LOG(2,("absent\n")); + /* setup TVout logmessage text */ + switch (si->ps.tv_encoder.type) + { + case NONE: + msg = "No"; + break; + case CH7003: + msg = "Chrontel CH7003"; + break; + case CH7004: + msg = "Chrontel CH7004"; + break; + case CH7005: + msg = "Chrontel CH7005"; + break; + case CH7006: + msg = "Chrontel CH7006"; + break; + case CH7007: + msg = "Chrontel CH7007"; + break; + case CH7008: + msg = "Chrontel CH7008"; + break; + case SAA7102: + msg = "Philips SAA7102"; + break; + case SAA7103: + msg = "Philips SAA7103"; + break; + case SAA7104: + msg = "Philips SAA7104"; + break; + case SAA7105: + msg = "Philips SAA7105"; + break; + case BT868: + msg = "Brooktree/Conexant BT868"; + break; + case BT869: + msg = "Brooktree/Conexant BT869"; + break; + case CX25870: + msg = "Conexant CX25870"; + break; + case CX25871: + msg = "Conexant CX25871"; + break; + case NVIDIA: + msg = "Nvidia internal"; + break; + default: + msg = "Unknown"; + break; + } + LOG(2, ("%s TV encoder detected; silicon revision is $%02x\n", + msg, si->ps.tv_encoder.version)); +// LOG(2,("primary_dvi: ")); +// if (si->ps.primary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n")); +// LOG(2,("secondary_dvi: ")); +// if (si->ps.secondary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n")); + LOG(2,("card memory_size: %3.3fMb\n", (si->ps.memory_size / (1024.0 * 1024.0)))); + LOG(2,("laptop: ")); + if (si->ps.laptop) LOG(2,("yes\n")); else LOG(2,("no\n")); + if (si->ps.tmds1_active) + { + LOG(2,("found DFP (digital flatpanel) on CRTC1; CRTC1 is ")); + if (si->ps.slaved_tmds1) LOG(2,("slaved\n")); else LOG(2,("master\n")); + LOG(2,("panel width: %d, height: %d, aspect ratio: %1.2f\n", + si->ps.p1_timing.h_display, si->ps.p1_timing.v_display, si->ps.panel1_aspect)); + } + if (si->ps.tmds2_active) + { + LOG(2,("found DFP (digital flatpanel) on CRTC2; CRTC2 is ")); + if (si->ps.slaved_tmds2) LOG(2,("slaved\n")); else LOG(2,("master\n")); + LOG(2,("panel width: %d, height: %d, aspect ratio: %1.2f\n", + si->ps.p2_timing.h_display, si->ps.p2_timing.v_display, si->ps.panel2_aspect)); + } + LOG(2,("monitor (output devices) setup matrix: $%02x\n", si->ps.monitors)); + LOG(2,("INFO: end pinsdump.\n")); +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_proto.h b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_proto.h new file mode 100644 index 0000000000..204e319dd9 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_proto.h @@ -0,0 +1,140 @@ +/* general card functions */ +status_t nv_general_powerup(void); +status_t nv_set_cas_latency(void); +void setup_virtualized_heads(bool); +void set_crtc_owner(bool); +status_t nv_general_output_select(bool); +status_t nv_general_head_select(bool); +status_t nv_general_validate_pic_size (display_mode *target, uint32 *bytes_per_row, bool *acc_mode); + +/* apsed: logging macros */ +#define MSG(args) do { /* if needed or si->settings with si NULL */ \ + nv_log args; \ +} while (0) +#define LOG(level_bit, args) do { \ + uint32 mod = (si->settings.logmask & 0xfffffff0) & MODULE_BIT; \ + uint32 lev = (si->settings.logmask & ~0xfffffff0) & level_bit; \ + if (mod && lev) nv_log args; \ +} while (0) + +/* support functions */ +void delay(bigtime_t i); +void nv_log(char *format, ...); + +/* i2c functions */ +status_t i2c_sec_tv_adapter(void); +char i2c_flag_error (char ErrNo); +void i2c_bstart (uint8 BusNR); +void i2c_bstop (uint8 BusNR); +uint8 i2c_readbyte(uint8 BusNR, bool Ack); +bool i2c_writebyte (uint8 BusNR, uint8 byte); +void i2c_readbuffer (uint8 BusNR, uint8* buf, uint8 size); +void i2c_writebuffer (uint8 BusNR, uint8* buf, uint8 size); +status_t i2c_init(void); + +/* card info functions */ +status_t parse_pins(void); +void set_pll(uint32 reg, uint32 clk); +void get_panel_modes(display_mode *p1, display_mode *p2, bool *pan1, bool *pan2); +void fake_panel_start(void); +void set_specs(void); +void dump_pins(void); + +/* DAC functions */ +bool nv_dac_crt_connected(void); +status_t nv_dac_mode(int,float); +status_t nv_dac_palette(uint8*,uint8*,uint8*); +status_t nv_dac_pix_pll_find(display_mode target,float * result,uint8 *,uint8 *,uint8 *, uint8); +status_t nv_dac_set_pix_pll(display_mode target); +status_t nv_dac_sys_pll_find(float, float*, uint8*, uint8*, uint8*, uint8); + +/* DAC2 functions */ +bool nv_dac2_crt_connected(void); +status_t nv_dac2_mode(int,float); +status_t nv_dac2_palette(uint8*,uint8*,uint8*); +status_t nv_dac2_pix_pll_find(display_mode target,float * result,uint8 *,uint8 *,uint8 *, uint8); +status_t nv_dac2_set_pix_pll(display_mode target); + +/* Brooktree TV functions */ +bool BT_probe(void); +uint8 BT_dpms(bool display); +uint8 BT_check_tvmode(display_mode target); +status_t BT_stop_tvout(void); +status_t BT_setmode(display_mode target); + +/* CRTC1 functions */ +status_t nv_crtc_interrupt_enable(bool); +status_t nv_crtc_update_fifo(void); +status_t nv_crtc_validate_timing( + uint16 *hd_e,uint16 *hs_s,uint16 *hs_e,uint16 *ht, + uint16 *vd_e,uint16 *vs_s,uint16 *vs_e,uint16 *vt); +status_t nv_crtc_set_timing(display_mode target); +status_t nv_crtc_depth(int mode); +status_t nv_crtc_set_display_start(uint32 startadd,uint8 bpp); +status_t nv_crtc_set_display_pitch(void); +status_t nv_crtc_dpms(bool, bool, bool, bool); +status_t nv_crtc_mem_priority(uint8); +status_t nv_crtc_cursor_init(void); +status_t nv_crtc_cursor_define(uint8*,uint8*); +status_t nv_crtc_cursor_position(uint16 x ,uint16 y); +status_t nv_crtc_cursor_show(void); +status_t nv_crtc_cursor_hide(void); +status_t nv_crtc_stop_tvout(void); +status_t nv_crtc_start_tvout(void); + +/* CRTC2 functions */ +status_t nv_crtc2_interrupt_enable(bool); +status_t nv_crtc2_update_fifo(void); +status_t nv_crtc2_validate_timing( + uint16 *hd_e,uint16 *hs_s,uint16 *hs_e,uint16 *ht, + uint16 *vd_e,uint16 *vs_s,uint16 *vs_e,uint16 *vt); +status_t nv_crtc2_set_timing(display_mode target); +status_t nv_crtc2_depth(int mode); +status_t nv_crtc2_set_display_start(uint32 startadd,uint8 bpp); +status_t nv_crtc2_set_display_pitch(void); +status_t nv_crtc2_dpms(bool, bool, bool, bool); +status_t nv_crtc2_mem_priority(uint8); +status_t nv_crtc2_cursor_init(void); +status_t nv_crtc2_cursor_define(uint8*,uint8*); +status_t nv_crtc2_cursor_position(uint16 x ,uint16 y); +status_t nv_crtc2_cursor_show(void); +status_t nv_crtc2_cursor_hide(void); +status_t nv_crtc2_stop_tvout(void); +status_t nv_crtc2_start_tvout(void); + +/* acceleration functions */ +status_t check_acc_capability(uint32 feature); +status_t nv_acc_init(void); +void nv_acc_assert_fifo(void); +status_t nv_acc_setup_blit(void); +status_t nv_acc_blit(uint16,uint16,uint16, uint16,uint16,uint16 ); +status_t nv_acc_setup_rectangle(uint32 color); +status_t nv_acc_rectangle(uint32 xs,uint32 xe,uint32 ys,uint32 yl); +status_t nv_acc_setup_rect_invert(void); +status_t nv_acc_rectangle_invert(uint32 xs,uint32 xe,uint32 ys,uint32 yl); +status_t nv_acc_transparent_blit(uint16,uint16,uint16, uint16,uint16,uint16, uint32); +status_t nv_acc_video_blit(uint16 xs,uint16 ys,uint16 ws, uint16 hs, + uint16 xd,uint16 yd,uint16 wd,uint16 hd); +status_t nv_acc_wait_idle(void); +/* DMA versions */ +status_t nv_acc_wait_idle_dma(void); +status_t nv_acc_init_dma(void); +void nv_acc_assert_fifo_dma(void); +void SCREEN_TO_SCREEN_BLIT_DMA(engine_token *et, blit_params *list, uint32 count); +void SCREEN_TO_SCREEN_TRANSPARENT_BLIT_DMA(engine_token *et, uint32 transparent_colour, blit_params *list, uint32 count); +void SCREEN_TO_SCREEN_SCALED_FILTERED_BLIT_DMA(engine_token *et, scaled_blit_params *list, uint32 count); +void FILL_RECTANGLE_DMA(engine_token *et, uint32 color, fill_rect_params *list, uint32 count); +void INVERT_RECTANGLE_DMA(engine_token *et, fill_rect_params *list, uint32 count); +void FILL_SPAN_DMA(engine_token *et, uint32 color, uint16 *list, uint32 count); + +/* backend scaler functions */ +status_t check_overlay_capability(uint32 feature); +void nv_bes_move_overlay(void); +status_t nv_bes_to_crtc(bool crtc); +status_t nv_bes_init(void); +status_t nv_configure_bes + (const overlay_buffer *ob, const overlay_window *ow,const overlay_view *ov, int offset); +status_t nv_release_bes(void); + +/* driver structures and enums */ +enum{BPP8 = 0, BPP15 = 1, BPP16 = 2, BPP24 = 3, BPP32 = 4}; diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_std.h b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_std.h new file mode 100644 index 0000000000..cc21aaecb0 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_std.h @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include "DriverInterface.h" +#include "nv_globals.h" +//apsed #include "nv_extern.h" +#include "nv_proto.h" +#include "nv_macros.h" +#include "nv_acc.h" diff --git a/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_support.c b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_support.c new file mode 100644 index 0000000000..c7dc8a1378 --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/engine/nv_support.c @@ -0,0 +1,38 @@ +/* Some commmon support functions */ +/* Mark Watson 2/2000; + * Rudolf Cornelissen 1/2004-11/2005 */ + +#define MODULE_BIT 0x00000800 + +#include +#include "nv_std.h" + +/*delays in multiple of microseconds*/ +void delay(bigtime_t i) +{ + bigtime_t start=system_time(); + while(system_time()-startvendor_id, si->device_id, si->bus, si->device, si->function, + accelerantIsClone); + myhand=fopen(fname,"a+"); + + if (myhand == NULL) return; + + va_start(args,fmt); + vsprintf (buffer, fmt, args); + fprintf(myhand, "%s", buffer); + fclose(myhand); +} diff --git a/src/add-ons/accelerants/nvidia_gpgpu/valid_mode_list b/src/add-ons/accelerants/nvidia_gpgpu/valid_mode_list new file mode 100644 index 0000000000..4c0f04023f --- /dev/null +++ b/src/add-ons/accelerants/nvidia_gpgpu/valid_mode_list @@ -0,0 +1,34 @@ +/*This file can be used to define custom timing for your monitor + * The format of each line is: + * { + * pixel clock frequency (kHz) + * width + * h-sync pulse start + * h-sync pulse end + * total pixels in line + * height + * v-sync pulse start + * v-sync pulse end + * total lines in frame + * sync polarity (0 is -ve,B_POSITIVE_HYSNC,B_POSITIVE_VSYNC) + * } + * + *To use this you must: + * Uncomment VALID MODE REQUIRED + * Fill in a number of modes that work with your display + * Change VALID MODES from three to the no. you defined + * run these commands: + * touch ProposeDisplayMode.c + * make install + */ + +//#define VALID_MODE_REQUIRED 1 + +#define VALID_MODES 3 + +/*note colour depth and mode flags are ignored*/ +static const display_timing valid_mode_list[] = { +{31500,640,648,744,840,480,481,500,500,0}, +{49500,800,808,888,1056,600,601,620,625,B_POSITIVE_HSYNC|B_POSITIVE_VSYNC}, +{78750,1024,1032,1128,1312,768,769,788,800,B_POSITIVE_HSYNC|B_POSITIVE_VSYNC} +};