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