📄 macfb.c
字号:
/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we don't know how to set *//* (c) 1999 David Huggins-Daines <dhd@debian.org> Primarily based on vesafb.c, by Gerd Knorr (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> Also uses information and code from: The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen Mellinger, Mikael Forselius, Michael Schmitz, and others. valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven. This code is free software. You may copy, modify, and distribute it subject to the terms and conditions of the GNU General Public License, version 2, or any later version, at your convenience. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/nubus.h>#include <linux/init.h>#include <linux/fb.h>#include <asm/setup.h>#include <asm/bootinfo.h>#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/macintosh.h>#include <asm/io.h>/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */#define DAC_BASE 0x50f24000/* Some addresses for the DAFB */#define DAFB_BASE 0xf9800200/* Address for the built-in Civic framebuffer in Quadra AVs */#define CIVIC_BASE 0x50f30800 /* Only tested on 660AV! *//* GSC (Gray Scale Controller) base address */#define GSC_BASE 0x50F20000/* CSC (Color Screen Controller) base address */#define CSC_BASE 0x50F20000static int (*macfb_setpalette) (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info) = NULL;static int valkyrie_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info);static int dafb_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static int rbv_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static int mdc_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static int toby_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static int civic_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static int csc_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *fb_info);static struct { unsigned char addr; /* Note: word-aligned */ char pad[3]; unsigned char lut;} __iomem *valkyrie_cmap_regs;static struct { unsigned char addr; unsigned char lut;} __iomem *v8_brazil_cmap_regs;static struct { unsigned char addr; char pad1[3]; /* word aligned */ unsigned char lut; char pad2[3]; /* word aligned */ unsigned char cntl; /* a guess as to purpose */} __iomem *rbv_cmap_regs;static struct { unsigned long reset; unsigned long pad1[3]; unsigned char pad2[3]; unsigned char lut;} __iomem *dafb_cmap_regs;static struct { unsigned char addr; /* OFFSET: 0x00 */ unsigned char pad1[15]; unsigned char lut; /* OFFSET: 0x10 */ unsigned char pad2[15]; unsigned char status; /* OFFSET: 0x20 */ unsigned char pad3[7]; unsigned long vbl_addr; /* OFFSET: 0x28 */ unsigned int status2; /* OFFSET: 0x2C */} __iomem *civic_cmap_regs;static struct { char pad1[0x40]; unsigned char clut_waddr; /* 0x40 */ char pad2; unsigned char clut_data; /* 0x42 */ char pad3[0x3]; unsigned char clut_raddr; /* 0x46 */} __iomem *csc_cmap_regs;/* We will leave these the way they are for the time being */struct mdc_cmap_regs { char pad1[0x200200]; unsigned char addr; char pad2[6]; unsigned char lut;};struct toby_cmap_regs { char pad1[0x90018]; unsigned char lut; /* TFBClutWDataReg, offset 0x90018 */ char pad2[3]; unsigned char addr; /* TFBClutAddrReg, offset 0x9001C */};struct jet_cmap_regs { char pad1[0xe0e000]; unsigned char addr; unsigned char lut;};#define PIXEL_TO_MM(a) (((a)*10)/28) /* width in mm at 72 dpi */ /* mode */static int video_slot = 0;static struct fb_var_screeninfo macfb_defined = { .bits_per_pixel = 8, .activate = FB_ACTIVATE_NOW, .width = -1, .height = -1, .right_margin = 32, .upper_margin = 16, .lower_margin = 4, .vsync_len = 4, .vmode = FB_VMODE_NONINTERLACED,};static struct fb_fix_screeninfo macfb_fix = { .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE,};static struct fb_info fb_info;static u32 pseudo_palette[16];static int inverse = 0;static int vidtest = 0;static int valkyrie_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ unsigned long flags; red >>= 8; green >>= 8; blue >>= 8; local_irq_save(flags); /* tell clut which address to fill */ nubus_writeb(regno, &valkyrie_cmap_regs->addr); nop(); /* send one color channel at a time */ nubus_writeb(red, &valkyrie_cmap_regs->lut); nop(); nubus_writeb(green, &valkyrie_cmap_regs->lut); nop(); nubus_writeb(blue, &valkyrie_cmap_regs->lut); local_irq_restore(flags); return 0;}/* Unlike the Valkyrie, the DAFB cannot set individual colormap registers. Therefore, we do what the MacOS driver does (no kidding!) and simply set them one by one until we hit the one we want. */static int dafb_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ /* FIXME: really, really need to use ioremap() here, phys_to_virt() doesn't work anymore */ static int lastreg = -1; unsigned long flags; red >>= 8; green >>= 8; blue >>= 8; local_irq_save(flags); /* fbdev will set an entire colourmap, but X won't. Hopefully this should accommodate both of them */ if (regno != lastreg+1) { int i; /* Stab in the dark trying to reset the CLUT pointer */ nubus_writel(0, &dafb_cmap_regs->reset); nop(); /* Loop until we get to the register we want */ for (i = 0; i < regno; i++) { nubus_writeb(info->cmap.red[i] >> 8, &dafb_cmap_regs->lut); nop(); nubus_writeb(info->cmap.green[i] >> 8, &dafb_cmap_regs->lut); nop(); nubus_writeb(info->cmap.blue[i] >> 8, &dafb_cmap_regs->lut); nop(); } } nubus_writeb(red, &dafb_cmap_regs->lut); nop(); nubus_writeb(green, &dafb_cmap_regs->lut); nop(); nubus_writeb(blue, &dafb_cmap_regs->lut); local_irq_restore(flags); lastreg = regno; return 0;}/* V8 and Brazil seem to use the same DAC. Sonora does as well. */static int v8_brazil_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info) { unsigned int bpp = info->var.bits_per_pixel; unsigned char _red =red>>8; unsigned char _green=green>>8; unsigned char _blue =blue>>8; unsigned char _regno; unsigned long flags; if (bpp > 8) return 1; /* failsafe */ local_irq_save(flags); /* On these chips, the CLUT register numbers are spread out across the register space. Thus: In 8bpp, all regnos are valid. In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff */ _regno = (regno << (8 - bpp)) | (0xFF >> bpp); nubus_writeb(_regno, &v8_brazil_cmap_regs->addr); nop(); /* send one color channel at a time */ nubus_writeb(_red, &v8_brazil_cmap_regs->lut); nop(); nubus_writeb(_green, &v8_brazil_cmap_regs->lut); nop(); nubus_writeb(_blue, &v8_brazil_cmap_regs->lut); local_irq_restore(flags); return 0;}static int rbv_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ /* use MSBs */ unsigned char _red =red>>8; unsigned char _green=green>>8; unsigned char _blue =blue>>8; unsigned char _regno; unsigned long flags; if (info->var.bits_per_pixel > 8) return 1; /* failsafe */ local_irq_save(flags); /* From the VideoToolbox driver. Seems to be saying that * regno #254 and #255 are the important ones for 1-bit color, * regno #252-255 are the important ones for 2-bit color, etc. */ _regno = regno + (256-(1 << info->var.bits_per_pixel)); /* reset clut? (VideoToolbox sez "not necessary") */ nubus_writeb(0xFF, &rbv_cmap_regs->cntl); nop(); /* tell clut which address to use. */ nubus_writeb(_regno, &rbv_cmap_regs->addr); nop(); /* send one color channel at a time. */ nubus_writeb(_red, &rbv_cmap_regs->lut); nop(); nubus_writeb(_green, &rbv_cmap_regs->lut); nop(); nubus_writeb(_blue, &rbv_cmap_regs->lut); local_irq_restore(flags); /* done. */ return 0;}/* Macintosh Display Card (8x24) */static int mdc_setpalette(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ volatile struct mdc_cmap_regs *cmap_regs = nubus_slot_addr(video_slot); /* use MSBs */ unsigned char _red =red>>8; unsigned char _green=green>>8; unsigned char _blue =blue>>8; unsigned char _regno=regno; unsigned long flags; local_irq_save(flags); /* the nop's are there to order writes. */ nubus_writeb(_regno, &cmap_regs->addr); nop(); nubus_writeb(_red, &cmap_regs->lut); nop(); nubus_writeb(_green, &cmap_regs->lut); nop(); nubus_writeb(_blue, &cmap_regs->lut); local_irq_restore(flags); return 0;}/* Toby frame buffer */static int toby_setpalette(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info) { volatile struct toby_cmap_regs *cmap_regs = nubus_slot_addr(video_slot); unsigned int bpp = info->var.bits_per_pixel; /* use MSBs */ unsigned char _red =~(red>>8); unsigned char _green=~(green>>8); unsigned char _blue =~(blue>>8); unsigned char _regno = (regno << (8 - bpp)) | (0xFF >> bpp); unsigned long flags; local_irq_save(flags); nubus_writeb(_regno, &cmap_regs->addr); nop(); nubus_writeb(_red, &cmap_regs->lut); nop(); nubus_writeb(_green, &cmap_regs->lut); nop(); nubus_writeb(_blue, &cmap_regs->lut); local_irq_restore(flags); return 0;}/* Jet frame buffer */static int jet_setpalette(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ volatile struct jet_cmap_regs *cmap_regs = nubus_slot_addr(video_slot); /* use MSBs */ unsigned char _red = (red>>8); unsigned char _green = (green>>8); unsigned char _blue = (blue>>8); unsigned long flags; local_irq_save(flags); nubus_writeb(regno, &cmap_regs->addr); nop(); nubus_writeb(_red, &cmap_regs->lut); nop(); nubus_writeb(_green, &cmap_regs->lut); nop(); nubus_writeb(_blue, &cmap_regs->lut); local_irq_restore(flags); return 0;}/* * Civic framebuffer -- Quadra AV built-in video. A chip * called Sebastian holds the actual color palettes, and * apparently, there are two different banks of 512K RAM * which can act as separate framebuffers for doing video * input and viewing the screen at the same time! The 840AV * Can add another 1MB RAM to give the two framebuffers * 1MB RAM apiece. * * FIXME: this doesn't seem to work anymore. */static int civic_setpalette (unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, struct fb_info *info){ static int lastreg = -1; unsigned long flags; int clut_status; if (info->var.bits_per_pixel > 8) return 1; /* failsafe */ red >>= 8; green >>= 8; blue >>= 8; local_irq_save(flags); /* * Set the register address */ nubus_writeb(regno, &civic_cmap_regs->addr); nop(); /* * Wait for VBL interrupt here; * They're usually not enabled from Penguin, so we won't check */#if 0 {#define CIVIC_VBL_OFFSET 0x120 volatile unsigned long *vbl = nubus_readl(civic_cmap_regs->vbl_addr + CIVIC_VBL_OFFSET); /* do interrupt setup stuff here? */ *vbl = 0L; nop(); /* clear */ *vbl = 1L; nop(); /* set */ while (*vbl != 0L) /* wait for next vbl */ { usleep(10); /* needed? */ } /* do interrupt shutdown stuff here? */ }#endif /* * Grab a status word and do some checking; * Then finally write the clut! */ clut_status = nubus_readb(&civic_cmap_regs->status2); if ((clut_status & 0x0008) == 0) {#if 0 if ((clut_status & 0x000D) != 0) { nubus_writeb(0x00, &civic_cmap_regs->lut); nop(); nubus_writeb(0x00, &civic_cmap_regs->lut); nop(); }#endif nubus_writeb( red, &civic_cmap_regs->lut); nop(); nubus_writeb(green, &civic_cmap_regs->lut); nop(); nubus_writeb( blue, &civic_cmap_regs->lut); nop(); nubus_writeb( 0x00, &civic_cmap_regs->lut); nop(); } else { unsigned char junk; junk = nubus_readb(&civic_cmap_regs->lut); nop(); junk = nubus_readb(&civic_cmap_regs->lut); nop(); junk = nubus_readb(&civic_cmap_regs->lut); nop(); junk = nubus_readb(&civic_cmap_regs->lut); nop(); if ((clut_status & 0x000D) != 0) { nubus_writeb(0x00, &civic_cmap_regs->lut); nop(); nubus_writeb(0x00, &civic_cmap_regs->lut); nop(); } nubus_writeb( red, &civic_cmap_regs->lut); nop(); nubus_writeb(green, &civic_cmap_regs->lut); nop(); nubus_writeb( blue, &civic_cmap_regs->lut); nop(); nubus_writeb( junk, &civic_cmap_regs->lut); nop(); } local_irq_restore(flags); lastreg = regno; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -