diff options
author | Paulo Cesar Pereira de Andrade <pcpa@mandriva.com.br> | 2008-09-24 18:58:35 -0300 |
---|---|---|
committer | Paulo Cesar Pereira de Andrade <pcpa@mandriva.com.br> | 2008-09-24 18:58:35 -0300 |
commit | bdba7c1397186cd15d519db55c2345db44990be4 (patch) | |
tree | 2de96c04aa4d32cbbc9cc82cf2f6cf783288f843 /src/smi_501.c | |
parent | e64b3d63be2a4148c4d10ac160e2bd2f3e8c7fd3 (diff) |
Fix incorrect understanding of the pixel clock from specs.
The clock that should be programmed is the bandwidth, and not the pixel
clock itself. Based on analysis of register contents, after "importing"
a slightly modified version of xf86ModeBandwidth() and properly converting
values, now it properly programs both, the LCD and a "clone" CRT or just
a secondary panel.
Modes generated by either gtf or cvt correctly works.
A (hopefully temporary) fallback was also added, and if the option
"UseFBDev" is used, the driver will not attempt to set/modify the
current video mode.
TODO: Generate a modeline "on the fly", based on monitor configured
specs, or some set of defaults, so that it should not be required to
specify a modeline in xorg.conf.
Diffstat (limited to 'src/smi_501.c')
-rw-r--r-- | src/smi_501.c | 94 |
1 files changed, 65 insertions, 29 deletions
diff --git a/src/smi_501.c b/src/smi_501.c index 53ba0a8..480a48d 100644 --- a/src/smi_501.c +++ b/src/smi_501.c @@ -51,6 +51,8 @@ authorization from The XFree86 Project or Silicon Motion. static void SMI501_ModeSet(ScrnInfoPtr pScrn, MSOCRegPtr mode); static char *format_integer_base2(int32_t word); +static int32_t SMI501_FindClock(double clock, Bool lcd, int32_t *x2_select, + int32_t *x2_divider, int32_t *x2_shift); static void SMI501_PrintRegs(ScrnInfoPtr pScrn); static void SMI501_SetClock(SMIPtr pSmi, int32_t port, int32_t pll, int32_t value); @@ -223,14 +225,30 @@ SMI501_DisplayPowerManagementSet(ScrnInfoPtr pScrn, } } +static double +xf86ModeBandwidth(DisplayModePtr mode, int depth) +{ + float a_active, a_total, active_percent, pixels_per_second; + int bytes_per_pixel = (depth + 7) / 8; + + if (!mode->HTotal || !mode->VTotal || !mode->Clock) + return 0.0; + + a_active = mode->HDisplay * mode->VDisplay; + a_total = mode->HTotal * mode->VTotal; + active_percent = a_active / a_total; + pixels_per_second = active_percent * mode->Clock * 1000.0; + + return ((double)(pixels_per_second * bytes_per_pixel)); +} + Bool SMI501_ModeInit(ScrnInfoPtr pScrn, DisplayModePtr xf86mode) { MSOCRegPtr save; MSOCRegPtr mode; SMIPtr pSmi = SMIPTR(pScrn); - double mclk; - int diff, best, divider, shift, x2_divider, x2_shift; + int32_t x2_select, x2_divider, x2_shift; save = pSmi->save; mode = pSmi->mode; @@ -250,6 +268,9 @@ SMI501_ModeInit(ScrnInfoPtr pScrn, DisplayModePtr xf86mode) /* Start with a fresh copy of registers before any mode change */ memcpy(mode, save, sizeof(MSOCRegRec)); + if (pSmi->UseFBDev) + return (TRUE); + /* Enable DAC -- 0: enable - 1: disable */ field(mode->misc_ctl, dac) = 0; @@ -295,30 +316,12 @@ SMI501_ModeInit(ScrnInfoPtr pScrn, DisplayModePtr xf86mode) break; } - /* Find clock best matching mode */ - best = 0x7fffffff; - for (mclk = 288000.0; mclk <= 336000.0; mclk += 48000.0) { - for (divider = 1; divider <= (pSmi->lcd ? 5 : 3); divider += 2) { - /* Start at 1 to match division by 2 */ - for (shift = 1; shift <= 8; shift++) { - /* Shift starts at 1 to add a division by two, matching - * description of P2XCLK and V2XCLK. */ - diff = (mclk / (divider << shift)) - xf86mode->Clock; - if (diff < 0) - diff = -diff; - if (diff < best) { - x2_shift = shift - 1; - x2_divider = divider == 1 ? 0 : divider == 3 ? 1 : 2; - - /* Remember best diff */ - best = diff; - } - } - } - } + (void)SMI501_FindClock(xf86ModeBandwidth(xf86mode, pScrn->depth), + pSmi->lcd, + &x2_select, &x2_divider, &x2_shift); if (pSmi->lcd) { - field(mode->clock, p2_select) = mclk == 288000.0 ? 0 : 1; + field(mode->clock, p2_select) = x2_select; field(mode->clock, p2_divider) = x2_divider; field(mode->clock, p2_shift) = x2_shift; @@ -371,7 +374,7 @@ SMI501_ModeInit(ScrnInfoPtr pScrn, DisplayModePtr xf86mode) xf86mode->VSyncStart; } else { - field(mode->clock, v2_select) = mclk == 288000.0 ? 0 : 1; + field(mode->clock, v2_select) = x2_select; field(mode->clock, v2_divider) = x2_divider; field(mode->clock, v2_shift) = x2_shift; @@ -384,12 +387,12 @@ SMI501_ModeInit(ScrnInfoPtr pScrn, DisplayModePtr xf86mode) field(mode->crt_display_ctl, enable) = 1; /* FIXME if non clone dual head, and secondary, need to - * properly set crt fb address properly ... */ + * properly set crt fb address ... */ field(mode->crt_fb_address, address) = 0; field(mode->crt_fb_address, mextern) = 0; /* local memory */ field(mode->crt_fb_address, pending) = 0; /* FIXME required? */ - /* >> 4 because of the "unused fields" that should be set to 0 */ + /* >> 4 because of the "unused bits" that should be set to 0 */ /* FIXME this should be used for virtual size? */ field(mode->crt_fb_width, offset) = pSmi->Stride >> 4; field(mode->crt_fb_width, width) = pSmi->Stride >> 4; @@ -434,7 +437,6 @@ SMI501_ModeSet(ScrnInfoPtr pScrn, MSOCRegPtr mode) /* Update gate first */ WRITE_SCR(pSmi, mode->current_gate, mode->gate.value); - /* Start with current value */ clock.value = READ_SCR(pSmi, mode->current_clock); field(clock, m_select) = field(mode->clock, m_select); @@ -556,6 +558,40 @@ format_integer_base2(int32_t word) return (buffer); } +static int32_t +SMI501_FindClock(double clock, Bool lcd, + int32_t *x2_select, int32_t *x2_divider, int32_t *x2_shift) +{ + double mclk; + int32_t diff, best, divider, shift; + + /* Find clock best matching mode */ + best = 0x7fffffff; + for (mclk = 288000.0; mclk <= 336000.0; mclk += 48000.0) { + for (divider = 1; divider <= (lcd ? 5 : 3); divider += 2) { + /* Start at 1 to match division by 2 */ + for (shift = 1; shift <= 8; shift++) { + /* Shift starts at 1 to add a division by two, matching + * description of P2XCLK and V2XCLK. */ + diff = (mclk / (divider << shift)) - clock; + if (diff < 0) + diff = -diff; + if (diff < best) { + *x2_shift = shift - 1; + *x2_divider = divider == 1 ? 0 : divider == 3 ? 1 : 2; + + /* Remember best diff */ + best = diff; + } + } + } + } + + *x2_select = mclk == 288000.0 ? 0 : 1; + + return (diff); +} + static void SMI501_PrintRegs(ScrnInfoPtr pScrn) { @@ -626,6 +662,6 @@ SMI501_SetClock(SMIPtr pSmi, int32_t port, int32_t pll, int32_t value) SMI501_WaitVSync(pSmi, 1); /* full register contents */ - WRITE_SCR(pSmi, port, pll); + WRITE_SCR(pSmi, port, value); SMI501_WaitVSync(pSmi, 1); } |