cirrusfb.c
来自「linux 内核源代码」· C语言 代码 · 共 2,477 行 · 第 1/5 页
C
2,477 行
static void cirrusfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);static void cirrusfb_imageblit(struct fb_info *info, const struct fb_image *image);/* function table of the above functions */static struct fb_ops cirrusfb_ops = { .owner = THIS_MODULE, .fb_open = cirrusfb_open, .fb_release = cirrusfb_release, .fb_setcolreg = cirrusfb_setcolreg, .fb_check_var = cirrusfb_check_var, .fb_set_par = cirrusfb_set_par, .fb_pan_display = cirrusfb_pan_display, .fb_blank = cirrusfb_blank, .fb_fillrect = cirrusfb_fillrect, .fb_copyarea = cirrusfb_copyarea, .fb_imageblit = cirrusfb_imageblit,};/*--- Hardware Specific Routines -------------------------------------------*/static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, struct cirrusfb_regs *regs, struct fb_info *info);/*--- Internal routines ----------------------------------------------------*/static void init_vgachip(struct fb_info *info);static void switch_monitor(struct cirrusfb_info *cinfo, int on);static void WGen(const struct cirrusfb_info *cinfo, int regnum, unsigned char val);static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);static void AttrOn(const struct cirrusfb_info *cinfo);static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red, unsigned char green, unsigned char blue);#if 0static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red, unsigned char *green, unsigned char *blue);#endifstatic void cirrusfb_WaitBLT(u8 __iomem *regbase);static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, u_short curx, u_short cury, u_short destx, u_short desty, u_short width, u_short height, u_short line_length);static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_short x, u_short y, u_short width, u_short height, u_char color, u_short line_length);static void bestclock(long freq, long *best, long *nom, long *den, long *div, long maxfreq);#ifdef CIRRUSFB_DEBUGstatic void cirrusfb_dump(void);static void cirrusfb_dbg_reg_dump(caddr_t regbase);static void cirrusfb_dbg_print_regs(caddr_t regbase, enum cirrusfb_dbg_reg_class reg_class, ...);static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);#endif /* CIRRUSFB_DEBUG *//*** END PROTOTYPES ********************************************************//*****************************************************************************//*** BEGIN Interface Used by the World ***************************************/static int opencount;/*--- Open /dev/fbx ---------------------------------------------------------*/static int cirrusfb_open(struct fb_info *info, int user){ if (opencount++ == 0) switch_monitor(info->par, 1); return 0;}/*--- Close /dev/fbx --------------------------------------------------------*/static int cirrusfb_release(struct fb_info *info, int user){ if (--opencount == 0) switch_monitor(info->par, 0); return 0;}/**** END Interface used by the World *************************************//****************************************************************************//**** BEGIN Hardware specific Routines **************************************//* Get a good MCLK value */static long cirrusfb_get_mclk(long freq, int bpp, long *div){ long mclk; assert(div != NULL); /* Calculate MCLK, in case VCLK is high enough to require > 50MHz. * 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 * should divide it by to get VCLK */ switch (freq) { case 24751 ... 25249: *div = 2; DPRINTK("Using VCLK = MCLK/2\n"); break; case 49501 ... 50499: *div = 1; DPRINTK("Using VCLK = MCLK\n"); break; default: *div = 0; break; } return mclk;}static int cirrusfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ int nom, den; /* translyting from pixels->bytes */ int yres, i; static struct { int xres, yres; } modes[] = { { 1600, 1280 }, { 1280, 1024 }, { 1024, 768 }, { 800, 600 }, { 640, 480 }, { -1, -1 } }; switch (var->bits_per_pixel) { case 1: nom = 4; den = 8; break; /* 8 pixel per byte, only 1/4th of mem usable */ case 8: case 16: case 24: case 32: nom = var->bits_per_pixel / 8; den = 1; break; /* 1 pixel == 1 byte */ default: printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..." "color depth not supported.\n", var->xres, var->yres, var->bits_per_pixel); DPRINTK("EXIT - EINVAL error\n"); return -EINVAL; } if (var->xres * nom / den * var->yres > info->screen_size) { printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..." "resolution too high to fit into video memory!\n", var->xres, var->yres, var->bits_per_pixel); DPRINTK("EXIT - EINVAL error\n"); return -EINVAL; } /* use highest possible virtual resolution */ if (var->xres_virtual == -1 && var->yres_virtual == -1) { printk(KERN_INFO "cirrusfb: using maximum available virtual resolution\n"); for (i = 0; modes[i].xres != -1; i++) { int size = modes[i].xres * nom / den * modes[i].yres; if (size < info->screen_size / 2) break; } if (modes[i].xres == -1) { printk(KERN_ERR "cirrusfb: could not find a virtual " "resolution that fits into video memory!!\n"); DPRINTK("EXIT - EINVAL error\n"); return -EINVAL; } var->xres_virtual = modes[i].xres; var->yres_virtual = modes[i].yres; printk(KERN_INFO "cirrusfb: virtual resolution set to " "maximum of %dx%d\n", var->xres_virtual, var->yres_virtual); } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) var->yoffset = 0; /* truncate xoffset and yoffset to maximum if too high */ if (var->xoffset > var->xres_virtual - var->xres) var->xoffset = var->xres_virtual - var->xres - 1; if (var->yoffset > var->yres_virtual - var->yres) var->yoffset = var->yres_virtual - var->yres - 1; switch (var->bits_per_pixel) { case 1: var->red.offset = 0; var->red.length = 1; var->green = var->red; var->blue = var->red; break; case 8: var->red.offset = 0; var->red.length = 6; var->green = var->red; var->blue = var->red; break; case 16: if (isPReP) { var->red.offset = 2; var->green.offset = -3; var->blue.offset = 8; } else { var->red.offset = 10; var->green.offset = 5; var->blue.offset = 0; } var->red.length = 5; var->green.length = 5; var->blue.length = 5; break; case 24: case 32: if (isPReP) { var->red.offset = 8; var->green.offset = 16; var->blue.offset = 24; } else { var->red.offset = 16; var->green.offset = 8; var->blue.offset = 0; } var->red.length = 8; var->green.length = 8; var->blue.length = 8; break; default: DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel); assert(false); /* should never occur */ break; } var->red.msb_right = var->green.msb_right = var->blue.msb_right = var->transp.offset = var->transp.length = var->transp.msb_right = 0; yres = var->yres; if (var->vmode & FB_VMODE_DOUBLE) yres *= 2; else if (var->vmode & FB_VMODE_INTERLACED) yres = (yres + 1) / 2; if (yres >= 1280) { printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; " "special treatment required! (TODO)\n"); DPRINTK("EXIT - EINVAL error\n"); return -EINVAL; } return 0;}static int cirrusfb_decode_var(const struct fb_var_screeninfo *var, struct cirrusfb_regs *regs, struct fb_info *info){ long freq; long maxclock; int maxclockidx = var->bits_per_pixel >> 3; struct cirrusfb_info *cinfo = info->par; int xres, hfront, hsync, hback; int yres, vfront, vsync, vback; switch (var->bits_per_pixel) { case 1: info->fix.line_length = var->xres_virtual / 8; info->fix.visual = FB_VISUAL_MONO10; break; case 8: info->fix.line_length = var->xres_virtual; info->fix.visual = FB_VISUAL_PSEUDOCOLOR; break; case 16: case 24: case 32: info->fix.line_length = var->xres_virtual * maxclockidx; info->fix.visual = FB_VISUAL_DIRECTCOLOR; break; default: DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel); assert(false); /* should never occur */ break; } info->fix.type = FB_TYPE_PACKED_PIXELS; /* convert from ps to kHz */ freq = PICOS2KHZ(var->pixclock); DPRINTK("desired pixclock: %ld kHz\n", freq); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; regs->multiplexing = 0; /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ if (freq > maxclock) { switch (cinfo->btype) { case BT_ALPINE: case BT_GD5480: regs->multiplexing = 1; break; default: printk(KERN_ERR "cirrusfb: Frequency greater " "than maxclock (%ld kHz)\n", maxclock); DPRINTK("EXIT - return -EINVAL\n"); return -EINVAL; } }#if 0 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where * the VCLK is double the pixel clock. */ switch (var->bits_per_pixel) { case 16: case 32: if (regs->HorizRes <= 800) /* Xbh has this type of clock for 32-bit */ freq /= 2; break; }#endif bestclock(freq, ®s->freq, ®s->nom, ®s->den, ®s->div, maxclock); regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel, ®s->divMCLK); xres = var->xres; hfront = var->right_margin; hsync = var->hsync_len; hback = var->left_margin; yres = var->yres; vfront = var->lower_margin; vsync = var->vsync_len; vback = var->upper_margin; if (var->vmode & FB_VMODE_DOUBLE) { yres *= 2; vfront *= 2; vsync *= 2; vback *= 2; } else if (var->vmode & FB_VMODE_INTERLACED) { yres = (yres + 1) / 2; vfront = (vfront + 1) / 2; vsync = (vsync + 1) / 2; vback = (vback + 1) / 2; } regs->HorizRes = xres; regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5; regs->HorizDispEnd = xres / 8 - 1; regs->HorizBlankStart = xres / 8; /* does not count with "-5" */ regs->HorizBlankEnd = regs->HorizTotal + 5; regs->HorizSyncStart = (xres + hfront) / 8 + 1; regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1; regs->VertRes = yres; regs->VertTotal = yres + vfront + vsync + vback - 2; regs->VertDispEnd = yres - 1; regs->VertBlankStart = yres; regs->VertBlankEnd = regs->VertTotal; regs->VertSyncStart = yres + vfront - 1; regs->VertSyncEnd = yres + vfront + vsync - 1; if (regs->VertRes >= 1024) { regs->VertTotal /= 2; regs->VertSyncStart /= 2; regs->VertSyncEnd /= 2; regs->VertDispEnd /= 2; } if (regs->multiplexing) { regs->HorizTotal /= 2; regs->HorizSyncStart /= 2; regs->HorizSyncEnd /= 2; regs->HorizDispEnd /= 2; } return 0;}static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val, int div){ assert(cinfo != NULL); if (div == 2) { /* VCLK = MCLK/2 */ unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E); vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1); vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f)); } else if (div == 1) { /* VCLK = MCLK */ unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E); vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1); vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f)); } else { vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f); }}/************************************************************************* cirrusfb_set_par_foo() actually writes the values for a new video mode into the hardware,**************************************************************************/static int cirrusfb_set_par_foo(struct fb_info *info){ struct cirrusfb_info *cinfo = info->par; struct fb_var_screeninfo *var = &info->var; struct cirrusfb_regs regs; u8 __iomem *regbase = cinfo->regbase; unsigned char tmp; int offset = 0, err; const struct cirrusfb_board_info_rec *bi; DPRINTK("ENTER\n"); DPRINTK("Requested mode: %dx%dx%d\n", var->xres, var->yres, var->bits_per_pixel); DPRINTK("pixclock: %d\n", var->pixclock); init_vgachip(info); err = cirrusfb_decode_var(var, ®s, info); if (err) { /* should never happen */ DPRINTK("mode change aborted. invalid var.\n"); return -EINVAL; } bi = &cirrusfb_board_info[cinfo->btype]; /* unlock register VGA_CRTC_H_TOTAL..CRT7 */ vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */ /* if debugging is enabled, all parameters get output before writing */ DPRINTK("CRT0: %ld\n", regs.HorizTotal); vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal); DPRINTK("CRT1: %ld\n", regs.HorizDispEnd); vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd); DPRINTK("CRT2: %ld\n", regs.HorizBlankStart); vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart); /* + 128: Compatible read */ DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32); vga_wcrt(regbase, VGA_CRTC_H_BLANK_END, 128 + (regs.HorizBlankEnd % 32)); DPRINTK("CRT4: %ld\n", regs.HorizSyncStart); vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart); tmp = regs.HorizSyncEnd % 32; if (regs.HorizBlankEnd & 32)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?