📄 fbmon.c
字号:
parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { if (edid_is_serial_block(block)) { copy_string(block, specs->serial_no); DPRINTK(" Serial Number: %s\n", specs->serial_no); } else if (edid_is_ascii_block(block)) { copy_string(block, specs->ascii); DPRINTK(" ASCII Block: %s\n", specs->ascii); } else if (edid_is_monitor_block(block)) { copy_string(block, specs->monitor); DPRINTK(" Monitor Name: %s\n", specs->monitor); } } DPRINTK(" Display Characteristics:\n"); get_monspecs(edid, specs); specs->modedb = fb_create_modedb(edid, &specs->modedb_len); /* * Workaround for buggy EDIDs that sets that the first * detailed timing is preferred but has not detailed * timing specified */ for (i = 0; i < specs->modedb_len; i++) { if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) { found = 1; break; } } if (!found) specs->misc &= ~FB_MISC_1ST_DETAIL; DPRINTK("========================================\n");}/* * VESA Generalized Timing Formula (GTF) */#define FLYBACK 550#define V_FRONTPORCH 1#define H_OFFSET 40#define H_SCALEFACTOR 20#define H_BLANKSCALE 128#define H_GRADIENT 600#define C_VAL 30#define M_VAL 300struct __fb_timings { u32 dclk; u32 hfreq; u32 vfreq; u32 hactive; u32 vactive; u32 hblank; u32 vblank; u32 htotal; u32 vtotal;};/** * fb_get_vblank - get vertical blank time * @hfreq: horizontal freq * * DESCRIPTION: * vblank = right_margin + vsync_len + left_margin * * given: right_margin = 1 (V_FRONTPORCH) * vsync_len = 3 * flyback = 550 * * flyback * hfreq * left_margin = --------------- - vsync_len * 1000000 */static u32 fb_get_vblank(u32 hfreq){ u32 vblank; vblank = (hfreq * FLYBACK)/1000; vblank = (vblank + 500)/1000; return (vblank + V_FRONTPORCH);}/** * fb_get_hblank_by_freq - get horizontal blank time given hfreq * @hfreq: horizontal freq * @xres: horizontal resolution in pixels * * DESCRIPTION: * * xres * duty_cycle * hblank = ------------------ * 100 - duty_cycle * * duty cycle = percent of htotal assigned to inactive display * duty cycle = C - (M/Hfreq) * * where: C = ((offset - scale factor) * blank_scale) * -------------------------------------- + scale factor * 256 * M = blank_scale * gradient * */static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres){ u32 c_val, m_val, duty_cycle, hblank; c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + H_SCALEFACTOR) * 1000; m_val = (H_BLANKSCALE * H_GRADIENT)/256; m_val = (m_val * 1000000)/hfreq; duty_cycle = c_val - m_val; hblank = (xres * duty_cycle)/(100000 - duty_cycle); return (hblank);}/** * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock * @dclk: pixelclock in Hz * @xres: horizontal resolution in pixels * * DESCRIPTION: * * xres * duty_cycle * hblank = ------------------ * 100 - duty_cycle * * duty cycle = percent of htotal assigned to inactive display * duty cycle = C - (M * h_period) * * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 * ----------------------------------------------- * 2 * M * M = 300; * C = 30; */static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres){ u32 duty_cycle, h_period, hblank; dclk /= 1000; h_period = 100 - C_VAL; h_period *= h_period; h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk); h_period *= 10000; h_period = int_sqrt(h_period); h_period -= (100 - C_VAL) * 100; h_period *= 1000; h_period /= 2 * M_VAL; duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100; hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8; hblank &= ~15; return (hblank);}/** * fb_get_hfreq - estimate hsync * @vfreq: vertical refresh rate * @yres: vertical resolution * * DESCRIPTION: * * (yres + front_port) * vfreq * 1000000 * hfreq = ------------------------------------- * (1000000 - (vfreq * FLYBACK) * */static u32 fb_get_hfreq(u32 vfreq, u32 yres){ u32 divisor, hfreq; divisor = (1000000 - (vfreq * FLYBACK))/1000; hfreq = (yres + V_FRONTPORCH) * vfreq * 1000; return (hfreq/divisor);}static void fb_timings_vfreq(struct __fb_timings *timings){ timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive); timings->vblank = fb_get_vblank(timings->hfreq); timings->vtotal = timings->vactive + timings->vblank; timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->dclk = timings->htotal * timings->hfreq;}static void fb_timings_hfreq(struct __fb_timings *timings){ timings->vblank = fb_get_vblank(timings->hfreq); timings->vtotal = timings->vactive + timings->vblank; timings->vfreq = timings->hfreq/timings->vtotal; timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->dclk = timings->htotal * timings->hfreq;}static void fb_timings_dclk(struct __fb_timings *timings){ timings->hblank = fb_get_hblank_by_dclk(timings->dclk, timings->hactive); timings->htotal = timings->hactive + timings->hblank; timings->hfreq = timings->dclk/timings->htotal; timings->vblank = fb_get_vblank(timings->hfreq); timings->vtotal = timings->vactive + timings->vblank; timings->vfreq = timings->hfreq/timings->vtotal;}/* * fb_get_mode - calculates video mode using VESA GTF * @flags: if: 0 - maximize vertical refresh rate * 1 - vrefresh-driven calculation; * 2 - hscan-driven calculation; * 3 - pixelclock-driven calculation; * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock * @var: pointer to fb_var_screeninfo * @info: pointer to fb_info * * DESCRIPTION: * Calculates video mode based on monitor specs using VESA GTF. * The GTF is best for VESA GTF compliant monitors but is * specifically formulated to work for older monitors as well. * * If @flag==0, the function will attempt to maximize the * refresh rate. Otherwise, it will calculate timings based on * the flag and accompanying value. * * If FB_IGNOREMON bit is set in @flags, monitor specs will be * ignored and @var will be filled with the calculated timings. * * All calculations are based on the VESA GTF Spreadsheet * available at VESA's public ftp (http://www.vesa.org). * * NOTES: * The timings generated by the GTF will be different from VESA * DMT. It might be a good idea to keep a table of standard * VESA modes as well. The GTF may also not work for some displays, * such as, and especially, analog TV. * * REQUIRES: * A valid info->monspecs, otherwise 'safe numbers' will be used. */int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info){ struct __fb_timings *timings; u32 interlace = 1, dscan = 1; u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0; timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL); if (!timings) return -ENOMEM; /* * If monspecs are invalid, use values that are enough * for 640x480@60 */ if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax || !info->monspecs.dclkmax || info->monspecs.hfmax < info->monspecs.hfmin || info->monspecs.vfmax < info->monspecs.vfmin || info->monspecs.dclkmax < info->monspecs.dclkmin) { hfmin = 29000; hfmax = 30000; vfmin = 60; vfmax = 60; dclkmin = 0; dclkmax = 25000000; } else { hfmin = info->monspecs.hfmin; hfmax = info->monspecs.hfmax; vfmin = info->monspecs.vfmin; vfmax = info->monspecs.vfmax; dclkmin = info->monspecs.dclkmin; dclkmax = info->monspecs.dclkmax; } timings->hactive = var->xres; timings->vactive = var->yres; if (var->vmode & FB_VMODE_INTERLACED) { timings->vactive /= 2; interlace = 2; } if (var->vmode & FB_VMODE_DOUBLE) { timings->vactive *= 2; dscan = 2; } switch (flags & ~FB_IGNOREMON) { case FB_MAXTIMINGS: /* maximize refresh rate */ timings->hfreq = hfmax; fb_timings_hfreq(timings); if (timings->vfreq > vfmax) { timings->vfreq = vfmax; fb_timings_vfreq(timings); } if (timings->dclk > dclkmax) { timings->dclk = dclkmax; fb_timings_dclk(timings); } break; case FB_VSYNCTIMINGS: /* vrefresh driven */ timings->vfreq = val; fb_timings_vfreq(timings); break; case FB_HSYNCTIMINGS: /* hsync driven */ timings->hfreq = val; fb_timings_hfreq(timings); break; case FB_DCLKTIMINGS: /* pixelclock driven */ timings->dclk = PICOS2KHZ(val) * 1000; fb_timings_dclk(timings); break; default: err = -EINVAL; } if (err || (!(flags & FB_IGNOREMON) && (timings->vfreq < vfmin || timings->vfreq > vfmax || timings->hfreq < hfmin || timings->hfreq > hfmax || timings->dclk < dclkmin || timings->dclk > dclkmax))) { err = -EINVAL; } else { var->pixclock = KHZ2PICOS(timings->dclk/1000); var->hsync_len = (timings->htotal * 8)/100; var->right_margin = (timings->hblank/2) - var->hsync_len; var->left_margin = timings->hblank - var->right_margin - var->hsync_len; var->vsync_len = (3 * interlace)/dscan; var->lower_margin = (1 * interlace)/dscan; var->upper_margin = (timings->vblank * interlace)/dscan - (var->vsync_len + var->lower_margin); } kfree(timings); return err;}#elseint fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var){ return 1;}void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs){ specs = NULL;}void fb_destroy_modedb(struct fb_videomode *modedb){}int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info){ return -EINVAL;}#endif /* CONFIG_FB_MODE_HELPERS *//* * fb_validate_mode - validates var against monitor capabilities * @var: pointer to fb_var_screeninfo * @info: pointer to fb_info * * DESCRIPTION: * Validates video mode against monitor capabilities specified in * info->monspecs. * * REQUIRES: * A valid info->monspecs. */int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info){ u32 hfreq, vfreq, htotal, vtotal, pixclock; u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; /* * If monspecs are invalid, use values that are enough * for 640x480@60 */ if (!info->monspecs.hfmax || !info->monspecs.vfmax || !info->monspecs.dclkmax || info->monspecs.hfmax < info->monspecs.hfmin || info->monspecs.vfmax < info->monspecs.vfmin || info->monspecs.dclkmax < info->monspecs.dclkmin) { hfmin = 29000; hfmax = 30000; vfmin = 60; vfmax = 60; dclkmin = 0; dclkmax = 25000000; } else { hfmin = info->monspecs.hfmin; hfmax = info->monspecs.hfmax; vfmin = info->monspecs.vfmin; vfmax = info->monspecs.vfmax; dclkmin = info->monspecs.dclkmin; dclkmax = info->monspecs.dclkmax; } if (!var->pixclock) return -EINVAL; pixclock = PICOS2KHZ(var->pixclock) * 1000; htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; if (var->vmode & FB_VMODE_INTERLACED) vtotal /= 2; if (var->vmode & FB_VMODE_DOUBLE) vtotal *= 2; hfreq = pixclock/htotal; hfreq = (hfreq + 500) / 1000 * 1000; vfreq = hfreq/vtotal; return (vfreq < vfmin || vfreq > vfmax || hfreq < hfmin || hfreq > hfmax || pixclock < dclkmin || pixclock > dclkmax) ? -EINVAL : 0;}#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)/* * We need to ensure that the EDID block is only returned for * the primary graphics adapter. */const unsigned char *fb_firmware_edid(struct device *device){ struct pci_dev *dev = NULL; struct resource *res = NULL; unsigned char *edid = NULL; if (device) dev = to_pci_dev(device); if (dev) res = &dev->resource[PCI_ROM_RESOURCE]; if (res && res->flags & IORESOURCE_ROM_SHADOW) edid = edid_info.dummy; return edid;}#elseconst unsigned char *fb_firmware_edid(struct device *device){ return NULL;}#endifEXPORT_SYMBOL(fb_firmware_edid);EXPORT_SYMBOL(fb_parse_edid);EXPORT_SYMBOL(fb_edid_to_monspecs);EXPORT_SYMBOL(fb_get_mode);EXPORT_SYMBOL(fb_validate_mode);EXPORT_SYMBOL(fb_destroy_modedb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -