1008 lines
25 KiB
C
1008 lines
25 KiB
C
/*++
|
||
|
||
Copyright (c) 1993, 1994 Weitek Corporation
|
||
|
||
Module Name:
|
||
|
||
clock.c
|
||
|
||
Abstract:
|
||
|
||
This module contains clock generator specific functions for the
|
||
Weitek P9 miniport device driver.
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History may be found at the end of this file.
|
||
|
||
--*/
|
||
|
||
#include "p9.h"
|
||
#include "p9gbl.h"
|
||
#include "clock.h"
|
||
#include "vga.h"
|
||
#include "ibm525.h"
|
||
#include "p91regs.h"
|
||
|
||
|
||
extern UCHAR
|
||
ReadIBM525(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT index
|
||
);
|
||
|
||
extern VOID
|
||
WriteIBM525(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT index,
|
||
UCHAR value
|
||
);
|
||
|
||
extern UCHAR
|
||
ReadP9ConfigRegister(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
UCHAR regnum
|
||
);
|
||
|
||
extern VOID
|
||
WriteP9ConfigRegister(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
UCHAR regnum,
|
||
UCHAR jValue
|
||
);
|
||
|
||
extern ULONG
|
||
Read9100FreqSel(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension
|
||
);
|
||
|
||
|
||
VOID
|
||
Write525PLL(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT usFreq
|
||
);
|
||
|
||
VOID
|
||
Write9100FreqSel(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG cs
|
||
);
|
||
|
||
|
||
VOID
|
||
P91WriteICD(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG data
|
||
);
|
||
|
||
VOID
|
||
P90WriteICD(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG data
|
||
);
|
||
|
||
|
||
|
||
|
||
VOID
|
||
ProgramClockSynth(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT usFrequency,
|
||
BOOLEAN bSetMemclk,
|
||
BOOLEAN bUseClockDoubler
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Program a custom frequency into a clock synthesizer. Either MEMCLK
|
||
or Pixel clock, determined by bSetMemclk.
|
||
|
||
Arguments:
|
||
|
||
HwDeviceExtension - Pointer to the miniport driver's device extension.
|
||
usFrequency = Frequency in Mhz, (this shoud be kept in the registry),
|
||
bSetMemclk == TRUE == MEMCLK.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth - Entry\n"));
|
||
|
||
switch (HwDeviceExtension->p91State.usClockID)
|
||
{
|
||
case CLK_ID_ICD2061A:
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth: Clock = CLK_ID_ICD2061A\n"));
|
||
|
||
if ((HwDeviceExtension->Dac.bRamdacUsePLL) &&
|
||
(HwDeviceExtension->Dac.usRamdacID == DAC_ID_IBM525) && (!bSetMemclk))
|
||
{
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth: DAC_ID_IBM525\n"));
|
||
VideoDebugPrint((2, "ProgramClockSynth: PLL Freq = %d\n", usFrequency));
|
||
|
||
Write525PLL(HwDeviceExtension, usFrequency);
|
||
|
||
//
|
||
// Check if there is an override value for the PLL Reference
|
||
// Divider. If so, set the reference frequency accordingly...
|
||
//
|
||
|
||
if (HwDeviceExtension->VideoData.ul525RefClkCnt != 0xFFFFFFFF)
|
||
{
|
||
VideoDebugPrint((2, "ProgramClockSynth: 525RefClkCnt = %ld\n",
|
||
HwDeviceExtension->VideoData.ul525RefClkCnt * 200L));
|
||
|
||
usFrequency = (USHORT) (HwDeviceExtension->VideoData.ul525RefClkCnt * 200L);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Set reference frequency to 5000 Mhz...
|
||
//
|
||
|
||
usFrequency = 5000;
|
||
}
|
||
VideoDebugPrint((2, "ProgramClockSynth: 525 Ref Frequency = %d\n", usFrequency));
|
||
}
|
||
|
||
DevSetClock(HwDeviceExtension,
|
||
usFrequency,
|
||
bSetMemclk,
|
||
bUseClockDoubler);
|
||
|
||
//
|
||
// Select custom frequency
|
||
//
|
||
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_EXTSEL9100);
|
||
|
||
break;
|
||
|
||
case CLK_ID_FIXED_MEMCLK:
|
||
|
||
//
|
||
// People using the IBM RGB525 RAMDAC with a fixed
|
||
// external oscillator will be using the RGB525's internal
|
||
// PLL. So the MEMCLK cannot be changed, and the pixel
|
||
// clock must be programmed through the RAMDAC.
|
||
// NOTE: This code will work even if the RAMDAC's
|
||
// internal PLL is bypassed and an external oscillator
|
||
// is used.
|
||
//
|
||
// NOTE: We don't print out any kind of error message
|
||
// if an attempt is made to program the memory clock
|
||
// frequency.
|
||
//
|
||
// We assume that the reference frequency is 50 MHz.
|
||
//
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth: Clock = CLK_ID_FIXED_MEMCLKC\n"));
|
||
|
||
if ((HwDeviceExtension->Dac.usRamdacID == DAC_ID_IBM525) &&
|
||
(!bSetMemclk))
|
||
{
|
||
Write525PLL(HwDeviceExtension, usFrequency);
|
||
}
|
||
|
||
if ((HwDeviceExtension->Dac.bRamdacUsePLL) &&
|
||
(HwDeviceExtension->Dac.usRamdacID == DAC_ID_IBM525) &&
|
||
(!bSetMemclk))
|
||
{
|
||
VideoDebugPrint((2, "ERROR: Trying to select a pixclk with RGB525 disabled.\n"));
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth - Exit\n"));
|
||
|
||
|
||
} // End of ProgramClockSynth()
|
||
|
||
|
||
|
||
|
||
VOID
|
||
DevSetClock(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT usFrequency,
|
||
BOOLEAN bSetMemclk,
|
||
BOOLEAN bUseClockDoubler
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the frequency synthesizer to the proper state for the current
|
||
video mode.
|
||
|
||
Arguments:
|
||
|
||
usFrequency == frequency.
|
||
bUseClockDoubler == TRUE == use clock doubler if appropriate.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT ftab[16]=
|
||
{
|
||
5100,5320,5850,6070,6440,6680,7350,7560,8090,
|
||
8320,9150,10000,12000,12000,12000,12000
|
||
};
|
||
|
||
USHORT ref = 5727; // reference freq 2*14.31818 *100*2
|
||
int i = 0; // index preset field
|
||
int m = 0; // power of 2 divisor field
|
||
int p; // multiplier field
|
||
int q; // divisor field
|
||
int qs; // starting q to prevent integer overflow
|
||
int bestq = 0; // best q so far
|
||
int bestp = 0; // best p so far
|
||
int bestd = 10000; // distance to best combination so far
|
||
int curd; // current distance
|
||
int curf; // current frequency
|
||
int j; // loop counter
|
||
ULONG data;
|
||
|
||
VideoDebugPrint((2, "DevSetClock - Entry\n"));
|
||
|
||
if (usFrequency == 0) // Prevent 0 from hanging us!
|
||
usFrequency = 3150;
|
||
|
||
|
||
if ((usFrequency > HwDeviceExtension->Dac.ulMaxClkFreq) &&
|
||
(bUseClockDoubler) &&
|
||
(HwDeviceExtension->Dac.usRamdacID != DAC_ID_IBM525))
|
||
{
|
||
//
|
||
// Enable the DAC clock doubler mode.
|
||
//
|
||
|
||
HwDeviceExtension->Dac.DACSetClkDblMode(HwDeviceExtension);
|
||
// 2x Clock multiplier enabled
|
||
usFrequency /= 2; // Use 1/2 the freq.
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Disable the DAC clock doubler mode.
|
||
//
|
||
|
||
HwDeviceExtension->Dac.DACClrClkDblMode(HwDeviceExtension);
|
||
}
|
||
|
||
while(usFrequency < 5000) // if they need a small frequency,
|
||
{
|
||
m += 1; // the hardware can divide by 2 to-the m
|
||
usFrequency *= 2; // so try for a higher frequency
|
||
}
|
||
|
||
for (j = 0; j < 16; j++) // find the range that best fits this frequency
|
||
{
|
||
if (usFrequency < ftab[j]) // when you find the frequency
|
||
{
|
||
i = j; // remember the table index
|
||
break; // and stop looking.
|
||
}
|
||
}
|
||
|
||
for (p = 0; p < 128; p++) // try all possible frequencies!
|
||
{
|
||
//well, start q high to avoid overflow
|
||
|
||
qs = div32(mul32((SHORT) ref, (SHORT) (p+3)), 0x7fff);
|
||
|
||
for (q = qs; q < 128; q++)
|
||
{
|
||
//
|
||
// calculate how good each frequency is
|
||
//
|
||
|
||
curf = div32(mul32((SHORT) ref, (SHORT) (p+3)), (SHORT) ((q + 2) << 1));
|
||
curd = usFrequency - curf;
|
||
|
||
if (curd < 0)
|
||
{
|
||
curd = -curd; // always calc a positive distance
|
||
}
|
||
|
||
if (curd < bestd) // if it's best of all so far
|
||
{
|
||
bestd = curd; // then remember everything about it
|
||
bestp = p; // but especially the multiplier
|
||
bestq = q; // and divisor
|
||
}
|
||
}
|
||
}
|
||
|
||
data = ((((long) i) << 17) | (((long) bestp) << 10) | (m << 7) | bestq);
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth: usFrequency = %d\n", usFrequency));
|
||
VideoDebugPrint((2, "ProgramClockSynth: data = %lx\n", data));
|
||
|
||
if (bSetMemclk)
|
||
{
|
||
data = data | IC_MREG; // Memclk
|
||
}
|
||
else
|
||
{
|
||
data = data | IC_REG2; // Pixclk
|
||
}
|
||
|
||
if (IS_DEV_P9100)
|
||
{
|
||
P91WriteICD(HwDeviceExtension, data);
|
||
}
|
||
else
|
||
{
|
||
P90WriteICD(HwDeviceExtension, data);
|
||
}
|
||
|
||
VideoDebugPrint((2, "DevSetClock - Exit\n"));
|
||
|
||
return;
|
||
|
||
} // End of DevSetClock()
|
||
|
||
|
||
VOID P91WriteICD(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG data
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Program the ICD2061a Frequency Synthesizer.
|
||
|
||
Arguments:
|
||
|
||
HwDeviceExtension - Pointer to the miniport driver's device extension.
|
||
data - Data to be written.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
int i;
|
||
ULONG savestate;
|
||
|
||
//
|
||
// Note: We might have to disable interrupts to preclude the ICD's
|
||
// watchdog timer from expiring resulting in the ICD resetting to the
|
||
// idle state.
|
||
//
|
||
savestate = Read9100FreqSel(HwDeviceExtension);
|
||
|
||
//
|
||
// First, send the "Unlock sequence" to the clock chip.
|
||
// Raise the data bit and send 5 unlock bits.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_DATA9100);
|
||
for (i = 0; i < 5; i++) // send at least 5 unlock bits
|
||
{
|
||
//
|
||
// Hold the data while lowering and raising the clock
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_DATA9100);
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_DATA9100 |
|
||
ICD2061_CLOCK9100);
|
||
}
|
||
|
||
//
|
||
// Then turn the data clock off and turn the clock on one more time...
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, 0);
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_CLOCK9100);
|
||
|
||
//
|
||
// Now send the start bit: Leave data off, adn lower the clock.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, 0);
|
||
|
||
//
|
||
// Leave data off and raise the clock.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_CLOCK9100);
|
||
|
||
//
|
||
// Localbus position for hacking bits out
|
||
// Next, send the 24 data bits.
|
||
//
|
||
for (i = 0; i < 24; i++)
|
||
{
|
||
//
|
||
// Leaving the clock high, raise the inverse of the data bit
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
((~data << ICD2061_DATASHIFT9100) &
|
||
ICD2061_DATA9100) | ICD2061_CLOCK9100);
|
||
|
||
//
|
||
// Leaving the inverse data in place, lower the clock.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
(~data << ICD2061_DATASHIFT9100) & ICD2061_DATA9100);
|
||
|
||
//
|
||
// Leaving the clock low, rais the data bit.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
(data << ICD2061_DATASHIFT9100) & ICD2061_DATA9100);
|
||
|
||
//
|
||
// Leaving the data bit in place, raise the clock.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
((data << ICD2061_DATASHIFT9100) & ICD2061_DATA9100)
|
||
| ICD2061_CLOCK9100);
|
||
|
||
data >>= 1; // get the next bit of the data
|
||
}
|
||
|
||
//
|
||
// Leaving the clock high, raise the data bit.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
ICD2061_CLOCK9100 | ICD2061_DATA9100);
|
||
|
||
//
|
||
// Leaving the data high, drop the clock low, then high again.
|
||
//
|
||
Write9100FreqSel(HwDeviceExtension, ICD2061_DATA9100);
|
||
Write9100FreqSel(HwDeviceExtension,
|
||
ICD2061_CLOCK9100 | ICD2061_DATA9100);
|
||
|
||
//
|
||
// Note: if interrupts were disabled, enable them here.
|
||
// before restoring the
|
||
// original value or the ICD
|
||
// will freak out.
|
||
Write9100FreqSel(HwDeviceExtension, savestate); // restore orig register value
|
||
|
||
return;
|
||
|
||
} // End of WriteICD()
|
||
|
||
|
||
VOID
|
||
P90WriteICD(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG data
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Program the ICD2061a Frequency Synthesizer for the Power 9000.
|
||
|
||
Arguments:
|
||
|
||
HwDeviceExtension - Pointer to the miniport driver's device extension.
|
||
data - Data to be written.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
int i;
|
||
int oldstate, savestate;
|
||
|
||
savestate = RD_ICD();
|
||
oldstate = savestate & ~(MISCD | MISCC);
|
||
|
||
// First, send the "Unlock sequence" to the clock chip.
|
||
|
||
WR_ICD(oldstate | MISCD); // raise the data bit
|
||
|
||
for (i = 0;i < 5;i++) // send at least 5 unlock bits
|
||
{
|
||
WR_ICD(oldstate | MISCD); // hold the data on while
|
||
WR_ICD(oldstate | MISCD | MISCC); // lowering and raising the clock
|
||
}
|
||
|
||
WR_ICD(oldstate); // then turn the data and clock off
|
||
WR_ICD(oldstate | MISCC); // and turn the clock on one more time.
|
||
|
||
// now send the start bit:
|
||
WR_ICD(oldstate); // leave data off, and lower the clock
|
||
WR_ICD(oldstate | MISCC); // leave data off, and raise the clock
|
||
|
||
// localbus position for hacking bits out
|
||
// Next, send the 24 data bits.
|
||
for (i = 0; i < 24; i++)
|
||
{
|
||
// leaving the clock high, raise the inverse of the data bit
|
||
|
||
WR_ICD(oldstate | ((~(((short) data) << 3)) & MISCD) | MISCC);
|
||
|
||
// leaving the inverse data in place, lower the clock
|
||
|
||
WR_ICD(oldstate | (~(((short) data) << 3)) & MISCD);
|
||
|
||
// leaving the clock low, rais the data bit
|
||
|
||
WR_ICD(oldstate | (((short) data) << 3) & MISCD);
|
||
|
||
// leaving the data bit in place, raise the clock
|
||
|
||
WR_ICD(oldstate | ((((short)data) << 3) & MISCD) | MISCC);
|
||
|
||
data >>= 1; // get the next bit of the data
|
||
}
|
||
|
||
// leaving the clock high, raise the data bit
|
||
WR_ICD(oldstate | MISCD | MISCC);
|
||
|
||
// leaving the data high, drop the clock low, then high again
|
||
WR_ICD(oldstate | MISCD);
|
||
WR_ICD(oldstate | MISCD | MISCC);
|
||
WR_ICD(oldstate | MISCD | MISCC); // Seem to need a delay
|
||
|
||
// before restoring the
|
||
// original value or the ICD
|
||
// will freak out.
|
||
|
||
WR_ICD(savestate); // restore original register value
|
||
|
||
return;
|
||
|
||
} // End of P90WriteICD()
|
||
|
||
|
||
VOID
|
||
Write9100FreqSel(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
ULONG cs
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write to the P9100 clock select register preserving the video coprocessor
|
||
enable bit.
|
||
|
||
Statically:
|
||
Bits [1:0] go to frequency select
|
||
Dynamically:
|
||
Bit 1: data
|
||
Bit 0: clock
|
||
|
||
Arguments:
|
||
|
||
HwDeviceExtension - Pointer to the miniport driver's device extension.
|
||
Clock select value to write.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Set the frequency select bits in the P9100 configuration
|
||
//
|
||
|
||
WriteP9ConfigRegister(HwDeviceExtension,
|
||
P91_CONFIG_CKSEL,
|
||
(UCHAR) ((cs << 2) |
|
||
HwDeviceExtension->p91State.bVideoPowerEnabled));
|
||
return;
|
||
|
||
} // End of Write9100FreqSel()
|
||
|
||
ULONG
|
||
Read9100FreqSel(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read from the P9100 clock select register.
|
||
|
||
Arguments:
|
||
|
||
HwDeviceExtension - Pointer to the miniport driver's device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Since the frequency select bits are in the P9100 configuration
|
||
// space, you have to treat VL and PCI differently.
|
||
//
|
||
return((ULONG)(ReadP9ConfigRegister(HwDeviceExtension, P91_CONFIG_CKSEL)
|
||
>> 2) & 0x03);
|
||
|
||
} // End of Read9100FreqSel()
|
||
|
||
VOID
|
||
Write525PLL(
|
||
PHW_DEVICE_EXTENSION HwDeviceExtension,
|
||
USHORT usFreq
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function programs the IBM RGB525 Ramdac to generate and use the
|
||
specified frequency as its pixel clock frequency.
|
||
|
||
Arguments:
|
||
|
||
Frequency.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT usDesiredFreq;
|
||
USHORT usOutputFreq;
|
||
USHORT usRoundedFreq;
|
||
USHORT usVCODivCount;
|
||
ULONG ulValue;
|
||
|
||
VideoDebugPrint((2, "Write525PLL------\n"));
|
||
|
||
usOutputFreq = usFreq;
|
||
|
||
//
|
||
// Calculate the DF and VCO Divide count for the specified output
|
||
// frequency. The calculations are based on the following table:
|
||
//
|
||
// DF | VCO Divide Count | Frequency Range | Step (MHz)
|
||
// ----+-------------------+---------------------+------------
|
||
// 00 | (4 x VF) - 65 | 16.25 - 32.00 MHz | 0.25
|
||
// 01 | (2 x VF) - 65 | 32.50 - 64.00 MHz | 0.50
|
||
// 10 | VF - 65 | 65.00 - 128.00 MHz | 1.00
|
||
// 11 | (VF / 2) - 65 | 130.00 - 250.00 MHz | 2.00
|
||
// -----------------------------------------------------------
|
||
// VF = Desired Video Frequency
|
||
// -----------------------------------------------------------
|
||
//
|
||
if ((usOutputFreq >= IBM525_DF0_LOW) &&
|
||
(usOutputFreq <= IBM525_DF0_HIGH))
|
||
{
|
||
//
|
||
// The requested frequency is in the DF0 frequency range.
|
||
//
|
||
|
||
usDesiredFreq = IBM525_FREQ_DF_0;
|
||
|
||
//
|
||
// Round the requested frequency to the nearest frequency step
|
||
// boundry.
|
||
//
|
||
|
||
usRoundedFreq = (usOutputFreq / IBM525_DF0_STEP) * IBM525_DF0_STEP;
|
||
if ((usOutputFreq - usRoundedFreq) >= (IBM525_DF0_STEP / 2))
|
||
{
|
||
//
|
||
// Round up.
|
||
//
|
||
usRoundedFreq += IBM525_DF0_STEP;
|
||
}
|
||
|
||
//
|
||
// Calculate the VCO Divide Count register value for the requested
|
||
// frequency.
|
||
//
|
||
|
||
usVCODivCount = ((usRoundedFreq * 4) - 6500) / 100;
|
||
}
|
||
else if ((usOutputFreq >= IBM525_DF1_LOW) &&
|
||
(usOutputFreq <= IBM525_DF1_HIGH))
|
||
{
|
||
//
|
||
// The requested frequency is in the DF1 frequency range.
|
||
//
|
||
|
||
usDesiredFreq = IBM525_FREQ_DF_1;
|
||
|
||
//
|
||
// Round the requested frequency to the nearest frequency step
|
||
// boundry.
|
||
//
|
||
|
||
usRoundedFreq = (usOutputFreq / IBM525_DF1_STEP) * IBM525_DF1_STEP;
|
||
if ((usOutputFreq - usRoundedFreq) >= (IBM525_DF1_STEP / 2))
|
||
{
|
||
//
|
||
// Round up.
|
||
//
|
||
usRoundedFreq += IBM525_DF1_STEP;
|
||
}
|
||
|
||
//
|
||
// Calculate the VCO Divide Count register value for the requested
|
||
// frequency.
|
||
//
|
||
|
||
usVCODivCount = ((usRoundedFreq * 2) - 6500) / 100;
|
||
}
|
||
else if ((usOutputFreq >= IBM525_DF2_LOW) &&
|
||
(usOutputFreq <= IBM525_DF2_HIGH))
|
||
{
|
||
//
|
||
// The requested frequency is in the DF2 frequency range.
|
||
//
|
||
|
||
usDesiredFreq = IBM525_FREQ_DF_2;
|
||
|
||
//
|
||
// Round the requested frequency to the nearest frequency step
|
||
// boundry.
|
||
//
|
||
|
||
usRoundedFreq = (usOutputFreq / IBM525_DF2_STEP) * IBM525_DF2_STEP;
|
||
if ((usOutputFreq - usRoundedFreq) >= (IBM525_DF2_STEP / 2))
|
||
{
|
||
//
|
||
// Round up.
|
||
//
|
||
usRoundedFreq += IBM525_DF2_STEP;
|
||
}
|
||
|
||
//
|
||
// Calculate the VCO Divide Count register value for the requested
|
||
// frequency.
|
||
//
|
||
|
||
usVCODivCount = (usRoundedFreq - 6500) / 100;
|
||
}
|
||
else if ((usOutputFreq >= IBM525_DF3_LOW) &&
|
||
(usOutputFreq <= IBM525_DF3_HIGH))
|
||
{
|
||
//
|
||
// The requested frequency is in the DF3 frequency range.
|
||
//
|
||
|
||
usDesiredFreq = IBM525_FREQ_DF_3;
|
||
|
||
//
|
||
// Round the requested frequency to the nearest frequency step
|
||
// boundry.
|
||
//
|
||
|
||
usRoundedFreq = (usOutputFreq / IBM525_DF3_STEP) * IBM525_DF3_STEP;
|
||
if ((usOutputFreq - usRoundedFreq) >= (IBM525_DF3_STEP / 2))
|
||
{
|
||
//
|
||
// Round up.
|
||
//
|
||
usRoundedFreq += IBM525_DF3_STEP;
|
||
}
|
||
|
||
//
|
||
// Calculate the VCO Divide Count register value for the requested
|
||
// frequency.
|
||
//
|
||
|
||
usVCODivCount = ((usRoundedFreq / 2) - 6500) / 100;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// The requested frequency is not supported...
|
||
//
|
||
|
||
VideoDebugPrint((2, "Write525PLL: Freq %d is not supported!\n", usFreq));
|
||
}
|
||
|
||
VideoDebugPrint((2, "Write525PLL: usRoundedFreq = %d\n", usRoundedFreq));
|
||
VideoDebugPrint((2, "Write525PLL: usVCODivCount = %d\n", usVCODivCount));
|
||
|
||
//
|
||
// Setup for writing to the PLL Reference Divider register.
|
||
//
|
||
|
||
//
|
||
// Check if there is an override value for the PLL Reference Divider.
|
||
//
|
||
if (HwDeviceExtension->VideoData.ul525RefClkCnt != 0xFFFFFFFF)
|
||
{
|
||
//
|
||
// Program REFCLK to the specified override...
|
||
//
|
||
WriteIBM525(HwDeviceExtension,
|
||
RGB525_FIXED_PLL_REF_DIV,
|
||
(UCHAR) HwDeviceExtension->VideoData.ul525RefClkCnt);
|
||
|
||
VideoDebugPrint((2, "ProgramClockSynth: 525RefClkCnt = %lx\n",
|
||
HwDeviceExtension->VideoData.ul525RefClkCnt));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Program REFCLK to a fixed 50MHz.
|
||
//
|
||
WriteIBM525(HwDeviceExtension, RGB525_FIXED_PLL_REF_DIV,
|
||
IBM525_PLLD_50MHZ);
|
||
}
|
||
|
||
//
|
||
// Set up for programming frequency register 9.
|
||
//
|
||
|
||
//
|
||
// Check if there is an override value for the VCO Divide Count Register.
|
||
//
|
||
|
||
if (HwDeviceExtension->VideoData.ul525VidClkFreq != 0xFFFFFFFF)
|
||
{
|
||
//
|
||
// Program the VCO Divide count register to the specified override...
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension,
|
||
RGB525_F9,
|
||
(UCHAR) HwDeviceExtension->VideoData.ul525VidClkFreq);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// No override value, so use the calculated value.
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension,
|
||
RGB525_F9,
|
||
(UCHAR) (usDesiredFreq | usVCODivCount));
|
||
}
|
||
|
||
//
|
||
// Program PLL Control Register 2.
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension, RGB525_PLL_CTL2, IBM525_PLL2_F9_REG);
|
||
|
||
//
|
||
// Program PLL Control Register 1.
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension, RGB525_PLL_CTL1,
|
||
(IBM525_PLL1_REFCLK_INPUT |
|
||
IBM525_PLL1_INT_FS) );
|
||
|
||
//
|
||
// Program DAC Operation Register.
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension, RGB525_DAC_OPER, IBM525_DO_DSR_FAST);
|
||
|
||
//
|
||
// Program Miscellaneous Control Register 1.
|
||
//
|
||
|
||
WriteIBM525(HwDeviceExtension, RGB525_MISC_CTL1, IBM525_MC1_VRAM_64_BITS);
|
||
|
||
//
|
||
// Program Miscellaneous Clock Control Register.
|
||
//
|
||
|
||
ulValue = ReadIBM525(HwDeviceExtension, RGB525_MISC_CLOCK_CTL);
|
||
|
||
//
|
||
// Now decide how to divide the PLL clock output.
|
||
//
|
||
if (!HwDeviceExtension->Dac.bRamdacDivides)
|
||
{
|
||
if (HwDeviceExtension->usBitsPixel == 24)
|
||
{
|
||
//
|
||
// 24 Bpp = 3 Byte Per Pixel
|
||
//
|
||
// At 24 Bpp, divide the clock by 8.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_8 | IBM525_MCC_PLL_ENABLE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Don't divide the clock when the P9100 is doing the dividing.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_1 | IBM525_MCC_PLL_ENABLE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
switch (HwDeviceExtension->usBitsPixel)
|
||
{
|
||
//
|
||
// 8 Bpp = 1 Byte Per Pixel
|
||
//
|
||
|
||
case 8:
|
||
{
|
||
//
|
||
// At 8 Bpp, divide the clock by 8.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_8 | IBM525_MCC_PLL_ENABLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// 16 Bpp = 2 Byte Per Pixel
|
||
//
|
||
|
||
case 15:
|
||
case 16:
|
||
{
|
||
//
|
||
// At 16 Bpp, divide the clock by 4.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_4 | IBM525_MCC_PLL_ENABLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// 24 Bpp = 3 Byte Per Pixel
|
||
//
|
||
|
||
case 24:
|
||
{
|
||
//
|
||
// At 24 Bpp, divide the clock by 8.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_8 | IBM525_MCC_PLL_ENABLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// 32 Bpp = 4 Byte Per Pixel
|
||
//
|
||
|
||
case 32:
|
||
{
|
||
//
|
||
// At 32 Bpp, divide the clock by 2.
|
||
//
|
||
ulValue |= IBM525_MCC_PLL_DIV_2 | IBM525_MCC_PLL_ENABLE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
WriteIBM525(HwDeviceExtension,
|
||
RGB525_MISC_CLOCK_CTL,
|
||
(UCHAR) ulValue);
|
||
|
||
return;
|
||
|
||
} // End of Write525PLL()
|