Commit 486ff387 authored by Krzysztof Helt's avatar Krzysztof Helt Committed by Linus Torvalds

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: default avatarKrzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 3b921832
...@@ -327,9 +327,7 @@ static const struct { ...@@ -327,9 +327,7 @@ static const struct {
#endif /* CONFIG_ZORRO */ #endif /* CONFIG_ZORRO */
struct cirrusfb_regs { struct cirrusfb_regs {
long multiplexing; int multiplexing;
long mclk;
long divMCLK;
}; };
#ifdef CIRRUSFB_DEBUG #ifdef CIRRUSFB_DEBUG
...@@ -461,45 +459,28 @@ static int cirrusfb_release(struct fb_info *info, int user) ...@@ -461,45 +459,28 @@ static int cirrusfb_release(struct fb_info *info, int user)
/****************************************************************************/ /****************************************************************************/
/**** BEGIN Hardware specific Routines **************************************/ /**** BEGIN Hardware specific Routines **************************************/
/* Get a good MCLK value */ /* Check if the MCLK is not a better clock source */
static long cirrusfb_get_mclk(long freq, int bpp, long *div) static int cirrusfb_check_mclk(struct cirrusfb_info *cinfo, long freq)
{ {
long mclk; long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
assert(div != NULL); /* Read MCLK value */
mclk = (14318 * mclk) >> 3;
/* Calculate MCLK, in case VCLK is high enough to require > 50MHz. DPRINTK("Read MCLK of %ld kHz\n", mclk);
* Assume a 64-bit data path for now. The formula is:
* ((B * PCLK * 2)/W) * 1.2
* B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
mclk = ((bpp / 8) * freq * 2) / 4;
mclk = (mclk * 12) / 10;
if (mclk < 50000)
mclk = 50000;
DPRINTK("Use MCLK of %ld kHz\n", mclk);
/* Calculate value for SR1F. Multiply by 2 so we can round up. */
mclk = ((mclk * 16) / 14318);
mclk = (mclk + 1) / 2;
DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
/* Determine if we should use MCLK instead of VCLK, and if so, what we /* Determine if we should use MCLK instead of VCLK, and if so, what we
* should divide it by to get VCLK */ * should divide it by to get VCLK
switch (freq) { */
case 24751 ... 25249:
*div = 2; if (abs(freq - mclk) < 250) {
DPRINTK("Using VCLK = MCLK/2\n");
break;
case 49501 ... 50499:
*div = 1;
DPRINTK("Using VCLK = MCLK\n"); DPRINTK("Using VCLK = MCLK\n");
break; return 1;
default: } else if (abs(freq - (mclk / 2)) < 250) {
*div = 0; DPRINTK("Using VCLK = MCLK/2\n");
break; return 2;
} }
return mclk; return 0;
} }
static int cirrusfb_check_var(struct fb_var_screeninfo *var, 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,30 +686,27 @@ static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
break; break;
} }
#endif #endif
regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
&regs->divMCLK);
return 0; return 0;
} }
static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val, static void cirrusfb_set_mclk_as_source(const struct cirrusfb_info *cinfo,
int div) int div)
{ {
unsigned char old1f, old1e;
assert(cinfo != NULL); assert(cinfo != NULL);
old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
if (div == 2) { if (div) {
/* VCLK = MCLK/2 */ DPRINTK("Set %s as pixclock source.\n",
unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E); (div == 2) ? "MCLK/2" : "MCLK");
vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1); old1f |= 0x40;
vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f)); old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
} else if (div == 1) { if (div == 2)
/* VCLK = MCLK */ old1e |= 1;
unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1); vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
} else {
vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
} }
vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
} }
/************************************************************************* /*************************************************************************
...@@ -904,6 +882,17 @@ static int cirrusfb_set_par_foo(struct fb_info *info) ...@@ -904,6 +882,17 @@ static int cirrusfb_set_par_foo(struct fb_info *info)
/* formula: VClk = (OSC * N) / (D * (1+P)) */ /* formula: VClk = (OSC * N) / (D * (1+P)) */
/* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */ /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
if (cinfo->btype == BT_ALPINE) {
/* if freq is close to mclk or mclk/2 select mclk
* as clock source
*/
int divMCLK = cirrusfb_check_mclk(cinfo, freq);
if (divMCLK) {
nom = 0;
cirrusfb_set_mclk_as_source(cinfo, divMCLK);
}
}
if (nom) {
vga_wseq(regbase, CL_SEQRB, nom); vga_wseq(regbase, CL_SEQRB, nom);
tmp = den << 1; tmp = den << 1;
if (div != 0) if (div != 0)
...@@ -917,6 +906,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info) ...@@ -917,6 +906,7 @@ static int cirrusfb_set_par_foo(struct fb_info *info)
DPRINTK("CL_SEQR1B: %ld\n", (long) tmp); DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
vga_wseq(regbase, CL_SEQR1B, tmp); vga_wseq(regbase, CL_SEQR1B, tmp);
}
if (yres >= 1024) if (yres >= 1024)
/* 1280x1024 */ /* 1280x1024 */
...@@ -1106,7 +1096,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) ...@@ -1106,7 +1096,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info)
case BT_ALPINE: case BT_ALPINE:
DPRINTK(" (for GD543x)\n"); DPRINTK(" (for GD543x)\n");
cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
/* We already set SRF and SR1F */ /* We already set SRF and SR1F */
break; break;
...@@ -1179,7 +1168,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) ...@@ -1179,7 +1168,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info)
case BT_ALPINE: case BT_ALPINE:
DPRINTK(" (for GD543x)\n"); DPRINTK(" (for GD543x)\n");
vga_wseq(regbase, CL_SEQR7, 0xa7); vga_wseq(regbase, CL_SEQR7, 0xa7);
cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
break; break;
case BT_GD5480: case BT_GD5480:
...@@ -1257,7 +1245,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info) ...@@ -1257,7 +1245,6 @@ static int cirrusfb_set_par_foo(struct fb_info *info)
case BT_ALPINE: case BT_ALPINE:
DPRINTK(" (for GD543x)\n"); DPRINTK(" (for GD543x)\n");
vga_wseq(regbase, CL_SEQR7, 0xa9); vga_wseq(regbase, CL_SEQR7, 0xa9);
cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
break; break;
case BT_GD5480: case BT_GD5480:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment