📄 cyber2000fb.c
字号:
} /* * we should be able to remove this test once fbcon has been * "improved" --rmk */ if (!err && con == cfb->currcon) { err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb); dcmap = &cfb->fb.cmap; } if (!err) fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1); return err;}static intcyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb, struct fb_var_screeninfo *var){ u_int Htotal, Hblankend, Hsyncend; u_int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2) hw->crtc[13] = hw->pitch; hw->crtc[17] = 0xe3; hw->crtc[14] = 0; hw->crtc[8] = 0; Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; if (Htotal > 2080) return -EINVAL; hw->crtc[0] = (Htotal >> 3) - 5; hw->crtc[1] = (var->xres >> 3) - 1; hw->crtc[2] = var->xres >> 3; hw->crtc[4] = (var->xres + var->right_margin) >> 3; Hblankend = (Htotal - 4*8) >> 3; hw->crtc[3] = BIT(Hblankend, 0, 0x1f, 0) | BIT(1, 0, 0x01, 7); Hsyncend = (var->xres + var->right_margin + var->hsync_len) >> 3; hw->crtc[5] = BIT(Hsyncend, 0, 0x1f, 0) | BIT(Hblankend, 5, 0x01, 7); Vdispend = var->yres - 1; Vsyncstart = var->yres + var->lower_margin; Vsyncend = var->yres + var->lower_margin + var->vsync_len; Vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin - 2; if (Vtotal > 2047) return -EINVAL; Vblankstart = var->yres + 6; Vblankend = Vtotal - 10; hw->crtc[6] = Vtotal; hw->crtc[7] = BIT(Vtotal, 8, 0x01, 0) | BIT(Vdispend, 8, 0x01, 1) | BIT(Vsyncstart, 8, 0x01, 2) | BIT(Vblankstart,8, 0x01, 3) | BIT(1, 0, 0x01, 4) | BIT(Vtotal, 9, 0x01, 5) | BIT(Vdispend, 9, 0x01, 6) | BIT(Vsyncstart, 9, 0x01, 7); hw->crtc[9] = BIT(0, 0, 0x1f, 0) | BIT(Vblankstart,9, 0x01, 5) | BIT(1, 0, 0x01, 6); hw->crtc[10] = Vsyncstart; hw->crtc[11] = BIT(Vsyncend, 0, 0x0f, 0) | 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 = BIT(Vtotal, 10, 0x01, 0) | BIT(Vdispend, 10, 0x01, 1) | BIT(Vsyncstart, 10, 0x01, 2) | BIT(Vblankstart,10, 0x01, 3) | 1 << 4; 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 |= DCLK_DIV_VFSEL; return 0;}/* * Decode the info required for the hardware. * This involves the PLL parameters for the dot clock, * CRTC registers, and accelerator settings. */static intcyber2000fb_decode_var(struct fb_var_screeninfo *var, struct cfb_info *cfb, struct par_info *hw){ int err; hw->width = var->xres_virtual; hw->palette_ctrl = 0x06; hw->vmode = var->vmode; switch (var->bits_per_pixel) {#ifdef FBCON_HAS_CFB8 case 8: /* PSEUDOCOLOUR, 256 */ hw->pixformat = PIXFORMAT_8BPP; hw->visualid = VISUALID_256; hw->pitch = hw->width >> 3; break;#endif#ifdef FBCON_HAS_CFB16 case 16:/* DIRECTCOLOUR, 64k */#ifndef CFB16_IS_CFB15 hw->pixformat = PIXFORMAT_16BPP; hw->visualid = VISUALID_64K; hw->pitch = hw->width >> 2; hw->palette_ctrl |= 0x10; break;#endif case 15:/* DIRECTCOLOUR, 32k */ hw->pixformat = PIXFORMAT_16BPP; hw->visualid = VISUALID_32K; hw->pitch = hw->width >> 2; hw->palette_ctrl |= 0x10; break;#endif#ifdef FBCON_HAS_CFB24 case 24:/* TRUECOLOUR, 16m */ hw->pixformat = PIXFORMAT_24BPP; hw->visualid = VISUALID_16M; hw->width *= 3; hw->pitch = hw->width >> 3; hw->palette_ctrl |= 0x10; break;#endif#ifdef FBCON_HAS_CFB32 case 32:/* TRUECOLOUR, 16m */ hw->pixformat = PIXFORMAT_32BPP; hw->visualid = VISUALID_16M_32; hw->pitch = hw->width >> 1; hw->palette_ctrl |= 0x10; break;#endif default: return -EINVAL; } err = cyber2000fb_decode_clock(hw, cfb, var); if (err) return err; err = cyber2000fb_decode_crtc(hw, cfb, var); if (err) return err; hw->width -= 1; hw->fetch = hw->pitch; if (!(cfb->mem_ctl2 & MEM_CTL2_64BIT)) hw->fetch <<= 1; hw->fetch += 1; return 0;}/* * Set the User Defined Part of the Display */static intcyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; struct display *display; struct par_info hw; int err, chgvar = 0; /* * CONUPDATE and SMOOTH_XPAN are equal. However, * SMOOTH_XPAN is only used internally by fbcon. */ if (var->vmode & FB_VMODE_CONUPDATE) { var->vmode |= FB_VMODE_YWRAP; var->xoffset = cfb->fb.var.xoffset; var->yoffset = cfb->fb.var.yoffset; } err = cyber2000fb_decode_var(var, (struct cfb_info *)info, &hw); if (err) return err; if (var->activate & FB_ACTIVATE_TEST) return 0; if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) return -EINVAL; if (cfb->fb.var.xres != var->xres) chgvar = 1; if (cfb->fb.var.yres != var->yres) chgvar = 1; if (cfb->fb.var.xres_virtual != var->xres_virtual) chgvar = 1; if (cfb->fb.var.yres_virtual != var->yres_virtual) chgvar = 1; if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) chgvar = 1; if (con < 0) { display = cfb->fb.disp; chgvar = 0; } else { display = fb_display + con; } var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; switch (var->bits_per_pixel) {#ifdef FBCON_HAS_CFB8 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; cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; cfb->dispsw = &fbcon_cfb8; display->dispsw_data = NULL; display->next_line = var->xres_virtual; break;#endif#ifdef FBCON_HAS_CFB16 case 16:/* DIRECTCOLOUR, 64k */#ifndef CFB16_IS_CFB15 var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; cfb->dispsw = &fbcon_cfb16; display->dispsw_data = cfb->fb.pseudo_palette; display->next_line = var->xres_virtual * 2; break;#endif case 15:/* DIRECTCOLOUR, 32k */ var->bits_per_pixel = 15; var->red.offset = 10; var->red.length = 5; var->green.offset = 5; var->green.length = 5; var->blue.offset = 0; var->blue.length = 5; cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; cfb->dispsw = &fbcon_cfb16; display->dispsw_data = cfb->fb.pseudo_palette; display->next_line = var->xres_virtual * 2; break;#endif#ifdef FBCON_HAS_CFB24 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; cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; cfb->dispsw = &fbcon_cfb24; display->dispsw_data = cfb->fb.pseudo_palette; display->next_line = var->xres_virtual * 3; break;#endif#ifdef FBCON_HAS_CFB32 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; cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; cfb->dispsw = &fbcon_cfb32; display->dispsw_data = cfb->fb.pseudo_palette; display->next_line = var->xres_virtual * 4; break;#endif default:/* in theory this should never happen */ printk(KERN_WARNING "%s: no support for %dbpp\n", cfb->fb.fix.id, var->bits_per_pixel); cfb->dispsw = &fbcon_dummy; break; } if (var->accel_flags & FB_ACCELF_TEXT && cfb->dispsw != &fbcon_dummy) display->dispsw = &fbcon_cyber_accel; else display->dispsw = cfb->dispsw; cfb->fb.fix.line_length = display->next_line; display->screen_base = cfb->fb.screen_base; display->line_length = cfb->fb.fix.line_length; display->visual = cfb->fb.fix.visual; display->type = cfb->fb.fix.type; display->type_aux = cfb->fb.fix.type_aux; display->ypanstep = cfb->fb.fix.ypanstep; display->ywrapstep = cfb->fb.fix.ywrapstep; display->can_soft_blank = 1; display->inverse = 0; cfb->fb.var = *var; cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; /* * Update the old var. The fbcon drivers still use this. * Once they are using cfb->fb.var, this can be dropped. * --rmk */ display->var = cfb->fb.var; /* * If we are setting all the virtual consoles, also set the * defaults used to create new consoles. */ if (var->activate & FB_ACTIVATE_ALL) cfb->fb.disp->var = cfb->fb.var; if (chgvar && info && cfb->fb.changevar) cfb->fb.changevar(con); cyber2000fb_update_start(cfb, var); cyber2000fb_set_timing(cfb, &hw); fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb); return 0;}/* * Pan or Wrap the Display */static intcyber2000fb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; u_int y_bottom; y_bottom = var->yoffset; if (!(var->vmode & FB_VMODE_YWRAP)) y_bottom += var->yres; if (var->xoffset > (var->xres_virtual - var->xres)) return -EINVAL; if (y_bottom > cfb->fb.var.yres_virtual) return -EINVAL; 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;}/* * Update the `var' structure (called by fbcon.c) * * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'. * Since it's called by a kernel driver, no range checking is done. */static int cyber2000fb_updatevar(int con, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; return cyber2000fb_update_start(cfb, &fb_display[con].var);}static int cyber2000fb_switch(int con, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; struct display *disp; struct fb_cmap *cmap; if (cfb->currcon >= 0) { disp = fb_display + cfb->currcon; /* * Save the old colormap and video mode. */ disp->var = cfb->fb.var; if (disp->cmap.len) fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0); } cfb->currcon = con; disp = fb_display + con; /* * Install the new colormap and change the video mode. By default, * fbcon sets all the colormaps and video modes to the default * values at bootup. * * Really, we want to set the colourmap size depending on the * depth of the new video mode. For now, we leave it at its * default 256 entry. */ if (disp->cmap.len) cmap = &disp->cmap; else cmap = fb_default_cmap(1 << disp->var.bits_per_pixel); fb_copy_cmap(cmap, &cfb->fb.cmap, 0); cfb->fb.var = disp->var; cfb->fb.var.activate = FB_ACTIVATE_NOW; cyber2000fb_set_var(&cfb->fb.var, con, &cfb->fb); return 0;}/* * (Un)Blank the display. */static void cyber2000fb_blank(int blank, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; int i; /* * 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 */ switch (blank) { case 4: /* powerdown - both sync lines down */ cyber2000_grphw(0x16, 0x05, cfb); break; case 3: /* hsync off */ cyber2000_grphw(0x16, 0x01, cfb); break; case 2: /* vsync off */ cyber2000_grphw(0x16, 0x04, cfb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -