smi_501.c revision 7104f784
1/* 2Copyright (C) 1994-1999 The XFree86 Project, Inc. All Rights Reserved. 3Copyright (C) 2000 Silicon Motion, Inc. All Rights Reserved. 4Copyright (C) 2008 Mandriva Linux. All Rights Reserved. 5 6Permission is hereby granted, free of charge, to any person obtaining a copy of 7this software and associated documentation files (the "Software"), to deal in 8the Software without restriction, including without limitation the rights to 9use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10of the Software, and to permit persons to whom the Software is furnished to do 11so, subject to the following conditions: 12 13The above copyright notice and this permission notice shall be included in all 14copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- 18NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the names of The XFree86 Project and 24Silicon Motion shall not be used in advertising or otherwise to promote the 25sale, use or other dealings in this Software without prior written 26authorization from The XFree86 Project or Silicon Motion. 27*/ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#include "smi.h" 34#include "smi_crtc.h" 35#include "smi_501.h" 36#include "regsmi.h" 37 38#define DPMS_SERVER 39#include <X11/extensions/dpms.h> 40 41/* Want to see register dumps for now */ 42#undef VERBLEV 43#define VERBLEV 1 44 45 46/* 47 * Prototypes 48 */ 49 50static char *format_integer_base2(int32_t word); 51static void SMI501_SetClock(SMIPtr pSmi, int32_t port, 52 int32_t pll, int32_t value); 53 54 55/* 56 * Implemementation 57 */ 58 59void 60SMI501_Save(ScrnInfoPtr pScrn) 61{ 62 SMIPtr pSmi = SMIPTR(pScrn); 63 MSOCRegPtr save = pSmi->save; 64 65 xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV, 66 "Register dump (Before Save)\n"); 67 SMI501_PrintRegs(pScrn); 68 69 /* Used mainly for DPMS info */ 70 save->system_ctl.value = READ_SCR(pSmi, SYSTEM_CTL); 71 72 /* Used basically to enable dac */ 73 save->misc_ctl.value = READ_SCR(pSmi, MISC_CTL); 74 75 /* Read it first to know if current power mode */ 76 save->power_ctl.value = READ_SCR(pSmi, POWER_CTL); 77 78 switch (save->power_ctl.f.mode) { 79 case 0: 80 save->current_gate = POWER0_GATE; 81 save->current_clock = POWER0_CLOCK; 82 break; 83 case 1: 84 save->current_gate = POWER1_GATE; 85 save->current_clock = POWER1_CLOCK; 86 break; 87 default: 88 /* FIXME 89 * Should be in sleep mode 90 * TODO 91 * select mode0 by default 92 */ 93 save->current_gate = POWER0_GATE; 94 save->current_clock = POWER0_CLOCK; 95 break; 96 } 97 98 save->gate.value = READ_SCR(pSmi, save->current_gate); 99 save->clock.value = READ_SCR(pSmi, save->current_clock); 100 101 /* FIXME Never changed */ 102 save->timing_ctl.value = READ_SCR(pSmi, TIMING_CTL); 103 104 save->pll_ctl.value = READ_SCR(pSmi, PLL_CTL); 105 save->device_id.value = READ_SCR(pSmi, DEVICE_ID); 106 save->sleep_gate.value = READ_SCR(pSmi, SLEEP_GATE); 107 108 save->panel_display_ctl.value = READ_SCR(pSmi, PANEL_DISPLAY_CTL); 109 save->panel_fb_address.value = READ_SCR(pSmi, PANEL_FB_ADDRESS); 110 save->panel_fb_width.value = READ_SCR(pSmi, PANEL_FB_WIDTH); 111 save->panel_wwidth.value = READ_SCR(pSmi, PANEL_WWIDTH); 112 save->panel_wheight.value = READ_SCR(pSmi, PANEL_WHEIGHT); 113 save->panel_plane_tl.value = READ_SCR(pSmi, PANEL_PLANE_TL); 114 save->panel_plane_br.value = READ_SCR(pSmi, PANEL_PLANE_BR); 115 save->panel_htotal.value = READ_SCR(pSmi, PANEL_HTOTAL); 116 save->panel_hsync.value = READ_SCR(pSmi, PANEL_HSYNC); 117 save->panel_vtotal.value = READ_SCR(pSmi, PANEL_VTOTAL); 118 save->panel_vsync.value = READ_SCR(pSmi, PANEL_VSYNC); 119 120 save->crt_display_ctl.value = READ_SCR(pSmi, CRT_DISPLAY_CTL); 121 save->crt_fb_address.value = READ_SCR(pSmi, CRT_FB_ADDRESS); 122 save->crt_fb_width.value = READ_SCR(pSmi, CRT_FB_WIDTH); 123 save->crt_htotal.value = READ_SCR(pSmi, CRT_HTOTAL); 124 save->crt_hsync.value = READ_SCR(pSmi, CRT_HSYNC); 125 save->crt_vtotal.value = READ_SCR(pSmi, CRT_VTOTAL); 126 save->crt_vsync.value = READ_SCR(pSmi, CRT_VSYNC); 127 128 save->alpha_display_ctl.value = READ_SCR(pSmi, ALPHA_DISPLAY_CTL); 129 save->alpha_fb_address.value = READ_SCR(pSmi, ALPHA_FB_ADDRESS); 130 save->alpha_fb_width.value = READ_SCR(pSmi, ALPHA_FB_WIDTH); 131 save->alpha_plane_tl.value = READ_SCR(pSmi, ALPHA_PLANE_TL); 132 save->alpha_plane_br.value = READ_SCR(pSmi, ALPHA_PLANE_BR); 133 save->alpha_chroma_key.value = READ_SCR(pSmi, ALPHA_CHROMA_KEY); 134 135 /* Also save accel state to properly restore kernel framebuffer */ 136 save->accel_src = READ_SCR(pSmi, ACCEL_SRC); 137 save->accel_dst = READ_SCR(pSmi, ACCEL_DST); 138 save->accel_dim = READ_SCR(pSmi, ACCEL_DIM); 139 save->accel_ctl = READ_SCR(pSmi, ACCEL_CTL); 140 save->accel_pitch = READ_SCR(pSmi, ACCEL_PITCH); 141 save->accel_fmt = READ_SCR(pSmi, ACCEL_FMT); 142 save->accel_clip_tl = READ_SCR(pSmi, ACCEL_CLIP_TL); 143 save->accel_clip_br = READ_SCR(pSmi, ACCEL_CLIP_BR); 144 save->accel_pat_lo = READ_SCR(pSmi, ACCEL_PAT_LO); 145 save->accel_pat_hi = READ_SCR(pSmi, ACCEL_PAT_HI); 146 save->accel_wwidth = READ_SCR(pSmi, ACCEL_WWIDTH); 147 save->accel_src_base = READ_SCR(pSmi, ACCEL_SRC_BASE); 148 save->accel_dst_base = READ_SCR(pSmi, ACCEL_DST_BASE); 149} 150 151void 152SMI501_DisplayPowerManagementSet(ScrnInfoPtr pScrn, 153 int PowerManagementMode, int flags) 154{ 155 SMIPtr pSmi = SMIPTR(pScrn); 156 157 if (pSmi->CurrentDPMS != PowerManagementMode) { 158 /* Set the DPMS mode to every output and CRTC */ 159 xf86DPMSSet(pScrn, PowerManagementMode, flags); 160 161 pSmi->CurrentDPMS = PowerManagementMode; 162 } 163} 164 165Bool 166SMI501_HWInit(ScrnInfoPtr pScrn) 167{ 168 MSOCRegPtr save; 169 MSOCRegPtr mode; 170 SMIPtr pSmi = SMIPTR(pScrn); 171 int32_t x_select, x_divider, x_shift; 172 173 save = pSmi->save; 174 mode = pSmi->mode; 175 176 /* Start with a fresh copy of registers before any mode change */ 177 memcpy(mode, save, sizeof(MSOCRegRec)); 178 179 if (pSmi->UseFBDev) 180 return (TRUE); 181 182 /* Enable DAC -- 0: enable - 1: disable */ 183 mode->misc_ctl.f.dac = 0; 184 185 /* Enable 2D engine */ 186 mode->gate.f.engine = 1; 187 /* Color space conversion */ 188 mode->gate.f.csc = 1; 189 /* ZV port */ 190 mode->gate.f.zv = 1; 191 /* Gpio, Pwm, and I2c */ 192 mode->gate.f.gpio = 1; 193 194 /* FIXME fixed at power mode 0 as in the smi sources */ 195 mode->power_ctl.f.status = 0; 196 mode->power_ctl.f.mode = 0; 197 198 if (pSmi->MCLK) { 199 xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV, 200 "MCLK request %d\n", pSmi->MCLK); 201 (void)SMI501_FindMemClock(pSmi->MCLK, &x_select, &x_divider, &x_shift); 202 mode->clock.f.m_select = x_select; 203 mode->clock.f.m_divider = x_divider; 204 mode->clock.f.m_shift = x_shift; 205 } 206 /* Else use what was configured by the kernel. */ 207 208 if (pSmi->MXCLK) { 209 xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV, 210 "MXCLK request %d\n", pSmi->MXCLK); 211 (void)SMI501_FindMemClock(pSmi->MXCLK, &x_select, &x_divider, &x_shift); 212 mode->clock.f.m1_select = x_select; 213 mode->clock.f.m1_divider = x_divider; 214 mode->clock.f.m1_shift = x_shift; 215 } 216 /* Else use what was configured by the kernel. */ 217 218 if (!pSmi->Dualhead) { 219 /* crt clones panel */ 220 mode->crt_display_ctl.f.enable = 0; 221 /* 0: select panel - 1: select crt */ 222 mode->crt_display_ctl.f.select = 0; 223 mode->crt_display_ctl.f.timing = 0; 224 } 225 226 SMI501_WriteMode_common(pScrn, mode); 227 228 return (TRUE); 229} 230 231void 232SMI501_WriteMode_common(ScrnInfoPtr pScrn, MSOCRegPtr mode) 233{ 234 int32_t pll; 235 MSOCClockRec clock; 236 SMIPtr pSmi = SMIPTR(pScrn); 237 238 if (!pSmi->UseFBDev) { 239 /* Update gate first */ 240 WRITE_SCR(pSmi, mode->current_gate, mode->gate.value); 241 242 clock.value = READ_SCR(pSmi, mode->current_clock); 243 244 if (pSmi->MCLK) { 245 clock.f.m_select = mode->clock.f.m_select; 246 pll = clock.value; 247 clock.f.m_divider = mode->clock.f.m_divider; 248 clock.f.m_shift = mode->clock.f.m_shift; 249 SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value); 250 } 251 252 if (pSmi->MXCLK) { 253 clock.f.m1_select = mode->clock.f.m1_select; 254 pll = clock.value; 255 clock.f.m1_divider = mode->clock.f.m1_divider; 256 clock.f.m1_shift = mode->clock.f.m1_shift; 257 SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value); 258 } 259 260 WRITE_SCR(pSmi, MISC_CTL, mode->misc_ctl.value); 261 262 WRITE_SCR(pSmi, POWER_CTL, mode->power_ctl.value); 263 } 264 265 /* Match configuration */ 266 /* FIXME some other fields should also be set, otherwise, since 267 * neither kernel nor driver change it, a reboot is required to 268 * modify or reset to default */ 269 mode->system_ctl.f.burst = mode->system_ctl.f.burst_read = 270 pSmi->PCIBurst != FALSE; 271 mode->system_ctl.f.retry = pSmi->PCIRetry != FALSE; 272 WRITE_SCR(pSmi, SYSTEM_CTL, mode->system_ctl.value); 273 274 if (!pSmi->Dualhead) 275 WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value); 276} 277 278void 279SMI501_WriteMode_lcd(ScrnInfoPtr pScrn, MSOCRegPtr mode) 280{ 281 int32_t pll; 282 MSOCClockRec clock; 283 SMIPtr pSmi = SMIPTR(pScrn); 284 285 if (!pSmi->UseFBDev) { 286 clock.value = READ_SCR(pSmi, mode->current_clock); 287 288 /* Alternate pll_select is only available for the SMI 502, 289 * and the bit should be only set in that case. */ 290 if (mode->clock.f.pll_select) 291 WRITE_SCR(pSmi, PLL_CTL, mode->pll_ctl.value); 292 clock.f.p2_select = mode->clock.f.p2_select; 293 pll = clock.value; 294 clock.f.p2_divider = mode->clock.f.p2_divider; 295 clock.f.p2_shift = mode->clock.f.p2_shift; 296 clock.f.pll_select = mode->clock.f.pll_select; 297 clock.f.p2_1xclck = mode->clock.f.p2_1xclck; 298 SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value); 299 300 WRITE_SCR(pSmi, PANEL_FB_ADDRESS, mode->panel_fb_address.value); 301 WRITE_SCR(pSmi, PANEL_FB_WIDTH, mode->panel_fb_width.value); 302 303 WRITE_SCR(pSmi, PANEL_WWIDTH, mode->panel_wwidth.value); 304 WRITE_SCR(pSmi, PANEL_WHEIGHT, mode->panel_wheight.value); 305 306 WRITE_SCR(pSmi, PANEL_PLANE_TL, mode->panel_plane_tl.value); 307 WRITE_SCR(pSmi, PANEL_PLANE_BR, mode->panel_plane_br.value); 308 309 WRITE_SCR(pSmi, PANEL_HTOTAL, mode->panel_htotal.value); 310 WRITE_SCR(pSmi, PANEL_HSYNC, mode->panel_hsync.value); 311 WRITE_SCR(pSmi, PANEL_VTOTAL, mode->panel_vtotal.value); 312 WRITE_SCR(pSmi, PANEL_VSYNC, mode->panel_vsync.value); 313 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 314 } 315} 316 317void 318SMI501_WriteMode_crt(ScrnInfoPtr pScrn, MSOCRegPtr mode) 319{ 320 int32_t pll; 321 MSOCClockRec clock; 322 SMIPtr pSmi = SMIPTR(pScrn); 323 324 if (!pSmi->UseFBDev) { 325 clock.value = READ_SCR(pSmi, mode->current_clock); 326 327 clock.f.v2_select = mode->clock.f.v2_select; 328 pll = clock.value; 329 clock.f.v2_divider = mode->clock.f.v2_divider; 330 clock.f.v2_shift = mode->clock.f.v2_shift; 331 clock.f.v2_1xclck = mode->clock.f.v2_1xclck; 332 SMI501_SetClock(pSmi, mode->current_clock, pll, clock.value); 333 334 WRITE_SCR(pSmi, CRT_FB_ADDRESS, mode->crt_fb_address.value); 335 WRITE_SCR(pSmi, CRT_FB_WIDTH, mode->crt_fb_width.value); 336 WRITE_SCR(pSmi, CRT_HTOTAL, mode->crt_htotal.value); 337 WRITE_SCR(pSmi, CRT_HSYNC, mode->crt_hsync.value); 338 WRITE_SCR(pSmi, CRT_VTOTAL, mode->crt_vtotal.value); 339 WRITE_SCR(pSmi, CRT_VSYNC, mode->crt_vsync.value); 340 WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value); 341 } 342} 343 344void 345SMI501_WriteMode_alpha(ScrnInfoPtr pScrn, MSOCRegPtr mode) 346{ 347 SMIPtr pSmi = SMIPTR(pScrn); 348 349 WRITE_SCR(pSmi, ALPHA_FB_ADDRESS, mode->alpha_fb_address.value); 350 WRITE_SCR(pSmi, ALPHA_FB_WIDTH, mode->alpha_fb_width.value); 351 352 WRITE_SCR(pSmi, ALPHA_PLANE_TL, mode->alpha_plane_tl.value); 353 WRITE_SCR(pSmi, ALPHA_PLANE_BR, mode->alpha_plane_br.value); 354 355 WRITE_SCR(pSmi, ALPHA_CHROMA_KEY, mode->alpha_chroma_key.value); 356 357 WRITE_SCR(pSmi, ALPHA_DISPLAY_CTL, mode->alpha_display_ctl.value); 358} 359 360void 361SMI501_WriteMode(ScrnInfoPtr pScrn, MSOCRegPtr restore) 362{ 363 SMIPtr pSmi = SMIPTR(pScrn); 364 365 SMI501_WriteMode_common(pScrn, restore); 366 SMI501_WriteMode_lcd(pScrn, restore); 367 SMI501_WriteMode_crt(pScrn, restore); 368#if SMI_CURSOR_ALPHA_PLANE 369 SMI501_WriteMode_alpha(pScrn, restore); 370#endif 371 372 /* This function should be called when switching to virtual console */ 373 WRITE_SCR(pSmi, ACCEL_SRC, restore->accel_src); 374 WRITE_SCR(pSmi, ACCEL_DST, restore->accel_dst); 375 WRITE_SCR(pSmi, ACCEL_DIM, restore->accel_dim); 376 WRITE_SCR(pSmi, ACCEL_CTL, restore->accel_ctl); 377 WRITE_SCR(pSmi, ACCEL_PITCH, restore->accel_pitch); 378 WRITE_SCR(pSmi, ACCEL_FMT, restore->accel_fmt); 379 WRITE_SCR(pSmi, ACCEL_CLIP_TL, restore->accel_clip_tl); 380 WRITE_SCR(pSmi, ACCEL_CLIP_BR, restore->accel_clip_br); 381 WRITE_SCR(pSmi, ACCEL_PAT_LO, restore->accel_pat_lo); 382 WRITE_SCR(pSmi, ACCEL_PAT_HI, restore->accel_pat_hi); 383 WRITE_SCR(pSmi, ACCEL_WWIDTH, restore->accel_wwidth); 384 WRITE_SCR(pSmi, ACCEL_SRC_BASE, restore->accel_src_base); 385 WRITE_SCR(pSmi, ACCEL_DST_BASE, restore->accel_dst_base); 386} 387 388void 389SMI501_PowerPanel(ScrnInfoPtr pScrn, MSOCRegPtr mode, Bool on) 390{ 391 SMIPtr pSmi = SMIPTR(pScrn); 392 393 if (on != FALSE) { 394 mode->panel_display_ctl.f.vdd = 1; 395 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 396 SMI501_WaitVSync(pSmi, 4); 397 398 mode->panel_display_ctl.f.signal = 1; 399 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 400 SMI501_WaitVSync(pSmi, 4); 401 402 mode->panel_display_ctl.f.bias = 1; 403 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 404 SMI501_WaitVSync(pSmi, 4); 405 406 mode->panel_display_ctl.f.fp = 1; 407 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 408 SMI501_WaitVSync(pSmi, 4); 409 } 410 else { 411 mode->panel_display_ctl.f.fp = 0; 412 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 413 SMI501_WaitVSync(pSmi, 4); 414 415 mode->panel_display_ctl.f.bias = 0; 416 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 417 SMI501_WaitVSync(pSmi, 4); 418 419 mode->panel_display_ctl.f.signal = 0; 420 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 421 SMI501_WaitVSync(pSmi, 4); 422 423 mode->panel_display_ctl.f.vdd = 0; 424 WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value); 425 SMI501_WaitVSync(pSmi, 4); 426 } 427} 428 429static char * 430format_integer_base2(int32_t word) 431{ 432 int i; 433 static char buffer[33]; 434 435 for (i = 0; i < 32; i++) { 436 if (word & (1 << i)) 437 buffer[31 - i] = '1'; 438 else 439 buffer[31 - i] = '0'; 440 } 441 442 return (buffer); 443} 444 445double 446SMI501_FindClock(double clock, int32_t max_divider, Bool has1xclck, 447 int32_t *x2_1xclck, 448 int32_t *x2_select, int32_t *x2_divider, int32_t *x2_shift) 449{ 450 double diff, best, mclk; 451 int32_t multiplier, divider, shift, xclck; 452 453 /* The Crystal input frequency is 24Mhz, and can be multiplied 454 * by 12 or 14 (actually, there are other values, see TIMING_CTL, 455 * MMIO 0x068) */ 456 457 /* Find clock best matching mode */ 458 best = 0x7fffffff; 459 for (multiplier = 12, mclk = multiplier * 24 * 1000.0; 460 mclk <= 14 * 24 * 1000.0; 461 multiplier += 2, mclk = multiplier * 24 * 1000.0) { 462 for (divider = 1; divider <= max_divider; divider += 2) { 463 for (shift = 0; shift < 8; shift++) { 464 /* Divider 1 not in specs for cards older then 502 */ 465 for (xclck = 1; xclck >= !has1xclck; xclck--) { 466 diff = (mclk / (divider << shift << xclck)) - clock; 467 if (fabs(diff) < best) { 468 *x2_shift = shift; 469 *x2_divider = divider == 1 ? 0 : divider == 3 ? 1 : 2; 470 *x2_select = mclk == 12 * 24 * 1000.0 ? 0 : 1; 471 *x2_1xclck = xclck == 0; 472 473 /* Remember best diff */ 474 best = fabs(diff); 475 } 476 } 477 } 478 } 479 } 480 481 xf86ErrorFVerb(VERBLEV, 482 "\tMatching clock %5.2f, diff %5.2f (%d/%d/%d/%d)\n", 483 ((*x2_select ? 14 : 12) * 24 * 1000.0) / 484 ((*x2_divider == 0 ? 1 : *x2_divider == 1 ? 3 : 5) << 485 *x2_shift << (*x2_1xclck ? 0 : 1)), 486 best, *x2_shift, *x2_divider, *x2_select, *x2_1xclck); 487 488 return (best); 489} 490 491double 492SMI501_FindMemClock(double clock, int32_t *x1_select, 493 int32_t *x1_divider, int32_t *x1_shift) 494{ 495 double diff, best, mclk; 496 int32_t multiplier, divider, shift; 497 498 best = 0x7fffffff; 499 for (multiplier = 12, mclk = multiplier * 24 * 1000.0; 500 mclk <= 14 * 24 * 1000.0; 501 multiplier += 2, mclk = multiplier * 24 * 1000.0) { 502 for (divider = 1; divider <= 3; divider += 2) { 503 for (shift = 0; shift < 8; shift++) { 504 diff = (mclk / (divider << shift)) - clock; 505 if (fabs(diff) < best) { 506 *x1_shift = shift; 507 *x1_divider = divider == 1 ? 0 : 1; 508 *x1_select = mclk == 12 * 24 * 1000.0 ? 0 : 1; 509 510 /* Remember best diff */ 511 best = fabs(diff); 512 } 513 } 514 } 515 } 516 517 xf86ErrorFVerb(VERBLEV, 518 "\tMatching clock %5.2f, diff %5.2f (%d/%d/%d)\n", 519 ((*x1_select ? 14 : 12) * 24 * 1000.0) / 520 ((*x1_divider == 0 ? 1 : 3) << *x1_shift), 521 best, *x1_shift, *x1_divider, *x1_select); 522 523 return (best); 524} 525 526 527double 528SMI501_FindPLLClock(double clock, int32_t *m, int32_t *n, int32_t *xclck) 529{ 530 int32_t M, N, K; 531 double diff, best; 532 double frequency; 533 534 /* This method, available only on the 502 is intended to cover the 535 * disadvantage of the other method where certain modes cannot be 536 * displayed correctly due to the big difference on the requested 537 * pixel clock, with the actual pixel clock that can be achieved by 538 * those divisions. In this method, N can be any integer between 2 539 * and 24, M can be any positive, 8 bits integer, and K is either 1 540 * or 2. 541 * To calculate the programmable PLL, the following formula is 542 * used: 543 * 544 * Requested Pixel Clock = Input Frequency * M / N 545 * 546 * Input Frequency is the crystal input frequency value (24 MHz in 547 * the SMI VGX Demo Board). 548 * 549 * K is a divisor, used by setting bit 15 of the PLL_CTL 550 * (PLL Output Divided by 2). 551 * 552 * So, it should be requested_clock = input_frequency * M / N / K 553 */ 554 555 /* That said, use what actually works, that is: 556 * requested_clock = input_frequency * K * M / N 557 * 558 * where requested_clock is modeline pixel clock, 559 * input_frequency is 12, K is either 1 or 2 (and sets bit15 accordingly), 560 * M is a non zero 8 bits unsigned integer, and N is a value from 2 to 24. 561 */ 562 563 best = 0x7fffffff; 564 frequency = 12 * 1000.0; 565 for (N = 2; N <= 24; N++) { 566 for (K = 1; K <= 2; K++) { 567 M = clock / frequency * K * N; 568 diff = ((int32_t)(frequency / K * M) / N) - clock; 569 /* Ensure M is larger then 0 and fits in 8 bits */ 570 if (M > 0 && M < 0x100 && fabs(diff) < best) { 571 *m = M; 572 *n = N; 573 *xclck = K == 1; 574 575 /* Remember best diff */ 576 best = fabs(diff); 577 } 578 } 579 } 580 581 xf86ErrorFVerb(VERBLEV, 582 "\tMatching alternate clock %5.2f, diff %5.2f (%d/%d/%d)\n", 583 frequency / (*xclck ? 1 : 2) * *m / *n, best, 584 *m, *n, *xclck); 585 586 return (best); 587} 588 589void 590SMI501_PrintRegs(ScrnInfoPtr pScrn) 591{ 592 int i; 593 SMIPtr pSmi = SMIPTR(pScrn); 594 595 xf86ErrorFVerb(VERBLEV, " SMI501 System Setup:\n"); 596 for (i = 0x00; i <= 0x74; i += 4) 597 xf86ErrorFVerb(VERBLEV, "\t%08x: %s\n", i, 598 format_integer_base2(READ_SCR(pSmi, i))); 599 xf86ErrorFVerb(VERBLEV, " SMI501 Display Setup:\n"); 600 for (i = 0x80000; i < 0x80400; i += 4) 601 xf86ErrorFVerb(VERBLEV, "\t%08x: %s\n", i, 602 format_integer_base2(READ_SCR(pSmi, i))); 603} 604 605void 606SMI501_WaitVSync(SMIPtr pSmi, int vsync_count) 607{ 608 MSOCCmdStatusRec status; 609 int32_t timeout; 610 611 while (vsync_count-- > 0) { 612 /* Wait for end of vsync */ 613 timeout = 0; 614 do { 615 /* bit 11: vsync active *if set* */ 616 status.value = READ_SCR(pSmi, CMD_STATUS); 617 if (++timeout == 10000) 618 break; 619 } while (status.f.pvsync); 620 621 /* Wait for start of vsync */ 622 timeout = 0; 623 do { 624 status.value = READ_SCR(pSmi, CMD_STATUS); 625 if (++timeout == 10000) 626 break; 627 } while (!status.f.pvsync); 628 } 629} 630 631static void 632SMI501_SetClock(SMIPtr pSmi, int32_t port, int32_t pll, int32_t value) 633{ 634 /* 635 * Rules to Program the Power Mode Clock Registers for Clock Selection 636 * 637 * 1. There should be only one clock source changed at a time. 638 * To change clock source for P2XCLK, V2XCLK, MCLK, M2XCLK 639 * simultaneously may cause the internal logic normal operation 640 * to be disrupted. There should be a minimum of 16mS wait from 641 * change one clock source to another. 642 * 2. When adjusting the clock rate, the PLL selection bit should 643 * be programmed first before changing the divider value for each 644 * clock source. For example, to change the P2XCLK clock rate: 645 * . bit 29 should be set first 646 * . wait for a minimum of 16ms (about one Vsync time) 647 * . adjust bits [28:24]. 648 * The minimum 16 ms wait is necessary for logic to settle down 649 * before the clock rate is changed. 650 * 3. There should be a minimum 16 ms wait after a clock source is 651 * changed before any operation that could result in a bus 652 * transaction. 653 */ 654 655 /* register contents selecting clock */ 656 WRITE_SCR(pSmi, port, pll); 657 SMI501_WaitVSync(pSmi, 1); 658 659 /* full register contents */ 660 WRITE_SCR(pSmi, port, value); 661 SMI501_WaitVSync(pSmi, 1); 662} 663