You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1450 lines
43 KiB
1450 lines
43 KiB
//***************************************************************************
|
|
//
|
|
// Module Name:
|
|
//
|
|
// video.c
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module contains the code to setup the timing values for chips
|
|
// and RAMDACs
|
|
//
|
|
// Environment:
|
|
//
|
|
// Kernel mode
|
|
//
|
|
//
|
|
// Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
|
|
// Copyright (c) 1995-1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include "permedia.h"
|
|
|
|
VOID CheckRGBClockInteraction(PULONG PixelClock, PULONG SystemClock);
|
|
|
|
ULONG TVP4020_CalculateMNPForClock(PVOID HwDeviceExtension,
|
|
ULONG RefClock,
|
|
ULONG ReqClock,
|
|
BOOLEAN IsPixClock,
|
|
ULONG MinClock,
|
|
ULONG MaxClock,
|
|
ULONG *rM,
|
|
ULONG *rN,
|
|
ULONG *rP);
|
|
|
|
ULONG Dac_SeparateClocks(ULONG PixelClock, ULONG SystemClock);
|
|
|
|
BOOLEAN InitializeVideo(PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PP2_VIDEO_FREQUENCIES VideoMode);
|
|
|
|
ULONG P2RD_CalculateMNPForClock(PVOID HwDeviceExtension,
|
|
ULONG RefClock,
|
|
ULONG ReqClock,
|
|
ULONG *rM,
|
|
ULONG *rN,
|
|
ULONG *rP);
|
|
|
|
BOOLEAN Program_P2RD(PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PP2_VIDEO_FREQUENCIES VideoMode,
|
|
ULONG Hsp,
|
|
ULONG Vsp,
|
|
ULONG RefClkSpeed,
|
|
PULONG pSystemClock,
|
|
PULONG pPixelClock);
|
|
|
|
#if defined(ALLOC_PRAGMA)
|
|
#pragma alloc_text(PAGE,CheckRGBClockInteraction)
|
|
#pragma alloc_text(PAGE,TVP4020_CalculateMNPForClock)
|
|
#pragma alloc_text(PAGE,Dac_SeparateClocks)
|
|
#pragma alloc_text(PAGE,InitializeVideo)
|
|
#pragma alloc_text(PAGE,P2RD_CalculateMNPForClock)
|
|
#pragma alloc_text(PAGE,Program_P2RD)
|
|
#endif
|
|
|
|
VOID
|
|
CheckRGBClockInteraction(
|
|
PULONG PixelClock,
|
|
PULONG SystemClock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ensure the output frequencies do not interract. The following must be true
|
|
fHigher != n*fLower +/- 3MHz, for all N >= 1
|
|
3MHz is the safe limit. 2MHz is sufficient.
|
|
|
|
Arguments:
|
|
|
|
PixelClock - Pointer to the clock rate for pixel output.
|
|
|
|
SystemClock - Pointer to the clock rate driving the Permedia2.
|
|
|
|
Return Value:
|
|
|
|
If the clocks interact then they are adjusted and returned in the pointer
|
|
values.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLONG fLower, fHigher;
|
|
LONG nfLower;
|
|
|
|
if (*PixelClock < *SystemClock)
|
|
{
|
|
fLower = PixelClock;
|
|
fHigher = SystemClock;
|
|
}
|
|
else
|
|
{
|
|
fLower = SystemClock;
|
|
fHigher = PixelClock;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
nfLower = *fLower;
|
|
|
|
while (nfLower - 20000 <= *fHigher)
|
|
{
|
|
if (*fHigher <= (nfLower + 20000))
|
|
{
|
|
//
|
|
// 100KHz adjustments
|
|
//
|
|
|
|
if (*fHigher > nfLower)
|
|
{
|
|
*fLower -= 1000;
|
|
*fHigher += 1000;
|
|
}
|
|
else
|
|
{
|
|
*fLower += 1000;
|
|
*fHigher -= 1000;
|
|
}
|
|
break;
|
|
}
|
|
nfLower += *fLower;
|
|
}
|
|
if ((nfLower - 20000) > *fHigher)
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
#define INITIALFREQERR 100000
|
|
|
|
ULONG
|
|
TVP4020_CalculateMNPForClock(
|
|
PVOID HwDeviceExtension,
|
|
ULONG RefClock, // In 100Hz units
|
|
ULONG ReqClock, // In 100Hz units
|
|
BOOLEAN IsPixClock, // is this the pixel or the sys clock
|
|
ULONG MinClock, // Min VCO rating
|
|
ULONG MaxClock, // Max VCO rating
|
|
ULONG *rM, // M Out
|
|
ULONG *rN, // N Out
|
|
ULONG *rP // P Out
|
|
)
|
|
{
|
|
ULONG M, N, P;
|
|
ULONG VCO, Clock;
|
|
LONG freqErr, lowestFreqErr = INITIALFREQERR;
|
|
ULONG ActualClock = 0;
|
|
|
|
for (N = 2; N <= 14; N++)
|
|
{
|
|
for (M = 2; M <= 255; M++)
|
|
{
|
|
VCO = ((RefClock * M) / N);
|
|
|
|
if ((VCO < MinClock) || (VCO > MaxClock))
|
|
continue;
|
|
|
|
for (P = 0; P <= 4; P++)
|
|
{
|
|
Clock = VCO >> P;
|
|
|
|
freqErr = (Clock - ReqClock);
|
|
|
|
if (freqErr < 0)
|
|
{
|
|
//
|
|
// PixelClock gets rounded up always so monitor reports
|
|
// correct frequency.
|
|
// TMM: I have changed this because it causes our refresh
|
|
// rate to be incorrect and some DirectDraw waitForVBlank
|
|
// tests fail.
|
|
// if (IsPixClock)
|
|
// continue;
|
|
//
|
|
|
|
freqErr = -freqErr;
|
|
}
|
|
|
|
if (freqErr < lowestFreqErr)
|
|
{
|
|
//
|
|
// Only replace if error is less; keep N small!
|
|
//
|
|
|
|
*rM = M;
|
|
*rN = N;
|
|
*rP = P;
|
|
|
|
ActualClock = Clock;
|
|
lowestFreqErr = freqErr;
|
|
|
|
//
|
|
// Return if we found an exact match
|
|
//
|
|
|
|
if (freqErr == 0)
|
|
return(ActualClock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(ActualClock);
|
|
}
|
|
|
|
|
|
ULONG Dac_SeparateClocks(ULONG PixelClock, ULONG SystemClock)
|
|
{
|
|
ULONG M, N, P;
|
|
|
|
//
|
|
// Ensure frequencies do not interract
|
|
//
|
|
|
|
P = 1;
|
|
|
|
do
|
|
{
|
|
M = P * SystemClock;
|
|
if ((M > PixelClock - 10000) && (M < PixelClock + 10000))
|
|
{
|
|
//
|
|
// Frequencies do interract. We can either change the
|
|
// PixelClock or change the System clock to avoid it.
|
|
//
|
|
|
|
SystemClock = (PixelClock - 10000) / P;
|
|
|
|
}
|
|
|
|
N = P * PixelClock;
|
|
|
|
if ((N > SystemClock - 10000) && (N < SystemClock + 10000))
|
|
{
|
|
//
|
|
// Frequencies do interract. We can either change the
|
|
// PixelClock or change the System clock to avoid it.
|
|
//
|
|
|
|
SystemClock = N - 10000;
|
|
|
|
}
|
|
|
|
P++;
|
|
|
|
} while ((M < PixelClock + 30000) || (N < SystemClock + 30000));
|
|
|
|
return (SystemClock);
|
|
}
|
|
|
|
BOOLEAN
|
|
InitializeVideo(
|
|
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PP2_VIDEO_FREQUENCIES VideoMode
|
|
)
|
|
{
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
|
|
PVIDEO_MODE_INFORMATION VideoModeInfo = &VideoMode->ModeEntry->ModeInformation;
|
|
ULONG index;
|
|
ULONG color;
|
|
ULONG ulValue;
|
|
ULONG ulValue1;
|
|
UCHAR pixelCtrl;
|
|
UCHAR pixelFormat;
|
|
ULONG dShift;
|
|
VESA_TIMING_STANDARD VESATimings;
|
|
ULONG Htot, Hss, Hse, Hbe, Hsp;
|
|
ULONG Vtot, Vss, Vse, Vbe, Vsp;
|
|
ULONG PixelClock, Freq;
|
|
ULONG VCO;
|
|
ULONG RefClkSpeed, SystemClock; // Speed of clocks in 100Hz units
|
|
ULONG VTGPolarity;
|
|
ULONG M, N, P, C, Q;
|
|
LONG gateAdjust;
|
|
BOOLEAN SecondTry;
|
|
USHORT usData;
|
|
ULONG DacDepth, depth, xRes, yRes;
|
|
ULONG xStride;
|
|
ULONG ClrComp5, ClrComp6;
|
|
ULONG pixelData;
|
|
|
|
P2_DECL;
|
|
TVP4020_DECL;
|
|
|
|
depth = VideoMode->BitsPerPel;
|
|
xRes = VideoMode->ScreenWidth;
|
|
yRes = VideoMode->ScreenHeight;
|
|
Freq = VideoMode->ScreenFrequency;
|
|
|
|
//
|
|
// For timing calculations need full depth in bits
|
|
//
|
|
|
|
if ((DacDepth = depth) == 15)
|
|
{
|
|
DacDepth = 16;
|
|
}
|
|
else if (depth == 12)
|
|
{
|
|
DacDepth = 32;
|
|
}
|
|
|
|
//
|
|
// convert screen stride from bytes to pixels
|
|
//
|
|
|
|
xStride = (8 * VideoModeInfo->ScreenStride) / DacDepth;
|
|
|
|
//
|
|
// Ensure minimum frequency of 60 Hz
|
|
//
|
|
|
|
if (Freq < 60)
|
|
{
|
|
DEBUG_PRINT((2, "Frequency raised to minimum of 60Hz\n"));
|
|
Freq = 60;
|
|
}
|
|
|
|
DEBUG_PRINT((2, "depth %d, xres %d, yres %d, freq %d\n",
|
|
depth, xRes, yRes, Freq));
|
|
|
|
//
|
|
// Get the video timing, from the registry, if an entry exists, or from
|
|
// the list of defaults, if it doesn't.
|
|
//
|
|
|
|
if (!GetVideoTiming ( HwDeviceExtension,
|
|
xRes,
|
|
yRes,
|
|
Freq,
|
|
depth,
|
|
&VESATimings ))
|
|
{
|
|
DEBUG_PRINT((1, "GetVideoTiming failed."));
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// We have got a valid set of VESA timigs
|
|
//
|
|
|
|
Htot = VESATimings.HTot;
|
|
Hss = VESATimings.HFP ;
|
|
Hse = Hss + VESATimings.HST;
|
|
Hbe = Hse + VESATimings.HBP;
|
|
Hsp = VESATimings.HSP;
|
|
Vtot = VESATimings.VTot;
|
|
Vss = VESATimings.VFP ;
|
|
Vse = Vss + VESATimings.VST;
|
|
Vbe = Vse + VESATimings.VBP;
|
|
Vsp = VESATimings.VSP;
|
|
|
|
//
|
|
// if we're zooming by 2 in Y then double the vertical timing values.
|
|
//
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_Y_BY2)
|
|
{
|
|
Vtot *= 2;
|
|
Vss *= 2;
|
|
Vse *= 2;
|
|
Vbe *= 2;
|
|
}
|
|
|
|
//
|
|
// Calculate Pixel Clock in 100 Hz units
|
|
//
|
|
|
|
PixelClock = (Htot * Vtot * Freq * 8) / 100;
|
|
pixelData = PixelClock * (DacDepth / 8);
|
|
|
|
if (pixelData > P2_MAX_PIXELDATA)
|
|
{
|
|
//
|
|
// Failed pixelData validation
|
|
//
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
RefClkSpeed = hwDeviceExtension->RefClockSpeed / 100; // 100Hz units
|
|
SystemClock = hwDeviceExtension->ChipClockSpeed / 100; // 100Hz units
|
|
|
|
//
|
|
// We do some basic initialization before setting up MCLK.
|
|
//
|
|
|
|
//
|
|
// disable the video control register
|
|
//
|
|
|
|
hwDeviceExtension->bVTGRunning = FALSE;
|
|
hwDeviceExtension->VideoControl = 0;
|
|
VideoPortWriteRegisterUlong( VIDEO_CONTROL,
|
|
hwDeviceExtension->VideoControl );
|
|
|
|
//
|
|
// Enable graphics mode, disable VGA
|
|
//
|
|
|
|
VideoPortWriteRegisterUchar( PERMEDIA_MMVGA_INDEX_REG,
|
|
PERMEDIA_VGA_CTRL_INDEX);
|
|
|
|
usData = (USHORT)VideoPortReadRegisterUchar(PERMEDIA_MMVGA_DATA_REG);
|
|
|
|
usData &= ~PERMEDIA_VGA_ENABLE;
|
|
usData = (usData << 8) | PERMEDIA_VGA_CTRL_INDEX;
|
|
|
|
VideoPortWriteRegisterUshort(PERMEDIA_MMVGA_INDEX_REG, usData);
|
|
|
|
//
|
|
// Setup Ramdac.
|
|
//
|
|
|
|
if (hwDeviceExtension->DacId == TVP4020_RAMDAC)
|
|
{
|
|
//
|
|
// No separate S/W reset for P2 pixel unit
|
|
//
|
|
|
|
//
|
|
// 1x64x64, cursor 1, ADDR[9:8] = 00, cursor off
|
|
//
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_CURSOR_CONTROL, 0x40);
|
|
|
|
//
|
|
// Redundant here; we just cleared the CCR above
|
|
//
|
|
|
|
TVP4020_LOAD_CURSOR_CTRL(TVP4020_CURSOR_OFF);
|
|
|
|
TVP4020_SET_CURSOR_COLOR0(0, 0, 0);
|
|
TVP4020_SET_CURSOR_COLOR1(0xFF, 0xFF, 0xFF);
|
|
|
|
//
|
|
// P2 sets the sync polarity in the RAMDAC rather than VideoControl
|
|
// 7.5 IRE, 8-bit data
|
|
//
|
|
|
|
ulValue = ((Hsp ? 0x0 : 0x1) << 2) | ((Vsp ? 0x0 : 0x1) << 3) | 0x12;
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MISC_CONTROL, ulValue);
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MODE_CONTROL, 0x00); // Mode Control
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_CK_CONTROL, 0x00); // Color-Key Control
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_PALETTE_PAGE, 0x00); // Palette page
|
|
|
|
//
|
|
// No zoom on P2 pixel unit
|
|
//
|
|
// No separate multiplex control on P2 pixel unit
|
|
//
|
|
// Start TI TVP4020 programming
|
|
//
|
|
|
|
switch (depth)
|
|
{
|
|
case 8:
|
|
|
|
//
|
|
// RGB, graphics, Color Index 8
|
|
//
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_COLOR_MODE, 0x30);
|
|
|
|
if( hwDeviceExtension->Capabilities & CAPS_8BPP_RGB )
|
|
{
|
|
ULONG Red, Green, Blue ;
|
|
|
|
//
|
|
// load BGR 2:3:3 ramp into LUT
|
|
//
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
{
|
|
Red = bPal8[index & 0x07];
|
|
Green = bPal8[(index >> 3 ) &0x07];
|
|
Blue = bPal4[(index >> 6 ) &0x03];
|
|
|
|
//
|
|
// EXTRA!! EXTRA!! EXTRA!!!
|
|
// After more research on more pleasing appearance
|
|
// we now added more grays, now we look not only for
|
|
// EXACT match of RED and GREEN, we consider it gray
|
|
// even when they differ by 1.
|
|
// Added 15-Jan-1996 -by- [olegsher]
|
|
//
|
|
// Maybe it's a special case of gray ?
|
|
//
|
|
|
|
if (abs((index & 0x07) - ((index >> 3 ) &0x07)) <= 1)
|
|
{
|
|
//
|
|
// This is a tricky part:
|
|
// the Blue field in BGR 2:3:3 color goes thru
|
|
// steps 00, 01, 10, 11 (Binary)
|
|
//
|
|
// the Red and Green go thru 000, 001, 010, 011,
|
|
// 100, 101, 110, 111 (Binary)
|
|
//
|
|
// We load the special gray values ONLY when Blue
|
|
// color is close in intensity to both Green and Red,
|
|
// i.e. Blue = 01, Green = 010 or 011,
|
|
// Blue = 10, Green = 100 or 101,
|
|
//
|
|
|
|
if ((((index >> 1) & 0x03) == ((index >> 6 ) & 0x03 )) ||
|
|
(((index >> 4) & 0x03) == ((index >> 6 ) & 0x03 )) ||
|
|
((Green == Red) && ( abs((index & 0x07) - ((index >> 5) & 0x06)) <= 1 )))
|
|
{
|
|
if( Blue || (Green == Red)) // Don't mess with dark colors
|
|
{
|
|
color = (Red * 2 + Green * 3 + Blue) / 6;
|
|
Red = Green = Blue = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
LUT_CACHE_SETRGB (index, Red, Green, Blue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (index = 0; index <= 0xff; ++index)
|
|
LUT_CACHE_SETRGB (index, index, index, index);
|
|
}
|
|
break;
|
|
|
|
case 15:
|
|
case 16:
|
|
|
|
//
|
|
// True color w/gamma, RGB, graphics, 5:5:5:1
|
|
//
|
|
|
|
pixelCtrl = 0xB4;
|
|
|
|
//
|
|
// True-color w/gamma, RGB, graphics, 5:6:5
|
|
//
|
|
|
|
if (depth == 16)
|
|
pixelCtrl |= 0x02;
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_COLOR_MODE, pixelCtrl);
|
|
|
|
//
|
|
// load linear ramp into LUT
|
|
//
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
{
|
|
ClrComp5 = (index & 0xF8) | (index >> 5);
|
|
ClrComp6 = (index & 0xFC) | (index >> 6);
|
|
|
|
LUT_CACHE_SETRGB (index,
|
|
ClrComp5,
|
|
depth == 16 ? ClrComp6 : ClrComp5, ClrComp5);
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
case 32:
|
|
|
|
//
|
|
// True color w/gamma, RGB, graphics, 8:8:8:8
|
|
//
|
|
|
|
pixelCtrl = 0xB8;
|
|
|
|
//
|
|
// True color w/gamma, RGB, graphics, packed-24
|
|
//
|
|
|
|
if (depth == 24)
|
|
pixelCtrl |= 0x01;
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_COLOR_MODE, pixelCtrl);
|
|
|
|
//
|
|
// load linear ramp into LUT
|
|
// standard 888 ramps
|
|
//
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
LUT_CACHE_SETRGB (index, index, index, index);
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG_PRINT((2, "Cannot set RAMDAC for bad depth %d\n", depth));
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// if the clocks are in danger of interacting adjust the system clock
|
|
//
|
|
|
|
SystemClock = Dac_SeparateClocks(PixelClock, SystemClock);
|
|
|
|
//
|
|
// Program system clock. This controls the speed of the Permedia 2.
|
|
//
|
|
|
|
SystemClock = TVP4020_CalculateMNPForClock(
|
|
HwDeviceExtension,
|
|
RefClkSpeed, // In 100Hz units
|
|
SystemClock, // In 100Hz units
|
|
FALSE, // SysClock
|
|
1500000, // Min VCO rating
|
|
3000000, // Max VCO rating
|
|
&M, // M Out
|
|
&N, // N Out
|
|
&P); // P Out
|
|
|
|
if (SystemClock == 0)
|
|
{
|
|
DEBUG_PRINT((1, "TVP4020_CalculateMNPForClock failed\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Can change P2 MCLK directly without switching to PCLK
|
|
//
|
|
// Program the Mclk PLL
|
|
//
|
|
// test mode: forces MCLK to constant high
|
|
//
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MEMCLK_REG_3, 0x06);
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MEMCLK_REG_2, N); // N
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MEMCLK_REG_1, M); // M
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_MEMCLK_REG_3, P | 0x08);// P / Enable
|
|
|
|
C = 1000000;
|
|
|
|
do
|
|
{
|
|
TVP4020_READ_INDEX_REG(__TVP4020_MEMCLK_STATUS, ulValue); // Status
|
|
|
|
} while ((!(ulValue & (1 << 4))) && (--C));
|
|
|
|
//
|
|
// No zoom on P2 pixel unit
|
|
//
|
|
// Program Pixel Clock to the correct value for the required resolution
|
|
//
|
|
|
|
PixelClock = TVP4020_CalculateMNPForClock(
|
|
HwDeviceExtension,
|
|
RefClkSpeed, // In 100Hz units
|
|
PixelClock, // In 100Hz units
|
|
TRUE, // Pixel Clock
|
|
1500000, // Min VCO rating
|
|
3000000, // Max VCO rating
|
|
&M, // M Out
|
|
&N, // N Out
|
|
&P); // P Out
|
|
|
|
if (PixelClock == 0)
|
|
{
|
|
DEBUG_PRINT((1, "TVP4020_CalculateMNPForClock failed\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Pixel Clock
|
|
//
|
|
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_PIXCLK_REG_C3, 0x06); // RESET PCLK PLL
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_PIXCLK_REG_C2, N ); // N
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_PIXCLK_REG_C1, M); // M
|
|
TVP4020_WRITE_INDEX_REG(__TVP4020_PIXCLK_REG_C3, P | 0x08);// Enable PCLK
|
|
|
|
M = 1000000;
|
|
|
|
do
|
|
{
|
|
TVP4020_READ_INDEX_REG(__TVP4020_PIXCLK_STATUS, ulValue);
|
|
|
|
} while ((!(ulValue & (1 << 4))) && (--M));
|
|
|
|
//
|
|
// No Loop Clock on P2
|
|
//
|
|
|
|
TVP4020_SET_PIXEL_READMASK (0xff);
|
|
|
|
//
|
|
// TMM: there is a rule that says if you muck about with the
|
|
// MCLK then you must set up the MEM_CONFIG register again.
|
|
//
|
|
}
|
|
else if (hwDeviceExtension->DacId == P2RD_RAMDAC)
|
|
{
|
|
if( !Program_P2RD( HwDeviceExtension,
|
|
VideoMode,
|
|
Hsp,
|
|
Vsp,
|
|
RefClkSpeed,
|
|
&SystemClock,
|
|
&PixelClock))
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the LUT cache size and set the first entry to zero, then
|
|
// write the LUT cache to the LUT
|
|
//
|
|
|
|
LUT_CACHE_SETSIZE (256);
|
|
LUT_CACHE_SETFIRST (0);
|
|
|
|
(void) Permedia2SetColorLookup ( hwDeviceExtension,
|
|
&(hwDeviceExtension->LUTCache.LUTCache),
|
|
sizeof (hwDeviceExtension->LUTCache),
|
|
TRUE, // Always update RAMDAC
|
|
FALSE ); // Don't Update cache entries
|
|
|
|
//
|
|
// Setup VTG
|
|
//
|
|
|
|
ulValue = 3; // RAMDAC pll pins for VClkCtl
|
|
|
|
if ((hwDeviceExtension->DacId == P2RD_RAMDAC) ||
|
|
(hwDeviceExtension->DacId == TVP4020_RAMDAC))
|
|
{
|
|
ULONG PCIDelay;
|
|
|
|
//
|
|
// TMM: The algorithm we used to calculate PCIDelay for P1 doesn't
|
|
// work for P2, frequent mode changes might cause hangups.
|
|
// So I took the value used by the BIOS for AGP and PCI systems
|
|
// and used that one. It works fine on PCI and VGA PCs.
|
|
//
|
|
|
|
PCIDelay = 32;
|
|
|
|
ulValue |= (PCIDelay << 2);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_PRINT((1, "Invalid RAMDAC type! \n"));
|
|
}
|
|
|
|
//
|
|
// dShift is now used as a multiplier, instead of a shift count.
|
|
// This is to support P2 packed-24 mode where the VESA horizontal
|
|
// timing values need to be multiplied by a non-power-of-two multiplier.
|
|
//
|
|
|
|
if ((hwDeviceExtension->DacId == TVP4020_RAMDAC && DacDepth > 8) ||
|
|
hwDeviceExtension->DacId == P2RD_RAMDAC)
|
|
{
|
|
dShift = DacDepth >> 3; // 64-bit pixel bus
|
|
}
|
|
else
|
|
{
|
|
dShift = DacDepth >> 2; // 32-bit pixel bus
|
|
}
|
|
|
|
//
|
|
// must load HgEnd before ScreenBase
|
|
//
|
|
|
|
VideoPortWriteRegisterUlong(HG_END, Hbe * dShift);
|
|
|
|
//
|
|
// Need to set up RAMDAC pll pins
|
|
//
|
|
|
|
VideoPortWriteRegisterUlong(V_CLK_CTL, ulValue);
|
|
|
|
VideoPortWriteRegisterUlong(SCREEN_BASE, 0);
|
|
VideoPortWriteRegisterUlong(SCREEN_STRIDE, (xStride >> 3) * (DacDepth >> 3)); // 64-bit units
|
|
VideoPortWriteRegisterUlong(H_TOTAL, (Htot * dShift) - 1);
|
|
VideoPortWriteRegisterUlong(HS_START, Hss * dShift);
|
|
VideoPortWriteRegisterUlong(HS_END, Hse * dShift);
|
|
VideoPortWriteRegisterUlong(HB_END, Hbe * dShift);
|
|
|
|
VideoPortWriteRegisterUlong(V_TOTAL, Vtot - 1);
|
|
VideoPortWriteRegisterUlong(VS_START, Vss - 1);
|
|
VideoPortWriteRegisterUlong(VS_END, Vse - 1);
|
|
VideoPortWriteRegisterUlong(VB_END, Vbe);
|
|
|
|
{
|
|
ULONG highWater, newChipConfig, oldChipConfig;
|
|
|
|
#define videoFIFOSize 32
|
|
#define videoFIFOLoWater 8
|
|
#define videoFIFOLatency 26
|
|
|
|
//
|
|
// Calculate the high-water, by taking into account
|
|
// the pixel clock, the pxiel size, add 1 for luck
|
|
//
|
|
|
|
highWater = (((videoFIFOLatency * PixelClock * DacDepth) /
|
|
(64 * SystemClock )) + 1);
|
|
|
|
//
|
|
// Trim the highwater, make sure it's not bigger than the FIFO size
|
|
//
|
|
|
|
if (highWater > videoFIFOSize)
|
|
highWater = videoFIFOSize;
|
|
|
|
highWater = videoFIFOSize - highWater;
|
|
|
|
//
|
|
// Make sure the highwater is greater than the low water mark.
|
|
//
|
|
|
|
if (highWater <= videoFIFOLoWater)
|
|
highWater = videoFIFOLoWater + 1;
|
|
|
|
ulValue = (highWater << 8) | videoFIFOLoWater;
|
|
|
|
VideoPortWriteRegisterUlong(VIDEO_FIFO_CTL, ulValue);
|
|
|
|
//
|
|
// select the appropriate Delta clock source
|
|
//
|
|
|
|
#define SCLK_SEL_PCI (0x0 << 10) // Delta Clk == PCI Clk
|
|
#define SCLK_SEL_PCIHALF (0x1 << 10) // Delta Clk == 1/2 PCI Clk
|
|
#define SCLK_SEL_MCLK (0x2 << 10) // Delta Clk == MClk
|
|
#define SCLK_SEL_MCLKHALF (0x3 << 10) // Delta Clk == 1/2 MClk
|
|
#define SCLK_SEL_MASK (0x3 << 10)
|
|
|
|
if (VideoPortGetRegistryParameters(HwDeviceExtension,
|
|
L"P2DeltaClockMode",
|
|
FALSE,
|
|
Permedia2RegistryCallback,
|
|
&ulValue) == NO_ERROR)
|
|
{
|
|
ulValue <<= 10;
|
|
ulValue &= SCLK_SEL_MASK;
|
|
}
|
|
else
|
|
{
|
|
if((hwDeviceExtension->deviceInfo.RevisionId == PERMEDIA2A_REV_ID) &&
|
|
(hwDeviceExtension->PciSpeed == 66))
|
|
{
|
|
ulValue = SCLK_SEL_PCI;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the default value
|
|
//
|
|
|
|
ulValue = SCLK_SEL_MCLKHALF;
|
|
|
|
}
|
|
}
|
|
|
|
newChipConfig = oldChipConfig = VideoPortReadRegisterUlong(CHIP_CONFIG);
|
|
newChipConfig &= ~SCLK_SEL_MASK;
|
|
newChipConfig |= ulValue;
|
|
|
|
VideoPortWriteRegisterUlong(CHIP_CONFIG, newChipConfig);
|
|
}
|
|
|
|
//
|
|
// Enable video out and set sync polaritys to active high.
|
|
// P2 uses 64-bit pixel bus for modes > 8BPP
|
|
//
|
|
|
|
VTGPolarity = (1 << 5) | (1 << 3) | 1;
|
|
|
|
if (hwDeviceExtension->DacId == P2RD_RAMDAC || DacDepth > 8)
|
|
{
|
|
//
|
|
// P2ST always uses 64-bit pixel bus
|
|
// P2 uses 64-bit pixel bus for modes > 8BPP
|
|
//
|
|
|
|
VTGPolarity |= (1 << 16);
|
|
}
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_Y_BY2)
|
|
VTGPolarity |= (1 << 2);
|
|
|
|
hwDeviceExtension->VideoControl = VTGPolarity;
|
|
VideoPortWriteRegisterUlong(VIDEO_CONTROL, hwDeviceExtension->VideoControl);
|
|
|
|
DEBUG_PRINT((2, "Loaded Permedia timing registers:\n"));
|
|
DEBUG_PRINT((2, "\tVClkCtl: 0x%x\n", 3));
|
|
DEBUG_PRINT((2, "\tScreenBase: 0x%x\n", 0));
|
|
DEBUG_PRINT((2, "\tScreenStride: 0x%x\n", xStride >> (3 - (DacDepth >> 4))));
|
|
DEBUG_PRINT((2, "\tHTotal: 0x%x\n", (Htot << dShift) - 1));
|
|
DEBUG_PRINT((2, "\tHsStart: 0x%x\n", Hss << dShift));
|
|
DEBUG_PRINT((2, "\tHsEnd: 0x%x\n", Hse << dShift));
|
|
DEBUG_PRINT((2, "\tHbEnd: 0x%x\n", Hbe << dShift));
|
|
DEBUG_PRINT((2, "\tHgEnd: 0x%x\n", Hbe << dShift));
|
|
DEBUG_PRINT((2, "\tVTotal: 0x%x\n", Vtot - 1));
|
|
DEBUG_PRINT((2, "\tVsStart: 0x%x\n", Vss - 1));
|
|
DEBUG_PRINT((2, "\tVsEnd: 0x%x\n", Vse - 1));
|
|
DEBUG_PRINT((2, "\tVbEnd: 0x%x\n", Vbe));
|
|
DEBUG_PRINT((2, "\tVideoControl: 0x%x\n", VTGPolarity));
|
|
|
|
//
|
|
// record the final chip clock in the registry
|
|
//
|
|
|
|
SystemClock *= 100;
|
|
VideoPortSetRegistryParameters(HwDeviceExtension,
|
|
L"HardwareInformation.CurrentChipClockSpeed",
|
|
&SystemClock,
|
|
sizeof(ULONG));
|
|
|
|
hwDeviceExtension->bVTGRunning = TRUE;
|
|
DEBUG_PRINT((2, "InitializeVideo Finished\n"));
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
ULONG P2RD_CalculateMNPForClock(
|
|
PVOID HwDeviceExtension,
|
|
ULONG RefClock, // In 100Hz units
|
|
ULONG ReqClock, // In 100Hz units
|
|
ULONG *rM, // M Out (feedback scaler)
|
|
ULONG *rN, // N Out (prescaler)
|
|
ULONG *rP // P Out (postscaler)
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculates prescaler, feedback scaler and postscaler values for the
|
|
STMACRO PLL61-1M used by P2RD.
|
|
|
|
--*/
|
|
|
|
{
|
|
const ULONG fMinVCO = 1280000; // min fVCO is 128MHz (in 100Hz units)
|
|
const ULONG fMaxVCO = 2560000; // max fVCO is 256MHz (in 100Hz units)
|
|
const ULONG fMinINTREF = 10000; // min fINTREF is 1MHz (in 100Hz units)
|
|
const ULONG fMaxINTREF = 20000; // max fINTREF is 2MHz (in 100Hz units)
|
|
|
|
ULONG M, N, P;
|
|
ULONG fINTREF;
|
|
ULONG fVCO;
|
|
ULONG ActualClock;
|
|
int Error;
|
|
int LowestError = INITIALFREQERR;
|
|
BOOLEAN bFoundFreq = FALSE;
|
|
int LoopCount;
|
|
|
|
for(P = 0; P <= 4; ++P)
|
|
{
|
|
ULONG fVCOLowest, fVCOHighest;
|
|
|
|
//
|
|
// it's pointless going through the main loop if all values of N
|
|
// produce an fVCO outside the acceptable range
|
|
//
|
|
|
|
N = 1;
|
|
M = (N * (1 << P) * ReqClock) / RefClock;
|
|
|
|
fVCOLowest = (RefClock * M) / N;
|
|
|
|
N = 255;
|
|
M = (N * (1 << P) * ReqClock) / RefClock;
|
|
|
|
fVCOHighest = (RefClock * M) / N;
|
|
|
|
if(fVCOHighest < fMinVCO || fVCOLowest > fMaxVCO)
|
|
continue;
|
|
|
|
for(N = 1; N <= 255; ++N)
|
|
{
|
|
fINTREF = RefClock / N;
|
|
if(fINTREF < fMinINTREF || fINTREF > fMaxINTREF)
|
|
{
|
|
if(fINTREF > fMaxINTREF)
|
|
{
|
|
//
|
|
// hopefully we'll get into range as the prescale
|
|
// value increases
|
|
//
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// already below minimum and it'll only get worse:
|
|
// move to the next postscale value
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
M = (N * (1 << P) * ReqClock) / RefClock;
|
|
if(M > 255)
|
|
{
|
|
//
|
|
// M, N & P registers are only 8 bits wide
|
|
//
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// we can expect rounding errors in calculating M, which will
|
|
// always be rounded down. So we'll checkout our calculated
|
|
// value of M along with (M+1)
|
|
//
|
|
|
|
for(LoopCount = (M == 255) ? 1 : 2; --LoopCount >= 0; ++M)
|
|
{
|
|
fVCO = (RefClock * M) / N;
|
|
|
|
if(fVCO >= fMinVCO && fVCO <= fMaxVCO)
|
|
{
|
|
ActualClock = fVCO / (1 << P);
|
|
|
|
Error = ActualClock - ReqClock;
|
|
if(Error < 0)
|
|
Error = -Error;
|
|
|
|
if(Error < LowestError)
|
|
{
|
|
bFoundFreq = TRUE;
|
|
LowestError = Error;
|
|
*rM = M;
|
|
*rN = N;
|
|
*rP = P;
|
|
if(Error == 0)
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
if(bFoundFreq)
|
|
ActualClock = (RefClock * *rM) / (*rN * (1 << *rP));
|
|
else
|
|
ActualClock = 0;
|
|
|
|
return(ActualClock);
|
|
}
|
|
|
|
|
|
BOOLEAN Program_P2RD(PHW_DEVICE_EXTENSION HwDeviceExtension,
|
|
PP2_VIDEO_FREQUENCIES VideoMode,
|
|
ULONG Hsp,
|
|
ULONG Vsp,
|
|
ULONG RefClkSpeed,
|
|
PULONG pSystemClock,
|
|
PULONG pPixelClock )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
initializes the P2RD registers and programs the DClk (pixel clock)
|
|
and MClk (system clock) PLLs. After programming the MClk, the
|
|
contents of all registers in the graphics core, the memory controller
|
|
and the video control should be assumed to be undefined
|
|
|
|
--*/
|
|
|
|
{
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
|
|
PVIDEO_MODE_INFORMATION VideoModeInfo = &VideoMode->ModeEntry->ModeInformation;
|
|
ULONG DacDepth, depth;
|
|
LONG mpxAdjust;
|
|
ULONG index;
|
|
ULONG color;
|
|
ULONG ulValue;
|
|
UCHAR pixelCtrl;
|
|
ULONG M, N, P;
|
|
P2_DECL;
|
|
P2RD_DECL;
|
|
|
|
depth = VideoMode->BitsPerPel;
|
|
|
|
//
|
|
// For timing calculations need full depth in bits
|
|
//
|
|
|
|
if ((DacDepth = depth) == 15)
|
|
DacDepth = 16;
|
|
else if (depth == 12)
|
|
DacDepth = 32;
|
|
|
|
VideoPortWriteRegisterUlong(P2RD_INDEX_CONTROL,
|
|
P2RD_IDX_CTL_AUTOINCREMENT_ENABLED);
|
|
|
|
ulValue = (Hsp ? P2RD_SYNC_CONTROL_HSYNC_ACTIVE_HIGH : P2RD_SYNC_CONTROL_HSYNC_ACTIVE_LOW) |
|
|
(Vsp ? P2RD_SYNC_CONTROL_VSYNC_ACTIVE_HIGH : P2RD_SYNC_CONTROL_VSYNC_ACTIVE_LOW);
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_X_BY2)
|
|
{
|
|
//
|
|
// it's a really low resolution (e.g. 320x200) so enable pixel
|
|
// doubling in the RAMDAC (we'll enable line doubling in the
|
|
// pixel unit too)
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL,
|
|
P2RD_MISC_CONTROL_HIGHCOLORRES |
|
|
P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED |
|
|
P2RD_MISC_CONTROL_PIXEL_DOUBLE);
|
|
}
|
|
else
|
|
{
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL,
|
|
P2RD_MISC_CONTROL_HIGHCOLORRES |
|
|
P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED);
|
|
}
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_SYNC_CONTROL, ulValue);
|
|
P2RD_LOAD_INDEX_REG(P2RD_DAC_CONTROL,
|
|
P2RD_DAC_CONTROL_BLANK_PEDESTAL_ENABLED);
|
|
|
|
ulValue = 0;
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_X_BY2)
|
|
{
|
|
ulValue |= P2RD_CURSOR_CONTROL_DOUBLE_X;
|
|
}
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_Y_BY2)
|
|
{
|
|
ulValue |= P2RD_CURSOR_CONTROL_DOUBLE_Y;
|
|
}
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_CONTROL, ulValue);
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_MODE, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_X_LOW, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_X_HIGH, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_Y_LOW, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_Y_HIGH, 0xff);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_HOTSPOT_X, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_CURSOR_HOTSPOT_Y, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_PAN, 0);
|
|
|
|
//
|
|
// the first 3-color cursor is the mini cursor which is always
|
|
// black & white. Set it up here
|
|
//
|
|
|
|
P2RD_CURSOR_PALETTE_CURSOR_RGB(0, 0x00,0x00,0x00);
|
|
P2RD_CURSOR_PALETTE_CURSOR_RGB(1, 0xff,0xff,0xff);
|
|
|
|
//
|
|
// stop dot and memory clocks
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK_CONTROL, 0);
|
|
P2RD_LOAD_INDEX_REG(P2RD_MCLK_CONTROL, 0);
|
|
|
|
if (VideoModeInfo->DriverSpecificAttributeFlags & CAPS_ZOOM_X_BY2)
|
|
{
|
|
//
|
|
// we're doubling each pixel so we double the pixel clock too
|
|
// NB. the PixelDouble field of RDMiscControl needs to be set also)
|
|
//
|
|
|
|
*pPixelClock *= 2;
|
|
}
|
|
|
|
*pPixelClock = P2RD_CalculateMNPForClock( HwDeviceExtension,
|
|
RefClkSpeed,
|
|
*pPixelClock,
|
|
&M,
|
|
&N,
|
|
&P );
|
|
|
|
if(*pPixelClock == 0)
|
|
{
|
|
DEBUG_PRINT((1, "P2RD_CalculateMNPForClock(PixelClock) failed\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// load both copies of the dot clock with our times (DCLK0 & DCLK1
|
|
// reserved for VGA only)
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK2_PRE_SCALE, N);
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK2_FEEDBACK_SCALE, M);
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK2_POST_SCALE, P);
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK3_PRE_SCALE, N);
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK3_FEEDBACK_SCALE, M);
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK3_POST_SCALE, P);
|
|
|
|
*pSystemClock = P2RD_CalculateMNPForClock( HwDeviceExtension,
|
|
RefClkSpeed,
|
|
*pSystemClock,
|
|
&M,
|
|
&N,
|
|
&P );
|
|
|
|
if(*pSystemClock == 0)
|
|
{
|
|
DEBUG_PRINT((1, "P2RD_CalculateMNPForClock(SystemClock) failed\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// load the system clock
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_MCLK_PRE_SCALE, N);
|
|
P2RD_LOAD_INDEX_REG(P2RD_MCLK_FEEDBACK_SCALE, M);
|
|
P2RD_LOAD_INDEX_REG(P2RD_MCLK_POST_SCALE, P);
|
|
|
|
//
|
|
// enable the dot clock
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_DCLK_CONTROL,
|
|
P2RD_DCLK_CONTROL_ENABLED | P2RD_DCLK_CONTROL_RUN);
|
|
|
|
|
|
M = 0x100000;
|
|
|
|
do
|
|
{
|
|
P2RD_READ_INDEX_REG(P2RD_DCLK_CONTROL, ulValue);
|
|
}
|
|
while((ulValue & P2RD_DCLK_CONTROL_LOCKED) == FALSE && --M);
|
|
|
|
if((ulValue & P2RD_DCLK_CONTROL_LOCKED) == FALSE)
|
|
{
|
|
DEBUG_PRINT((1, "Program_P2RD: PixelClock failed to lock\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// enable the system clock
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_MCLK_CONTROL,
|
|
P2RD_MCLK_CONTROL_ENABLED | P2RD_MCLK_CONTROL_RUN);
|
|
|
|
|
|
M = 0x100000;
|
|
|
|
do
|
|
{
|
|
P2RD_READ_INDEX_REG(P2RD_MCLK_CONTROL, ulValue);
|
|
}
|
|
while((ulValue & P2RD_MCLK_CONTROL_LOCKED) == FALSE && --M);
|
|
|
|
if((ulValue & P2RD_MCLK_CONTROL_LOCKED) == FALSE)
|
|
{
|
|
DEBUG_PRINT((1, "Program_P2RD: SystemClock failed to lock\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
switch (depth)
|
|
{
|
|
case 8:
|
|
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
|
|
ulValue &= ~P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
P2RD_LOAD_INDEX_REG(P2RD_PIXEL_SIZE, P2RD_PIXEL_SIZE_8BPP);
|
|
|
|
if (hwDeviceExtension->Capabilities & CAPS_8BPP_RGB)
|
|
{
|
|
ULONG Red, Green, Blue ;
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_COLOR_FORMAT,
|
|
P2RD_COLOR_FORMAT_8BPP | P2RD_COLOR_FORMAT_RGB);
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
{
|
|
Red = bPal8[index & 0x07];
|
|
Green = bPal8[(index >> 3 ) & 0x07];
|
|
Blue = bPal4[(index >> 6 ) & 0x03];
|
|
|
|
if( Red == Green) // Maybe it's a special case of gray ?
|
|
{
|
|
//
|
|
// This is a tricky part:
|
|
// the Blue field in BGR 2:3:3 color goes thru
|
|
// steps 00, 01, 10, 11 (Binary)
|
|
// the Red and Green go thru 000, 001, 010, 011,
|
|
// 100, 101, 110, 111 (Binary)
|
|
// We load the special gray values ONLY when Blue
|
|
// color is close in intensity to both Green and Red,
|
|
// i.e. Blue = 01, Green = 010 or 011,
|
|
// Blue = 10, Green = 100 or 101,
|
|
//
|
|
|
|
if ( ((index >> 1) & 0x03) == ((index >> 6 ) & 0x03 ) )
|
|
{
|
|
Blue = Red;
|
|
}
|
|
}
|
|
LUT_CACHE_SETRGB (index, Red, Green, Blue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Color indexed mode
|
|
//
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_COLOR_FORMAT,
|
|
P2RD_COLOR_FORMAT_CI8 | P2RD_COLOR_FORMAT_RGB);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 15:
|
|
case 16:
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_PIXEL_SIZE, P2RD_PIXEL_SIZE_16BPP);
|
|
|
|
#if GAMMA_CORRECTION
|
|
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
ulValue &= ~P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
|
|
//
|
|
// load linear ramp into LUT as default
|
|
//
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
LUT_CACHE_SETRGB (index, index, index, index);
|
|
|
|
pixelCtrl = 0;
|
|
|
|
#else
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
ulValue |= P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
pixelCtrl = P2RD_COLOR_FORMAT_LINEAR_EXT;
|
|
|
|
#endif
|
|
|
|
pixelCtrl |=
|
|
(depth == 16) ? P2RD_COLOR_FORMAT_16BPP : P2RD_COLOR_FORMAT_15BPP;
|
|
|
|
pixelCtrl |= P2RD_COLOR_FORMAT_RGB;
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_COLOR_FORMAT, pixelCtrl);
|
|
|
|
break;
|
|
|
|
case 12:
|
|
case 24:
|
|
case 32:
|
|
|
|
P2RD_LOAD_INDEX_REG(P2RD_PIXEL_SIZE, P2RD_PIXEL_SIZE_32BPP);
|
|
P2RD_LOAD_INDEX_REG(P2RD_COLOR_FORMAT,
|
|
P2RD_COLOR_FORMAT_32BPP | P2RD_COLOR_FORMAT_RGB);
|
|
|
|
if (depth == 12)
|
|
{
|
|
USHORT cacheIndex;
|
|
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
ulValue &= ~P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
|
|
//
|
|
// use auto-increment to load a ramp into entries 0 to 15
|
|
//
|
|
|
|
for (index = 0, cacheIndex = 0;
|
|
index <= 0xff;
|
|
index += 0x11, cacheIndex++)
|
|
{
|
|
LUT_CACHE_SETRGB (index, index, index, index);
|
|
}
|
|
|
|
//
|
|
// load ramp in every 16th entry from 16 to 240
|
|
//
|
|
|
|
color = 0x11;
|
|
for (index = 0x10; index <= 0xf0; index += 0x10, color += 0x11)
|
|
LUT_CACHE_SETRGB (index, color, color, color);
|
|
|
|
P2RD_SET_PIXEL_READMASK(0x0f);
|
|
}
|
|
else
|
|
{
|
|
|
|
#if GAMMA_CORRECTION
|
|
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
ulValue &= ~P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
|
|
//
|
|
// load linear ramp into LUT as default
|
|
//
|
|
|
|
for (index = 0; index <= 0xff; ++index)
|
|
LUT_CACHE_SETRGB (index, index, index, index);
|
|
|
|
#else
|
|
P2RD_READ_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
ulValue |= P2RD_MISC_CONTROL_DIRECT_COLOR_ENABLED;
|
|
P2RD_LOAD_INDEX_REG(P2RD_MISC_CONTROL, ulValue);
|
|
|
|
#endif // GAMMA_CORRECTION
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG_PRINT((1, "bad depth %d passed to Program_P2RD\n", depth));
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|