📄 cyber2000fb.c
字号:
hw->crtc[6] = Vtotal; hw->crtc[7] = ENCODE_BIT(Vtotal, 8, 0x01, 0) | ENCODE_BIT(Vdispend, 8, 0x01, 1) | ENCODE_BIT(Vsyncstart, 8, 0x01, 2) | ENCODE_BIT(Vblankstart, 8, 0x01, 3) | ENCODE_BIT(1, 0, 0x01, 4) | ENCODE_BIT(Vtotal, 9, 0x01, 5) | ENCODE_BIT(Vdispend, 9, 0x01, 6) | ENCODE_BIT(Vsyncstart, 9, 0x01, 7); hw->crtc[9] = ENCODE_BIT(0, 0, 0x1f, 0) | ENCODE_BIT(Vblankstart, 9, 0x01, 5) | ENCODE_BIT(1, 0, 0x01, 6); hw->crtc[10] = Vsyncstart; hw->crtc[11] = ENCODE_BIT(Vsyncend, 0, 0x0f, 0) | ENCODE_BIT(1, 0, 0x01, 7); hw->crtc[12] = Vdispend; hw->crtc[15] = Vblankstart; hw->crtc[16] = Vblankend; hw->crtc[18] = 0xff; /* * overflow - graphics reg 0x11 * 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10 * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT */ hw->crtc_ofl = ENCODE_BIT(Vtotal, 10, 0x01, 0) | ENCODE_BIT(Vdispend, 10, 0x01, 1) | ENCODE_BIT(Vsyncstart, 10, 0x01, 2) | ENCODE_BIT(Vblankstart, 10, 0x01, 3) | EXT_CRT_VRTOFL_LINECOMP10; /* woody: set the interlaced bit... */ /* FIXME: what about doublescan? */ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) hw->crtc_ofl |= EXT_CRT_VRTOFL_INTERLACE; return 0;}/* * The following was discovered by a good monitor, bit twiddling, theorising * and but mostly luck. Strangely, it looks like everyone elses' PLL! * * Clock registers: * fclock = fpll / div2 * fpll = fref * mult / div1 * where: * fref = 14.318MHz (69842ps) * mult = reg0xb0.7:0 * div1 = (reg0xb1.5:0 + 1) * div2 = 2^(reg0xb1.7:6) * fpll should be between 115 and 260 MHz * (8696ps and 3846ps) */static intcyber2000fb_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 = cfb->ref_ps; u_int div2, t_div1, best_div1, best_mult; int best_diff; int vco; /* * Step 1: * find div2 such that 115MHz < fpll < 260MHz * and 0 <= div2 < 4 */ for (div2 = 0; div2 < 4; div2++) { u_long new_pll; new_pll = pll_ps / cfb->divisors[div2]; if (8696 > new_pll && new_pll > 3846) { pll_ps = new_pll; break; } } if (div2 == 4) return -EINVAL; /* * Step 2: * Given pll_ps and ref_ps, find: * pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005 * where { 1 < best_div1 < 32, 1 < best_mult < 256 } * pll_ps_calc = best_div1 / (ref_ps * best_mult) */ best_diff = 0x7fffffff; best_mult = 32; best_div1 = 255; for (t_div1 = 32; t_div1 > 1; t_div1 -= 1) { u_int rr, t_mult, t_pll_ps; int diff; /* * Find the multiplier for this divisor */ rr = ref_ps * t_div1; t_mult = (rr + pll_ps / 2) / pll_ps; /* * Is the multiplier within the correct range? */ if (t_mult > 256 || t_mult < 2) continue; /* * Calculate the actual clock period from this multiplier * and divisor, and estimate the error. */ t_pll_ps = (rr + t_mult / 2) / t_mult; diff = pll_ps - t_pll_ps; if (diff < 0) diff = -diff; if (diff < best_diff) { best_diff = diff; best_mult = t_mult; best_div1 = t_div1; } /* * If we hit an exact value, there is no point in continuing. */ if (diff == 0) break; } /* * Step 3: * combine values */ hw->clock_mult = best_mult - 1; hw->clock_div = div2 << 6 | (best_div1 - 1); vco = ref_ps * best_div1 / best_mult; if ((ref_ps == 40690) && (vco < 5556)) /* Set VFSEL when VCO > 180MHz (5.556 ps). */ hw->clock_div |= EXT_DCLK_DIV_VFSEL; return 0;}/* * Set the User Defined Part of the Display */static intcyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; struct par_info hw; unsigned int mem; int err; var->transp.msb_right = 0; var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; var->transp.offset = 0; var->transp.length = 0; switch (var->bits_per_pixel) { case 8: /* PSEUDOCOLOUR, 256 */ var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; break; case 16:/* DIRECTCOLOUR, 64k or 32k */ switch (var->green.length) { case 6: /* RGB565, 64k */ var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; break; default: case 5: /* RGB555, 32k */ var->red.offset = 10; var->red.length = 5; var->green.offset = 5; var->green.length = 5; var->blue.offset = 0; var->blue.length = 5; break; case 4: /* RGB444, 4k + transparency? */ var->transp.offset = 12; var->transp.length = 4; var->red.offset = 8; var->red.length = 4; var->green.offset = 4; var->green.length = 4; var->blue.offset = 0; var->blue.length = 4; break; } break; case 24:/* TRUECOLOUR, 16m */ var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; break; case 32:/* TRUECOLOUR, 16m */ var->transp.offset = 24; var->transp.length = 8; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; break; default: return -EINVAL; } mem = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8); if (mem > cfb->fb.fix.smem_len) var->yres_virtual = cfb->fb.fix.smem_len * 8 / (var->bits_per_pixel * var->xres_virtual); if (var->yres > var->yres_virtual) var->yres = var->yres_virtual; if (var->xres > var->xres_virtual) var->xres = var->xres_virtual; err = cyber2000fb_decode_clock(&hw, cfb, var); if (err) return err; err = cyber2000fb_decode_crtc(&hw, cfb, var); if (err) return err; return 0;}static int cyber2000fb_set_par(struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; struct fb_var_screeninfo *var = &cfb->fb.var; struct par_info hw; unsigned int mem; hw.width = var->xres_virtual; hw.ramdac = RAMDAC_VREFEN | RAMDAC_DAC8BIT; switch (var->bits_per_pixel) { case 8: hw.co_pixfmt = CO_PIXFMT_8BPP; hw.pitch = hw.width >> 3; hw.extseqmisc = EXT_SEQ_MISC_8; break; case 16: hw.co_pixfmt = CO_PIXFMT_16BPP; hw.pitch = hw.width >> 2; switch (var->green.length) { case 6: /* RGB565, 64k */ hw.extseqmisc = EXT_SEQ_MISC_16_RGB565; break; case 5: /* RGB555, 32k */ hw.extseqmisc = EXT_SEQ_MISC_16_RGB555; break; case 4: /* RGB444, 4k + transparency? */ hw.extseqmisc = EXT_SEQ_MISC_16_RGB444; break; default: BUG(); } break; case 24:/* TRUECOLOUR, 16m */ hw.co_pixfmt = CO_PIXFMT_24BPP; hw.width *= 3; hw.pitch = hw.width >> 3; hw.ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN); hw.extseqmisc = EXT_SEQ_MISC_24_RGB888; break; case 32:/* TRUECOLOUR, 16m */ hw.co_pixfmt = CO_PIXFMT_32BPP; hw.pitch = hw.width >> 1; hw.ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN); hw.extseqmisc = EXT_SEQ_MISC_32; break; default: BUG(); } /* * Sigh, this is absolutely disgusting, but caused by * the way the fbcon developers want to separate out * the "checking" and the "setting" of the video mode. * * If the mode is not suitable for the hardware here, * we can't prevent it being set by returning an error. * * In theory, since NetWinders contain just one VGA card, * we should never end up hitting this problem. */ BUG_ON(cyber2000fb_decode_clock(&hw, cfb, var) != 0); BUG_ON(cyber2000fb_decode_crtc(&hw, cfb, var) != 0); hw.width -= 1; hw.fetch = hw.pitch; if (!(cfb->mem_ctl2 & MEM_CTL2_64BIT)) hw.fetch <<= 1; hw.fetch += 1; cfb->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; /* * Same here - if the size of the video mode exceeds the * available RAM, we can't prevent this mode being set. * * In theory, since NetWinders contain just one VGA card, * we should never end up hitting this problem. */ mem = cfb->fb.fix.line_length * var->yres_virtual; BUG_ON(mem > cfb->fb.fix.smem_len); /* * 8bpp displays are always pseudo colour. 16bpp and above * are direct colour or true colour, depending on whether * the RAMDAC palettes are bypassed. (Direct colour has * palettes, true colour does not.) */ if (var->bits_per_pixel == 8) cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else if (hw.ramdac & RAMDAC_BYPASS) cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; else cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; cyber2000fb_set_timing(cfb, &hw); cyber2000fb_update_start(cfb, var); return 0;}/* * Pan or Wrap the Display */static intcyber2000fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; if (cyber2000fb_update_start(cfb, var)) return -EINVAL; cfb->fb.var.xoffset = var->xoffset; cfb->fb.var.yoffset = var->yoffset; if (var->vmode & FB_VMODE_YWRAP) { cfb->fb.var.vmode |= FB_VMODE_YWRAP; } else { cfb->fb.var.vmode &= ~FB_VMODE_YWRAP; } return 0;}/* * (Un)Blank the display. * * Blank the screen if blank_mode != 0, else unblank. If * blank == NULL then the caller blanks by setting the CLUT * (Color Look Up Table) to all black. Return 0 if blanking * succeeded, != 0 if un-/blanking failed due to e.g. a * video mode which doesn't support it. Implements VESA * suspend and powerdown modes on hardware that supports * disabling hsync/vsync: * blank_mode == 2: suspend vsync * blank_mode == 3: suspend hsync * blank_mode == 4: powerdown * * wms...Enable VESA DMPS compatible powerdown mode * run "setterm -powersave powerdown" to take advantage */static int cyber2000fb_blank(int blank, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; unsigned int sync = 0; int i; switch (blank) { case FB_BLANK_POWERDOWN: /* powerdown - both sync lines down */ sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_0; break; case FB_BLANK_HSYNC_SUSPEND: /* hsync off */ sync = EXT_SYNC_CTL_VS_NORMAL | EXT_SYNC_CTL_HS_0; break; case FB_BLANK_VSYNC_SUSPEND: /* vsync off */ sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_NORMAL; break; case FB_BLANK_NORMAL: /* soft blank */ default: /* unblank */ break; } cyber2000_grphw(EXT_SYNC_CTL, sync, cfb); if (blank <= 1) { /* turn on ramdacs */ cfb->ramdac_powerdown &= ~(RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN); cyber2000fb_write_ramdac_ctrl(cfb); } /* * Soft blank/unblank the display. */ if (blank) { /* soft blank */ for (i = 0; i < NR_PALETTE; i++) { cyber2000fb_writeb(i, 0x3c8, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); } } else { /* unblank */ for (i = 0; i < NR_PALETTE; i++) { cyber2000fb_writeb(i, 0x3c8, cfb); cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb); cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb); cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb); } } if (blank >= 2) { /* turn off ramdacs */ cfb->ramdac_powerdown |= RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN; cyber2000fb_write_ramdac_ctrl(cfb); } return 0;}static struct fb_ops cyber2000fb_ops = { .owner = THIS_MODULE, .fb_check_var = cyber2000fb_check_var, .fb_set_par = cyber2000fb_set_par, .fb_setcolreg = cyber2000fb_setcolreg, .fb_blank = cyber2000fb_blank, .fb_pan_display = cyber2000fb_pan_display, .fb_fillrect = cyber2000fb_fillrect, .fb_copyarea = cyber2000fb_copyarea, .fb_imageblit = cyber2000fb_imageblit, .fb_sync = cyber2000fb_sync,};/* * This is the only "static" reference to the internal data structures * of this driver. It is here solely at the moment to support the other * CyberPro modules external to this driver. */static struct cfb_info *int_cfb_info;/* * Enable access to the extended registers */void cyber2000fb_enable_extregs(struct cfb_info *cfb){ cfb->func_use_count += 1; if (cfb->func_use_count == 1) { int old; old = cyber2000_grphr(EXT_FUNC_CTL, cfb); old |= EXT_FUNC_CTL_EXTREGENBL; cyber2000_grphw(EXT_FUNC_CTL, old, cfb); }}EXPORT_SYMBOL(cyber2000fb_enable_extregs);/* * Disable access to the extended registers */void cyber2000fb_disable_extregs(struct cfb_info *cfb){ if (cfb->func_use_count == 1) { int old; old = cyber2000_grphr(EXT_FUNC_CTL, cfb); old &= ~EXT_FUNC_CTL_EXTREGENBL; cyber2000_grphw(EXT_FUNC_CTL, old, cfb); } if (cfb->func_use_count == 0) printk(KERN_ERR "disable_extregs: count = 0\n"); else cfb->func_use_count -= 1;}EXPORT_SYMBOL(cyber2000fb_disable_extregs);void cyber2000fb_get_fb_var(struct cfb_info *cfb, struct fb_var_screeninfo *var){ memcpy(var, &cfb->fb.var, sizeof(struct fb_var_screeninfo));}EXPORT_SYMBOL(cyber2000fb_get_fb_var);/* * Attach a capture/tv driver to the core CyberX0X0 driver. */int cyber2000fb_attach(struct cyberpro_info *info, int idx){ if (int_cfb_info != NULL) { info->dev = int_cfb_info->dev; info->regs = int_cfb_info->regs; info->fb = int_cfb_info->fb.screen_base; 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; strlcpy(info->dev_name, int_cfb_info->fb.fix.id, sizeof(info->dev_name)); } return int_cfb_info != NULL;}EXPORT_SYMBOL(cyber2000fb_attach);/* * Detach a capture/tv driver from the core CyberX0X0 driver. */void cyber2000fb_detach(int idx){}EXPORT_SYMBOL(cyber2000fb_detach);/* * These parameters give * 640x480, hsync 31.5kHz, vsync 60Hz */static struct fb_videomode __devinitdata cyber2000fb_default_mode = { .refresh = 60, .xres = 640, .yres = 480, .pixclock = 39722, .left_margin = 56, .right_margin = 16, .upper_margin = 34, .lower_margin = 9, .hsync_len = 88, .vsync_len = 2, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED};static char igs_regs[] = { EXT_CRT_IRQ, 0, EXT_CRT_TEST, 0, EXT_SYNC_CTL, 0, EXT_SEG_WRITE_PTR, 0, EXT_SEG_READ_PTR, 0, EXT_BIU_MISC, EXT_BIU_MISC_LIN_ENABLE | EXT_BIU_MISC_COP_ENABLE | EXT_BIU_MISC_COP_BFC, EXT_FUNC_CTL, 0, CURS_H_START, 0, CURS_H_START + 1, 0, CURS_H_PRESET, 0, CURS_V_START, 0, CURS_V_START + 1, 0, CURS_V_PRESET, 0, CURS_CTL, 0, EXT_ATTRIB_CTL, EXT_ATTRIB_CTL_EXT, EXT_OVERSCAN_RED, 0,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -