summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Menzel <paulepanter@users.sourceforge.net>2009-06-06 11:38:08 +0200
committerPaul Menzel <paulepanter@users.sourceforge.net>2009-06-06 11:38:08 +0200
commita04bc4d035822c71d2bb3c38924b8672e35d6c36 (patch)
treefc3c704acc12360a89865840d4182a4d15ac5d4f
parenta00004e5f5a17028f58872d1ab70356525965788 (diff)
parent9238ea9f919feda110af1725c3020d6199daac34 (diff)
downloadxf86-video-intel-frc-a04bc4d035822c71d2bb3c38924b8672e35d6c36.tar.gz
xf86-video-intel-frc-a04bc4d035822c71d2bb3c38924b8672e35d6c36.tar.bz2
Merge branch 'ulf' into dlf
-rw-r--r--src/i810_driver.c18
-rw-r--r--src/i830.h7
-rw-r--r--src/i830_crt.c6
-rw-r--r--src/i830_display.c17
-rw-r--r--src/i830_driver.c152
-rw-r--r--src/i830_sdvo.c8
-rw-r--r--src/i830_video.c405
7 files changed, 591 insertions, 22 deletions
diff --git a/src/i810_driver.c b/src/i810_driver.c
index 36b6474d..f01c5dc5 100644
--- a/src/i810_driver.c
+++ b/src/i810_driver.c
@@ -262,7 +262,13 @@ typedef enum {
OPTION_NO_DDC,
OPTION_SHOW_CACHE,
OPTION_XVMC_SURFACES,
- OPTION_PAGEFLIP
+ OPTION_PAGEFLIP,
+ OPTION_SYNC_FIELDS,
+ OPTION_YSCALE_FTUNE,
+ OPTION_YRGB_VPHASE,
+ OPTION_UV_VPHASE,
+ OPTION_SCHED_PRIO,
+ OPTION_SYF_DEBUG,
} I810Opts;
static const OptionInfoRec I810Options[] = {
@@ -275,7 +281,13 @@ static const OptionInfoRec I810Options[] = {
{OPTION_NO_DDC, "NoDDC", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_SHOW_CACHE, "ShowCache", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_XVMC_SURFACES, "XvMCSurfaces", OPTV_INTEGER, {0}, FALSE},
- {OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_SYNC_FIELDS, "SyncFields", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_YSCALE_FTUNE, "SF_YScaleFineTune",OPTV_INTEGER,{0}, FALSE},
+ {OPTION_YRGB_VPHASE, "SF_YRGB_VPhase",OPTV_INTEGER, {0}, FALSE},
+ {OPTION_UV_VPHASE, "SF_UV_VPhase", OPTV_INTEGER, {0}, FALSE},
+ {OPTION_SCHED_PRIO, "SF_SchedPrio", OPTV_INTEGER, {0}, FALSE},
+ {OPTION_SYF_DEBUG, "SF_Debug", OPTV_INTEGER, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
/* *INDENT-ON* */
@@ -2861,7 +2873,9 @@ I810ValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags)
xf86DrvMsg(scrnIndex, X_PROBED,
"Removing interlaced mode \"%s\"\n", mode->name);
}
+#if 0 /* allow interlaced mode */
return MODE_BAD;
+#endif
}
return MODE_OK;
}
diff --git a/src/i830.h b/src/i830.h
index eb467250..4be8bff2 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -523,6 +523,13 @@ typedef struct _I830Rec {
Bool *overlayOn;
#endif
+ Bool sync_fields;
+ int YScale_ftune;
+ int YRGB_vphase;
+ int UV_vphase;
+ int SchedPrio;
+ int SYF_debug;
+
/* EXA render state */
float scale_units[2][2];
/** Transform pointers for src/mask, or NULL if identity */
diff --git a/src/i830_crt.c b/src/i830_crt.c
index 26cf4178..86ba9fb9 100644
--- a/src/i830_crt.c
+++ b/src/i830_crt.c
@@ -87,7 +87,7 @@ i830_crt_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
if (pMode->Flags & V_DBLSCAN)
return MODE_NO_DBLESCAN;
- if (pMode->Clock > 400000 || pMode->Clock < 25000)
+ if (pMode->Clock > 400000 || pMode->Clock < 12000) /* lower minimum dotclk */
return MODE_CLOCK_RANGE;
return MODE_OK;
@@ -392,6 +392,10 @@ i830_crt_detect(xf86OutputPtr output)
out:
i830ReleaseLoadDetectPipe (output, dpms_mode);
+ /*
+ * allow Xserver to run even without a CRT connected
+ */
+ status = XF86OutputStatusConnected;
return status;
}
diff --git a/src/i830_display.c b/src/i830_display.c
index 00577dbb..aaf02644 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -95,9 +95,9 @@ typedef struct {
#define I8XX_P2_LVDS_FAST 7
#define I8XX_P2_SLOW_LIMIT 165000
-#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MIN 12000 /* allow for PAL modes */
#define I9XX_DOT_MAX 400000
-#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MIN 1000000 /* allow for PAL modes */
#define I9XX_VCO_MAX 2800000
/* Haven't found any reason to go this fast, but newer chips support it */
@@ -964,6 +964,14 @@ static Bool
i830_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode,
DisplayModePtr adjusted_mode)
{
+ if (mode->Flags & V_INTERLACE) {
+ mode->CrtcVDisplay = adjusted_mode->CrtcVDisplay = mode->VDisplay;
+ mode->CrtcVSyncStart = adjusted_mode->CrtcVSyncStart = mode->VSyncStart;
+ mode->CrtcVSyncEnd = adjusted_mode->CrtcVSyncEnd = mode->VSyncEnd;
+ mode->CrtcVBlankStart = adjusted_mode->CrtcVBlankStart = mode->CrtcVDisplay;
+ mode->CrtcVBlankEnd = adjusted_mode->CrtcVBlankEnd = mode->VTotal;
+ mode->CrtcVTotal = adjusted_mode->CrtcVTotal = mode->VTotal;
+ }
return TRUE;
}
@@ -1355,6 +1363,11 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode,
/* Wait for the clocks to stabilize. */
usleep(150);
+ if (adjusted_mode->Flags & V_INTERLACE) {
+ pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
+ } else {
+ pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION;
+ }
OUTREG(htot_reg, (adjusted_mode->CrtcHDisplay - 1) |
((adjusted_mode->CrtcHTotal - 1) << 16));
OUTREG(hblank_reg, (adjusted_mode->CrtcHBlankStart - 1) |
diff --git a/src/i830_driver.c b/src/i830_driver.c
index a9f44782..ef2b3a21 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -200,6 +200,8 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#if HAVE_SYS_MMAN_H && HAVE_MPROTECT
#include <sys/mman.h>
#endif
+#include "sys/resource.h"
+#include <sched.h>
#ifdef INTEL_XVMC
#define _INTEL_XVMC_SERVER_
@@ -319,6 +321,12 @@ typedef enum {
#ifdef INTEL_XVMC
OPTION_XVMC,
#endif
+ OPTION_SYNC_FIELDS,
+ OPTION_YSCALE_FTUNE,
+ OPTION_YRGB_VPHASE,
+ OPTION_UV_VPHASE,
+ OPTION_SCHED_PRIO,
+ OPTION_SYF_DEBUG,
} I830Opts;
static OptionInfoRec I830Options[] = {
@@ -347,6 +355,12 @@ static OptionInfoRec I830Options[] = {
#ifdef INTEL_XVMC
{OPTION_XVMC, "XvMC", OPTV_BOOLEAN, {0}, TRUE},
#endif
+ {OPTION_SYNC_FIELDS, "SyncFields", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_YSCALE_FTUNE,"SF_YScaleFineTune",OPTV_INTEGER,{0}, FALSE},
+ {OPTION_YRGB_VPHASE, "SF_YRGB_VPhase",OPTV_INTEGER, {0}, FALSE},
+ {OPTION_UV_VPHASE, "SF_UV_VPhase", OPTV_INTEGER, {0}, FALSE},
+ {OPTION_SCHED_PRIO, "SF_SchedPrio", OPTV_INTEGER, {0}, FALSE},
+ {OPTION_SYF_DEBUG, "SF_Debug", OPTV_INTEGER, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
/* *INDENT-ON* */
@@ -1698,6 +1712,69 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
pI830->colorKey);
#endif
+/* --- SYNC FIELDS setup --- */
+ if (xf86GetOptValInteger(pI830->Options, OPTION_SYNC_FIELDS,
+ &(pI830->sync_fields))) {
+ from = X_CONFIG;
+ } else {
+ pI830->sync_fields = TRUE;
+ from = X_DEFAULT;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from, "sync fields %sactivated\n",
+ pI830->sync_fields ? "" : "de");
+ if (pI830->sync_fields) {
+ if (xf86GetOptValInteger(pI830->Options, OPTION_YSCALE_FTUNE,
+ &(pI830->YScale_ftune))) {
+ from = X_CONFIG;
+ } else {
+ pI830->YScale_ftune = 0;
+ from = X_DEFAULT;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from, "vertical scale fine tuning set to %d\n",
+ pI830->YScale_ftune);
+ if (xf86GetOptValInteger(pI830->Options, OPTION_YRGB_VPHASE,
+ &(pI830->YRGB_vphase))) {
+ from = X_CONFIG;
+ } else {
+ pI830->YRGB_vphase = 0;
+ from = X_DEFAULT;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from, "Y/RGB vertical phase set to 0x%x\n",
+ pI830->YRGB_vphase);
+ if (xf86GetOptValInteger(pI830->Options, OPTION_UV_VPHASE,
+ &(pI830->UV_vphase))) {
+ from = X_CONFIG;
+ } else {
+ pI830->UV_vphase = 0;
+ from = X_DEFAULT;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from, "UV vertical phase set to 0x%x\n",
+ pI830->UV_vphase);
+ if (xf86GetOptValInteger(pI830->Options, OPTION_SCHED_PRIO,
+ &(pI830->SchedPrio))) {
+ from = X_CONFIG;
+ } else {
+ pI830->SchedPrio = ~0;
+ from = X_DEFAULT;
+ }
+ if (pI830->SchedPrio == ~0) {
+ xf86DrvMsg(pScrn->scrnIndex, from, "scheduling priority not set explicitly\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, from, "scheduling priority requested %d\n",
+ pI830->SchedPrio);
+ }
+ if (xf86GetOptValInteger(pI830->Options, OPTION_SYF_DEBUG,
+ &(pI830->SYF_debug))) {
+ from = X_CONFIG;
+ } else {
+ pI830->SYF_debug = 0;
+ from = X_DEFAULT;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from, "sync fields debug %sactivated: %d\n",
+ pI830->SYF_debug ? "" : "de", pI830->SYF_debug);
+ }
+/* --- SYNC FIELDS setup --- */
+
#ifdef XF86DRI
pI830->allowPageFlip = FALSE;
from = (!pI830->directRenderingDisabled &&
@@ -1782,6 +1859,81 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
/* Set display resolution */
xf86SetDpi(pScrn, 0, 0);
+/* --- SYNC FIELDS check --- */
+ if (!(pScrn->currentMode->Flags & V_INTERLACE)
+ && pI830->sync_fields) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Cannot support sync fields on non interlaced displays, disabled\n");
+ pI830->sync_fields = 0;
+ }
+
+ /*
+ * sync_fields currently only works with these modelines
+ * ModeLine "1440x576_50i" 27.75 1440 1488 1609 1769 576 580 585 625 -hsync -vsync interlace
+ * Modeline "1600x1200_50i" 65.92 1600 1696 1864 2131 1200 1203 1207 1238 -hsync +vsync interlace
+ */
+ if (pI830->sync_fields &&
+ ((pScrn->currentMode->Clock != 27750 || pScrn->currentMode->HDisplay != 1440 || pScrn->currentMode->VDisplay != 576) &&
+ (pScrn->currentMode->Clock != 65920 || pScrn->currentMode->HDisplay != 1600 || pScrn->currentMode->VDisplay != 1200)
+ ) ) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Cannot support sync fields with current timing, disabled\n");
+ pI830->sync_fields = 0;
+ }
+
+#define MAX_SCHEDPRIO_1CORE -20
+
+ if (pI830->sync_fields) {
+ if (pI830->SchedPrio != ~0) {
+ if (pI830->SchedPrio < MAX_SCHEDPRIO_1CORE) {
+ struct sched_param sched;
+
+ sched.sched_priority = -pI830->SchedPrio;
+ if (sched_setscheduler(0, SCHED_FIFO, &sched)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "failed to set SCHED_FIFO priority as requested: %s\n", strerror(errno));
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "set SCHED_FIFO priority to (policy %d/ priority %d) as requested\n",
+ sched_getscheduler(0),
+ sched_getparam(0, &sched) ? ~0 : sched.sched_priority);
+ }
+ } else if (pI830->SchedPrio) {
+ if (setpriority(PRIO_PROCESS, 0, pI830->SchedPrio)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "failed to set scheduling priority as requested: %s\n", strerror(errno));
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "set scheduling priority to %d as requested\n", getpriority(PRIO_PROCESS, 0));
+ }
+ }
+ } else {
+ FILE *f;
+ int cores = ~0;
+
+ /*
+ * on single core systems enable higher prio per default
+ */
+ if (f = fopen("/proc/cpuinfo", "r")) {
+ char buf[256];
+ while (fgets(buf, 255, f)) {
+#if 0 /* do not change the default priority at the moment */
+ sscanf(buf, "processor : %d", &cores);
+#endif
+ }
+ fclose(f);
+ }
+ if (!cores) {
+ if (setpriority(PRIO_PROCESS, 0, MAX_SCHEDPRIO_1CORE)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "failed to set scheduling priority (single core system): %s\n", strerror(errno));
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "set scheduling priority to %d (single core system)\n", getpriority(PRIO_PROCESS, 0));
+ }
+ }
+ }
+ }
+/* --- SYNC FIELDS check --- */
+
/* Load the required sub modules */
if (!xf86LoadSubModule(pScrn, "fb")) {
PreInitCleanup(pScrn);
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index 0dc6dca3..083f46af 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -1096,7 +1096,15 @@ i830_sdvo_detect(xf86OutputPtr output)
if (response[0] != 0 || response[1] != 0)
return XF86OutputStatusConnected;
else
+
+ /*
+ * allow Xserver to run even without a CRT connected
+ */
+#if 0
return XF86OutputStatusDisconnected;
+#else
+ return XF86OutputStatusConnected;
+#endif
}
static DisplayModePtr
diff --git a/src/i830_video.c b/src/i830_video.c
index 8bbc7ff4..5b4d5d22 100644
--- a/src/i830_video.c
+++ b/src/i830_video.c
@@ -94,6 +94,7 @@
#define TIMER_MASK (OFF_TIMER | FREE_TIMER)
+static void vga_sync_fields(I830Ptr);
static void I830InitOffscreenImages(ScreenPtr);
static XF86VideoAdaptorPtr I830SetupImageVideoOverlay(ScreenPtr);
@@ -603,19 +604,35 @@ I830InitVideo(ScreenPtr pScreen)
xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
xvContrast = MAKE_ATOM("XV_CONTRAST");
- /* Set up textured video if we can do it at this depth and we are on
- * supported hardware.
- */
- if (pScrn->bitsPerPixel >= 16 && (IS_I9XX(pI830) || IS_I965G(pI830)) &&
- !(!IS_I965G(pI830) && pScrn->displayWidth > 2048))
- {
- texturedAdaptor = I830SetupImageVideoTextured(pScreen);
- if (texturedAdaptor != NULL) {
- adaptors[num_adaptors++] = texturedAdaptor;
- xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Set up textured video\n");
- } else {
- xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
- "Failed to set up textured video\n");
+#if 0 /* always use overlay */
+ if (pI830->sync_fields) {
+#else
+ if (1) {
+#endif
+ /*
+ * we deactivate textured XV method for compatibility with older xine-lib
+ * versions not providing a configuration parameter for this.
+ * for further information see:
+ * 'video.device.xv_preferred_method' in file 'config_xineliboutput'
+ */
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VGA_SYNC_FIELDS disabled textured video mode\n");
+ } else {
+
+ /*
+ * Set up textured video if we can do it at this depth and we are on
+ * supported hardware.
+ */
+ if (pScrn->bitsPerPixel >= 16 && (IS_I9XX(pI830) || IS_I965G(pI830)) &&
+ !(!IS_I965G(pI830) && pScrn->displayWidth > 2048))
+ {
+ texturedAdaptor = I830SetupImageVideoTextured(pScreen);
+ if (texturedAdaptor != NULL) {
+ adaptors[num_adaptors++] = texturedAdaptor;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Set up textured video\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to set up textured video\n");
+ }
}
}
@@ -2013,7 +2030,11 @@ i830_display_video(ScrnInfoPtr pScrn, xf86CrtcPtr crtc,
* Y down-scale factor as a multiple of 4096.
*/
xscaleFract = ((src_w - 1) << 12) / drw_w;
- yscaleFract = ((src_h - 1) << 12) / drw_h;
+ if (pI830->sync_fields) {
+ yscaleFract = (((src_h >> 1) - 1 + pI830->YScale_ftune) << 12) / drw_h;
+ } else {
+ yscaleFract = ((src_h - 1) << 12) / drw_h;
+ }
/* Calculate the UV scaling factor. */
xscaleFractUV = xscaleFract / uvratio;
@@ -2113,9 +2134,15 @@ i830_display_video(ScrnInfoPtr pScrn, xf86CrtcPtr crtc,
}
}
}
-
- OCMD = OVERLAY_ENABLE;
-
+ if (pI830->sync_fields) {
+ OCMD = OVERLAY_ENABLE | BUF_TYPE_FIELD | TVSYNC_FLIP_ENABLE;
+ overlay->YRGB_VPH = pI830->YRGB_vphase;
+ overlay->UV_VPH = pI830->UV_vphase;
+ overlay->HORZ_PH = 0;
+ overlay->INIT_PHS = 0;
+ } else {
+ OCMD = OVERLAY_ENABLE;
+ }
switch (id) {
case FOURCC_YV12:
case FOURCC_I420:
@@ -2525,6 +2552,9 @@ I830PutImage(ScrnInfoPtr pScrn,
}
if (!pPriv->textured) {
+ if (pI830->sync_fields) {
+ vga_sync_fields(pI830);
+ }
i830_display_video(pScrn, crtc, destId, width, height, dstPitch,
x1, y1, x2, y2, &dstBox, src_w, src_h,
drw_w, drw_h);
@@ -2964,3 +2994,344 @@ i830_crtc_dpms_video(xf86CrtcPtr crtc, Bool on)
pPriv->oneLineMode = FALSE;
}
}
+
+/* --- 8< --- */
+/*
+ * field cycle duration in usecs for PAL
+ */
+#define SYF_PAL_FIELD_CYCLE 20000
+
+/*
+ * frame cycle duration in usecs for PAL
+ */
+#define SYF_PAL_FRAME_CYCLE (SYF_PAL_FIELD_CYCLE << 1)
+
+/*
+ * dependent on interlaced (progressive) input frame rate 25 (50) fps
+ * we set this to 0 (1). other params should adjust accordingly.
+ */
+#define SYF_INPUT_DOUBLE_RATE 0
+
+/* NOT CURRENTLY USED
+ * prefilter to prevent stray updates.
+ * our software PLL will not try to lock for these.
+ */
+#define SYF_CATCH_RANGE (SYF_PAL_FRAME_CYCLE - 500 >> SYF_INPUT_DOUBLE_RATE)
+
+/*
+ * we average 25 (50) frames to yield a cycle time of
+ * about one second for analysis of frame rate data.
+ * this serves as frequency divider for our software PLL.
+ */
+#define SYF_PLL_DIVIDER (25 << SYF_INPUT_DOUBLE_RATE)
+
+/*
+ * input frame cycle duration in usecs for 25 (50) fps
+ */
+#define SYF_FRAME_CYCLE (SYF_PAL_FRAME_CYCLE >> SYF_INPUT_DOUBLE_RATE)
+
+/* NOT CURRENTLY USED
+ * we slightly displace sync point from center position. this makes the
+ * system less susceptible to temporary field delivery irregularities.
+ */
+#define SYF_SYNC_POINT_OFFSET 0
+
+/*
+ * offset in usecs from double buffer switch where we preferredly try to place double
+ * buffer updates.
+ * this minimizes sensivity to jitter of our software PLL phase comparator.
+ */
+#define SYF_SYNC_POINT (SYF_PAL_FIELD_CYCLE - SYF_SYNC_POINT_OFFSET)
+
+/*
+ * one trim increment compensates drift speed for about 29usec/sec.
+ * this represents resolution of our VCO input.
+ */
+#define SYF_MIN_STEP_USEC 700
+
+/* NOT CURRENTLY USED
+ * factor weighting drift against sync point displacement
+ * when calculating overall compensation
+ */
+#define SYF_DISP_DRIFT_FACTOR 1
+
+/*
+ * to allow for smooth adaption also on slower devices we delimit maximum
+ * trim change per step size.
+ * this implements some kind of low pass filter for our VCO input.
+ */
+#define SYF_MAX_TRIM_REL 1
+
+/*
+ * maximum absolute trim values allowed due to current
+ * hardware/driver contraints.
+ * this delimits 'maximum voltage' controlling our VCO.
+ * currently allowed range is defined symmetrically around the center voltage.
+ */
+#define SYF_MAX_TRIM_ABS 2
+
+#ifdef STANDALONE
+
+#define DOVSTA 0x30008
+#define HTOTAL_A 0x60000
+#define HBLANK_A 0x60004
+#define HSYNC_A 0x60008
+#define PIPEA_DSL 0x70000
+#define PIPEACONF 0x70008
+
+#define ErrorF printf
+#define B(a) (*(argv + (a)) ? *(argv + (a)) : 0)
+#define INREG(reg) *(volatile unsigned *)(vptr + (reg))
+#define OUTREG(reg, val) INREG(reg) = (val)
+#define min(a, b) ((a) <= (b) ? (a) : (b))
+#define max(a, b) ((a) >= (b) ? (a) : (b))
+#define abs(a) ((a) >= 0 ? (a) : -(a))
+
+#define VSF_SUB(a, b, c) \
+ if ((a).tv_usec < (b).tv_usec) { \
+ (c).tv_sec = (a).tv_sec - (b).tv_sec - 1; \
+ (c).tv_usec = (a).tv_usec - (b).tv_usec + 1000000; \
+ } else { \
+ (c).tv_sec = (a).tv_sec - (b).tv_sec; \
+ (c).tv_usec = (a).tv_usec - (b).tv_usec; \
+ }
+
+#define VSF_TV2USEC(a, b) \
+ (b) = (a).tv_sec * 1000000 + (a).tv_usec;
+
+struct _I830_s {
+ int SYF_debug;
+} I830 = {
+ 801,
+}, *pI830 = &I830;
+
+#else
+
+#define B(a) (a)
+
+#endif
+
+#include <time.h>
+#include <unistd.h>
+
+#define OC_FIELD (1 << 19)
+
+typedef struct _syf_t {
+ int log;
+ int cnt;
+ int trim;
+ int drift;
+ int spoint;
+
+ /* HW specific */
+ int lfield;
+ int deadln;
+ int dyzone;
+ int tshift;
+ int hsync_a;
+ int htotal_a;
+} syf_t;
+
+extern void log_graph(int, int);
+
+void
+log_graph(val, symb)
+{
+ static char headr[] = "|<- -20ms 0 +20ms ->|";
+ static char meter[sizeof(headr)];
+
+ if (!symb || symb == 1) {
+ time_t t; struct tm *tm;
+
+ time(&t); tm = localtime(&t);
+ ErrorF("%02d:%02d:%02d %s", tm->tm_hour, tm->tm_min, tm->tm_sec, symb ? meter : headr);
+ memset(meter, '-', sizeof(headr) - 1);
+ return;
+ }
+ val = (SYF_SYNC_POINT + val) / 500;
+ val = min(val, (int)sizeof(headr) - 2);
+ val = max(val, 0);
+ if (symb == ':') {
+ meter[val] = meter[val] == '%' ? '#' : symb;
+ } else if (symb == '*') {
+ meter[val] = meter[val] == '%' ? '1' :
+ meter[val] == ':' ? '2' :
+ meter[val] == '#' ? '3' : symb;
+ } else {
+ meter[val] = symb;
+ }
+}
+/* --- 8< --- */
+
+void
+vga_sync_fields(pI830)
+ I830Ptr pI830;
+{
+ static syf_t syf;
+ static int vbl_usec_prev = ~0;
+ int dovsta, dovsta_1, pipea_dsl, pipea_dsl_1, trim, vbl_usec;
+
+/* --- 8< --- */
+ if (!syf.htotal_a) {
+ syf.htotal_a = INREG(HTOTAL_A);
+ syf.hsync_a = INREG(HSYNC_A);
+ switch (syf.htotal_a & 0x0000ffff) {
+ case 1439:
+ syf.lfield = 313;
+ syf.deadln = 287;
+ syf.dyzone = 32;
+ syf.tshift = 6;
+ break;
+ case 1599:
+ syf.lfield = 619;
+ syf.deadln = 599;
+ syf.dyzone = 0;
+ syf.tshift = 5;
+ break;
+ }
+ }
+ trim = (INREG(HTOTAL_A) >> 16) - (syf.htotal_a >> 16);
+
+ /*
+ * the chip does not provide current field status directly.
+ * but we can derive that from xor'ed dovsta and pipea_dsl contents.
+ * _______________ ________
+ * ____| |_______________| !(dovsta & OC_FIELD)
+ * ____ ___________ ___________ ____
+ * |___| |___| |___| pipea_dsl < DEADLN
+ * ________ 287 _______________
+ * |_______________| |____ field status
+ * 0 313
+ *
+ * "1440x576_50i" 27.75 1440 1488 1609 1769 576 580 585 625 -hsync -vsync interlace
+ *
+ * DOVSTA 0x80104000 PIPEA_DSL 0x00000000 0 0 0
+ * [...]
+ * DOVSTA 0x00104000 PIPEA_DSL 0x0000011e 286 286 18304
+ * DOVSTA 0x80085000 PIPEA_DSL 0x0000011f 287 287 18368
+ * [...]
+ * DOVSTA 0x80184000 PIPEA_DSL 0x00000138 312 312 19968
+ * DOVSTA 0x80184000 PIPEA_DSL 0x00000000 0 313 20032
+ * [...]
+ * DOVSTA 0x80184000 PIPEA_DSL 0x0000011e 286 599 38336
+ * DOVSTA 0x80105000 PIPEA_DSL 0x0000011f 287 600 38400
+ * [...]
+ * DOVSTA 0x80104000 PIPEA_DSL 0x00000138 312 625 40000
+ * DOVSTA 0x80104000 PIPEA_DSL 0x00000000 0 0 0
+ *
+ * "1600x1200_50i" 65.92 1600 1696 1864 2131 1200 1203 1207 1238 -hsync +vsync interlace
+ *
+ * DOVSTA 0x80104000 PIPEA_DSL 0x00000000 0 0 0
+ * [...]
+ * DOVSTA 0x80100000 PIPEA_DSL 0x00000256 598 598 19136
+ * DOVSTA 0x80185000 PIPEA_DSL 0x00000257 599 599 19168
+ * [...]
+ * DOVSTA 0x80184000 PIPEA_DSL 0x0000026a 618 618 19776
+ * DOVSTA 0x80180000 PIPEA_DSL 0x00000000 0 619 19808
+ * [...]
+ * DOVSTA 0x80184000 PIPEA_DSL 0x00000256 598 1217 38944
+ * DOVSTA 0x80105000 PIPEA_DSL 0x00000257 599 1218 38976
+ * [...]
+ * DOVSTA 0x80100000 PIPEA_DSL 0x0000026a 618 1237 39584
+ * DOVSTA 0x80104000 PIPEA_DSL 0x00000000 0 0 0
+ */
+
+#define SHIFTV (syf.lfield - syf.deadln)
+#define LFRAME (syf.lfield << 1)
+
+ /*
+ * the next few lines implement a simple glitch detection/correction
+ */
+ dovsta = INREG(DOVSTA);
+ pipea_dsl = INREG(PIPEA_DSL);
+ dovsta_1 = INREG(DOVSTA);
+ pipea_dsl_1 = INREG(PIPEA_DSL);
+ if ((dovsta ^ dovsta_1) & OC_FIELD && pipea_dsl == pipea_dsl_1) {
+ dovsta = ~dovsta;
+ }
+
+ /* 287 + 26 (+ 313) == 313 (626) */
+ vbl_usec = ((pipea_dsl < syf.deadln ^ !(dovsta & OC_FIELD)) * syf.lfield + pipea_dsl + SHIFTV) % LFRAME << syf.tshift;
+
+ if (vbl_usec_prev == ~0) {
+ vbl_usec_prev = vbl_usec;
+ log_graph(0, 0);
+ ErrorF(" R\n");
+#ifdef STANDALONE
+ goto main_loop_end;
+#else
+ return;
+#endif
+ }
+ if (pI830->SYF_debug) {
+ if (abs(vbl_usec - vbl_usec_prev) > pI830->SYF_debug) {
+ log_graph(vbl_usec - vbl_usec_prev, '%'); ++syf.log;
+ }
+ if (abs(vbl_usec - SYF_SYNC_POINT) > pI830->SYF_debug) {
+ log_graph(vbl_usec - SYF_SYNC_POINT, ':'); ++syf.log;
+ }
+ }
+
+#ifndef STANDALONE
+
+ /*
+ * we must delay the next double buffer update
+ * until even field has been processed. this occurs
+ * after scan line 286 has been passed.
+ * on a sufficiently synchronized system running at a
+ * sync point of about 20000 usecs this imposes
+ * no additional sleep time.
+ *
+ * 0 even 20000 odd 40000
+ * |-------------------|-------------------|
+ * ...---sleep--->|
+ * 18368
+ */
+ if (SYF_PAL_FIELD_CYCLE - vbl_usec > 0) {
+ usleep(SYF_PAL_FIELD_CYCLE - vbl_usec + syf.dyzone);
+ }
+#endif
+ syf.spoint += vbl_usec - SYF_SYNC_POINT;
+ syf.drift += vbl_usec - vbl_usec_prev;
+ vbl_usec_prev = vbl_usec;
+ ++syf.cnt;
+ if (!(syf.cnt % SYF_PLL_DIVIDER)) {
+ syf.spoint /= SYF_PLL_DIVIDER;
+ syf.trim = (syf.drift + syf.spoint / SYF_DISP_DRIFT_FACTOR) / SYF_MIN_STEP_USEC;
+ syf.trim = max(syf.trim, -SYF_MAX_TRIM_REL);
+ syf.trim = min(syf.trim, SYF_MAX_TRIM_REL);
+
+ if (pI830->SYF_debug & 1 || syf.log) {
+ log_graph(0, '+');
+ log_graph(syf.spoint, '|');
+ log_graph(syf.drift, '*');
+ log_graph(0, 1);
+ ErrorF("%7d %7d [%3d%+4d]\n",
+ syf.drift, syf.spoint, (char)(trim & 0xff), syf.trim);
+ }
+ syf.spoint = 0;
+ syf.drift = 0;
+ syf.log = 0;
+ }
+ if (B(1) && syf.trim) {
+ int t = (char)(trim & 0xff);
+
+ if (syf.trim > 0) {
+ t = min(t + 1, SYF_MAX_TRIM_ABS);
+ --syf.trim;
+ } else {
+ t = max(t - 1, -SYF_MAX_TRIM_ABS);
+ ++syf.trim;
+ }
+ if (trim != t) {
+ t <<= 16;
+ OUTREG(HTOTAL_A, syf.htotal_a + t);
+ OUTREG(HBLANK_A, syf.htotal_a + t);
+ OUTREG(HSYNC_A, syf.hsync_a + (t << 1));
+ OUTREG(PIPEACONF, INREG(PIPEACONF));
+ }
+ }
+/* --- 8< --- */
+
+}
+