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