aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKrzysztof Helt <krzysztof.h1@wp.pl>2008-10-16 01:03:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-16 14:21:44 -0400
commit486ff387c0f27030a3cfb142469ba140f2d8976e (patch)
tree0a351c71ab3fb1b0793334a9c043f8c8760ec37a /drivers
parent3b921832d483a2b9d6fabdbb5f871a4f18cb9b65 (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')
-rw-r--r--drivers/video/cirrusfb.c119
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
329struct cirrusfb_regs { 329struct 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 */
465static long cirrusfb_get_mclk(long freq, int bpp, long *div) 463static 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
505static int cirrusfb_check_var(struct fb_var_screeninfo *var, 486static 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 &regs->divMCLK);
710
711 return 0; 689 return 0;
712} 690}
713 691
714static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val, 692static 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: