diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
commit | 6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch) | |
tree | 0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/video/cyber2000fb.c | |
parent | ecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff) |
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine
status unknown.
Diffstat (limited to 'drivers/video/cyber2000fb.c')
-rw-r--r-- | drivers/video/cyber2000fb.c | 344 |
1 files changed, 181 insertions, 163 deletions
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 8a8bea5a0..52228d56b 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -12,8 +12,13 @@ * especially for the colourmap stuff. Once fbcon has been fully migrated, * we can kill the last 5 references to cfb->currcon. * - * We also use the new hotplug PCI subsystem. This doesn't work fully in - * the case of multiple CyberPro cards yet however. + * We also use the new hotplug PCI subsystem. I'm not sure if there are any + * such cards, but I'm erring on the side of caution. We don't want to go + * pop just because someone does have one. + * + * Note that this doesn't work fully in the case of multiple CyberPro cards + * with grabbers. We currently can only attach to the first CyberPro card + * found. */ #include <linux/config.h> #include <linux/module.h> @@ -47,7 +52,7 @@ /* * This is the offset of the PCI space in physical memory */ -#ifdef CONFIG_ARCH_FOOTBRIDGE +#ifdef CONFIG_FOOTBRIDGE #define PCI_PHYS_OFFSET 0x80000000 #else #define PCI_PHYS_OFFSET 0x00000000 @@ -62,6 +67,8 @@ struct cfb_info { struct display_switch *dispsw; struct pci_dev *dev; signed int currcon; + int func_use_count; + u_long ref_ps; /* * Clock divisors @@ -72,7 +79,10 @@ struct cfb_info { u8 red, green, blue; } palette[NR_PALETTE]; + u_char mem_ctl1; u_char mem_ctl2; + u_char mclk_mult; + u_char mclk_div; }; /* -------------------- Hardware specific routines ------------------------- */ @@ -363,7 +373,7 @@ static const u_char crtc_idx[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; -static void cyber2000fb_set_timing(struct par_info *hw) +static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw) { u_int i; @@ -416,11 +426,10 @@ static void cyber2000fb_set_timing(struct par_info *hw) cyber2000_attrw(0x14, 0x00); /* PLL registers */ - cyber2000_grphw(0xb0, hw->clock_mult); - cyber2000_grphw(0xb1, hw->clock_div); - - cyber2000_grphw(0xb2, 0xdb); - cyber2000_grphw(0xb3, 0x54); /* MCLK: 75MHz */ + cyber2000_grphw(DCLK_MULT, hw->clock_mult); + cyber2000_grphw(DCLK_DIV, hw->clock_div); + cyber2000_grphw(MCLK_MULT, cfb->mclk_mult); + cyber2000_grphw(MCLK_DIV, cfb->mclk_div); cyber2000_grphw(0x90, 0x01); cyber2000_grphw(0xb9, 0x80); cyber2000_grphw(0xb9, 0x00); @@ -438,7 +447,9 @@ static void cyber2000fb_set_timing(struct par_info *hw) cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) | ((hw->pitch >> 4) & 0x30)); cyber2000_grphw(0x77, hw->visualid); - cyber2000_grphw(0x33, 0x0c); + + /* make sure we stay in linear mode */ + cyber2000_grphw(0x33, 0x0d); /* * Set up accelerator registers @@ -468,21 +479,6 @@ cyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var) } /* - * Open/Release the frame buffer device - */ -static int cyber2000fb_open(struct fb_info *info, int user) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static int cyber2000fb_release(struct fb_info *info, int user) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -/* * Set the Colormap */ static int @@ -535,6 +531,7 @@ cyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb, Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; + if (Htotal > 2080) return -EINVAL; @@ -619,7 +616,7 @@ cyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb, struct fb_var_screeninfo *var) { u_long pll_ps = var->pixclock; - const u_long ref_ps = 69842; + const u_long ref_ps = cfb->ref_ps; u_int div2, t_div1, best_div1, best_mult; int best_diff; @@ -641,7 +638,6 @@ cyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb, if (div2 == 4) return -EINVAL; -#if 1 /* * Step 2: * Given pll_ps and ref_ps, find: @@ -689,94 +685,7 @@ cyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb, if (diff == 0) break; } -#else - /* Note! This table will be killed shortly. --rmk */ - /* - * 1600x1200 1280x1024 1152x864 1024x768 800x600 640x480 - * 5051 5051 yes 76* - * 5814 5814 no 66 - * 6411 6411 no 60 - * 7408 7408 yes 75* - * 74* - * 7937 7937 yes 70* - * 9091 4545 yes 80* - * 75* 100* - * 9260 4630 yes 60* - * 10000 5000 no 70 90 - * 12500 6250 yes 47-lace* 60* - * 43-lace* - * 12699 6349 yes 75* - * 13334 6667 no 72 - * 70 - * 14815 7407 yes 100* - * 15385 7692 yes 47-lace* 60* - * 43-lace* - * 17656 4414 no 90 - * 20000 5000 no 72 - * 20203 5050 yes 75* - * 22272 5568 yes 43-lace* 70* 100* - * 25000 6250 yes 60* - * 25057 6264 no 90 - * 27778 6944 yes 56* - * 48-lace* - * 31747 7936 yes 75* - * 32052 8013 no 72 - * 39722 /6 6620 no - * 39722 /8 4965 yes 60* - */ - /* /1 /2 /4 /6 /8 */ - /* (2010) (2000) */ - if (pll_ps >= 4543 && pll_ps <= 4549) { - best_mult = 169; /*u220.0 110.0 54.99 36.663 27.497 */ - best_div1 = 11; /* 4546 9092 18184 27276 36367 */ - } else if (pll_ps >= 4596 && pll_ps <= 4602) { - best_mult = 243; /* 217.5 108.7 54.36 36.243 27.181 */ - best_div1 = 16; /* 4599 9197 18395 27592 36789 */ - } else if (pll_ps >= 4627 && pll_ps <= 4633) { - best_mult = 181; /*u216.0, 108.0, 54.00, 36.000 27.000 */ - best_div1 = 12; /* 4630 9260 18520 27780 37040 */ - } else if (pll_ps >= 4962 && pll_ps <= 4968) { - best_mult = 211; /*u201.0, 100.5, 50.25, 33.500 25.125 */ - best_div1 = 15; /* 4965 9930 19860 29790 39720 */ - } else if (pll_ps >= 5005 && pll_ps <= 5011) { - best_mult = 251; /* 200.0 99.8 49.92 33.280 24.960 */ - best_div1 = 18; /* 5008 10016 20032 30048 40064 */ - } else if (pll_ps >= 5047 && pll_ps <= 5053) { - best_mult = 83; /*u198.0, 99.0, 49.50, 33.000 24.750 */ - best_div1 = 6; /* 5050 10100 20200 30300 40400 */ - } else if (pll_ps >= 5490 && pll_ps <= 5496) { - best_mult = 89; /* 182.0 91.0 45.51 30.342 22.756 */ - best_div1 = 7; /* 5493 10986 21972 32958 43944 */ - } else if (pll_ps >= 5567 && pll_ps <= 5573) { - best_mult = 163; /*u179.5 89.8 44.88 29.921 22.441 */ - best_div1 = 13; /* 5570 11140 22281 33421 44562 */ - } else if (pll_ps >= 6246 && pll_ps <= 6252) { - best_mult = 190; /*u160.0, 80.0, 40.00, 26.671 20.003 */ - best_div1 = 17; /* 6249 12498 24996 37494 49992 */ - } else if (pll_ps >= 6346 && pll_ps <= 6352) { - best_mult = 209; /*u158.0, 79.0, 39.50, 26.333 19.750 */ - best_div1 = 19; /* 6349 12698 25396 38094 50792 */ - } else if (pll_ps >= 6648 && pll_ps <= 6655) { - best_mult = 210; /*u150.3 75.2 37.58 25.057 18.792 */ - best_div1 = 20; /* 6652 13303 26606 39909 53213 */ - } else if (pll_ps >= 6943 && pll_ps <= 6949) { - best_mult = 181; /*u144.0 72.0 36.00 23.996 17.997 */ - best_div1 = 18; /* 6946 13891 27782 41674 55565 */ - } else if (pll_ps >= 7404 && pll_ps <= 7410) { - best_mult = 198; /*u134.0 67.5 33.75 22.500 16.875 */ - best_div1 = 21; /* 7407 14815 29630 44445 59260 */ - } else if (pll_ps >= 7689 && pll_ps <= 7695) { - best_mult = 227; /*u130.0 65.0 32.50 21.667 16.251 */ - best_div1 = 25; /* 7692 15384 30768 46152 61536 */ - } else if (pll_ps >= 7808 && pll_ps <= 7814) { - best_mult = 152; /* 128.0 64.0 32.00 21.337 16.003 */ - best_div1 = 17; /* 7811 15623 31245 46868 62490 */ - } else if (pll_ps >= 7934 && pll_ps <= 7940) { - best_mult = 44; /*u126.0 63.0 31.498 20.999 15.749 */ - best_div1 = 5; /* 7937 15874 31748 47622 63494 */ - } else - return -EINVAL; -#endif + /* * Step 3: * combine values @@ -1018,7 +927,7 @@ cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, cfb->fb.changevar(con); cyber2000fb_update_start(cfb, var); - cyber2000fb_set_timing(&hw); + cyber2000fb_set_timing(cfb, &hw); fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb); return 0; @@ -1180,10 +1089,8 @@ gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) return 0; } -static struct fb_ops cyber2000fb_ops = -{ - fb_open: cyber2000fb_open, - fb_release: cyber2000fb_release, +static struct fb_ops cyber2000fb_ops = { + owner: THIS_MODULE, fb_set_var: cyber2000fb_set_var, fb_set_cmap: cyber2000fb_set_cmap, fb_pan_display: cyber2000fb_pan_display, @@ -1195,26 +1102,32 @@ static struct fb_ops cyber2000fb_ops = /* * Enable access to the extended registers - * Bug: this should track the usage of these registers */ -static void cyber2000fb_enable_extregs(void) +static void cyber2000fb_enable_extregs(struct cfb_info *cfb) { - int old; + cfb->func_use_count += 1; + + if (cfb->func_use_count == 1) { + int old; - old = cyber2000_grphr(FUNC_CTL); - cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL); + old = cyber2000_grphr(FUNC_CTL); + cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL); + } } /* * Disable access to the extended registers - * Bug: this should track the usage of these registers */ -static void cyber2000fb_disable_extregs(void) +static void cyber2000fb_disable_extregs(struct cfb_info *cfb) { - int old; + if (cfb->func_use_count == 1) { + int old; - old = cyber2000_grphr(FUNC_CTL); - cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL); + old = cyber2000_grphr(FUNC_CTL); + cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL); + } + + cfb->func_use_count -= 1; } /* @@ -1227,7 +1140,7 @@ static struct cfb_info *int_cfb_info; /* * Attach a capture/tv driver to the core CyberX0X0 driver. */ -int cyber2000fb_attach(struct cyberpro_info *info) +int cyber2000fb_attach(struct cyberpro_info *info, int idx) { if (int_cfb_info != NULL) { info->dev = int_cfb_info->dev; @@ -1236,6 +1149,7 @@ int cyber2000fb_attach(struct cyberpro_info *info) info->fb_size = int_cfb_info->fb.fix.smem_len; info->enable_extregs = cyber2000fb_enable_extregs; info->disable_extregs = cyber2000fb_disable_extregs; + info->info = int_cfb_info; strncpy(info->dev_name, int_cfb_info->fb.fix.id, sizeof(info->dev_name)); @@ -1248,7 +1162,7 @@ int cyber2000fb_attach(struct cyberpro_info *info) /* * Detach a capture/tv driver from the core CyberX0X0 driver. */ -void cyber2000fb_detach(void) +void cyber2000fb_detach(int idx) { MOD_DEC_USE_COUNT; } @@ -1281,8 +1195,8 @@ int __init cyber2000fb_setup(char *options) } static char igs_regs[] __devinitdata = { - 0x10, 0x10, 0x12, 0x00, 0x13, 0x00, - 0x31, 0x00, 0x32, 0x00, 0x33, 0x01, + 0x12, 0x00, 0x13, 0x00, + 0x31, 0x00, 0x32, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x01, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, @@ -1290,22 +1204,129 @@ static char igs_regs[] __devinitdata = { 0x74, 0x0b, 0x75, 0x17, 0x76, 0x00, 0x7a, 0xc8 }; -static inline void cyberpro_init_hw(struct cfb_info *cfb) +/* + * We need to wake up the CyberPro, and make sure its in linear memory + * mode. Unfortunately, this is specific to the platform and card that + * we are running on. + * + * On x86 and ARM, should we be initialising the CyberPro first via the + * IO registers, and then the MMIO registers to catch all cases? Can we + * end up in the situation where the chip is in MMIO mode, but not awake + * on an x86 system? + * + * Note that on the NetWinder, the firmware automatically detects the + * type, width and size, and leaves this in extended registers 0x71 and + * 0x72 for us. + */ +static inline void cyberpro_init_hw(struct cfb_info *cfb, int at_boot) { int i; /* - * Wake up the CyberPro + * Wake up the CyberPro. */ +#ifdef __sparc__ +#ifdef __sparc_v9__ +#error "You loose, consult DaveM." +#else + /* + * SPARC does not have an "outb" instruction, so we generate + * I/O cycles storing into a reserved memory space at + * physical address 0x3000000 + */ + { + unsigned char *iop; + + iop = ioremap(0x3000000, 0x5000); + if (iop == NULL) { + prom_printf("iga5000: cannot map I/O\n"); + return -ENOMEM; + } + + writeb(0x18, iop + 0x46e8); + writeb(0x01, iop + 0x102); + writeb(0x08, iop + 0x46e8); + writeb(0x33, iop + 0x3ce); + writeb(0x01, iop + 0x3cf); + + iounmap((void *)iop); + } +#endif + + if (at_boot) { + /* + * Use mclk from BIOS. Only read this if we're + * initialising this card for the first time. + * FIXME: what about hotplug? + */ + cfb->mclk_mult = cyber2000_grphr(MCLK_MULT); + cfb->mclk_div = cyber2000_grphr(MCLK_DIV); + } +#endif +#ifdef __i386__ + /* + * x86 is simple, we just do regular outb's instead of + * cyber2000_outb. + */ + outb(0x18, 0x46e8); + outb(0x01, 0x102); + outb(0x08, 0x46e8); + outb(0x33, 0x3ce); + outb(0x01, 0x3cf); + + if (at_boot) { + /* + * Use mclk from BIOS. Only read this if we're + * initialising this card for the first time. + * FIXME: what about hotplug? + */ + cfb->mclk_mult = cyber2000_grphr(MCLK_MULT); + cfb->mclk_div = cyber2000_grphr(MCLK_DIV); + } +#endif +#ifdef __arm__ cyber2000_outb(0x18, 0x46e8); cyber2000_outb(0x01, 0x102); cyber2000_outb(0x08, 0x46e8); + cyber2000_outb(0x33, 0x3ce); + cyber2000_outb(0x01, 0x3cf); + + /* + * MCLK on the NetWinder is fixed at 75MHz + */ + cfb->mclk_mult = 0xdb; + cfb->mclk_div = 0x54; +#endif /* * Initialise the CyberPro */ for (i = 0; i < sizeof(igs_regs); i += 2) cyber2000_grphw(igs_regs[i], igs_regs[i+1]); + + if (at_boot) { + /* + * get the video RAM size and width from the VGA register. + * This should have been already initialised by the BIOS, + * but if it's garbage, claim default 1MB VRAM (woody) + */ + cfb->mem_ctl1 = cyber2000_grphr(MEM_CTL1); + cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2); + } else { + /* + * Reprogram the MEM_CTL1 and MEM_CTL2 registers + */ + cyber2000_grphw(MEM_CTL1, cfb->mem_ctl1); + cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2); + } + + /* + * Ensure thatwe are using the correct PLL. + * (CyberPro 5000's may be programmed to use + * an additional set of PLLs. + */ + cyber2000_outb(0xba, 0x3ce); + cyber2000_outb(cyber2000_inb(0x3cf) & 0x80, 0x3cf); } static struct cfb_info * __devinit @@ -1323,6 +1344,7 @@ cyberpro_alloc_fb_info(struct pci_dev *dev, const struct pci_device_id *id) cfb->currcon = -1; cfb->dev = dev; + cfb->ref_ps = 69842; cfb->divisors[0] = 1; cfb->divisors[1] = 2; cfb->divisors[2] = 4; @@ -1466,13 +1488,6 @@ cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id) u_long smem_size; int err; - /* - * We can only accept one CyberPro device at the moment. We can - * kill this once int_cfb_info and CyberRegs have been killed. - */ - if (int_cfb_info) - return -EBUSY; - err = pci_enable_device(dev); if (err) return err; @@ -1486,14 +1501,7 @@ cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id) if (err) goto failed; - cyberpro_init_hw(cfb); - - /* - * get the video RAM size and width from the VGA register. - * This should have been already initialised by the BIOS, - * but if it's garbage, claim default 1MB VRAM (woody) - */ - cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2); + cyberpro_init_hw(cfb, 1); switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) { case MEM_CTL2_SIZE_4MB: smem_size = 0x00400000; break; @@ -1519,6 +1527,12 @@ cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id) cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb); + /* + * Calculate the hsync and vsync frequencies. Note that + * we split the 1e12 constant up so that we can preserve + * the precision and fit the results into 32-bit registers. + * (1953125000 * 512 = 1e12) + */ h_sync = 1953125000 / cfb->fb.var.pixclock; h_sync = h_sync * 512 / (cfb->fb.var.xres + cfb->fb.var.left_margin + cfb->fb.var.right_margin + cfb->fb.var.hsync_len); @@ -1538,7 +1552,8 @@ cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id) * Our driver data */ dev->driver_data = cfb; - int_cfb_info = cfb; + if (int_cfb_info == NULL) + int_cfb_info = cfb; return 0; @@ -1555,7 +1570,15 @@ static void __devexit cyberpro_remove(struct pci_dev *dev) struct cfb_info *cfb = (struct cfb_info *)dev->driver_data; if (cfb) { - unregister_framebuffer(&cfb->fb); + /* + * If unregister_framebuffer fails, then + * we will be leaving hooks that could cause + * oopsen laying around. + */ + if (unregister_framebuffer(&cfb->fb)) + printk(KERN_WARNING "%s: danger Will Robinson, " + "danger danger! Oopsen imminent!\n", + cfb->fb.fix.id); cyberpro_unmap_smem(cfb); cyberpro_unmap_mmio(cfb); cyberpro_free_fb_info(cfb); @@ -1565,7 +1588,8 @@ static void __devexit cyberpro_remove(struct pci_dev *dev) * valid. */ dev->driver_data = NULL; - int_cfb_info = NULL; + if (cfb == int_cfb_info) + int_cfb_info = NULL; } } @@ -1581,12 +1605,7 @@ static void cyberpro_resume(struct pci_dev *dev) struct cfb_info *cfb = (struct cfb_info *)dev->driver_data; if (cfb) { - cyberpro_init_hw(cfb); - - /* - * Reprogram the MEM_CTL2 register - */ - cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2); + cyberpro_init_hw(cfb, 0); /* * Restore the old video mode and the palette. @@ -1620,7 +1639,8 @@ static struct pci_driver cyberpro_driver = { /* * I don't think we can use the "module_init" stuff here because - * the fbcon stuff may not be initialised yet. + * the fbcon stuff may not be initialised yet. Hence the #ifdef + * around module_init. */ int __init cyber2000fb_init(void) { @@ -1636,5 +1656,3 @@ static void __exit cyberpro_exit(void) module_init(cyber2000fb_init); #endif module_exit(cyberpro_exit); - -MODULE_DEVICE_TABLE(pci, cyberpro_pci_table); |