Logo Search packages:      
Sourcecode: s3switch version File versions  Download package

s3switch.c

// Simple utility to switch a Savage board between CRT/LCD devices.
// T. N. Roberts, 99-Aug-26.

// Linux x86 only.

#include <stdio.h>
#define extern
#include <asm/io.h>
#undef extern

#include "lrmi.h"

// Usage:
//  s3switch [-q] [crt|lcd|both]

// Define the Savage chip classes.  PCI id's stolen from xf86PciInfo.h

#define PCI_CHIP_SAVAGE3D     0x8A20
#define PCI_CHIP_SAVAGE3D_MV  0x8A21
#define PCI_CHIP_SAVAGE4      0x8A22
#define PCI_CHIP_SAVAGE2000   0x9102
#define PCI_CHIP_PROSAVAGE_PM 0x8A25
#define PCI_CHIP_PROSAVAGE_KM 0x8A26
#define PCI_CHIP_SAVAGE_MX_MV 0x8c10
#define PCI_CHIP_SAVAGE_MX    0x8c11
#define PCI_CHIP_SAVAGE_IX_MV 0x8c12
#define PCI_CHIP_SAVAGE_IX    0x8c13
#define PCI_CHIP_TWISTERP     0x8d01
#define PCI_CHIP_TWISTERK     0x8d02
#define PCI_CHIP_PROSAVAGE_DDR      0x8d03
#define PCI_CHIP_PROSAVAGE_DDRK     0x8d04
#define PCI_CHIP_SUPSAV_MX128 0x8c22
#define PCI_CHIP_SUPSAV_MX64  0x8c24
#define PCI_CHIP_SUPSAV_MX64C 0x8c26
#define PCI_CHIP_SUPSAV_IX128SDR    0x8c2a
#define PCI_CHIP_SUPSAV_IX128DDR    0x8c2b
#define PCI_CHIP_SUPSAV_IX64SDR     0x8c2c
#define PCI_CHIP_SUPSAV_IX64DDR     0x8c2d
#define PCI_CHIP_SUPSAV_IXCSDR      0x8c2e
#define PCI_CHIP_SUPSAV_IXCDDR      0x8c2f

enum {
  S3_SAVAGE3D,
  S3_SAVAGE4,
  S3_SAVAGEMXIX,
  S3_SAVAGE2000,
  S3_PROSAVAGE,
  S3_SUPERSAVAGE
} ChipClass;

// Define the device attachment bits.  This is CR6D on the non-mobile
// chips, and CR6B on the mobiles.

// Savage3D does not support LCD, and the Savage4 does not support TV.

#define CRT_ACTIVE      0x01
#define LCD_ACTIVE      0x02
#define TV_ACTIVE       0x04
#define CRT_ATTACHED    0x10
#define LCD_ATTACHED    0x20
#define TV_ATTACHED     0x40
#define DUO_ON          0x80

static char * devices[] = {
    " CRT", " LCD", " TV"
};

// Define the TV format bits in CR6B (non-mobile) or CRC0 (mobile).

#define TV_FORMAT_MASK  0x0c
#define TV_FORMAT_NTSCJ 0x00
#define TV_FORMAT_NTSC  0x04
#define TV_FORMAT_PAL   0x08

// Global state:

unsigned int gPCIid = 0;
unsigned char jTvFormat = 0;
unsigned char jDevices = 0;
unsigned char cr79 = 0;

void
usage()
{
    puts( "Usage: s3switch [-q] [crt|lcd|both|tv] [ntsc|ntscj|pal]" );
    puts( "  -q requests quiet operation." );
    puts( "  crt, lcd and tv activates output to those devices.  Several devices may be" );
    puts( "    specified.  Only devices which are actually attached may be activated." );
    puts( "  both is a shortcut for 'crt lcd'." );
    puts( "  ntscj, ntsc and pal specify the video format for TV output." );
    puts( "    This is supported on Savage3D only.");
    puts( "  With no parameters, displays all devices currently attached and active.");
}

void
IOAccess( int enable )
{
    /* Allow or disallow access to I/O ports. */

    ioperm( 0x40, 4, enable );
    ioperm( 0x61, 1, enable );
    ioperm( 0x80, 1, enable );
    ioperm( 0x3b0, 0x30, enable );
}


void
fetch_bios_data()
{
    // Figure out what kind of Savage it is.

    outb( 0x2d, 0x3d4 );
    gPCIid = inb( 0x3d5 ) << 8;
    outb( 0x2e, 0x3d4 );
    gPCIid |= inb( 0x3d5 );

    switch( gPCIid ) {
      case PCI_CHIP_SAVAGE3D:
      case PCI_CHIP_SAVAGE3D_MV:
          ChipClass = S3_SAVAGE3D;
          break;
      case PCI_CHIP_SAVAGE4:
          ChipClass = S3_SAVAGE4;
          break;
      case PCI_CHIP_SAVAGE2000:
          ChipClass = S3_SAVAGE2000;
          break;
      case PCI_CHIP_PROSAVAGE_PM:
      case PCI_CHIP_PROSAVAGE_KM:
      case PCI_CHIP_TWISTERP:
      case PCI_CHIP_TWISTERK:
      case PCI_CHIP_PROSAVAGE_DDR:
      case PCI_CHIP_PROSAVAGE_DDRK:
          ChipClass = S3_PROSAVAGE;
          break;
      case PCI_CHIP_SAVAGE_MX_MV:
      case PCI_CHIP_SAVAGE_MX:
      case PCI_CHIP_SAVAGE_IX_MV:
      case PCI_CHIP_SAVAGE_IX:
          ChipClass = S3_SAVAGEMXIX;
          break;
      case PCI_CHIP_SUPSAV_MX128:
      case PCI_CHIP_SUPSAV_MX64:
      case PCI_CHIP_SUPSAV_MX64C:
      case PCI_CHIP_SUPSAV_IX128SDR:
      case PCI_CHIP_SUPSAV_IX128DDR:
      case PCI_CHIP_SUPSAV_IX64SDR:
      case PCI_CHIP_SUPSAV_IX64DDR:
      case PCI_CHIP_SUPSAV_IXCSDR:
      case PCI_CHIP_SUPSAV_IXCDDR:
          ChipClass = S3_SUPERSAVAGE;
          break;
      default:
          printf( "PCI id is not a recognized Savage: %04x\n", gPCIid );
          exit(-1);
    }

    if( ChipClass == S3_SAVAGEMXIX ) 
    {
      outb( 0xc0, 0x3d4 );
      jTvFormat = inb( 0x3d5 );
      outb( 0x6b, 0x3d4 );
      jDevices = inb( 0x3d5 );
    }
    else 
    {
      outb( 0x6b, 0x3d4 );
      jTvFormat = inb( 0x3d5 );
      outb( 0x6d, 0x3d4 );
      jDevices = inb( 0x3d5 );
    }

    outb( 0x79, 0x3d4 );
    cr79 = inb( 0x3d5 );

    //printf( "Device ID: %04x\n", gPCIid);

    // The Savage4 and Savage2000 are the only chips which actually detect
    // the presence of the devices.  For the others, we just have to assume.

    switch( ChipClass )
    {
      case S3_SAVAGE3D:
          jDevices = (jDevices & 0x0f) 
            | CRT_ATTACHED 
            | TV_ATTACHED;
          break;

      case S3_SAVAGE4:
      case S3_SAVAGE2000:
          /* These two get it right. */
          break;

      case S3_SAVAGEMXIX:
      case S3_PROSAVAGE:
      case S3_SUPERSAVAGE:
      default:
          jDevices = (jDevices & 0x0f) 
            | CRT_ATTACHED 
            | TV_ATTACHED 
            | LCD_ATTACHED;
          break;
    }
}


unsigned short
set_active_device( int iDevice )
{
    struct LRMI_regs r;
    int iResult = 0;

    if (!LRMI_init())
      return 1;

    /* Go set the active device. */

    memset( &r, 0, sizeof(r) );

    r.eax = 0x4f14;     // S3 extended functions
    r.ebx = 0x0003;     // set active device
    r.ecx = iDevice;

    if( ChipClass == S3_SAVAGEMXIX )
      r.ecx |= DUO_ON;

    iResult = LRMI_int( 0x10, &r );

    if( !iResult )
    {
      fprintf( stderr, "Could not set device (vm86 failure)\n" );
      return 1;
    }

    if ( (r.eax & 0xffff) != 0x4f )
    {
      fprintf( stderr, "BIOS returned error code.\n" );
      return 1;
    }

    return 0;
}



unsigned short
set_tv_state( int state )
{
    struct LRMI_regs r;
    int iResult = 0;

    if (!LRMI_init())
      return 1;

    /* And go set the TV state. */

    memset( &r, 0, sizeof(r) );

    r.eax = 0x4f14;     // S3 extended functions
    r.ebx = 0x0007;     // set tv state
    r.ecx = state;
    r.edx = TV_FORMAT_MASK;

    iResult = LRMI_int( 0x10, &r );

    if( !iResult )
    {
      fprintf( stderr, "Could not set TV state (vm86 failure)\n" );
      return 1;
    }

    if ( (r.eax & 0xffff) != 0x4f )
    {
      fprintf( stderr, "BIOS returned error code.\n" );
      return 1;
    }

    return 0;
}

void
print_current_state()
{
    int i;

    printf( "Devices attached: " );

    if( !(jDevices & 0x70) )
    {
        // How can this be?
        printf( "none" );
    }
    else
        for( i = 0; i < 3; i++ )
            if( jDevices & (0x10 << i) )
                printf( devices[i] );

    printf( "\nDevices active:   " );

    if( !(jDevices & 0x07) )
    {
        // How can this be?
        printf( "none\n" );
    }
    else
        for( i = 0; i < 3; i++ )
            if( jDevices & (0x01 << i) )
                printf( devices[i] );

    if( jDevices & TV_ATTACHED )
    {
        static char * szTV[] = { "NTSC-J", "NTSC", "PAL" };

      printf( 
          "\nCurrent TV format is %s", 
          szTV[(jTvFormat & TV_FORMAT_MASK) >> 2]
      );
    }

    printf( "\n" );
}


void
set_new_state( int newstate )
{
    // We should prohibit TV on Savage4.

    if( ((jDevices >> 4) & newstate) != newstate )
    {
        fprintf( stderr, "You attempted to activate a device which is not connected.\n" );
        // Alternatively, quiet = 0, return.
        print_current_state();
        exit( -2 );
    }

    set_active_device( newstate );

    // If the LCD state changed, we need to adjust cr79 in Savage4.
    // These values are somewhat magical, and are set by the X server.

    if( (ChipClass == S3_SAVAGE4) || (ChipClass == S3_SAVAGE2000) )
    {
      if( (jDevices & LCD_ACTIVE) && !(newstate & LCD_ACTIVE) )
      {
          // The LCD was alive and now it isn't.  We can increase cr79.

          if( (cr79 == 5) || (cr79 == 8) )
          {
            cr79 = (cr79 == 5) ? 8 : 0x0e;
            ioperm( 0x3d4, 2, 1 );
            outw( (cr79 << 8) | 0x79, 0x3d4 );
            ioperm( 0x3d4, 2, 0 );
          }
      }
      else if( !(jDevices & LCD_ACTIVE) && (newstate & LCD_ACTIVE) )
      {
          // The LCD was off and now it's on.  We must cut back cr79.

          if( (cr79 == 8) || (cr79 == 0xe) )
          {
            cr79 = (cr79 == 8) ? 5 : 8;
            ioperm( 0x3d4, 2, 1 );
            outw( (cr79 << 8) | 0x79, 0x3d4 );
            ioperm( 0x3d4, 2, 0 );
          }
      }
    }

    fetch_bios_data();

    return;
}


void
set_new_tvstate( int tvstate )
{
    if( ChipClass == S3_SAVAGE4 )
      return;

    set_tv_state( tvstate );

    fetch_bios_data();

    return;
}


int
main( int argc, char ** argv )
{
    int quiet = 0;
    int newstate = 0;
    int newtv = 0;

    if( geteuid() != 0 )
    {
      fprintf( stderr, "s3switch must be setuid root.\n" );
        exit( -1 );
    }

    // Scan through the argument list.  We do very primitive checking here.

    while( *++argv )
    {
      if( strcmp( *argv, "-q" ) == 0 )
          quiet++;
      else if( strcasecmp( *argv, "crt" ) == 0 )
          newstate |= CRT_ACTIVE;
      else if( strcasecmp( *argv, "lcd" ) == 0 )
          newstate |= LCD_ACTIVE;
      else if( strcasecmp( *argv, "both" ) == 0 )
          newstate |= CRT_ACTIVE | LCD_ACTIVE;
      else if( strcasecmp( *argv, "tv" ) == 0 )
          newstate |= TV_ACTIVE;
      else if( strcasecmp( *argv, "ntsc-j" ) == 0 )
          newtv = TV_FORMAT_NTSCJ;
      else if( strcasecmp( *argv, "ntscj" ) == 0 )
          newtv = TV_FORMAT_NTSCJ;
      else if( strcasecmp( *argv, "ntsc" ) == 0 )
          newtv = TV_FORMAT_NTSC;
      else if( strcasecmp( *argv, "pal" ) == 0 )
          newtv = TV_FORMAT_PAL;
      else if( strcmp( *argv, "-h" ) == 0 )
      {
          usage();
          exit( 0 );
      }
      else
      {
          fprintf( stderr, "Unknown argument: %s\n", *argv );
          usage();
          exit( -1 );
      }
    }
    
    IOAccess( 1 );

    fetch_bios_data();

    if( newtv )
        set_new_tvstate( newtv );

    if( newstate )
        set_new_state( newstate );

    if( !quiet )
        print_current_state( );

    IOAccess( 0 );

    return 0;
}

Generated by  Doxygen 1.6.0   Back to index