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