📄 atyfb_base.c
字号:
/* This is horror! When we simulate, say 640x480 on an 800x600 LCD monitor, the CRTC should be programmed 800x600 values for the non visible part, but 640x480 for the visible part. This code has been tested on a laptop with it's 1400x1050 LCD monitor and a conventional monitor both switched on. Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, works with little glitches also with DOUBLESCAN modes */ if (yres < par->lcd_height) { VScan = par->lcd_height / yres; if(VScan > 1) { VScan = 2; vmode |= FB_VMODE_DOUBLE; } } h_sync_strt = h_disp + par->lcd_right_margin; h_sync_end = h_sync_strt + par->lcd_hsync_len; h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly; h_total = h_disp + par->lcd_hblank_len; v_sync_strt = v_disp + par->lcd_lower_margin / VScan; v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan; v_total = v_disp + par->lcd_vblank_len / VScan; }#endif /* CONFIG_FB_ATY_GENERIC_LCD */ h_disp = (h_disp >> 3) - 1; h_sync_strt = (h_sync_strt >> 3) - 1; h_sync_end = (h_sync_end >> 3) - 1; h_total = (h_total >> 3) - 1; h_sync_wid = h_sync_end - h_sync_strt; FAIL_MAX("h_disp too large", h_disp, 0xff); FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff); /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/ if(h_sync_wid > 0x1f) h_sync_wid = 0x1f; FAIL_MAX("h_total too large", h_total, 0x1ff); if (vmode & FB_VMODE_DOUBLE) { v_disp <<= 1; v_sync_strt <<= 1; v_sync_end <<= 1; v_total <<= 1; } vdisplay = yres;#ifdef CONFIG_FB_ATY_GENERIC_LCD if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) vdisplay = par->lcd_height;#endif v_disp--; v_sync_strt--; v_sync_end--; v_total--; v_sync_wid = v_sync_end - v_sync_strt; FAIL_MAX("v_disp too large", v_disp, 0x7ff); FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff); /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/ if(v_sync_wid > 0x1f) v_sync_wid = 0x1f; FAIL_MAX("v_total too large", v_total, 0x7ff); c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0; /* output */ crtc->vxres = vxres; crtc->vyres = vyres; crtc->xoffset = xoffset; crtc->yoffset = yoffset; crtc->bpp = bpp; crtc->off_pitch = ((yoffset*vxres+xoffset)*bpp/64) | (vxres<<19); crtc->vline_crnt_vline = 0; crtc->h_tot_disp = h_total | (h_disp<<16); crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly<<8) | ((h_sync_strt & 0x100)<<4) | (h_sync_wid<<16) | (h_sync_pol<<21); crtc->v_tot_disp = v_total | (v_disp<<16); crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid<<16) | (v_sync_pol<<21); /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */ crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync; crtc->gen_cntl |= CRTC_VGA_LINEAR; /* Enable doublescan mode if requested */ if (vmode & FB_VMODE_DOUBLE) crtc->gen_cntl |= CRTC_DBL_SCAN_EN; /* Enable interlaced mode if requested */ if (vmode & FB_VMODE_INTERLACED) crtc->gen_cntl |= CRTC_INTERLACE_EN;#ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { vdisplay = yres; if(vmode & FB_VMODE_DOUBLE) vdisplay <<= 1; crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH); crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ USE_SHADOWED_VEND | USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); crtc->lcd_gen_cntl |= (DONT_SHADOW_VPAR/* | LOCK_8DOT*/); /* MOBILITY M1 tested, FIXME: LT */ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); if (!M64_HAS(LT_LCD_REGS)) crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) & ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3); crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO | HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | HORZ_STRETCH_MODE | HORZ_STRETCH_EN); if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) { do { /* * The horizontal blender misbehaves when HDisplay is less than a * a certain threshold (440 for a 1024-wide panel). It doesn't * stretch such modes enough. Use pixel replication instead of * blending to stretch modes that can be made to exactly fit the * panel width. The undocumented "NoLCDBlend" option allows the * pixel-replicated mode to be slightly wider or narrower than the * panel width. It also causes a mode that is exactly half as wide * as the panel to be pixel-replicated, rather than blended. */ int HDisplay = xres & ~7; int nStretch = par->lcd_width / HDisplay; int Remainder = par->lcd_width % HDisplay; if ((!Remainder && ((nStretch > 2))) || (((HDisplay * 16) / par->lcd_width) < 7)) { static const char StretchLoops[] = {10, 12, 13, 15, 16}; int horz_stretch_loop = -1, BestRemainder; int Numerator = HDisplay, Denominator = par->lcd_width; int Index = 5; ATIReduceRatio(&Numerator, &Denominator); BestRemainder = (Numerator * 16) / Denominator; while (--Index >= 0) { Remainder = ((Denominator - Numerator) * StretchLoops[Index]) % Denominator; if (Remainder < BestRemainder) { horz_stretch_loop = Index; if (!(BestRemainder = Remainder)) break; } } if ((horz_stretch_loop >= 0) && !BestRemainder) { int horz_stretch_ratio = 0, Accumulator = 0; int reuse_previous = 1; Index = StretchLoops[horz_stretch_loop]; while (--Index >= 0) { if (Accumulator > 0) horz_stretch_ratio |= reuse_previous; else Accumulator += Denominator; Accumulator -= Numerator; reuse_previous <<= 1; } crtc->horz_stretching |= (HORZ_STRETCH_EN | ((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) | (horz_stretch_ratio & HORZ_STRETCH_RATIO)); break; /* Out of the do { ... } while (0) */ } } crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN | (((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND)); } while (0); } if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) { crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN | (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0)); if (!M64_HAS(LT_LCD_REGS) && xres <= (M64_HAS(MOBIL_BUS)?1024:800)) crtc->ext_vert_stretch |= VERT_STRETCH_MODE; } else { /* * Don't use vertical blending if the mode is too wide or not * vertically stretched. */ crtc->vert_stretching = 0; } /* copy to shadow crtc */ crtc->shadow_h_tot_disp = crtc->h_tot_disp; crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid; crtc->shadow_v_tot_disp = crtc->v_tot_disp; crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid; }#endif /* CONFIG_FB_ATY_GENERIC_LCD */ if (M64_HAS(MAGIC_FIFO)) { /* FIXME: display FIFO low watermark values */ crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM); } crtc->dp_pix_width = dp_pix_width; crtc->dp_chain_mask = dp_chain_mask; return 0;}static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var){ u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync; u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; u32 pix_width; u32 double_scan, interlace; /* input */ h_total = crtc->h_tot_disp & 0x1ff; h_disp = (crtc->h_tot_disp >> 16) & 0xff; h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100); h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7; h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f; h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1; v_total = crtc->v_tot_disp & 0x7ff; v_disp = (crtc->v_tot_disp >> 16) & 0x7ff; v_sync_strt = crtc->v_sync_strt_wid & 0x7ff; v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f; v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1; c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0; pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK; double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN; interlace = crtc->gen_cntl & CRTC_INTERLACE_EN; /* convert */ xres = (h_disp + 1) * 8; yres = v_disp + 1; left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly; right = (h_sync_strt - h_disp) * 8 + h_sync_dly; hslen = h_sync_wid * 8; upper = v_total - v_sync_strt - v_sync_wid; lower = v_sync_strt - v_disp; vslen = v_sync_wid; sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); switch (pix_width) {#if 0 case CRTC_PIX_WIDTH_4BPP: bpp = 4; var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break;#endif case CRTC_PIX_WIDTH_8BPP: bpp = 8; var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break; case CRTC_PIX_WIDTH_15BPP: /* RGB 555 */ bpp = 16; var->red.offset = 10; var->red.length = 5; var->green.offset = 5; var->green.length = 5; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; break; case CRTC_PIX_WIDTH_16BPP: /* RGB 565 */ bpp = 16; var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; break; case CRTC_PIX_WIDTH_24BPP: /* RGB 888 */ bpp = 24; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break; case CRTC_PIX_WIDTH_32BPP: /* ARGB 8888 */ bpp = 32; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 24; var->transp.length = 8; break; default: PRINTKE("Invalid pixel width\n"); return -EINVAL; } /* output */ var->xres = xres; var->yres = yres; var->xres_virtual = crtc->vxres; var->yres_virtual = crtc->vyres; var->bits_per_pixel = bpp; var->left_margin = left; var->right_margin = right; var->upper_margin = upper; var->lower_margin = lower; var->hsync_len = hslen; var->vsync_len = vslen; var->sync = sync; var->vmode = FB_VMODE_NONINTERLACED; /* In double scan mode, the vertical parameters are doubled, so we need to half them to get the right values. In interlaced mode the values are already correct, so no correction is necessary. */ if (interlace) var->vmode = FB_VMODE_INTERLACED; if (double_scan) { var->vmode = FB_VMODE_DOUBLE; var->yres>>=1; var->upper_margin>>=1; var->lower_margin>>=1; var->vsync_len>>=1; } return 0;}/* ------------------------------------------------------------------------- */static int atyfb_set_par(struct fb_info *info){ struct atyfb_par *par = (struct atyfb_par *) info->par; struct fb_var_screeninfo *var = &info->var; u32 tmp, pixclock; int err;#ifdef DEBUG struct fb_var_screeninfo debug; u32 pixclock_in_ps;#endif if (par->asleep) return 0; if ((err = aty_var_to_crtc(info, var, &par->crtc))) return err; pixclock = atyfb_get_pixclock(var, par); if (pixclock == 0) { PRINTKE("Invalid pixclock\n"); return -EINVAL; } else { if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &par->pll))) return err; } par->accel_flags = var->accel_flags; /* hack */ if (var->accel_flags) { info->fbops->fb_sync = atyfb_sync; info->flags &= ~FBINFO_HWACCEL_DISABLED; } else { info->fbops->fb_sync = NULL; info->flags |= FBINFO_HWACCEL_DISABLED; } if (par->blitter_may_be_busy) wait_for_idle(par); aty_set_crtc(par, &par->crtc); par->dac_ops->set_dac(info, &par->pll, var->bits_per_pixel, par->accel_flags); par->pll_ops->set_pll(info, &par->pll);#ifdef DEBUG if(par->pll_ops && par->pll_ops->pll_to_var) pixclock_in_ps = par->pll_ops->pll_to_var(info, &(par->pll)); else pixclock_in_ps = 0; if(0 == pixclock_in_ps) { PRINTKE("ALERT ops->pll_to_var get 0\n"); pixclock_in_ps = pixclock; } memset(&debug, 0, sizeof(debug)); if(!aty_crtc_to_var(&(par->crtc), &debug)) { u32 hSync, vRefresh; u32 h_disp, h_sync_strt, h_sync_end, h_total; u32 v_disp, v_sync_strt, v_sync_end, v_total; h_disp = debug.xres; h_sync_strt = h_disp + debug.right_margin; h_sync_end = h_sync_strt + debug.hsync_len; h_total = h_sync_end + debug.left_margin; v_disp = debug.yres; v_sync_strt = v_disp + debug.lower_margin; v_sync_end = v_sync_strt + debug.vsync_len; v_total = v_sync_end + debug.upper_margin; hSync = 1000000000 / (pixclock_in_ps * h_total); vRefresh = (hSync * 1000) / v_total; if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) vRefresh *= 2; if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) vRefresh /= 2; DPRINTK("atyfb_set_par\n"); DPRINTK(" Set Visible Mode to %ix%i-%i\n", var->xres, var->yres, var->bits_per_pixel); DPRINTK(" Virtual resolution %ix%i, pixclock_in_ps %i (calculated %i)\n", var->xres_virtual, var->yres_virtual, pixclock, pixclock_in_ps); DPRINTK(" Dot clock: %i MHz\n", 1000000 / pixclock_in_ps); DPRINTK(" Horizontal sync: %i kHz\n", hSync); DPRINTK(" Vertical refresh: %i Hz\n", vRefresh); DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n", 1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps, h_disp, h_sync_strt, h_sync_end, h_total, v_disp, v_sync_strt, v_sync_end, v_total); DPRINTK(" fb style: %i %i %i %i %i %i %i %i %i\n", pixclock_in_ps, debug.left_margin, h_disp, debug.right_margin, debug.hsync_len, debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -