diff options
| author | Krzysztof Helt <krzysztof.h1@wp.pl> | 2008-10-16 01:03:42 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:44 -0400 |
| commit | 486ff387c0f27030a3cfb142469ba140f2d8976e (patch) | |
| tree | 0a351c71ab3fb1b0793334a9c043f8c8760ec37a | |
| parent | 3b921832d483a2b9d6fabdbb5f871a4f18cb9b65 (diff) | |
cirrusfb: do not change MCLK for Alpine chips
A memory clock value (MCLK) is changed to a minimum required by a current
mode bandwidth. This usually lowers the MCLK to its minimum (50 MHz) thus
decreasing the card performance. Just leave the MCLK value set by card
BIOS.
The CL-GD5446 Technical Reference Manual point 9.9.1.3 states that if a
pixclock value is close (~1%) to the MCLK or MCLK/2 this may result in a
jitter on the screen. A countermeasure is to use the MCLK as pixclock
source instead of a VCLK. The patch implements this as well.
Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | drivers/video/cirrusfb.c | 119 |
1 files changed, 53 insertions, 66 deletions
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 9e0a1c58fde4..048b139f0e50 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c | |||
| @@ -327,9 +327,7 @@ static const struct { | |||
| 327 | #endif /* CONFIG_ZORRO */ | 327 | #endif /* CONFIG_ZORRO */ |
| 328 | 328 | ||
| 329 | struct cirrusfb_regs { | 329 | struct cirrusfb_regs { |
| 330 | long multiplexing; | 330 | int multiplexing; |
| 331 | long mclk; | ||
| 332 | long divMCLK; | ||
| 333 | }; | 331 | }; |
| 334 | 332 | ||
| 335 | #ifdef CIRRUSFB_DEBUG | 333 | #ifdef CIRRUSFB_DEBUG |
| @@ -461,45 +459,28 @@ static int cirrusfb_release(struct fb_info *info, int user) | |||
| 461 | /****************************************************************************/ | 459 | /****************************************************************************/ |
| 462 | /**** BEGIN Hardware specific Routines **************************************/ | 460 | /**** BEGIN Hardware specific Routines **************************************/ |
| 463 | 461 | ||
| 464 | /* Get a good MCLK value */ | 462 | /* Check if the MCLK is not a better clock source */ |
| 465 | static long cirrusfb_get_mclk(long freq, int bpp, long *div) | 463 | static int cirrusfb_check_mclk(struct cirrusfb_info *cinfo, long freq) |
| 466 | { | 464 | { |
| 467 | long mclk; | 465 | long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f; |
| 468 | 466 | ||
| 469 | assert(div != NULL); | 467 | /* Read MCLK value */ |
| 470 | 468 | mclk = (14318 * mclk) >> 3; | |
| 471 | /* Calculate MCLK, in case VCLK is high enough to require > 50MHz. | 469 | DPRINTK("Read MCLK of %ld kHz\n", mclk); |
| 472 | * Assume a 64-bit data path for now. The formula is: | ||
| 473 | * ((B * PCLK * 2)/W) * 1.2 | ||
| 474 | * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */ | ||
| 475 | mclk = ((bpp / 8) * freq * 2) / 4; | ||
| 476 | mclk = (mclk * 12) / 10; | ||
| 477 | if (mclk < 50000) | ||
| 478 | mclk = 50000; | ||
| 479 | DPRINTK("Use MCLK of %ld kHz\n", mclk); | ||
| 480 | |||
| 481 | /* Calculate value for SR1F. Multiply by 2 so we can round up. */ | ||
| 482 | mclk = ((mclk * 16) / 14318); | ||
| 483 | mclk = (mclk + 1) / 2; | ||
| 484 | DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk); | ||
| 485 | 470 | ||
| 486 | /* Determine if we should use MCLK instead of VCLK, and if so, what we | 471 | /* Determine if we should use MCLK instead of VCLK, and if so, what we |
| 487 | * should divide it by to get VCLK */ | 472 | * should divide it by to get VCLK |
| 488 | switch (freq) { | 473 | */ |
| 489 | case 24751 ... 25249: | 474 | |
| 490 | *div = 2; | 475 | if (abs(freq - mclk) < 250) { |
| 491 | DPRINTK("Using VCLK = MCLK/2\n"); | ||
| 492 | break; | ||
| 493 | case 49501 ... 50499: | ||
| 494 | *div = 1; | ||
| 495 | DPRINTK("Using VCLK = MCLK\n"); | 476 | DPRINTK("Using VCLK = MCLK\n"); |
| 496 | break; | 477 | return 1; |
| 497 | default: | 478 | } else if (abs(freq - (mclk / 2)) < 250) { |
| 498 | *div = 0; | 479 | DPRINTK("Using VCLK = MCLK/2\n"); |
| 499 | break; | 480 | return 2; |
| 500 | } | 481 | } |
| 501 | 482 | ||
| 502 | return mclk; | 483 | return 0; |
| 503 | } | 484 | } |
| 504 | 485 | ||
| 505 | static int cirrusfb_check_var(struct fb_var_screeninfo *var, | 486 | static int cirrusfb_check_var(struct fb_var_screeninfo *var, |
| @@ -705,30 +686,27 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, | |||
| 705 | break; | 686 | break; |
| 706 | } | 687 | } |
| 707 | #endif | 688 | #endif |
| 708 | regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel, | ||
| 709 | ®s->divMCLK); | ||
| 710 | |||
| 711 | return 0; | 689 | return 0; |
| 712 | } | 690 | } |
| 713 | 691 | ||
| 714 | static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val, | 692 | static void cirrusfb_set_mclk_as_source(const struct cirrusfb_info *cinfo, |
| 715 | int div) | 693 | int div) |
| 716 | { | 694 | { |
| 695 | unsigned char old1f, old1e; | ||
| 717 | assert(cinfo != NULL); | 696 | assert(cinfo != NULL); |
| 697 | old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40; | ||
| 718 | 698 | ||
| 719 | if (div == 2) { | 699 | if (div) { |
| 720 | /* VCLK = MCLK/2 */ | 700 | DPRINTK("Set %s as pixclock source.\n", |
| 721 | unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E); | 701 | (div == 2) ? "MCLK/2" : "MCLK"); |
| 722 | vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1); | 702 | old1f |= 0x40; |
| 723 | vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f)); | 703 | old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1; |
| 724 | } else if (div == 1) { | 704 | if (div == 2) |
| 725 | /* VCLK = MCLK */ | 705 | old1e |= 1; |
| 726 | unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E); | 706 | |
| 727 | vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1); | 707 | vga_wseq(cinfo->regbase, CL_SEQR1E, old1e); |
| 728 | vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f)); | ||
| 729 | } else { | ||
| 730 | vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f); | ||
| 731 | } | 708 | } |
| 709 | vga_wseq(cinfo->regbase, CL_SEQR1F, old1f); | ||
| 732 | } | 710 | } |
| 733 | 711 | ||
| 734 | /************************************************************************* | 712 | /************************************************************************* |
| @@ -904,19 +882,31 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
| 904 | /* formula: VClk = (OSC * N) / (D * (1+P)) */ | 882 | /* formula: VClk = (OSC * N) / (D * (1+P)) */ |
| 905 | /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */ | 883 | /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */ |
| 906 | 884 | ||
| 907 | vga_wseq(regbase, CL_SEQRB, nom); | 885 | if (cinfo->btype == BT_ALPINE) { |
| 908 | tmp = den << 1; | 886 | /* if freq is close to mclk or mclk/2 select mclk |
| 909 | if (div != 0) | 887 | * as clock source |
| 910 | tmp |= 1; | 888 | */ |
| 889 | int divMCLK = cirrusfb_check_mclk(cinfo, freq); | ||
| 890 | if (divMCLK) { | ||
| 891 | nom = 0; | ||
| 892 | cirrusfb_set_mclk_as_source(cinfo, divMCLK); | ||
| 893 | } | ||
| 894 | } | ||
| 895 | if (nom) { | ||
| 896 | vga_wseq(regbase, CL_SEQRB, nom); | ||
| 897 | tmp = den << 1; | ||
| 898 | if (div != 0) | ||
| 899 | tmp |= 1; | ||
| 911 | 900 | ||
| 912 | /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */ | 901 | /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */ |
| 913 | if ((cinfo->btype == BT_SD64) || | 902 | if ((cinfo->btype == BT_SD64) || |
| 914 | (cinfo->btype == BT_ALPINE) || | 903 | (cinfo->btype == BT_ALPINE) || |
| 915 | (cinfo->btype == BT_GD5480)) | 904 | (cinfo->btype == BT_GD5480)) |
| 916 | tmp |= 0x80; | 905 | tmp |= 0x80; |
| 917 | 906 | ||
| 918 | DPRINTK("CL_SEQR1B: %ld\n", (long) tmp); | 907 | DPRINTK("CL_SEQR1B: %ld\n", (long) tmp); |
| 919 | vga_wseq(regbase, CL_SEQR1B, tmp); | 908 | vga_wseq(regbase, CL_SEQR1B, tmp); |
| 909 | } | ||
| 920 | 910 | ||
| 921 | if (yres >= 1024) | 911 | if (yres >= 1024) |
| 922 | /* 1280x1024 */ | 912 | /* 1280x1024 */ |
| @@ -1106,7 +1096,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
| 1106 | 1096 | ||
| 1107 | case BT_ALPINE: | 1097 | case BT_ALPINE: |
| 1108 | DPRINTK(" (for GD543x)\n"); | 1098 | DPRINTK(" (for GD543x)\n"); |
| 1109 | cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK); | ||
| 1110 | /* We already set SRF and SR1F */ | 1099 | /* We already set SRF and SR1F */ |
| 1111 | break; | 1100 | break; |
| 1112 | 1101 | ||
| @@ -1179,7 +1168,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
| 1179 | case BT_ALPINE: | 1168 | case BT_ALPINE: |
| 1180 | DPRINTK(" (for GD543x)\n"); | 1169 | DPRINTK(" (for GD543x)\n"); |
| 1181 | vga_wseq(regbase, CL_SEQR7, 0xa7); | 1170 | vga_wseq(regbase, CL_SEQR7, 0xa7); |
| 1182 | cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK); | ||
| 1183 | break; | 1171 | break; |
| 1184 | 1172 | ||
| 1185 | case BT_GD5480: | 1173 | case BT_GD5480: |
| @@ -1257,7 +1245,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) | |||
| 1257 | case BT_ALPINE: | 1245 | case BT_ALPINE: |
| 1258 | DPRINTK(" (for GD543x)\n"); | ||
