diff --git a/src/fix.5c b/src/fix.5c new file mode 100644 index 00000000..b758a433 --- /dev/null +++ b/src/fix.5c @@ -0,0 +1,14 @@ +/* + * Convert CSC fix point values to floats + */ + +real fixval (int fix) +{ + int exp = fix >> 9; + int mant = fix & ((1 << 9) - 1); + real ret; + if (exp == 0x7) + return 1.0; + ret = (2 ** -exp) * mant / (1 << 9); + return ret; +} diff --git a/src/i830.h b/src/i830.h index e484fce1..20bcde66 100644 --- a/src/i830.h +++ b/src/i830.h @@ -511,6 +511,7 @@ extern void I830SelectBuffer(ScrnInfoPtr pScrn, int buffer); extern void I830RefreshRing(ScrnInfoPtr pScrn); extern void I830EmitFlush(ScrnInfoPtr pScrn); +extern Bool I830DGAReInit(ScreenPtr pScreen); extern Bool I830DGAInit(ScreenPtr pScreen); #ifdef I830_XV diff --git a/src/i830_dga.c b/src/i830_dga.c index b53b667e..c312c6d6 100644 --- a/src/i830_dga.c +++ b/src/i830_dga.c @@ -83,8 +83,8 @@ DGAFunctionRec I830DGAFuncs = { #endif }; -Bool -I830DGAInit(ScreenPtr pScreen) +static DGAModePtr +I830DGAModes (ScreenPtr pScreen, int *nump) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); @@ -93,8 +93,6 @@ I830DGAInit(ScreenPtr pScreen) int Bpp = pScrn->bitsPerPixel >> 3; int num = 0; - MARKER(); - pMode = firstMode = pScrn->modes; while (pMode) { @@ -103,7 +101,7 @@ I830DGAInit(ScreenPtr pScreen) if (!newmodes) { xfree(modes); - return FALSE; + return NULL; } modes = newmodes; @@ -159,7 +157,42 @@ I830DGAInit(ScreenPtr pScreen) if (pMode == firstMode) break; } + *nump = num; + return modes; +} +Bool +I830DGAReInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + int num; + DGAModePtr modes; + + modes = I830DGAModes (pScreen, &num); + if (!modes) + return FALSE; + + if (pI830->DGAModes) + xfree (pI830->DGAModes); + + pI830->numDGAModes = num; + pI830->DGAModes = modes; + return DGAReInitModes (pScreen, modes, num); +} + +Bool +I830DGAInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + I830Ptr pI830 = I830PTR(pScrn); + int num; + DGAModePtr modes; + + modes = I830DGAModes (pScreen, &num); + if (!modes) + return FALSE; + pI830->numDGAModes = num; pI830->DGAModes = modes; diff --git a/src/i830_driver.c b/src/i830_driver.c index 41bd0678..23729de5 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -3604,6 +3604,7 @@ I830CheckDevicesTimer(OsTimerPtr timer, CARD32 now, pointer arg) xf86ProbeOutputModes (pScrn); xf86SetScrnInfoModes (pScrn); + I830DGAReInit (pScrn->pScreen); xf86SwitchMode(pScrn->pScreen, pScrn->currentMode); /* Clear the BIOS's hotkey press flags */ diff --git a/src/i830_randr.c b/src/i830_randr.c index 0a103846..3d6febc1 100644 --- a/src/i830_randr.c +++ b/src/i830_randr.c @@ -97,6 +97,7 @@ xf86RandR12GetInfo (ScreenPtr pScreen, Rotation *rotations) /* Re-probe the outputs for new monitors or modes */ xf86ProbeOutputModes (scrp); xf86SetScrnInfoModes (scrp); + I830DGAReInit (pScreen); for (mode = scrp->modes; ; mode = mode->next) { @@ -787,6 +788,7 @@ xf86RandR12GetInfo12 (ScreenPtr pScreen, Rotation *rotations) xf86ProbeOutputModes (pScrn); xf86SetScrnInfoModes (pScrn); + I830DGAReInit (pScreen); return xf86RandR12SetInfo12 (pScreen); } diff --git a/src/i830_tv.c b/src/i830_tv.c index 05b68259..0b43908a 100644 --- a/src/i830_tv.c +++ b/src/i830_tv.c @@ -83,61 +83,20 @@ struct i830_tv_priv { CARD32 save_TV_CTL; }; -enum burst_modes { - TV_SC_NTSC_MJ, - TV_SC_PAL, - TV_SC_PAL_NC, - TV_SC_PAL_M, - TV_SC_NTSC_443 -}; +typedef struct { + int blank, black, burst; +} video_levels_t; -const struct tv_sc_mode { - char *name; - int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; - CARD32 sc_reset; - Bool pal_burst; -} tv_sc_modes[] = { - [TV_SC_NTSC_MJ] = { - "NTSC M/J", - 27456, 0, 135, 20800, 0, - TV_SC_RESET_EVERY_4, - FALSE - }, - [TV_SC_PAL] = { - "PAL", - 27648, 625, 168, 4122, 67, - TV_SC_RESET_EVERY_8, - TRUE - }, - [TV_SC_PAL_NC] = { - "PAL Nc", - 27648, 625, 135, 23578, 134, - TV_SC_RESET_EVERY_8, - TRUE - }, - [TV_SC_PAL_M] = { - "PAL M", - 27456, 0, 135, 16704, 0, - TV_SC_RESET_EVERY_8, - TRUE - }, - [TV_SC_NTSC_443] = { - "NTSC-4.43", - 27456, 525, 168, 4093, 310, - TV_SC_RESET_NEVER, - FALSE - }, -}; +typedef struct { + float ry, gy, by, ay; + float ru, gu, bu, au; + float rv, gv, bv, av; +} color_conversion_t; -/** - * Register programming values for TV modes. - * - * These values account for -1s required. - */ -const struct tv_mode { +typedef struct { char *name; CARD32 oversample; - int hsync_end, hblank_end, hblank_start, htotal; + int hsync_end, hblank_start, hblank_end, htotal; Bool progressive; int vsync_start_f1, vsync_start_f2, vsync_len; Bool veq_ena; @@ -149,20 +108,264 @@ const struct tv_mode { int vburst_start_f2, vburst_end_f2; int vburst_start_f3, vburst_end_f3; int vburst_start_f4, vburst_end_f4; -} tv_modes[] = { + /* + * subcarrier programming + */ + int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; + CARD32 sc_reset; + Bool pal_burst; + /* + * blank/black levels + */ + video_levels_t composite_levels, svideo_levels; + color_conversion_t composite_color, svideo_color; +} tv_mode_t; + +#define TV_PLL_CLOCK 107520 + +/* + * Sub carrier DDA + * + * I think this works as follows: + * + * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 + * + * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value + * + * So, + * dda1_ideal = subcarrier/pixel * 4096 + * dda1_inc = floor (dda1_ideal) + * dda2 = dda1_ideal - dda1_inc + * + * then pick a ratio for dda2 that gives the closest approximation. If + * you can't get close enough, you can play with dda3 as well. This + * seems likely to happen when dda2 is small as the jumps would be larger + * + * To invert this, + * + * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) + * + * The constants below were all computed using a 107.520MHz clock + */ + +/** + * Register programming values for TV modes. + * + * These values account for -1s required. + */ + +const tv_mode_t tv_modes[] = { { - "480i", - TV_OVERSAMPLE_8X, - 64, 124, 836, 857, - FALSE, - 6, 7, 6, - TRUE, 0, 1, 18, - 20, 21, 240, - TRUE, - 72, 34, 9, 240, 10, 240, 9, 240, 10, 240 + .name = "NTSC 480i", + .oversample = TV_OVERSAMPLE_8X, + + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = FALSE, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = TRUE, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = TRUE, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 3.5800000 actual 3.5800000 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 7624, .dda2_size = 20013, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_4, + .pal_burst = FALSE, + + .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, + .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, + .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, + }, + + .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, + .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, + .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, + }, + }, + { + .name = "NTSC-Japan 480i", + .oversample = TV_OVERSAMPLE_8X, + + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + .hsync_end = 64, .hblank_end = 124, + .hblank_start = 836, .htotal = 857, + + .progressive = FALSE, + + .vsync_start_f1 = 6, .vsync_start_f2 = 7, + .vsync_len = 6, + + .veq_ena = TRUE, .veq_start_f1 = 0, + .veq_start_f2 = 1, .veq_len = 18, + + .vi_end_f1 = 20, .vi_end_f2 = 21, + .nbr_end = 240, + + .burst_ena = TRUE, + .hburst_start = 72, .hburst_len = 34, + .vburst_start_f1 = 9, .vburst_end_f1 = 240, + .vburst_start_f2 = 10, .vburst_end_f2 = 240, + .vburst_start_f3 = 9, .vburst_end_f3 = 240, + .vburst_start_f4 = 10, .vburst_end_f4 = 240, + + /* desired 3.5800000 actual 3.5800000 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 7624, .dda2_size = 20013, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_4, + .pal_burst = FALSE, + + .composite_levels = { .blank = 225, .black = 225, .burst = 113 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5495, + .ru =-0.0810, .gu =-0.1590, .bu = 0.2400, .au = 1.0000, + .rv = 0.3378, .gv =-0.2829, .bv =-0.0549, .av = 1.0000, + }, + + .svideo_levels = { .blank = 266, .black = 266, .burst = 133 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6494, + .ru =-0.0957, .gu =-0.1879, .bu = 0.2836, .au = 1.0000, + .rv = 0.3992, .gv =-0.3343, .bv =-0.0649, .av = 1.0000, + }, } +#if 0 + { + /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ + .name = "PAL", + /* desired 4.4336180 actual 4.4336180 clock 107.52 */ + .dda1_inc = 168, + .dda2_inc = 18557, .dda2_size = 20625, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = TRUE + + .composite_levels = { .blank = 237, .black = 237, .burst = 118 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5379, + .ru =-0.0793, .gu =-0.1557, .bu = 0.2350, .au = 1.0000, + .rv = 0.3307, .gv =-0.2769, .bv =-0.0538, .av = 1.0000, + }, + + .svideo_levels = { .blank = 280, .black = 280, .burst = 139 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6357, + .ru =-0.0937, .gu =-0.1840, .bu = 0.2777, .au = 1.0000, + .rv = 0.3908, .gv =-0.3273, .bv =-0.0636, .av = 1.0000, + }, + }, + { + /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.576MHz */ + .name = "PAL M", + /* desired 3.5756110 actual 3.5756110 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 5611, .dda2_size = 26250, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = TRUE + + .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, + .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, + .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, + }, + + .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, + .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, + .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, + }, + }, + { + /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 3.582MHz */ + .name = "PAL Nc", + /* desired 3.5820560 actual 3.5820560 clock 107.52 */ + .dda1_inc = 136, + .dda2_inc = 12056, .dda2_size = 26250, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_EVERY_8, + .pal_burst = TRUE + + .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, + .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, + .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, + }, + + .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, + .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, + .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, + }, + }, + { + /* 525 lines, 60 fields, 15.734KHz line, Sub-Carrier 4.43MHz */ + .name = "NTSC-4.43(nonstandard)", + /* desired 4.4336180 actual 4.4336180 clock 107.52 */ + .dda1_inc = 168, + .dda2_inc = 18557, .dda2_size = 20625, + .dda3_inc = 0, .dda3_size = 0, + .sc_reset = TV_SC_RESET_NEVER, + .pal_burst = FALSE + + .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, + .composite_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, + .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, + .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, + }, + + .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, + .svideo_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, + .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, + .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, + }, + }, +#endif }; +static const video_levels_t component_level = { + .blank = 279, .black = 279 +}; + +static const color_conversion_t sdtv_component_color = { + .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6364, + .ru =-0.1687, .gu =-0.3313, .bu = 0.5000, .au = 1.0000, + .rv = 0.5000, .gv =-0.4187, .bv =-0.0813, .av = 1.0000, +}; + +static const color_conversion_t hdtv_component_color = { + .ry = 0.2126, .gy = 0.7152, .by = 0.0722, .ay = 0.6364, + .ru =-0.1146, .gu =-0.3854, .bu = 0.5000, .au = 1.0000, + .rv = 0.5000, .gv =-0.4542, .bv =-0.0458, .av = 1.0000, +}; + static void i830_tv_dpms(xf86OutputPtr output, int mode) { @@ -377,6 +580,43 @@ i830_tv_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, return TRUE; } +static CARD32 +i830_float_to_csc (float fin) +{ + CARD32 exp; + CARD32 mant; + CARD32 ret; + float f = fin; + + /* somehow the color conversion knows the signs of all the values */ + if (f < 0) f = -f; + + if (f >= 1) + { + exp = 0x7; + mant = 1 << 8; + } + else + { + for (exp = 0; exp < 3 && f < 0.5; exp++) + f *= 2.0; + mant = (f * (1 << 9) + 0.5); + if (mant >= (1 << 9)) + mant = (1 << 9) - 1; + } + ret = (exp << 9) | mant; + return ret; +} + +static CARD16 +i830_float_to_luma (float f) +{ + CARD16 ret; + + ret = (f * (1 << 9)); + return ret; +} + static void i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) @@ -387,14 +627,15 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, I830OutputPrivatePtr intel_output = output->driver_private; I830CrtcPrivatePtr intel_crtc = crtc->driver_private; struct i830_tv_priv *dev_priv = intel_output->dev_priv; - enum tv_type type; - const struct tv_mode *tv_mode; - const struct tv_sc_mode *sc_mode; + const tv_mode_t *tv_mode; CARD32 tv_ctl, tv_filter_ctl; CARD32 hctl1, hctl2, hctl3; CARD32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; CARD32 scctl1, scctl2, scctl3; int i; + const video_levels_t *video_levels; + const color_conversion_t *color_conversion; + Bool burst_ena; /* Need to actually choose or construct the appropriate * mode. For now, just set the first one in the list, with @@ -402,16 +643,40 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, */ OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE); tv_mode = &tv_modes[0]; - sc_mode = &tv_sc_modes[TV_SC_NTSC_MJ]; - - type = dev_priv->type; + + tv_ctl = 0; + switch (dev_priv->type) { + default: + case TV_TYPE_UNKNOWN: + case TV_TYPE_COMPOSITE: + tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; + video_levels = &tv_mode->composite_levels; + color_conversion = &tv_mode->composite_color; + burst_ena = tv_mode->burst_ena; + break; + case TV_TYPE_COMPONENT: + tv_ctl |= TV_ENC_OUTPUT_COMPONENT; + video_levels = &component_level; + if (tv_mode->burst_ena) + color_conversion = &sdtv_component_color; + else + color_conversion = &hdtv_component_color; + burst_ena = FALSE; + break; + case TV_TYPE_SVIDEO: + tv_ctl |= TV_ENC_OUTPUT_SVIDEO; + video_levels = &tv_mode->svideo_levels; + color_conversion = &tv_mode->svideo_color; + burst_ena = tv_mode->burst_ena; + break; + } hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | (tv_mode->htotal << TV_HTOTAL_SHIFT); hctl2 = (tv_mode->hburst_start << 16) | (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); - if (tv_mode->burst_ena) + if (burst_ena) hctl2 |= TV_BURST_ENA; hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | @@ -443,44 +708,32 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); - tv_ctl = 0; if (intel_crtc->pipe == 1) tv_ctl |= TV_ENC_PIPEB_SELECT; - switch (type) { - case TV_TYPE_COMPOSITE: - tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; - break; - case TV_TYPE_COMPONENT: - tv_ctl |= TV_ENC_OUTPUT_COMPONENT; - break; - case TV_TYPE_SVIDEO: - tv_ctl |= TV_ENC_OUTPUT_SVIDEO; - break; - default: - case TV_TYPE_UNKNOWN: - tv_ctl |= TV_ENC_OUTPUT_SVIDEO_COMPOSITE; - break; - } tv_ctl |= tv_mode->oversample; if (tv_mode->progressive) tv_ctl |= TV_PROGRESSIVE; - if (sc_mode->pal_burst) + if (tv_mode->pal_burst) tv_ctl |= TV_PAL_BURST; - scctl1 = TV_SC_DDA1_EN | TV_SC_DDA2_EN; - if (sc_mode->dda3_size != 0) + scctl1 = TV_SC_DDA1_EN; + + if (tv_mode->dda2_inc) + scctl1 |= TV_SC_DDA2_EN; + + if (tv_mode->dda3_inc) scctl1 |= TV_SC_DDA3_EN; - scctl1 |= sc_mode->sc_reset; - /* XXX: set the burst level */ - scctl1 |= 113 << TV_BURST_LEVEL_SHIFT; /* from BIOS */ - scctl1 |= sc_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; + + scctl1 |= tv_mode->sc_reset; + scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; + scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; - scctl2 = sc_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | - sc_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; + scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | + tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; - scctl3 = sc_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | - sc_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; + scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | + tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; /* Enable two fixes for the chips that need them. */ if (pI830->PciInfo->chipType < PCI_CHIP_I945_G) @@ -503,17 +756,37 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, OUTREG(TV_SC_CTL_1, scctl1); OUTREG(TV_SC_CTL_2, scctl2); OUTREG(TV_SC_CTL_3, scctl3); - /* XXX match BIOS */ - OUTREG(TV_CSC_Y, 0x0332012D); - OUTREG(TV_CSC_Y2, 0x07D30133); - OUTREG(TV_CSC_U, 0x076A0564); - OUTREG(TV_CSC_U2, 0x030D0200); - OUTREG(TV_CSC_V, 0x037A033D); - OUTREG(TV_CSC_V2, 0x06F60200); + + OUTREG(TV_CSC_Y, + (i830_float_to_csc(color_conversion->ry) << 16) | + (i830_float_to_csc(color_conversion->gy))); + OUTREG(TV_CSC_Y2, + (i830_float_to_csc(color_conversion->by) << 16) | + (i830_float_to_luma(color_conversion->ay))); + + OUTREG(TV_CSC_U, + (i830_float_to_csc(color_conversion->ru) << 16) | + (i830_float_to_csc(color_conversion->gu))); + + OUTREG(TV_CSC_U2, + (i830_float_to_csc(color_conversion->bu) << 16) | + (i830_float_to_luma(color_conversion->au))); + + OUTREG(TV_CSC_V, + (i830_float_to_csc(color_conversion->rv) << 16) | + (i830_float_to_csc(color_conversion->gv))); + + OUTREG(TV_CSC_V2, + (i830_float_to_csc(color_conversion->bv) << 16) | + (i830_float_to_luma(color_conversion->av))); + OUTREG(TV_CLR_KNOBS, 0x00606000); - OUTREG(TV_CLR_LEVEL, 0x013C010A); + OUTREG(TV_CLR_LEVEL, ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | + (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); + OUTREG(TV_WIN_POS, 0x00360024); OUTREG(TV_WIN_SIZE, 0x02640198); + OUTREG(TV_FILTER_CTL_1, 0x8000085E); OUTREG(TV_FILTER_CTL_2, 0x00017878); OUTREG(TV_FILTER_CTL_3, 0x0000BC3C); @@ -530,10 +803,10 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, OUTREG(TV_CTL, tv_ctl); } -static const DisplayModeRec tvModes[] = { +static const DisplayModeRec reported_modes[] = { { .name = "NTSC 480i", - .Clock = 108000, + .Clock = TV_PLL_CLOCK, .HDisplay = 1024, .HSyncStart = 1048, @@ -545,7 +818,7 @@ static const DisplayModeRec tvModes[] = { .VSyncEnd = 777, .VTotal = 806, - .type = M_T_DEFAULT + .type = M_T_DRIVER } }; @@ -644,7 +917,8 @@ i830_tv_detect(xf86OutputPtr output) { if (intel_output->load_detect_temp) { - mode = tvModes[0]; + /* we only need the pixel clock set correctly here */ + mode = reported_modes[0]; xf86SetModeCrtc (&mode, INTERLACE_HALVE_V); i830PipeSetMode (crtc, &mode, FALSE); } @@ -671,43 +945,25 @@ i830_tv_detect(xf86OutputPtr output) static DisplayModePtr i830_tv_get_modes(xf86OutputPtr output) { - ScrnInfoPtr pScrn = output->scrn; - I830Ptr pI830 = I830PTR(pScrn); - DisplayModePtr new; - char stmp[32]; + ScrnInfoPtr pScrn = output->scrn; + I830Ptr pI830 = I830PTR(pScrn); + DisplayModePtr new, first = NULL, *tail = &first;; + int i; (void) pI830; - new = xnfcalloc(1, sizeof (DisplayModeRec)); - sprintf(stmp, "480i"); - new->name = xnfalloc(strlen(stmp) + 1); - strcpy(new->name, stmp); - - new->Clock = 108000; - - /* - new->HDisplay = 640; - new->HSyncStart = 664; - new->HSyncEnd = 704; - new->HTotal = 832; - - new->VDisplay = 480; - new->VSyncStart = 489; - new->VSyncEnd = 491; - new->VTotal = 520; - */ - new->HDisplay = 1024; - new->HSyncStart = 1048; - new->HSyncEnd = 1184; - new->HTotal = 1344; - - new->VDisplay = 768; - new->VSyncStart = 771; - new->VSyncEnd = 777; - new->VTotal = 806; - new->type = M_T_DRIVER; + for (i = 0; i < sizeof (reported_modes) / sizeof (reported_modes[0]); i++) + { + new = xnfcalloc(1, sizeof (DisplayModeRec)); - return new; + *new = reported_modes[i]; + new->name = xnfalloc(strlen(reported_modes[i].name) + 1); + strcpy(new->name, reported_modes[i].name); + *tail = new; + tail = &new->next; + } + + return first; } static void diff --git a/src/tv.5c b/src/tv.5c new file mode 100644 index 00000000..b4a2ba61 --- /dev/null +++ b/src/tv.5c @@ -0,0 +1,128 @@ +/* + * tv.5c + * + * Compute tv encoder subcarrier dda constants + * + * The TV encoder subcarrier must be set precisely to the + * required frequency or the cumulative phase errors will be + * quite visible in the output. To accomplish this, the TV encoder + * has a complex circuit that takes a fixed clock, generated by the PLL + * and generates a precise subcarrier clock from that using the following + * formula: + * + * subcarrier = pixel_clock * (S1 + (S2 + (S3/Z3)) / Z2) / 4096 + * + * Careful selection of the constants will provide the necessarily + * precise clock. + * + * In the code below, S1 is represented by dda1, S2/Z2 by dda2 and S3/Z3 + * by dda3. + */ + +typedef struct { + int step; + int size; +} term_t; + +/* + * Find the approximation closest, but no larger than 'v', where + * 0 <= v < 1, and the result denominator must be less than 30000. + */ +term_t approx (rational v) +{ + rational best_dist = 1.0; + term_t best; + + for (int den = 20000; den < 30000; den++) + { + int num = floor (v * den); + term_t approx = { step = num, size = den }; + rational dist = v - approx.step/approx.size; + if (dist >= 0 && dist < best_dist) + { + best_dist = dist; + best = approx; + } + } + return best; +} + +typedef struct { + rational subcarrier; + rational pixel; + rational result; + term_t dda1; + term_t dda2; + term_t dda3; +} dda; + +/* + * Compute the dda constants for the given pixel clock and + * desired subcarrier frequency + */ + +dda find_dda (rational pixel, rational subcarrier) +{ + dda d; + + d.subcarrier = subcarrier; + d.pixel = pixel; + + rational dda1 = subcarrier / pixel * 4096; + d.dda1 = (term_t) { step = floor (dda1), size = 4096 }; + + rational dda2 = dda1 - d.dda1.step; + d.dda2 = approx (dda2); + + rational dda3 = dda2 * d.dda2.size - d.dda2.step; + d.dda3 = approx (dda3); + + /* Compute the resulting pixel clock to compare */ + d.result = d.pixel * (d.dda1.step + + (d.dda2.step + d.dda3.step/d.dda3.size) / + d.dda2.size) / d.dda1.size; + return d; +} + +/* + * Print out the computed constants + */ +void print_dda (dda d) +{ + printf ("\t/* desired %9.7f actual %9.7f clock %g */\n", + d.subcarrier, d.result, d.pixel); + printf ("\t.dda1_inc\t= %6d,\n", d.dda1.step); + printf ("\t.dda2_inc\t= %6d,\t.dda2_size\t= %6d,\n", + d.dda2.step, d.dda2.step != 0 ? d.dda2.size : 0); + printf ("\t.dda3_inc\t= %6d,\t.dda3_size\t= %6d,\n", + d.dda3.step, d.dda3.step != 0 ? d.dda3.size : 0); +} + +/* + * These are all of the required subcarrier frequencies + */ +rational[] subcarriers = { + /* these are the values we use; for some reason, this generates + * a more stable image (at least for NTSC) */ + 3.580, 4.434, 3.582, 3.576, 4.430, + + /* these are the values pulled out of the various specs */ + 3.579545, 4.433618, 3.582056, 3.575611, 4.433618 +}; + +/* + * We fix the pixel clock to a value which the hardware can + * generate exactly + */ +rational pixel = 107.520; + +void main () +{ + for (int i = 0; i < dim(subcarriers); i++) + { + dda d = find_dda (pixel, subcarriers[i]); + print_dda (d); + } +} + +main ();