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