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 /drivers/video/cirrusfb.c | |
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>
Diffstat (limited to 'drivers/video/cirrusfb.c')
-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"); | 1246 | DPRINTK(" (for GD543x)\n"); |
1259 | vga_wseq(regbase, CL_SEQR7, 0xa9); | 1247 | vga_wseq(regbase, CL_SEQR7, 0xa9); |
1260 | cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK); | ||
1261 | break; | 1248 | break; |
1262 | 1249 | ||
1263 | case BT_GD5480: | 1250 | case BT_GD5480: |