From 063436816da4a726f46d4997764bd8fee00aedc0 Mon Sep 17 00:00:00 2001 From: RudolfC Date: Mon, 4 Jan 2016 22:12:55 +0000 Subject: [PATCH] nVidia driver: Added basic dualhead support for native Haiku ScreenPrefs app --- .../private/graphics/nvidia/DriverInterface.h | 18 +- .../accelerants/nvidia/InitAccelerant.c | 6 +- .../accelerants/nvidia/ProposeDisplayMode.c | 225 +++++++++++++++--- .../accelerants/nvidia/SetDisplayMode.c | 6 +- .../accelerants/nvidia/engine/nv_general.c | 2 +- .../drivers/graphics/nvidia/README.html | 10 +- .../drivers/graphics/nvidia/UPDATE.html | 5 +- 7 files changed, 226 insertions(+), 46 deletions(-) diff --git a/headers/private/graphics/nvidia/DriverInterface.h b/headers/private/graphics/nvidia/DriverInterface.h index a6360af3e4..4c94ab2e9f 100644 --- a/headers/private/graphics/nvidia/DriverInterface.h +++ b/headers/private/graphics/nvidia/DriverInterface.h @@ -5,7 +5,7 @@ Other authors: Mark Watson; Apsed; - Rudolf Cornelissen 10/2002-10/2009. + Rudolf Cornelissen 10/2002-12/2015. */ #ifndef DRIVERINTERFACE_H @@ -63,6 +63,20 @@ typedef struct { #define TV_VIDEO (1<<12) #define TV_PRIMARY (1<<13) +/* additional timing flags for GetMode/SetMode for Haiku ScreenPrefs panel */ +enum { + RADEON_MODE_MULTIMON_REQUEST = 1 << 25, + RADEON_MODE_MULTIMON_REPLY = 1 << 26 +}; + +/* operation codes tunneled via ProposeDisplayMode for Haiku ScreenPrefs panel */ +typedef enum { + ms_swap = 'sw', + ms_use_laptop_panel = 'up', + ms_tv_standard = 'tv' +} multi_mon_settings; + + #define SKD_MOVE_CURSOR 0x00000001 #define SKD_PROGRAM_CLUT 0x00000002 #define SKD_SET_START_ADDR 0x00000004 @@ -306,6 +320,8 @@ typedef struct { bool acc_mode; /* signals (non)accelerated mode */ bool interlaced_tv_mode;/* signals interlaced CRTC TV output mode */ bool crtc_switch_mode; /* signals dualhead switch mode if panels are used */ + bool haiku_prefs_used; /* signals use of Haiku ScreenPrefs app for special modes */ + bool Haiku_switch_head; /* signals Haiku ScreenPrefs panel want inverted mode later on */ /*frame buffer config - for BDirectScreen*/ frame_buffer_config fbc; /* bytes_per_row and start of frame buffer: head1 */ diff --git a/src/add-ons/accelerants/nvidia/InitAccelerant.c b/src/add-ons/accelerants/nvidia/InitAccelerant.c index d57f58777f..9c241b40c8 100644 --- a/src/add-ons/accelerants/nvidia/InitAccelerant.c +++ b/src/add-ons/accelerants/nvidia/InitAccelerant.c @@ -4,7 +4,7 @@ Other authors: Mark Watson, - Rudolf Cornelissen 10/2002-3/2009. + Rudolf Cornelissen 10/2002-12/2015. */ #define MODULE_BIT 0x00800000 @@ -214,6 +214,10 @@ status_t INIT_ACCELERANT(int the_fd) /* every 3D add-on will reset this bit-flag when it's done. */ si->engine.threeD.newmode = 0xffffffff; + /* we did not detect the Haiku ScreenPrefs app yet */ + si->haiku_prefs_used = false; + si->Haiku_switch_head = false; + /* a winner! */ result = B_OK; /* ensure that INIT_ACCELERANT won't be executed again (copies should be clones) */ diff --git a/src/add-ons/accelerants/nvidia/ProposeDisplayMode.c b/src/add-ons/accelerants/nvidia/ProposeDisplayMode.c index 680676d660..f08094c89b 100644 --- a/src/add-ons/accelerants/nvidia/ProposeDisplayMode.c +++ b/src/add-ons/accelerants/nvidia/ProposeDisplayMode.c @@ -4,7 +4,7 @@ Other authors for NV driver: Mark Watson, - Rudolf Cornelissen 9/2002-9/2009 + Rudolf Cornelissen 9/2002-12/2015 */ #define MODULE_BIT 0x00400000 @@ -96,6 +96,114 @@ static const display_mode mode_list[] = { }; +// transform official mode to internal, multi-screen mode enhanced mode +static void Haiku_DetectTranslateMultiMode(display_mode *mode) +{ + mode->flags &= ~DUALHEAD_BITS; + + if( mode->virtual_width == 2 * mode->timing.h_display ) { + LOG(4, ("Haiku: horizontal combine mode\n")); + if (si->Haiku_switch_head) + mode->flags |= DUALHEAD_SWITCH; + else + mode->flags |= DUALHEAD_ON; + } else if( mode->virtual_height == 2 * mode->timing.v_display ) { + LOG(4, ("Haiku: vertical combine mode not supported\n")); + } else { + /* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better + activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */ + /* note please: + - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox); + - also (on laptops) this will shorten battery life a bit of course.. */ + mode->flags |= DUALHEAD_CLONE; + } +} + + +// check and execute tunnel settings command +static status_t Haiku_CheckMultiMonTunnel(display_mode *mode, const display_mode *low, const display_mode *high, bool *isTunneled ) +{ + if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 && + (mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 ) + { + mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST; + mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY; + + // still process request, just in case someone set this flag + // combination by mistake + + *isTunneled = true; + return B_OK; + } + + // check magic params + if( mode->space != 0 || low->space != 0 || high->space != 0 + || low->virtual_width != 0xffff || low->virtual_height != 0xffff + || high->virtual_width != 0 || high->virtual_height != 0 + || mode->timing.pixel_clock != 0 + || low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' ) + { + *isTunneled = false; + return B_OK; + } + + *isTunneled = true; + + /* enable Haiku special handling */ + if (!si->haiku_prefs_used) + LOG(4, ("PROPOSEMODE: Haiku screenprefs tunnel detected.\n")); + si->haiku_prefs_used = true; + + /* note please: + Haiku ScreenPrefs does not issue a SetMode command after changing these settings (per se), but relies on + driver switching outputs directly. These settings are not dependant on workspace there, and are not part of + the mode in the Radeon driver. In the Matrox and nVidia drivers they are though. So we need SetMode + to be issued. (yes: sometimes I switch monitors when I switch workspace.. ;-) + Also the RADEON driver saves these settings to a file so it remembers these after reboots. We don't atm. + If the mode as proposed by the driver would be saved by the ScreenPrefs panel, we would 'remember' it over + reboots though (via app_server settings).. */ + + switch( mode->h_display_start ) { + case ms_swap: + switch( mode->v_display_start ) { + case 0: + if (si->Haiku_switch_head) + mode->timing.flags = 1; + else + mode->timing.flags = 0; + LOG(4, ("Haiku: tunnel access target=swap, command=get, value=%u\n", mode->timing.flags)); + return B_OK; + case 1: + si->Haiku_switch_head = mode->timing.flags != 0; + LOG(4, ("Haiku: tunnel access target=swap, command=set, value=%u\n", mode->timing.flags)); + /* Haiku's screenprefs panel expects us to directly set the mode.. (but it should do that itself) */ + SET_DISPLAY_MODE(&si->dm); + return B_OK; + } + break; + + case ms_use_laptop_panel: + LOG(4, ("Haiku: tunnel access target=usepanel, command=%s, value=%u\n", + (mode->v_display_start == 1) ? "set" : "get", mode->timing.flags)); + // we refuse this setting (makes no sense for us: laptop panel is treated as normal screen) + return B_ERROR; + break; + + case ms_tv_standard: + LOG(4, ("Haiku: tunnel access target=tvstandard, command=%s, value=%u\n", + (mode->v_display_start == 1) ? "set" : "get", mode->timing.flags)); + // let's forget about this for now.. + return B_ERROR; + break; + } + + LOG(4, ("Haiku: unhandled tunnel access target=$%x, command=%u, value=%u\n", + mode->h_display_start, mode->v_display_start, mode->timing.flags)); + + return B_ERROR; +} + + /*! Check mode is between low and high limits. Returns: @@ -128,6 +236,25 @@ PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const displa / ((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; + bool isTunneled = false; + // check whether we got a tunneled settings command + result = Haiku_CheckMultiMonTunnel(target, low, high, &isTunneled); + if (isTunneled) + return result; + + /* since we (might be) called by the Haiku ScreenPrefs panel, check for special modes and translate + them from (for us) non-native modes to native modes (as used in DualheadSetup by Mark Watson). + These 'native modes' survive system reboots by the way, at least when set using DualheadSetup. */ + + /* note please: apparantly Haiku Screenprefs does not save the modes as set by the driver, but as originally requested + by the ScreenPrefs panel. Modifications done by the driver are therefore not saved. + It would be nice if the screenprefs panel in Haiku would save the modified modeflags (after proposemode or setmode), + that would also make the driver remember switched heads (now it's a temporary setting). */ + + if (si->haiku_prefs_used) { + // check how many heads are needed by target mode + Haiku_DetectTranslateMultiMode(target); + } LOG(1, ("PROPOSEMODE: (ENTER) requested virtual_width %d, virtual_height %d\n", target->virtual_width, target->virtual_height)); @@ -447,19 +574,56 @@ GET_MODE_LIST(display_mode *dm) } +static void checkAndAddMode(const display_mode *src, display_mode *dst) +{ + uint32 j, pix_clk_range; + display_mode low, high; + color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8}; + + /* 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[si->mode_count] = *src; + /* poke the specific space */ + dst[si->mode_count].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[si->mode_count], &low, &high) == B_OK) { + /* count it, so move on to next mode */ + si->mode_count++; + } + } +} + + /*! 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; + uint32 i; const display_mode *src; - display_mode *dst, low, high; - color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8}; + display_mode *dst; + display_mode custom_mode; - /* 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); + /* figure out how big the list could be (4 colorspaces, 3 virtual types per mode), and adjust up to nearest multiple of B_PAGE_SIZE */ + max_size = (((MODE_COUNT * 4 * 3) * 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", @@ -473,35 +637,26 @@ create_mode_list(void) 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++; - } - } + // standard mode + /* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better + activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */ + /* note please: + - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox); + - also (on laptops) this will shorten battery life a bit of course.. */ + custom_mode = *src; + custom_mode.flags |= DUALHEAD_CLONE; + checkAndAddMode(&custom_mode, dst); + // double width mode for Haiku ScreenPrefs panel + /* note please: These modes should not be added. Instead the mode.flags should be used during setting screen as these will + automatically generate the needed other properties of the mode. Besides, virtual size is meant to be used for + pan&scan modes (viewports), i.e. for certain games. + The currently used method will fail programs that are meant to work pan@scan if the virtual width (or height) are + exactly twice the view area. */ + custom_mode = *src; + custom_mode.virtual_width *= 2; + custom_mode.flags |= DUALHEAD_ON; + checkAndAddMode(&custom_mode, dst); + // we don't support double height modes /* advance to next mode */ src++; } diff --git a/src/add-ons/accelerants/nvidia/SetDisplayMode.c b/src/add-ons/accelerants/nvidia/SetDisplayMode.c index 0f4e4fab86..330c6f89d8 100644 --- a/src/add-ons/accelerants/nvidia/SetDisplayMode.c +++ b/src/add-ons/accelerants/nvidia/SetDisplayMode.c @@ -6,7 +6,7 @@ Other authors: Mark Watson, Apsed, - Rudolf Cornelissen 11/2002-3/2009 + Rudolf Cornelissen 11/2002-12/2015 */ #define MODULE_BIT 0x00200000 @@ -41,8 +41,8 @@ status_t SET_DISPLAY_MODE(display_mode *mode_to_set) 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", + LOG(1, ("SETMODE: requested target pixelclock %ukHz\n", target.timing.pixel_clock)); + LOG(1, ("SETMODE: requested virtual_width %u, virtual_height %u\n", target.virtual_width, target.virtual_height)); /* See BOUNDS WARNING above... */ diff --git a/src/add-ons/accelerants/nvidia/engine/nv_general.c b/src/add-ons/accelerants/nvidia/engine/nv_general.c index 4dc24e673c..0b33922d51 100644 --- a/src/add-ons/accelerants/nvidia/engine/nv_general.c +++ b/src/add-ons/accelerants/nvidia/engine/nv_general.c @@ -92,7 +92,7 @@ status_t nv_general_powerup() { status_t status; - LOG(1,("POWERUP: Haiku nVidia Accelerant 1.08 running.\n")); + LOG(1,("POWERUP: Haiku nVidia Accelerant 1.09 running.\n")); /* log VBLANK INT usability status */ if (si->ps.int_assigned) diff --git a/src/add-ons/kernel/drivers/graphics/nvidia/README.html b/src/add-ons/kernel/drivers/graphics/nvidia/README.html index 2b2cf01cd3..44fe6666f3 100644 --- a/src/add-ons/kernel/drivers/graphics/nvidia/README.html +++ b/src/add-ons/kernel/drivers/graphics/nvidia/README.html @@ -39,13 +39,13 @@ Geforce 8xxx and later series cards (NV50, G80, also known as GPGPU, general pur
  • Full BWindowScreen support (used for hardware pageflipping, scrolling/panning and acceleration in applications/games);
  • DPMS support for both DVI and most laptop panels, and for analog connected screens (on both heads on dualhead cards), but not (yet?) for external DVI panels on laptops;
  • B_YCbCr422 hardware overlay support on both TNT and GeForce series cards, except for GeForce 6xxx and 7xxx series (GeForce 6800 works though). Overlay output 'follows head' in dualhead stretch/switch modes; -
  • Dualhead support on GeForce dualhead cards (use 'Dualhead Setup' from BeBits for now); +
  • Dualhead support on GeForce dualhead cards (use 'Dualhead Setup' or Haiku native ScreenPrefs panel (with limitations));
  • DVI and laptop panel support;
  • Widescreen mode support (all screens must be widescreen type;
  • Basic AGP mode support on AGP cards, using the new (seperate) Haiku AGP busmanager;
  • Basic ('legacy') PCIe support;
  • Coldstart support for analog connected screens on most cards except TNT1, GeForce 6xxx and 7xxx series; -
  • TVout support on cards with Brooktree BT868/BT869 and Conexant CX25870/CX25871 encoders (use 'Dualhead Setup' 0.04 from BeBits for now); +
  • TVout support on cards with Brooktree BT868/BT869 and Conexant CX25870/CX25871 encoders (use 'Dualhead Setup' 0.05 for now);
  • DDC/EDID support for monitor capability detection. Known limitations: @@ -250,10 +250,14 @@ The acceleration engine is enabled.
  • true:
    The acceleration engine is disabled. +Note please: +

    Rudolf Cornelissen.
    -

    (Page last updated on December 26, 2015)

    +

    (Page last updated on January 4, 2016)

    diff --git a/src/add-ons/kernel/drivers/graphics/nvidia/UPDATE.html b/src/add-ons/kernel/drivers/graphics/nvidia/UPDATE.html index 08f9ab2b89..ee2a08dfd9 100644 --- a/src/add-ons/kernel/drivers/graphics/nvidia/UPDATE.html +++ b/src/add-ons/kernel/drivers/graphics/nvidia/UPDATE.html @@ -4,7 +4,7 @@

    Changes done for each driverversion:

    -

    head (SVN 1.08, Rudolf)

    +

    head (Haiku repository 1.09, Rudolf)

    nv_driver 0.80 (Rudolf)