smi_501.c revision 7104f784
17104f784Smrg/*
27104f784SmrgCopyright (C) 1994-1999 The XFree86 Project, Inc.  All Rights Reserved.
37104f784SmrgCopyright (C) 2000 Silicon Motion, Inc.  All Rights Reserved.
47104f784SmrgCopyright (C) 2008 Mandriva Linux.  All Rights Reserved.
57104f784Smrg
67104f784SmrgPermission is hereby granted, free of charge, to any person obtaining a copy of
77104f784Smrgthis software and associated documentation files (the "Software"), to deal in
87104f784Smrgthe Software without restriction, including without limitation the rights to
97104f784Smrguse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
107104f784Smrgof the Software, and to permit persons to whom the Software is furnished to do
117104f784Smrgso, subject to the following conditions:
127104f784Smrg
137104f784SmrgThe above copyright notice and this permission notice shall be included in all
147104f784Smrgcopies or substantial portions of the Software.
157104f784Smrg
167104f784SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177104f784SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
187104f784SmrgNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
197104f784SmrgXFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
207104f784SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
217104f784SmrgWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
227104f784Smrg
237104f784SmrgExcept as contained in this notice, the names of The XFree86 Project and
247104f784SmrgSilicon Motion shall not be used in advertising or otherwise to promote the
257104f784Smrgsale, use or other dealings in this Software without prior written
267104f784Smrgauthorization from The XFree86 Project or Silicon Motion.
277104f784Smrg*/
287104f784Smrg
297104f784Smrg#ifdef HAVE_CONFIG_H
307104f784Smrg#include "config.h"
317104f784Smrg#endif
327104f784Smrg
337104f784Smrg#include "smi.h"
347104f784Smrg#include "smi_crtc.h"
357104f784Smrg#include "smi_501.h"
367104f784Smrg#include "regsmi.h"
377104f784Smrg
387104f784Smrg#define DPMS_SERVER
397104f784Smrg#include <X11/extensions/dpms.h>
407104f784Smrg
417104f784Smrg/* Want to see register dumps for now */
427104f784Smrg#undef VERBLEV
437104f784Smrg#define VERBLEV		1
447104f784Smrg
457104f784Smrg
467104f784Smrg/*
477104f784Smrg * Prototypes
487104f784Smrg */
497104f784Smrg
507104f784Smrgstatic char *format_integer_base2(int32_t word);
517104f784Smrgstatic void SMI501_SetClock(SMIPtr pSmi, int32_t port,
527104f784Smrg			    int32_t pll, int32_t value);
537104f784Smrg
547104f784Smrg
557104f784Smrg/*
567104f784Smrg * Implemementation
577104f784Smrg */
587104f784Smrg
597104f784Smrgvoid
607104f784SmrgSMI501_Save(ScrnInfoPtr pScrn)
617104f784Smrg{
627104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
637104f784Smrg    MSOCRegPtr	save = pSmi->save;
647104f784Smrg
657104f784Smrg    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV,
667104f784Smrg		   "Register dump (Before Save)\n");
677104f784Smrg    SMI501_PrintRegs(pScrn);
687104f784Smrg
697104f784Smrg    /* Used mainly for DPMS info */
707104f784Smrg    save->system_ctl.value = READ_SCR(pSmi, SYSTEM_CTL);
717104f784Smrg
727104f784Smrg    /* Used basically to enable dac */
737104f784Smrg    save->misc_ctl.value = READ_SCR(pSmi, MISC_CTL);
747104f784Smrg
757104f784Smrg    /* Read it first to know if current power mode */
767104f784Smrg    save->power_ctl.value = READ_SCR(pSmi, POWER_CTL);
777104f784Smrg
787104f784Smrg    switch (save->power_ctl.f.mode) {
797104f784Smrg	case 0:
807104f784Smrg	    save->current_gate  = POWER0_GATE;
817104f784Smrg	    save->current_clock = POWER0_CLOCK;
827104f784Smrg	    break;
837104f784Smrg	case 1:
847104f784Smrg	    save->current_gate  = POWER1_GATE;
857104f784Smrg	    save->current_clock = POWER1_CLOCK;
867104f784Smrg	    break;
877104f784Smrg	default:
887104f784Smrg	    /* FIXME
897104f784Smrg	     * Should be in sleep mode
907104f784Smrg	     * TODO
917104f784Smrg	     * select mode0 by default
927104f784Smrg	     */
937104f784Smrg	    save->current_gate = POWER0_GATE;
947104f784Smrg	    save->current_clock = POWER0_CLOCK;
957104f784Smrg	    break;
967104f784Smrg    }
977104f784Smrg
987104f784Smrg    save->gate.value  = READ_SCR(pSmi, save->current_gate);
997104f784Smrg    save->clock.value = READ_SCR(pSmi, save->current_clock);
1007104f784Smrg
1017104f784Smrg    /* FIXME Never changed */
1027104f784Smrg    save->timing_ctl.value = READ_SCR(pSmi, TIMING_CTL);
1037104f784Smrg
1047104f784Smrg    save->pll_ctl.value = READ_SCR(pSmi, PLL_CTL);
1057104f784Smrg    save->device_id.value = READ_SCR(pSmi, DEVICE_ID);
1067104f784Smrg    save->sleep_gate.value = READ_SCR(pSmi, SLEEP_GATE);
1077104f784Smrg
1087104f784Smrg    save->panel_display_ctl.value = READ_SCR(pSmi, PANEL_DISPLAY_CTL);
1097104f784Smrg    save->panel_fb_address.value = READ_SCR(pSmi, PANEL_FB_ADDRESS);
1107104f784Smrg    save->panel_fb_width.value = READ_SCR(pSmi, PANEL_FB_WIDTH);
1117104f784Smrg    save->panel_wwidth.value = READ_SCR(pSmi, PANEL_WWIDTH);
1127104f784Smrg    save->panel_wheight.value = READ_SCR(pSmi, PANEL_WHEIGHT);
1137104f784Smrg    save->panel_plane_tl.value = READ_SCR(pSmi, PANEL_PLANE_TL);
1147104f784Smrg    save->panel_plane_br.value = READ_SCR(pSmi, PANEL_PLANE_BR);
1157104f784Smrg    save->panel_htotal.value = READ_SCR(pSmi, PANEL_HTOTAL);
1167104f784Smrg    save->panel_hsync.value = READ_SCR(pSmi, PANEL_HSYNC);
1177104f784Smrg    save->panel_vtotal.value = READ_SCR(pSmi, PANEL_VTOTAL);
1187104f784Smrg    save->panel_vsync.value = READ_SCR(pSmi, PANEL_VSYNC);
1197104f784Smrg
1207104f784Smrg    save->crt_display_ctl.value = READ_SCR(pSmi, CRT_DISPLAY_CTL);
1217104f784Smrg    save->crt_fb_address.value = READ_SCR(pSmi, CRT_FB_ADDRESS);
1227104f784Smrg    save->crt_fb_width.value = READ_SCR(pSmi, CRT_FB_WIDTH);
1237104f784Smrg    save->crt_htotal.value = READ_SCR(pSmi, CRT_HTOTAL);
1247104f784Smrg    save->crt_hsync.value = READ_SCR(pSmi, CRT_HSYNC);
1257104f784Smrg    save->crt_vtotal.value = READ_SCR(pSmi, CRT_VTOTAL);
1267104f784Smrg    save->crt_vsync.value = READ_SCR(pSmi, CRT_VSYNC);
1277104f784Smrg
1287104f784Smrg    save->alpha_display_ctl.value = READ_SCR(pSmi, ALPHA_DISPLAY_CTL);
1297104f784Smrg    save->alpha_fb_address.value = READ_SCR(pSmi, ALPHA_FB_ADDRESS);
1307104f784Smrg    save->alpha_fb_width.value = READ_SCR(pSmi, ALPHA_FB_WIDTH);
1317104f784Smrg    save->alpha_plane_tl.value = READ_SCR(pSmi, ALPHA_PLANE_TL);
1327104f784Smrg    save->alpha_plane_br.value = READ_SCR(pSmi, ALPHA_PLANE_BR);
1337104f784Smrg    save->alpha_chroma_key.value = READ_SCR(pSmi, ALPHA_CHROMA_KEY);
1347104f784Smrg
1357104f784Smrg    /* Also save accel state to properly restore kernel framebuffer */
1367104f784Smrg    save->accel_src = READ_SCR(pSmi, ACCEL_SRC);
1377104f784Smrg    save->accel_dst = READ_SCR(pSmi, ACCEL_DST);
1387104f784Smrg    save->accel_dim = READ_SCR(pSmi, ACCEL_DIM);
1397104f784Smrg    save->accel_ctl = READ_SCR(pSmi, ACCEL_CTL);
1407104f784Smrg    save->accel_pitch = READ_SCR(pSmi, ACCEL_PITCH);
1417104f784Smrg    save->accel_fmt = READ_SCR(pSmi, ACCEL_FMT);
1427104f784Smrg    save->accel_clip_tl = READ_SCR(pSmi, ACCEL_CLIP_TL);
1437104f784Smrg    save->accel_clip_br = READ_SCR(pSmi, ACCEL_CLIP_BR);
1447104f784Smrg    save->accel_pat_lo = READ_SCR(pSmi, ACCEL_PAT_LO);
1457104f784Smrg    save->accel_pat_hi = READ_SCR(pSmi, ACCEL_PAT_HI);
1467104f784Smrg    save->accel_wwidth = READ_SCR(pSmi, ACCEL_WWIDTH);
1477104f784Smrg    save->accel_src_base = READ_SCR(pSmi, ACCEL_SRC_BASE);
1487104f784Smrg    save->accel_dst_base = READ_SCR(pSmi, ACCEL_DST_BASE);
1497104f784Smrg}
1507104f784Smrg
1517104f784Smrgvoid
1527104f784SmrgSMI501_DisplayPowerManagementSet(ScrnInfoPtr pScrn,
1537104f784Smrg				 int PowerManagementMode, int flags)
1547104f784Smrg{
1557104f784Smrg    SMIPtr		pSmi = SMIPTR(pScrn);
1567104f784Smrg
1577104f784Smrg    if (pSmi->CurrentDPMS != PowerManagementMode) {
1587104f784Smrg	/* Set the DPMS mode to every output and CRTC */
1597104f784Smrg	xf86DPMSSet(pScrn, PowerManagementMode, flags);
1607104f784Smrg
1617104f784Smrg	pSmi->CurrentDPMS = PowerManagementMode;
1627104f784Smrg    }
1637104f784Smrg}
1647104f784Smrg
1657104f784SmrgBool
1667104f784SmrgSMI501_HWInit(ScrnInfoPtr pScrn)
1677104f784Smrg{
1687104f784Smrg    MSOCRegPtr	save;
1697104f784Smrg    MSOCRegPtr	mode;
1707104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
1717104f784Smrg    int32_t	x_select, x_divider, x_shift;
1727104f784Smrg
1737104f784Smrg    save = pSmi->save;
1747104f784Smrg    mode = pSmi->mode;
1757104f784Smrg
1767104f784Smrg    /* Start with a fresh copy of registers before any mode change */
1777104f784Smrg    memcpy(mode, save, sizeof(MSOCRegRec));
1787104f784Smrg
1797104f784Smrg    if (pSmi->UseFBDev)
1807104f784Smrg	return (TRUE);
1817104f784Smrg
1827104f784Smrg    /* Enable DAC -- 0: enable - 1: disable */
1837104f784Smrg    mode->misc_ctl.f.dac = 0;
1847104f784Smrg
1857104f784Smrg    /* Enable 2D engine */
1867104f784Smrg    mode->gate.f.engine = 1;
1877104f784Smrg    /* Color space conversion */
1887104f784Smrg    mode->gate.f.csc = 1;
1897104f784Smrg    /* ZV port */
1907104f784Smrg    mode->gate.f.zv = 1;
1917104f784Smrg    /* Gpio, Pwm, and I2c */
1927104f784Smrg    mode->gate.f.gpio = 1;
1937104f784Smrg
1947104f784Smrg    /* FIXME fixed at power mode 0 as in the smi sources */
1957104f784Smrg    mode->power_ctl.f.status = 0;
1967104f784Smrg    mode->power_ctl.f.mode = 0;
1977104f784Smrg
1987104f784Smrg    if (pSmi->MCLK) {
1997104f784Smrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV,
2007104f784Smrg		       "MCLK request %d\n", pSmi->MCLK);
2017104f784Smrg	(void)SMI501_FindMemClock(pSmi->MCLK, &x_select, &x_divider, &x_shift);
2027104f784Smrg	mode->clock.f.m_select = x_select;
2037104f784Smrg	mode->clock.f.m_divider = x_divider;
2047104f784Smrg	mode->clock.f.m_shift = x_shift;
2057104f784Smrg    }
2067104f784Smrg    /* Else use what was configured by the kernel. */
2077104f784Smrg
2087104f784Smrg    if (pSmi->MXCLK) {
2097104f784Smrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV,
2107104f784Smrg		       "MXCLK request %d\n", pSmi->MXCLK);
2117104f784Smrg	(void)SMI501_FindMemClock(pSmi->MXCLK, &x_select, &x_divider, &x_shift);
2127104f784Smrg	mode->clock.f.m1_select = x_select;
2137104f784Smrg	mode->clock.f.m1_divider = x_divider;
2147104f784Smrg	mode->clock.f.m1_shift = x_shift;
2157104f784Smrg    }
2167104f784Smrg    /* Else use what was configured by the kernel. */
2177104f784Smrg
2187104f784Smrg    if (!pSmi->Dualhead) {
2197104f784Smrg	/* crt clones panel */
2207104f784Smrg	mode->crt_display_ctl.f.enable = 0;
2217104f784Smrg	/* 0: select panel - 1: select crt */
2227104f784Smrg	mode->crt_display_ctl.f.select = 0;
2237104f784Smrg	mode->crt_display_ctl.f.timing = 0;
2247104f784Smrg    }
2257104f784Smrg
2267104f784Smrg    SMI501_WriteMode_common(pScrn, mode);
2277104f784Smrg
2287104f784Smrg    return (TRUE);
2297104f784Smrg}
2307104f784Smrg
2317104f784Smrgvoid
2327104f784SmrgSMI501_WriteMode_common(ScrnInfoPtr pScrn, MSOCRegPtr mode)
2337104f784Smrg{
2347104f784Smrg    int32_t		pll;
2357104f784Smrg    MSOCClockRec	clock;
2367104f784Smrg    SMIPtr		pSmi = SMIPTR(pScrn);
2377104f784Smrg
2387104f784Smrg    if (!pSmi->UseFBDev) {
2397104f784Smrg	/* Update gate first */
2407104f784Smrg	WRITE_SCR(pSmi, mode->current_gate, mode->gate.value);
2417104f784Smrg
2427104f784Smrg	clock.value = READ_SCR(pSmi, mode->current_clock);
2437104f784Smrg
2447104f784Smrg	if (pSmi->MCLK) {
2457104f784Smrg	    clock.f.m_select = mode->clock.f.m_select;
2467104f784Smrg	    pll = clock.value;
2477104f784Smrg	    clock.f.m_divider = mode->clock.f.m_divider;
2487104f784Smrg	    clock.f.m_shift = mode->clock.f.m_shift;
2497104f784Smrg	    SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value);
2507104f784Smrg	}
2517104f784Smrg
2527104f784Smrg	if (pSmi->MXCLK) {
2537104f784Smrg	    clock.f.m1_select = mode->clock.f.m1_select;
2547104f784Smrg	    pll = clock.value;
2557104f784Smrg	    clock.f.m1_divider = mode->clock.f.m1_divider;
2567104f784Smrg	    clock.f.m1_shift = mode->clock.f.m1_shift;
2577104f784Smrg	    SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value);
2587104f784Smrg	}
2597104f784Smrg
2607104f784Smrg	WRITE_SCR(pSmi, MISC_CTL, mode->misc_ctl.value);
2617104f784Smrg
2627104f784Smrg	WRITE_SCR(pSmi, POWER_CTL, mode->power_ctl.value);
2637104f784Smrg    }
2647104f784Smrg
2657104f784Smrg    /* Match configuration */
2667104f784Smrg    /* FIXME some other fields should also be set, otherwise, since
2677104f784Smrg     * neither kernel nor driver change it, a reboot is required to
2687104f784Smrg     * modify or reset to default */
2697104f784Smrg    mode->system_ctl.f.burst = mode->system_ctl.f.burst_read =
2707104f784Smrg	pSmi->PCIBurst != FALSE;
2717104f784Smrg    mode->system_ctl.f.retry = pSmi->PCIRetry != FALSE;
2727104f784Smrg    WRITE_SCR(pSmi, SYSTEM_CTL, mode->system_ctl.value);
2737104f784Smrg
2747104f784Smrg    if (!pSmi->Dualhead)
2757104f784Smrg	WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value);
2767104f784Smrg}
2777104f784Smrg
2787104f784Smrgvoid
2797104f784SmrgSMI501_WriteMode_lcd(ScrnInfoPtr pScrn, MSOCRegPtr mode)
2807104f784Smrg{
2817104f784Smrg    int32_t		pll;
2827104f784Smrg    MSOCClockRec	clock;
2837104f784Smrg    SMIPtr		pSmi = SMIPTR(pScrn);
2847104f784Smrg
2857104f784Smrg    if (!pSmi->UseFBDev) {
2867104f784Smrg	clock.value = READ_SCR(pSmi, mode->current_clock);
2877104f784Smrg
2887104f784Smrg	/* Alternate pll_select is only available for the SMI 502,
2897104f784Smrg	 * and the bit should be only set in that case. */
2907104f784Smrg	if (mode->clock.f.pll_select)
2917104f784Smrg	    WRITE_SCR(pSmi, PLL_CTL, mode->pll_ctl.value);
2927104f784Smrg	clock.f.p2_select = mode->clock.f.p2_select;
2937104f784Smrg	pll = clock.value;
2947104f784Smrg	clock.f.p2_divider = mode->clock.f.p2_divider;
2957104f784Smrg	clock.f.p2_shift = mode->clock.f.p2_shift;
2967104f784Smrg	clock.f.pll_select = mode->clock.f.pll_select;
2977104f784Smrg	clock.f.p2_1xclck = mode->clock.f.p2_1xclck;
2987104f784Smrg	SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value);
2997104f784Smrg
3007104f784Smrg	WRITE_SCR(pSmi, PANEL_FB_ADDRESS, mode->panel_fb_address.value);
3017104f784Smrg	WRITE_SCR(pSmi, PANEL_FB_WIDTH, mode->panel_fb_width.value);
3027104f784Smrg
3037104f784Smrg	WRITE_SCR(pSmi, PANEL_WWIDTH, mode->panel_wwidth.value);
3047104f784Smrg	WRITE_SCR(pSmi, PANEL_WHEIGHT, mode->panel_wheight.value);
3057104f784Smrg
3067104f784Smrg	WRITE_SCR(pSmi, PANEL_PLANE_TL, mode->panel_plane_tl.value);
3077104f784Smrg	WRITE_SCR(pSmi, PANEL_PLANE_BR, mode->panel_plane_br.value);
3087104f784Smrg
3097104f784Smrg	WRITE_SCR(pSmi, PANEL_HTOTAL, mode->panel_htotal.value);
3107104f784Smrg	WRITE_SCR(pSmi, PANEL_HSYNC, mode->panel_hsync.value);
3117104f784Smrg	WRITE_SCR(pSmi, PANEL_VTOTAL, mode->panel_vtotal.value);
3127104f784Smrg	WRITE_SCR(pSmi, PANEL_VSYNC, mode->panel_vsync.value);
3137104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
3147104f784Smrg    }
3157104f784Smrg}
3167104f784Smrg
3177104f784Smrgvoid
3187104f784SmrgSMI501_WriteMode_crt(ScrnInfoPtr pScrn, MSOCRegPtr mode)
3197104f784Smrg{
3207104f784Smrg    int32_t		pll;
3217104f784Smrg    MSOCClockRec	clock;
3227104f784Smrg    SMIPtr		pSmi = SMIPTR(pScrn);
3237104f784Smrg
3247104f784Smrg    if (!pSmi->UseFBDev) {
3257104f784Smrg	clock.value = READ_SCR(pSmi, mode->current_clock);
3267104f784Smrg
3277104f784Smrg	clock.f.v2_select = mode->clock.f.v2_select;
3287104f784Smrg	pll = clock.value;
3297104f784Smrg	clock.f.v2_divider = mode->clock.f.v2_divider;
3307104f784Smrg	clock.f.v2_shift = mode->clock.f.v2_shift;
3317104f784Smrg	clock.f.v2_1xclck = mode->clock.f.v2_1xclck;
3327104f784Smrg	SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value);
3337104f784Smrg
3347104f784Smrg	WRITE_SCR(pSmi, CRT_FB_ADDRESS, mode->crt_fb_address.value);
3357104f784Smrg	WRITE_SCR(pSmi, CRT_FB_WIDTH, mode->crt_fb_width.value);
3367104f784Smrg	WRITE_SCR(pSmi, CRT_HTOTAL, mode->crt_htotal.value);
3377104f784Smrg	WRITE_SCR(pSmi, CRT_HSYNC, mode->crt_hsync.value);
3387104f784Smrg	WRITE_SCR(pSmi, CRT_VTOTAL, mode->crt_vtotal.value);
3397104f784Smrg	WRITE_SCR(pSmi, CRT_VSYNC, mode->crt_vsync.value);
3407104f784Smrg	WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value);
3417104f784Smrg    }
3427104f784Smrg}
3437104f784Smrg
3447104f784Smrgvoid
3457104f784SmrgSMI501_WriteMode_alpha(ScrnInfoPtr pScrn, MSOCRegPtr mode)
3467104f784Smrg{
3477104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
3487104f784Smrg
3497104f784Smrg    WRITE_SCR(pSmi, ALPHA_FB_ADDRESS, mode->alpha_fb_address.value);
3507104f784Smrg    WRITE_SCR(pSmi, ALPHA_FB_WIDTH, mode->alpha_fb_width.value);
3517104f784Smrg
3527104f784Smrg    WRITE_SCR(pSmi, ALPHA_PLANE_TL, mode->alpha_plane_tl.value);
3537104f784Smrg    WRITE_SCR(pSmi, ALPHA_PLANE_BR, mode->alpha_plane_br.value);
3547104f784Smrg
3557104f784Smrg    WRITE_SCR(pSmi, ALPHA_CHROMA_KEY, mode->alpha_chroma_key.value);
3567104f784Smrg
3577104f784Smrg    WRITE_SCR(pSmi, ALPHA_DISPLAY_CTL, mode->alpha_display_ctl.value);
3587104f784Smrg}
3597104f784Smrg
3607104f784Smrgvoid
3617104f784SmrgSMI501_WriteMode(ScrnInfoPtr pScrn, MSOCRegPtr restore)
3627104f784Smrg{
3637104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
3647104f784Smrg
3657104f784Smrg    SMI501_WriteMode_common(pScrn, restore);
3667104f784Smrg    SMI501_WriteMode_lcd(pScrn, restore);
3677104f784Smrg    SMI501_WriteMode_crt(pScrn, restore);
3687104f784Smrg#if SMI_CURSOR_ALPHA_PLANE
3697104f784Smrg    SMI501_WriteMode_alpha(pScrn, restore);
3707104f784Smrg#endif
3717104f784Smrg
3727104f784Smrg    /* This function should be called when switching to virtual console */
3737104f784Smrg    WRITE_SCR(pSmi, ACCEL_SRC, restore->accel_src);
3747104f784Smrg    WRITE_SCR(pSmi, ACCEL_DST, restore->accel_dst);
3757104f784Smrg    WRITE_SCR(pSmi, ACCEL_DIM, restore->accel_dim);
3767104f784Smrg    WRITE_SCR(pSmi, ACCEL_CTL, restore->accel_ctl);
3777104f784Smrg    WRITE_SCR(pSmi, ACCEL_PITCH, restore->accel_pitch);
3787104f784Smrg    WRITE_SCR(pSmi, ACCEL_FMT, restore->accel_fmt);
3797104f784Smrg    WRITE_SCR(pSmi, ACCEL_CLIP_TL, restore->accel_clip_tl);
3807104f784Smrg    WRITE_SCR(pSmi, ACCEL_CLIP_BR, restore->accel_clip_br);
3817104f784Smrg    WRITE_SCR(pSmi, ACCEL_PAT_LO, restore->accel_pat_lo);
3827104f784Smrg    WRITE_SCR(pSmi, ACCEL_PAT_HI, restore->accel_pat_hi);
3837104f784Smrg    WRITE_SCR(pSmi, ACCEL_WWIDTH, restore->accel_wwidth);
3847104f784Smrg    WRITE_SCR(pSmi, ACCEL_SRC_BASE, restore->accel_src_base);
3857104f784Smrg    WRITE_SCR(pSmi, ACCEL_DST_BASE, restore->accel_dst_base);
3867104f784Smrg}
3877104f784Smrg
3887104f784Smrgvoid
3897104f784SmrgSMI501_PowerPanel(ScrnInfoPtr pScrn, MSOCRegPtr mode, Bool on)
3907104f784Smrg{
3917104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
3927104f784Smrg
3937104f784Smrg    if (on != FALSE) {
3947104f784Smrg	mode->panel_display_ctl.f.vdd = 1;
3957104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
3967104f784Smrg	SMI501_WaitVSync(pSmi, 4);
3977104f784Smrg
3987104f784Smrg	mode->panel_display_ctl.f.signal = 1;
3997104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4007104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4017104f784Smrg
4027104f784Smrg	mode->panel_display_ctl.f.bias = 1;
4037104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4047104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4057104f784Smrg
4067104f784Smrg	mode->panel_display_ctl.f.fp = 1;
4077104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4087104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4097104f784Smrg    }
4107104f784Smrg    else {
4117104f784Smrg	mode->panel_display_ctl.f.fp = 0;
4127104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4137104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4147104f784Smrg
4157104f784Smrg	mode->panel_display_ctl.f.bias = 0;
4167104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4177104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4187104f784Smrg
4197104f784Smrg	mode->panel_display_ctl.f.signal = 0;
4207104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4217104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4227104f784Smrg
4237104f784Smrg	mode->panel_display_ctl.f.vdd = 0;
4247104f784Smrg	WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
4257104f784Smrg	SMI501_WaitVSync(pSmi, 4);
4267104f784Smrg    }
4277104f784Smrg}
4287104f784Smrg
4297104f784Smrgstatic char *
4307104f784Smrgformat_integer_base2(int32_t word)
4317104f784Smrg{
4327104f784Smrg    int		i;
4337104f784Smrg    static char	buffer[33];
4347104f784Smrg
4357104f784Smrg    for (i = 0; i < 32; i++) {
4367104f784Smrg	if (word & (1 << i))
4377104f784Smrg	    buffer[31 - i] = '1';
4387104f784Smrg	else
4397104f784Smrg	    buffer[31 - i] = '0';
4407104f784Smrg    }
4417104f784Smrg
4427104f784Smrg    return (buffer);
4437104f784Smrg}
4447104f784Smrg
4457104f784Smrgdouble
4467104f784SmrgSMI501_FindClock(double clock, int32_t max_divider, Bool has1xclck,
4477104f784Smrg		 int32_t *x2_1xclck,
4487104f784Smrg		 int32_t *x2_select, int32_t *x2_divider, int32_t *x2_shift)
4497104f784Smrg{
4507104f784Smrg    double	diff, best, mclk;
4517104f784Smrg    int32_t	multiplier, divider, shift, xclck;
4527104f784Smrg
4537104f784Smrg    /* The Crystal input frequency is 24Mhz, and can be multiplied
4547104f784Smrg     * by 12 or 14 (actually, there are other values, see TIMING_CTL,
4557104f784Smrg     * MMIO 0x068) */
4567104f784Smrg
4577104f784Smrg    /* Find clock best matching mode */
4587104f784Smrg    best = 0x7fffffff;
4597104f784Smrg    for (multiplier = 12, mclk  = multiplier * 24 * 1000.0;
4607104f784Smrg	 mclk <= 14 * 24 * 1000.0;
4617104f784Smrg	 multiplier += 2, mclk  = multiplier * 24 * 1000.0) {
4627104f784Smrg	for (divider = 1; divider <= max_divider; divider += 2) {
4637104f784Smrg	    for (shift = 0; shift < 8; shift++) {
4647104f784Smrg		/* Divider 1 not in specs for cards older then 502 */
4657104f784Smrg		for (xclck = 1; xclck >= !has1xclck; xclck--) {
4667104f784Smrg		    diff = (mclk / (divider << shift << xclck)) - clock;
4677104f784Smrg		    if (fabs(diff) < best) {
4687104f784Smrg			*x2_shift = shift;
4697104f784Smrg			*x2_divider = divider == 1 ? 0 : divider == 3 ? 1 : 2;
4707104f784Smrg			*x2_select = mclk == 12 * 24 * 1000.0 ? 0 : 1;
4717104f784Smrg			*x2_1xclck = xclck == 0;
4727104f784Smrg
4737104f784Smrg			/* Remember best diff */
4747104f784Smrg			best = fabs(diff);
4757104f784Smrg		    }
4767104f784Smrg		}
4777104f784Smrg	    }
4787104f784Smrg	}
4797104f784Smrg    }
4807104f784Smrg
4817104f784Smrg    xf86ErrorFVerb(VERBLEV,
4827104f784Smrg		   "\tMatching clock %5.2f, diff %5.2f (%d/%d/%d/%d)\n",
4837104f784Smrg		   ((*x2_select ? 14 : 12) * 24 * 1000.0) /
4847104f784Smrg		   ((*x2_divider == 0 ? 1 : *x2_divider == 1 ? 3 : 5) <<
4857104f784Smrg		    *x2_shift << (*x2_1xclck ? 0 : 1)),
4867104f784Smrg		   best, *x2_shift, *x2_divider, *x2_select, *x2_1xclck);
4877104f784Smrg
4887104f784Smrg    return (best);
4897104f784Smrg}
4907104f784Smrg
4917104f784Smrgdouble
4927104f784SmrgSMI501_FindMemClock(double clock, int32_t *x1_select,
4937104f784Smrg		    int32_t *x1_divider, int32_t *x1_shift)
4947104f784Smrg{
4957104f784Smrg    double	diff, best, mclk;
4967104f784Smrg    int32_t	multiplier, divider, shift;
4977104f784Smrg
4987104f784Smrg    best = 0x7fffffff;
4997104f784Smrg    for (multiplier = 12, mclk  = multiplier * 24 * 1000.0;
5007104f784Smrg	 mclk <= 14 * 24 * 1000.0;
5017104f784Smrg	 multiplier += 2, mclk = multiplier * 24 * 1000.0) {
5027104f784Smrg	for (divider = 1; divider <= 3; divider += 2) {
5037104f784Smrg	    for (shift = 0; shift < 8; shift++) {
5047104f784Smrg		diff = (mclk / (divider << shift)) - clock;
5057104f784Smrg		if (fabs(diff) < best) {
5067104f784Smrg		    *x1_shift = shift;
5077104f784Smrg		    *x1_divider = divider == 1 ? 0 : 1;
5087104f784Smrg		    *x1_select = mclk == 12 * 24 * 1000.0 ? 0 : 1;
5097104f784Smrg
5107104f784Smrg		    /* Remember best diff */
5117104f784Smrg		    best = fabs(diff);
5127104f784Smrg		}
5137104f784Smrg	    }
5147104f784Smrg	}
5157104f784Smrg    }
5167104f784Smrg
5177104f784Smrg    xf86ErrorFVerb(VERBLEV,
5187104f784Smrg		   "\tMatching clock %5.2f, diff %5.2f (%d/%d/%d)\n",
5197104f784Smrg		   ((*x1_select ? 14 : 12) * 24 * 1000.0) /
5207104f784Smrg		   ((*x1_divider == 0 ? 1 : 3) << *x1_shift),
5217104f784Smrg		    best, *x1_shift, *x1_divider, *x1_select);
5227104f784Smrg
5237104f784Smrg    return (best);
5247104f784Smrg}
5257104f784Smrg
5267104f784Smrg
5277104f784Smrgdouble
5287104f784SmrgSMI501_FindPLLClock(double clock, int32_t *m, int32_t *n, int32_t *xclck)
5297104f784Smrg{
5307104f784Smrg    int32_t	M, N, K;
5317104f784Smrg    double	diff, best;
5327104f784Smrg    double	frequency;
5337104f784Smrg
5347104f784Smrg    /*   This method, available only on the 502 is intended to cover the
5357104f784Smrg     * disadvantage of the other method where certain modes cannot be
5367104f784Smrg     * displayed correctly due to the big difference on the requested
5377104f784Smrg     * pixel clock, with the actual pixel clock that can be achieved by
5387104f784Smrg     * those divisions. In this method, N can be any integer between 2
5397104f784Smrg     * and 24, M can be any positive, 8 bits integer, and K is either 1
5407104f784Smrg     * or 2.
5417104f784Smrg     *   To calculate the programmable PLL, the following formula is
5427104f784Smrg     * used:
5437104f784Smrg     *
5447104f784Smrg     *	Requested Pixel Clock = Input Frequency * M / N
5457104f784Smrg     *
5467104f784Smrg     *   Input Frequency is the crystal input frequency value (24 MHz in
5477104f784Smrg     * the SMI VGX Demo Board).
5487104f784Smrg     *
5497104f784Smrg     *   K is a divisor, used by setting bit 15 of the PLL_CTL
5507104f784Smrg     * (PLL Output Divided by 2).
5517104f784Smrg     *
5527104f784Smrg     *   So, it should be requested_clock = input_frequency * M / N / K
5537104f784Smrg     */
5547104f784Smrg
5557104f784Smrg    /* That said, use what actually works, that is:
5567104f784Smrg     * requested_clock = input_frequency * K * M / N
5577104f784Smrg     *
5587104f784Smrg     * where requested_clock is modeline pixel clock,
5597104f784Smrg     * input_frequency is 12, K is either 1 or 2 (and sets bit15 accordingly),
5607104f784Smrg     * M is a non zero 8 bits unsigned integer, and N is a value from 2 to 24.
5617104f784Smrg     */
5627104f784Smrg
5637104f784Smrg    best = 0x7fffffff;
5647104f784Smrg    frequency = 12 * 1000.0;
5657104f784Smrg    for (N = 2; N <= 24; N++) {
5667104f784Smrg	for (K = 1; K <= 2; K++) {
5677104f784Smrg	    M = clock / frequency * K * N;
5687104f784Smrg	    diff = ((int32_t)(frequency / K * M) / N) - clock;
5697104f784Smrg	    /* Ensure M is larger then 0 and fits in 8 bits */
5707104f784Smrg	    if (M > 0 && M < 0x100 && fabs(diff) < best) {
5717104f784Smrg		*m = M;
5727104f784Smrg		*n = N;
5737104f784Smrg		*xclck = K == 1;
5747104f784Smrg
5757104f784Smrg		/* Remember best diff */
5767104f784Smrg		best = fabs(diff);
5777104f784Smrg	    }
5787104f784Smrg	}
5797104f784Smrg    }
5807104f784Smrg
5817104f784Smrg    xf86ErrorFVerb(VERBLEV,
5827104f784Smrg		   "\tMatching alternate clock %5.2f, diff %5.2f (%d/%d/%d)\n",
5837104f784Smrg		   frequency / (*xclck ? 1 : 2) * *m / *n, best,
5847104f784Smrg		   *m, *n, *xclck);
5857104f784Smrg
5867104f784Smrg    return (best);
5877104f784Smrg}
5887104f784Smrg
5897104f784Smrgvoid
5907104f784SmrgSMI501_PrintRegs(ScrnInfoPtr pScrn)
5917104f784Smrg{
5927104f784Smrg    int		i;
5937104f784Smrg    SMIPtr	pSmi = SMIPTR(pScrn);
5947104f784Smrg
5957104f784Smrg    xf86ErrorFVerb(VERBLEV, "    SMI501 System Setup:\n");
5967104f784Smrg    for (i = 0x00; i <= 0x74; i += 4)
5977104f784Smrg	xf86ErrorFVerb(VERBLEV, "\t%08x: %s\n", i,
5987104f784Smrg		       format_integer_base2(READ_SCR(pSmi, i)));
5997104f784Smrg    xf86ErrorFVerb(VERBLEV, "    SMI501 Display Setup:\n");
6007104f784Smrg    for (i = 0x80000; i < 0x80400; i += 4)
6017104f784Smrg	xf86ErrorFVerb(VERBLEV, "\t%08x: %s\n", i,
6027104f784Smrg		       format_integer_base2(READ_SCR(pSmi, i)));
6037104f784Smrg}
6047104f784Smrg
6057104f784Smrgvoid
6067104f784SmrgSMI501_WaitVSync(SMIPtr pSmi, int vsync_count)
6077104f784Smrg{
6087104f784Smrg    MSOCCmdStatusRec	status;
6097104f784Smrg    int32_t		timeout;
6107104f784Smrg
6117104f784Smrg    while (vsync_count-- > 0) {
6127104f784Smrg	/* Wait for end of vsync */
6137104f784Smrg	timeout = 0;
6147104f784Smrg	do {
6157104f784Smrg	    /* bit 11: vsync active *if set* */
6167104f784Smrg	    status.value = READ_SCR(pSmi, CMD_STATUS);
6177104f784Smrg	    if (++timeout == 10000)
6187104f784Smrg		break;
6197104f784Smrg	} while (status.f.pvsync);
6207104f784Smrg
6217104f784Smrg	/* Wait for start of vsync */
6227104f784Smrg	timeout = 0;
6237104f784Smrg	do {
6247104f784Smrg	    status.value = READ_SCR(pSmi, CMD_STATUS);
6257104f784Smrg	    if (++timeout == 10000)
6267104f784Smrg		break;
6277104f784Smrg	} while (!status.f.pvsync);
6287104f784Smrg    }
6297104f784Smrg}
6307104f784Smrg
6317104f784Smrgstatic void
6327104f784SmrgSMI501_SetClock(SMIPtr pSmi, int32_t port, int32_t pll, int32_t value)
6337104f784Smrg{
6347104f784Smrg    /*
6357104f784Smrg     *	Rules to Program the Power Mode Clock Registers for Clock Selection
6367104f784Smrg     *
6377104f784Smrg     *	1. There should be only one clock source changed at a time.
6387104f784Smrg     *	   To change clock source for P2XCLK, V2XCLK, MCLK, M2XCLK
6397104f784Smrg     *	   simultaneously may cause the internal logic normal operation
6407104f784Smrg     *	   to be disrupted. There should be a minimum of 16mS wait from
6417104f784Smrg     *	   change one clock source to another.
6427104f784Smrg     *	2. When adjusting the clock rate, the PLL selection bit should
6437104f784Smrg     *	   be programmed first before changing the divider value for each
6447104f784Smrg     *	   clock source. For example, to change the P2XCLK clock rate:
6457104f784Smrg     *		. bit 29 should be set first
6467104f784Smrg     *		. wait for a minimum of 16ms (about one Vsync time)
6477104f784Smrg     *		. adjust bits [28:24].
6487104f784Smrg     *	   The minimum 16 ms wait is necessary for logic to settle down
6497104f784Smrg     *	   before the clock rate is changed.
6507104f784Smrg     *	3. There should be a minimum 16 ms wait after a clock source is
6517104f784Smrg     *	   changed before any operation that could result in a bus
6527104f784Smrg     *	   transaction.
6537104f784Smrg     */
6547104f784Smrg
6557104f784Smrg    /* register contents selecting clock */
6567104f784Smrg    WRITE_SCR(pSmi, port, pll);
6577104f784Smrg    SMI501_WaitVSync(pSmi, 1);
6587104f784Smrg
6597104f784Smrg    /* full register contents */
6607104f784Smrg    WRITE_SCR(pSmi, port, value);
6617104f784Smrg    SMI501_WaitVSync(pSmi, 1);
6627104f784Smrg}
663