📄 voyager.c
字号:
if (++timeout == VSYNCTIMEOUT)
break;
}
while (status == CMD_INTPR_STATUS_PANEL_SYNC_ACTIVE);
/* Wait for start of vsync */
timeout = 0;
do
{
status = FIELD_GET(regRead32(CMD_INTPR_STATUS),
CMD_INTPR_STATUS,
PANEL_SYNC);
if (++timeout == VSYNCTIMEOUT)
break;
}
while (status == CMD_INTPR_STATUS_PANEL_SYNC_INACTIVE);
}
}
/**********************************************************************
*
* panelPowerSequence
*
* Purpose
* Turn the panel On/Off
*
* Parameters
* [in]
* on_off - Turn panel On/Off. Can be:
* PANEL_ON
* PANEL_OFF
* vsync_delay - Number of Vsyncs to wait after each signal is
* turned on/off
*
* [out]
* None
*
* Returns
* Nothing
*
**********************************************************************/
static void panelPowerSequence(panel_state_t on_off, int vsync_delay)
{
unsigned long panelControl = regRead32(PANEL_DISPLAY_CTRL);
if (on_off == PANEL_ON)
{
// Turn on FPVDDEN.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, FPVDDEN, HIGH);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn on FPDATA.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, DATA, ENABLE);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn on FPVBIAS.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, VBIASEN, HIGH);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn on FPEN.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, FPEN, HIGH);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
}
else
{
// Turn off FPEN.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, FPEN, LOW);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn off FPVBIASEN.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, VBIASEN, LOW);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn off FPDATA.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, DATA, DISABLE);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
panelWaitVSync(vsync_delay);
// Turn off FPVDDEN.
panelControl = FIELD_SET(panelControl,
PANEL_DISPLAY_CTRL, FPVDDEN, LOW);
regWrite32(PANEL_DISPLAY_CTRL, panelControl);
}
}
/**********************************************************************
*
* panelOn
*
* Purpose
* Turn the panel ON
*
* Parameters
* [in]
* nVSyncDelay - Number of Vsyncs to wait after each signal is
* turned on
*
* [out]
* None
*
* Returns
* Nothing
*
**********************************************************************/
void panelOn(unsigned long nVSyncDelay)
{
panelPowerSequence(PANEL_ON, nVSyncDelay);
}
/**********************************************************************
*
* panelOff
*
* Purpose
* Turn the panel OFF
*
* Parameters
* [in]
* nVSyncDelay - Number of Vsyncs to wait after each signal is
* turned off
*
* [out]
* None
*
* Returns
* Nothing
*
**********************************************************************/
void panelOff(unsigned long nVSyncDelay)
{
panelPowerSequence(PANEL_OFF, nVSyncDelay);
}
/**********************************************************************
*
* panelUseCRT
*
* Purpose
* Enable/disable routing of panel output to CRT monitor
*
* Parameters
* [in]
* bEnable - TRUE enables routing of panel output to CRT monitor
* FALSE disables routing of panel output to CRT monitor
*
* [out]
* None
*
* Returns
* Nothing
*
**********************************************************************/
void panelUseCRT(int bEnable)
{
unsigned long panel_ctrl = 0;
unsigned long crt_ctrl = 0;
panel_ctrl = regRead32(PANEL_DISPLAY_CTRL);
crt_ctrl = regRead32(CRT_DISPLAY_CTRL);
if (bEnable)
{
/* Enable panel graphics plane */
panel_ctrl = FIELD_SET(panel_ctrl, PANEL_DISPLAY_CTRL, PLANE, ENABLE);
/* Disable CRT graphics plane */
crt_ctrl = FIELD_SET(crt_ctrl, CRT_DISPLAY_CTRL, PLANE, DISABLE);
/* Route panel data to CRT monitor */
crt_ctrl = FIELD_SET(crt_ctrl, CRT_DISPLAY_CTRL, SELECT, PANEL);
}
else
{
/* Disable panel graphics plane */
panel_ctrl = FIELD_SET(panel_ctrl, PANEL_DISPLAY_CTRL, PLANE, DISABLE);
/* Enable CRT graphics plane */
crt_ctrl = FIELD_SET(crt_ctrl, CRT_DISPLAY_CTRL, PLANE, ENABLE);
/* Do not route panel data to CRT monitor */
crt_ctrl = FIELD_SET(crt_ctrl, CRT_DISPLAY_CTRL, SELECT, CRT);
}
regWrite32(PANEL_DISPLAY_CTRL, panel_ctrl);
regWrite32(CRT_DISPLAY_CTRL, crt_ctrl);
}
// Program new power mode.
void setPower(unsigned long nGates, unsigned long Clock)
{
unsigned long gate_reg, clock_reg;
unsigned long control_value;
// Get current power mode.
control_value = FIELD_GET(regRead32(POWER_MODE_CTRL),
POWER_MODE_CTRL,
MODE);
switch (control_value)
{
case POWER_MODE_CTRL_MODE_MODE0:
// Switch from mode 0 to mode 1.
gate_reg = POWER_MODE1_GATE;
clock_reg = POWER_MODE1_CLOCK;
control_value = FIELD_SET(control_value,
POWER_MODE_CTRL, MODE, MODE1);
break;
case POWER_MODE_CTRL_MODE_MODE1:
case POWER_MODE_CTRL_MODE_SLEEP:
// Switch from mode 1 or sleep to mode 0.
gate_reg = POWER_MODE0_GATE;
clock_reg = POWER_MODE0_CLOCK;
control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, MODE0);
break;
default:
// Invalid mode
return;
}
// Program new power mode.
regWrite32(gate_reg, nGates);
regWrite32(clock_reg, Clock);
regWrite32(POWER_MODE_CTRL, control_value);
// When returning from sleep, wait until finished.
while (FIELD_GET(regRead32(POWER_MODE_CTRL),
POWER_MODE_CTRL,
SLEEP_STATUS) == POWER_MODE_CTRL_SLEEP_STATUS_ACTIVE) ;
}
// Set DPMS state.
void setDPMS(DPMS_t state)
{
unsigned long value;
value = regRead32(SYSTEM_CTRL);
switch (state)
{
case DPMS_ON:
value = FIELD_SET(value, SYSTEM_CTRL, DPMS, VPHP);
break;
case DPMS_STANDBY:
value = FIELD_SET(value, SYSTEM_CTRL, DPMS, VPHN);
break;
case DPMS_SUSPEND:
value = FIELD_SET(value, SYSTEM_CTRL, DPMS, VNHP);
break;
case DPMS_OFF:
value = FIELD_SET(value, SYSTEM_CTRL, DPMS, VNHN);
break;
}
regWrite32(SYSTEM_CTRL, value);
}
// Perform a rounded division.
long roundDiv(long num, long denom)
{
/* n / d + 1 / 2 = (2n + d) / 2d */
return (2 * num + denom) / (2 * denom);
}
// Finds clock closest to the requested.
long findClock(long requested_clock, clock_select_t *clock, display_t display)
{
long mclk;
int divider, shift;
long best_diff = 999999999;
// Try 288MHz and 336MHz clocks.
for (mclk = 288000000; mclk <= 336000000; mclk += 48000000)
{
// For CRT, try dividers 1 and 3, for panel, try divider 5 as well.
for (divider = 1; divider <= (display == PANEL ? 5 : 3);
divider += 2)
{
// Try all 8 shift values.
for (shift = 0; shift < 8; shift++)
{
// Calculate difference with requested clock.
long diff = roundDiv(mclk, divider << shift) - requested_clock;
if (diff < 0)
{
diff = -diff;
}
// If the difference is less than the current, use it.
if (diff < best_diff)
{
// Store best difference.
best_diff = diff;
// Store clock values.
clock->mclk = mclk;
clock->divider = divider;
clock->shift = shift;
}
}
}
}
// Return best clock.
return clock->mclk / (clock->divider << clock->shift);
}
// Finds the requested mode in the mode table.
mode_table_t *findMode(mode_table_t *mode_table, int width, int height,
int refresh_rate)
{
// Walk the entire mode table.
while (mode_table->pixel_clock != 0)
{
// If this mode matches the requested mode, return it!
if ((mode_table->horizontal_display_end == width)
&& (mode_table->vertical_display_end == height)
&& (mode_table->vertical_frequency == refresh_rate))
{
return(mode_table);
}
// Next entry in the mode table.
mode_table++;
}
// No mode found.
return(NULL);
}
// Converts the VESA timing into Voyager timing.
void adjustMode(mode_table_t *vesaMode, mode_table_t *mode, display_t display)
{
long blank_width, sync_start, sync_width;
clock_select_t clock;
// Calculate the VESA line and screen frequencies.
vesaMode->horizontal_frequency = roundDiv(vesaMode->pixel_clock,
vesaMode->horizontal_total);
vesaMode->vertical_frequency = roundDiv(vesaMode->horizontal_frequency,
vesaMode->vertical_total);
// Calculate the sync percentages of the VESA mode.
blank_width = vesaMode->horizontal_total
- vesaMode->horizontal_display_end;
sync_start = roundDiv((vesaMode->horizontal_sync_start -
vesaMode->horizontal_display_end) * 100, blank_width);
sync_width = roundDiv(vesaMode->horizontal_sync_width * 100, blank_width);
// Copy VESA mode into Voyager mode.
*mode = *vesaMode;
// Find the best pixel clock.
mode->pixel_clock = findClock(vesaMode->pixel_clock * 2, &clock, display) / 2;
// Calculate the horizontal total based on the pixel clock and VESA line
// frequency.
mode->horizontal_total = roundDiv(mode->pixel_clock,
vesaMode->horizontal_frequency);
// Calculate the sync start and width based on the VESA percentages.
blank_width = mode->horizontal_total - mode->horizontal_display_end;
mode->horizontal_sync_start = mode->horizontal_display_end
+ roundDiv(blank_width * sync_start, 100);
mode->horizontal_sync_width = roundDiv(blank_width * sync_width, 100);
// Calculate the line and screen frequencies.
mode->horizontal_frequency = roundDiv(mode->pixel_clock,
mode->horizontal_total);
mode->vertical_frequency = roundDiv(mode->horizontal_frequency,
mode->vertical_total);
}
// Fill the register structure.
void setModeRegisters(reg_table_t *register_table, mode_table_t *mode,
display_t display, int bpp)
{
clock_select_t clock;
memset(&clock, 0, sizeof(clock));
// Calculate the clock register values.
findClock(mode->pixel_clock * 2, &clock, display);
if (display == PANEL)
{
// Set clock value for panel.
register_table->clock
= (clock.mclk == 288000000
? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_SELECT, 288)
: FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_SELECT, 336))
| (clock.divider == 1
? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 1)
: (clock.divider == 3
? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 3)
: FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 5)))
| FIELD_VALUE(0, CURRENT_POWER_CLOCK, P2XCLK_SHIFT, clock.shift);
// Set control register value.
register_table->control
= (mode->vertical_sync_polarity == POSITIVE
? FIELD_SET(0, PANEL_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_HIGH)
: FIELD_SET(0, PANEL_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_LOW))
| (mode->horizontal_sync_polarity == POSITIVE
? FIELD_SET(0, PANEL_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_HIGH)
: FIELD_SET(0, PANEL_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_LOW))
| FIELD_SET(0, PANEL_DISPLAY_CTRL, TIMING, ENABLE)
| FIELD_SET(0, PANEL_DISPLAY_CTRL, PLANE, ENABLE)
| (bpp == 8
? FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 8)
: (bpp == 16
? FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 16)
: FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 32)));
// Set timing registers.
register_table->horizontal_total
= FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, TOTAL,
mode->horizontal_total - 1)
| FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, DISPLAY_END,
mode->horizontal_display_end - 1);
register_table->horizontal_sync
= FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, WIDTH,
mode->horizontal_sync_width)
| FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, START,
mode->horizontal_sync_start - 1);
register_table->vertical_total
= FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, TOTAL,
mode->vertical_total - 1)
| FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, DISPLAY_END,
mode->vertical_display_end - 1);
register_table->vertical_sync
= FIELD_VALUE(0, PANEL_VERTICAL_SYNC, HEIGHT,
mode->vertical_sync_height)
| FIELD_VALUE(0, PANEL_VERTICAL_SYNC, START,
mode->vertical_sync_start - 1);
}
else
{
// Set clock value for CRT.
register_table->clock
= (clock.mclk == 288000000
? FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_SELECT, 288)
: FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_SELECT, 336))
| (clock.divider == 1
? FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_DIVIDER, 1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -