mode.c

来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C语言 代码 · 共 1,370 行 · 第 1/3 页

C
1,370
字号
  // INTREF is the input frequency to the PLL core (it's the frequency of the
  // reference clock after it's been divided by the prescaler, N).

  // Local constants.

  const ULONG MinVCO = 2000000;    // min VCO is 200MHz (in 100Hz units)
  const ULONG MaxVCO = 6220000;    // max VCO is 622MHz (in 100Hz units)
  const ULONG MinINTREF = 10000;   // min INTREF is 1MHz (in 100Hz units)
  const ULONG MaxINTREF = 20000;   // max INTREF is 2MHz (in 100Hz units)

  const ULONG InitialFreqError = 100000;  // An arbitrary constant.

  // Local variables.

  ULONG    M, N, P;
  ULONG    INTREF;
  ULONG    VCO;
  ULONG    ActualClock;  // Return value for this function.
  LONG     Error;        // Error values might be negative.
  LONG     LowestError = InitialFreqError;
  BOOL     FoundFreq = FALSE;
  ULONG    InnerLoopIterations = 0;
  LONG     LoopCount;
  ULONG    VCOLowest, VCOHighest;

  // Check parameters.

  AssertWritePtr(ReturnM, sizeof(ULONG));
  AssertWritePtr(ReturnN, sizeof(ULONG));
  AssertWritePtr(ReturnP, sizeof(ULONG));

  Enter(L"CalculateMNPForClock");

  for (P = 0; P <= 5; P++) {

    // It's pointless going through the main loop if all values of N produce
    // an VCO outside the acceptable range

    N = 1;
    M = (N * (1 << P) * RequiredFrequency) / (2 * RefClockFrequency);
    VCOLowest = (2 * RefClockFrequency * M) / N;

    N = 255;
    M = (N * (1 << P) * RequiredFrequency) / (2 * RefClockFrequency);
    VCOHighest = (2 * RefClockFrequency * M) / N;

    if (VCOHighest < MinVCO || VCOLowest > MaxVCO) {
      continue;
    }

    for (N = 1; N <= 255; N++, InnerLoopIterations++) {

      INTREF = RefClockFrequency / N;

      if (INTREF < MinINTREF || INTREF > MaxINTREF) {
           
        if (INTREF > MaxINTREF) {

          // 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) * RequiredFrequency) / (2 * RefClockFrequency);
            
      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++) {

        VCO = (2 * RefClockFrequency * M) / N;

        if (VCO >= MinVCO && VCO <= MaxVCO) {
                    
          ActualClock = VCO / (1 << P);

          Error = ActualClock - RequiredFrequency;
          if (Error < 0) Error = -Error;

          if (Error < LowestError) {
                        
            FoundFreq = TRUE;
            LowestError = Error;

            // Code above protects us from truncation here.

            *ReturnM = (BYTE)M;
            *ReturnN = (BYTE)N;
            *ReturnP = (BYTE)P;

            // Escape the madness if we have a perfect match.

            if (Error == 0) goto Done;
          }
        }
      }
    }
  }

Done:
    
  if (FoundFreq)
    ActualClock = (2 * RefClockFrequency * (*ReturnM)) / ((*ReturnN) * (1 << (*ReturnP)));
  else
    ActualClock = 0;

  Exit(L"CalculateMNPForClock");  
  
  return (ActualClock);
}

void
PrimarySurfaceSetMode(
  USHORT VideoMode,
  USHORT PixelFormat
  )
{
  // PrimarySurfaceSetMode
  // This function handles allocating a primary surface for the given display
  // mode. It also programs the RAMDAC to use the new primary. This function
  // is not intended to be reused. Rather, it makes the SetMode function easir
  // to read.

  // Local variables.

  LPVOID          PrimaryAddress;

  // Check parameters.

  Assert(VideoMode < l_NumVideoModeTable);
  Assert(PixelFormat < l_NumPixelTable);

  Enter(L"PrimarySurfaceSetMode");

  // Free up the previous primary surface.

  if (l_PrimarySurface.Ptr != NULL) {

    VideoFree(l_PrimarySurface.Ptr);
  }

  // Allocate new primary surface.

  PrimaryAddress = VideoAlloc(ComputeMemRequired(VideoMode, PixelFormat), 16);

  if (PrimaryAddress != NULL) {

    // No need to wait for vsync to call SetScreenBase, as video is disabled.
                                  
    SetScreenBase(PrimaryAddress);

    // Fill out the module local primary surface structure.

    l_PrimarySurface.Ptr = PrimaryAddress;
    l_PrimarySurface.Size.cx = l_VideoModeTable[VideoMode].HResolution;
    l_PrimarySurface.Size.cy = l_VideoModeTable[VideoMode].VResolution;
    l_PrimarySurface.Format = (const FORMAT *)&l_PixelTable[PixelFormat];
    l_PrimarySurface.FreeFormat = FALSE;
    l_PrimarySurface.Stride = (LONG)ComputeStride(VideoMode, PixelFormat);
    l_PrimarySurface.Type = VideoMemory;
    l_PrimarySurface.ColorKeyType = NoColorKey;
    memset(&l_PrimarySurface.SourceColorKey, 0, sizeof(COLOR_SPACE));
    memset(&l_PrimarySurface.DestColorKey, 0, sizeof(COLOR_SPACE));
  }
  else {

    Error(L"Failed to allocate video memory for use as primary surface!\n");
  }

  Exit(L"PrimarySurfaceSetMode");
}

void
SetScreenBase(
  LPVOID NewPrimary
  )
{
  // SetScreenBase
  // This function takes a virtual address of a video memory allocation that
  // the DAC will start reading out of. We assume that it has the correct
  // dimensions and bits per pixel. We do check in debug builds that the
  // pointer is valid, is in video memory, and is 128 bit aligned. This is
  // the final function called in a primary surface flip. This should be
  // called in vsync, as we set up flipping for free running.

  // Local variables.

  ULONG NewPrimaryOffset;

  // Check parameters.

  AssertVideoPtr(NewPrimary);

  Enter(L"SetScreenBase");

  NewPrimaryOffset = VirtualToOffset(NewPrimary);

  // Should be on 128 bit boundary : convert to 128 bit units.

  Assert((NewPrimaryOffset % 16) == 0);

  NewPrimaryOffset >>= 4;

  // Program the offset into the Permedia3.

  WaitForInputFIFO(2);

  WriteRegUlong(r_ScreenBase, NewPrimaryOffset);
  WriteRegUlong(r_ScreenBaseRight, NewPrimaryOffset);

  Exit(L"SetScreenBase");
}

void
VideoSetMode(
  USHORT VideoMode,
  USHORT PixelFormat
  )
{
  // VideoSetMode
  // This function handles programming the video timing parameters into the
  // RAMDAC. It is not intended to be reuseable, rather, it makes the SetMode
  // function easir to read.

  // Local variables.

  const PERM3_VIDEO_MODE * VideoModeIndirect;

  ULONG HBlankWidth;
  ULONG HSyncEnd;

  ULONG VBlankWidth;
  ULONG VSyncEnd;

  // Check parameters.

  Assert(VideoMode < l_NumVideoModeTable);
  Assert(PixelFormat < l_NumPixelTable);

  Enter(L"VideoSetMode");

  // Save some array lookups.

  VideoModeIndirect = &l_VideoModeTable[VideoMode];

  WaitForInputFIFO(11);

  // Make sure we do NOT use byte doubling. This is only useful for 8 bpp
  // modes where we would violate VESA timing requirements by fetching 128
  // bits at a time : it's too many pixels. Byte doubling causes the RAMDAC
  // to fetch only 64 bits doubled. We only support 32 and 16 bpp modes, so
  // we don't need it. (It would impact programming the pixel clock as well.)

  // This write turns off striping as well, which is only important in
  // systems with multiple rasterization chips.

  WriteRegUlong(r_MiscControl, 0);

  // We need to program the video parameters (specifically HgEnd,) before the
  // ScreenBase is set.

  // Note that all of the video parameter registers need to be programmed in
  // terms of 128 bit units. All of the timing parameters are given in chars,
  // so convert. Also, we also require the last line on screen on in a period
  // rather than the width, so note the use of - 1 terms in those cases.

  // We are not panning, so HgEnd == HbEnd.

  HSyncEnd = VideoModeIndirect->HFrontPorch + VideoModeIndirect->HSyncTime;
  HBlankWidth = HSyncEnd + VideoModeIndirect->HBackPorch;

  WriteRegUlong(r_HgEnd,   CharToOctWord(HBlankWidth, PixelFormat));
  WriteRegUlong(r_HTotal,  CharToOctWord(VideoModeIndirect->HTotalTime, PixelFormat) - 1);
  WriteRegUlong(r_HsStart, CharToOctWord(VideoModeIndirect->HFrontPorch, PixelFormat));
  WriteRegUlong(r_HsEnd,   CharToOctWord(HSyncEnd, PixelFormat));
  WriteRegUlong(r_HbEnd,   CharToOctWord(HBlankWidth, PixelFormat));

  VSyncEnd = VideoModeIndirect->VFrontPorch + VideoModeIndirect->VSyncTime;
  VBlankWidth = VSyncEnd + VideoModeIndirect->VBackPorch;

  WriteRegUlong(r_VTotal,  VideoModeIndirect->VTotalTime - 1);
  WriteRegUlong(r_VsStart, VideoModeIndirect->VFrontPorch - 1);
  WriteRegUlong(r_VsEnd,   VSyncEnd - 1);
  WriteRegUlong(r_VbEnd,   VBlankWidth);

  // Setup the stride. Again, programed in 128 bit units (OctWords.)

  WriteRegUlong(r_ScreenStride, ComputeStride(VideoMode, PixelFormat) / 16);

  Exit(L"VideoSetMode");
}

ULONG
CharToOctWord(
  ULONG  CharCount,
  USHORT PixelFormat
  )
{
  // CharToOctWord
  // This function converts a character count into a 128 bit unit count. This
  // conversion uses the current number of bits per pixel.

  // Local variables.

  ULONG OctWordCount;  // Return value for this function.

  // Check parameters.

  Assert(PixelFormat < l_NumPixelTable);

  Enter(L"CharToOctWord");

  OctWordCount = (CharCount * PIXELS_PER_CHAR * l_PixelTable[PixelFormat].BitsPerPixel) / 128;

  Exit(L"CharToOctWord");
  
  return OctWordCount;
}

void
MiscSetMode(
  USHORT VideoMode,
  USHORT PixelFormat
  )
{
  // MiscSetMode
  // This function handles all of the other misceleneous SetMode tasks not
  // handled by the other *SetMode functions. This includes the interrupts
  // and video FIFO. This function is not intended to be reuseable, but
  // rather, serves to simplefy the SetMode call.

  // Local variables.

  ULONG VideoControl;
  ULONG PixelSize;
  ULONG AperturePixelSize;

  // Check parameters.

  Assert(VideoMode < l_NumVideoModeTable);
  Assert(PixelFormat < l_NumPixelTable);

  Enter(L"MiscSetMode");

  WaitForInputFIFO(7);

  // !TODO! Do something real here instead of just writing defaults. See
  // video.c::360 for the iterative FifoControl formula for when interrupts
  // are enabled.

  WriteRegUlong(r_InterruptLine, 0);

  // The << 8 sticks the high threshold value over the 
  // b_FifoControl_HighThreshold bits.
  
  WriteMaskedRegUlong(r_FifoControl,
                      b_FifoControl_LowThreshold | b_FifoControl_HighThreshold,
                      (0x01) | (0x01 << 8));

  // Set up video control. We do everything but renable the GP, which is done
  // in the top level SetMode function.

  VideoControl = 0;

  // Set vertical and horizontal sync control to Active High. The RAMDAC will
  // invert if necessary.

  VideoControl |= (1 << 5);
  VideoControl |= (1 << 3);

  // Set buffer swap control to free running. We will use the interrupt to
  // insure our flips are during the vertical blank period.

  VideoControl |= (1 << 9);

  // Set the SyncMode to SyncToVSA (!TODO! Why? What is this?)

  VideoControl |= (1 << 16);

  // Set the PixelSize appropriately.

  PixelSize = 0;
  switch (l_PixelTable[PixelFormat].BitsPerPixel) {

  case 8:  
    VideoControl |= (0 << 19); 
    PixelSize |= 2; 
    AperturePixelSize = 0; 
    break;
  case 16: 
    VideoControl |= (1 << 19); 
    PixelSize |= 1; 
    AperturePixelSize = 1;
    break;
  case 32: 
    VideoControl |= (2 << 19); 
    AperturePixelSize = 2;
    break;
  default: 
    Error(L"Unknown bits per pixel detected!\n"); 
    break;
  };

  WriteMaskedRegUlong(r_VideoControl,
                      b_VideoControl_PixelSize | 
                      b_VideoControl_SyncMode |
                      b_VideoControl_BufferSwap | 
                      b_VideoControl_HSyncCtl |
                      b_VideoControl_VSyncCtl,
                      VideoControl);

  // Drawing registers related to the display mode.

  // Setup the screen scissor clipping.

  WriteRegUlong(r_ScissorModeOr,
                b_ScissorModeOr_ScreenScissorEnable);

  WriteRegUlong(r_ScreenSize, PackXY(l_VideoModeTable[VideoMode].HResolution,
                                     l_VideoModeTable[VideoMode].VResolution));

  // Setup the pixel size in the drawing units.

  WriteRegUlong(r_PixelSize, PixelSize);

  // Setup the memory bypass registers.

  WriteRegUlong(r_MemBypassWriteMask, 0xFFFFFFFF);

  WriteRegUlong(r_ByAperture1Mode,
                (AperturePixelSize << 5));

  WriteRegUlong(r_ByAperture2Mode,
                (AperturePixelSize << 5));            

  Exit(L"MiscSetMode");
}


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?