aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2006-12-22 04:10:36 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-12-22 11:55:50 -0500
commit31fccf7fe4097e62f038bdfe8f4f68ecaea8ebe7 (patch)
treed0a62c413b17572e2d8baa4fbd79fde042458b40 /drivers/video
parent5e40508e5fee2dac7b04d5bc5b5ef3b452f0a899 (diff)
[PATCH] gxt4500: Fix colormap and PLL setting, support GXT6000P
This fixes some bugs in the gxt4500 framebuffer driver, and adds support for GXT6000P cards. First, I had the red and blue channels swapped in the colormap update code, resulting in penguins' noses and feet turning blue (though the penguins weren't actually shivering :). Secondly, the code that calculated the values to put in the PLL that generates the pixel clock wasn't observing some constraints that I wasn't originally aware of, but am now that I have some documentation on the chip. The GXT6000P is essentially identical from software's point of view, except for a different reference clock for the PLL, and the addition of a geometry engine (which this driver doesn't use). Signed-off-by: Paul Mackerras <paulus@samba.org> Cc: James Simmons <jsimmons@infradead.org> Cc: "Antonino A. Daplas" <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/gxt4500.c95
1 files changed, 64 insertions, 31 deletions
diff --git a/drivers/video/gxt4500.c b/drivers/video/gxt4500.c
index 3adf6ab0768f..23a6bcc3e3ce 100644
--- a/drivers/video/gxt4500.c
+++ b/drivers/video/gxt4500.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Frame buffer device for IBM GXT4500P display adaptor 2 * Frame buffer device for IBM GXT4500P and GXT6000P display adaptors
3 * 3 *
4 * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@samba.org> 4 * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@samba.org>
5 */ 5 */
@@ -11,8 +11,10 @@
11#include <linux/pci.h> 11#include <linux/pci.h>
12#include <linux/pci_ids.h> 12#include <linux/pci_ids.h>
13#include <linux/delay.h> 13#include <linux/delay.h>
14#include <linux/string.h>
14 15
15#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c 16#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c
17#define PCI_DEVICE_ID_IBM_GXT6000P 0x170
16 18
17/* GXT4500P registers */ 19/* GXT4500P registers */
18 20
@@ -94,6 +96,7 @@ static const unsigned char pixsize[] = {
94#define PLL_M 0x4040 96#define PLL_M 0x4040
95#define PLL_N 0x4044 97#define PLL_N 0x4044
96#define PLL_POSTDIV 0x4048 98#define PLL_POSTDIV 0x4048
99#define PLL_C 0x404c
97 100
98/* Hardware cursor */ 101/* Hardware cursor */
99#define CURSOR_X 0x4078 102#define CURSOR_X 0x4078
@@ -140,6 +143,7 @@ struct gxt4500_par {
140 int pixfmt; /* pixel format, see DFA_PIX_* values */ 143 int pixfmt; /* pixel format, see DFA_PIX_* values */
141 144
142 /* PLL parameters */ 145 /* PLL parameters */
146 int refclk_ps; /* ref clock period in picoseconds */
143 int pll_m; /* ref clock divisor */ 147 int pll_m; /* ref clock divisor */
144 int pll_n; /* VCO divisor */ 148 int pll_n; /* VCO divisor */
145 int pll_pd1; /* first post-divisor */ 149 int pll_pd1; /* first post-divisor */
@@ -166,6 +170,21 @@ static const struct fb_videomode defaultmode __devinitdata = {
166 .vmode = FB_VMODE_NONINTERLACED 170 .vmode = FB_VMODE_NONINTERLACED
167}; 171};
168 172
173/* List of supported cards */
174enum gxt_cards {
175 GXT4500P,
176 GXT6000P
177};
178
179/* Card-specific information */
180static const struct cardinfo {
181 int refclk_ps; /* period of PLL reference clock in ps */
182 const char *cardname;
183} cardinfo[] = {
184 [GXT4500P] = { .refclk_ps = 9259, .cardname = "IBM GXT4500P" },
185 [GXT6000P] = { .refclk_ps = 40000, .cardname = "IBM GXT6000P" },
186};
187
169/* 188/*
170 * The refclk and VCO dividers appear to use a linear feedback shift 189 * The refclk and VCO dividers appear to use a linear feedback shift
171 * register, which gets reloaded when it reaches a terminal value, at 190 * register, which gets reloaded when it reaches a terminal value, at
@@ -203,27 +222,16 @@ static const unsigned char ndivtab[] = {
203/* 130 */ 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e, 222/* 130 */ 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e,
204/* 140 */ 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98, 223/* 140 */ 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98,
205/* 150 */ 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2, 224/* 150 */ 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2,
206/* 160 */ 0x69, 0xb4, 0xda, 0xed, 0x76, 0xbb, 0x5d, 0xae, 0xd7, 0x6b, 225/* 160 */ 0x69,
207/* 170 */ 0xb5, 0x5a, 0xad, 0x56, 0xab, 0xd5, 0x6a, 0x35, 0x1a, 0x8d,
208/* 180 */ 0x46, 0x23, 0x11, 0x88, 0x44, 0x22, 0x91, 0xc8, 0x64, 0x32,
209/* 190 */ 0x19, 0x0c, 0x86, 0x43, 0x21, 0x10, 0x08, 0x04, 0x02, 0x81,
210/* 200 */ 0x40, 0xa0, 0xd0, 0x68, 0x34, 0x9a, 0xcd, 0x66, 0x33, 0x99,
211/* 210 */ 0x4c, 0xa6, 0x53, 0xa9, 0xd4, 0xea, 0x75, 0x3a, 0x9d, 0xce,
212/* 220 */ 0xe7, 0xf3, 0xf9, 0x7c, 0x3e, 0x1f, 0x8f, 0x47, 0xa3, 0x51,
213/* 230 */ 0xa8, 0x54, 0xaa, 0x55, 0x2a, 0x15, 0x0a, 0x05, 0x82, 0xc1,
214/* 240 */ 0x60, 0xb0, 0x58, 0xac, 0xd6, 0xeb, 0xf5, 0x7a, 0xbd, 0xde,
215/* 250 */ 0x6f, 0x37, 0x1b, 0x0d, 0x06, 0x03, 0x01,
216}; 226};
217 227
218#define REF_PERIOD_PS 9259 /* period of reference clock in ps */
219
220static int calc_pll(int period_ps, struct gxt4500_par *par) 228static int calc_pll(int period_ps, struct gxt4500_par *par)
221{ 229{
222 int m, n, pdiv1, pdiv2, postdiv; 230 int m, n, pdiv1, pdiv2, postdiv;
223 int pll_period, best_error, t; 231 int pll_period, best_error, t, intf;
224 232
225 /* only deal with range 1MHz - 400MHz */ 233 /* only deal with range 5MHz - 300MHz */
226 if (period_ps < 2500 || period_ps > 1000000) 234 if (period_ps < 3333 || period_ps > 200000)
227 return -1; 235 return -1;
228 236
229 best_error = 1000000; 237 best_error = 1000000;
@@ -231,14 +239,17 @@ static int calc_pll(int period_ps, struct gxt4500_par *par)
231 for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) { 239 for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) {
232 postdiv = pdiv1 * pdiv2; 240 postdiv = pdiv1 * pdiv2;
233 pll_period = (period_ps + postdiv - 1) / postdiv; 241 pll_period = (period_ps + postdiv - 1) / postdiv;
234 /* keep pll in range 500..1250 MHz */ 242 /* keep pll in range 350..600 MHz */
235 if (pll_period < 800 || pll_period > 2000) 243 if (pll_period < 1666 || pll_period > 2857)
236 continue; 244 continue;
237 for (m = 3; m <= 40; ++m) { 245 for (m = 1; m <= 64; ++m) {
238 n = REF_PERIOD_PS * m * postdiv / period_ps; 246 intf = m * par->refclk_ps;
239 if (n < 5 || n > 256) 247 if (intf > 500000)
248 break;
249 n = intf * postdiv / period_ps;
250 if (n < 3 || n > 160)
240 continue; 251 continue;
241 t = REF_PERIOD_PS * m * postdiv / n; 252 t = par->refclk_ps * m * postdiv / n;
242 t -= period_ps; 253 t -= period_ps;
243 if (t >= 0 && t < best_error) { 254 if (t >= 0 && t < best_error) {
244 par->pll_m = m; 255 par->pll_m = m;
@@ -257,7 +268,7 @@ static int calc_pll(int period_ps, struct gxt4500_par *par)
257 268
258static int calc_pixclock(struct gxt4500_par *par) 269static int calc_pixclock(struct gxt4500_par *par)
259{ 270{
260 return REF_PERIOD_PS * par->pll_m * par->pll_pd1 * par->pll_pd2 271 return par->refclk_ps * par->pll_m * par->pll_pd1 * par->pll_pd2
261 / par->pll_n; 272 / par->pll_n;
262} 273}
263 274
@@ -357,7 +368,7 @@ static int gxt4500_set_par(struct fb_info *info)
357 struct gxt4500_par *par = info->par; 368 struct gxt4500_par *par = info->par;
358 struct fb_var_screeninfo *var = &info->var; 369 struct fb_var_screeninfo *var = &info->var;
359 int err; 370 int err;
360 u32 ctrlreg; 371 u32 ctrlreg, tmp;
361 unsigned int dfa_ctl, pixfmt, stride; 372 unsigned int dfa_ctl, pixfmt, stride;
362 unsigned int wid_tiles, i; 373 unsigned int wid_tiles, i;
363 unsigned int prefetch_pix, htot; 374 unsigned int prefetch_pix, htot;
@@ -376,10 +387,25 @@ static int gxt4500_set_par(struct fb_info *info)
376 writereg(par, DTG_CONTROL, ctrlreg); 387 writereg(par, DTG_CONTROL, ctrlreg);
377 388
378 /* set PLL registers */ 389 /* set PLL registers */
390 tmp = readreg(par, PLL_C) & ~0x7f;
391 if (par->pll_n < 38)
392 tmp |= 0x29;
393 if (par->pll_n < 69)
394 tmp |= 0x35;
395 else if (par->pll_n < 100)
396 tmp |= 0x76;
397 else
398 tmp |= 0x7e;
399 writereg(par, PLL_C, tmp);
379 writereg(par, PLL_M, mdivtab[par->pll_m - 1]); 400 writereg(par, PLL_M, mdivtab[par->pll_m - 1]);
380 writereg(par, PLL_N, ndivtab[par->pll_n - 2]); 401 writereg(par, PLL_N, ndivtab[par->pll_n - 2]);
381 writereg(par, PLL_POSTDIV, 402 tmp = ((8 - par->pll_pd2) << 3) | (8 - par->pll_pd1);
382 ((8 - par->pll_pd1) << 3) | (8 - par->pll_pd2)); 403 if (par->pll_pd1 == 8 || par->pll_pd2 == 8) {
404 /* work around erratum */
405 writereg(par, PLL_POSTDIV, tmp | 0x9);
406 udelay(1);
407 }
408 writereg(par, PLL_POSTDIV, tmp);
383 msleep(20); 409 msleep(20);
384 410
385 /* turn off hardware cursor */ 411 /* turn off hardware cursor */
@@ -483,8 +509,8 @@ static int gxt4500_setcolreg(unsigned int reg, unsigned int red,
483 509
484 if (reg > 1023) 510 if (reg > 1023)
485 return 1; 511 return 1;
486 cmap_entry = ((transp & 0xff00) << 16) | ((blue & 0xff00) << 8) | 512 cmap_entry = ((transp & 0xff00) << 16) | ((red & 0xff00) << 8) |
487 (green & 0xff00) | (red >> 8); 513 (green & 0xff00) | (blue >> 8);
488 writereg(par, CMAP + reg * 4, cmap_entry); 514 writereg(par, CMAP + reg * 4, cmap_entry);
489 515
490 if (reg < 16 && par->pixfmt != DFA_PIX_8BIT) { 516 if (reg < 16 && par->pixfmt != DFA_PIX_8BIT) {
@@ -585,6 +611,7 @@ static int __devinit gxt4500_probe(struct pci_dev *pdev,
585 struct gxt4500_par *par; 611 struct gxt4500_par *par;
586 struct fb_info *info; 612 struct fb_info *info;
587 struct fb_var_screeninfo var; 613 struct fb_var_screeninfo var;
614 enum gxt_cards cardtype;
588 615
589 err = pci_enable_device(pdev); 616 err = pci_enable_device(pdev);
590 if (err) { 617 if (err) {
@@ -613,7 +640,11 @@ static int __devinit gxt4500_probe(struct pci_dev *pdev,
613 goto err_free_fb; 640 goto err_free_fb;
614 } 641 }
615 par = info->par; 642 par = info->par;
643 cardtype = ent->driver_data;
644 par->refclk_ps = cardinfo[cardtype].refclk_ps;
616 info->fix = gxt4500_fix; 645 info->fix = gxt4500_fix;
646 strlcpy(info->fix.id, cardinfo[cardtype].cardname,
647 sizeof(info->fix.id));
617 info->pseudo_palette = par->pseudo_palette; 648 info->pseudo_palette = par->pseudo_palette;
618 649
619 info->fix.mmio_start = reg_phys; 650 info->fix.mmio_start = reg_phys;
@@ -703,8 +734,10 @@ static void __devexit gxt4500_remove(struct pci_dev *pdev)
703 734
704/* supported chipsets */ 735/* supported chipsets */
705static const struct pci_device_id gxt4500_pci_tbl[] = { 736static const struct pci_device_id gxt4500_pci_tbl[] = {
706 { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4500P, 737 { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4500P),
707 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 738 .driver_data = GXT4500P },
739 { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT6000P),
740 .driver_data = GXT6000P },
708 { 0 } 741 { 0 }
709}; 742};
710 743
@@ -735,7 +768,7 @@ static void __exit gxt4500_exit(void)
735module_exit(gxt4500_exit); 768module_exit(gxt4500_exit);
736 769
737MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 770MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
738MODULE_DESCRIPTION("FBDev driver for IBM GXT4500P"); 771MODULE_DESCRIPTION("FBDev driver for IBM GXT4500P/6000P");
739MODULE_LICENSE("GPL"); 772MODULE_LICENSE("GPL");
740module_param(mode_option, charp, 0); 773module_param(mode_option, charp, 0);
741MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); 774MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");