[ltp] success with simultaneous CRT/LCD display on T21.
D. Sen
linux-thinkpad@www.bm-soft.com
Tue, 10 Jul 2001 14:26:39 -0400
This is a multi-part message in MIME format.
--------------54F8AFA65C969D6F3BB748C5
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
I managed to get simultaneous CRT + LCD display working on the T21.
I have included the source to the modified s3switch utility that will
let you get both the CRT + LCD display up. I have only changed one line
from the Tim Roberts' original code
(.http://www.probo.com/timr/savage40.html) The lrmi package from
http://sourceforge.net/projects/lrmi/ is needed to compile the code.
I also had to delete the line " Option "LCDClock" ......" from my
/etc/X11/XF86Config-4 to get a correct display from both the LCD and
the CRT
Six months after buying the T21, I can use it to make presentations! No
thanks to S3 or IBM for providing any sort of hint/information on how
this problem could have been solved. I am glad Tim made the code
available so I could tinker with it ......albeit blindly :-)
DS
--
D. Sen, Room E167
AT&T Labs-Research
Shannon Laboratory
180 Park Ave.
Florham Park NJ 07932-0971
Ph: 973-360-8546
http://www.research.att.com/~dsen
--------------54F8AFA65C969D6F3BB748C5
Content-Type: text/plain; charset=us-ascii;
name="s3switch.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="s3switch.c"
// Simple utility to switch a Savage board between CRT/LCD devices.
// T. N. Roberts, 99-Aug-26.
// D. Sen, 2001-Jul-10 (Modified to include "duo_on" for T21s with 8c12 chipsets"
// 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
enum {
S3_SAVAGE3D,
S3_SAVAGE4,
S3_SAVAGEMXIX,
S3_SAVAGE2000,
S3_PROSAVAGE
} 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:
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;
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, jDevices=%x, cr79=%x\n", gPCIid, jDevices, cr79);
// The Savage4 and Savage2000 are the only chips which actually detect
// the presence of the devices. For the others, we just have to assume.
if( ChipClass == S3_SAVAGE3D )
{
jDevices = (jDevices & 0x0f) | CRT_ATTACHED | TV_ATTACHED;
}
if( ChipClass == S3_SAVAGEMXIX )
{
jDevices = (jDevices & 0x0f) | CRT_ATTACHED | TV_ATTACHED | LCD_ATTACHED;
}
}
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|0x80; // dsen added |0x80 for DUO_ON on 20010629
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;
}
--------------54F8AFA65C969D6F3BB748C5--
----- The Linux ThinkPad mailing list -----
The linux-thinkpad mailing list home page is at:
http://www.bm-soft.com/~bm/tp_mailing.html