// SPDX-License-Identifier: GPL-2.0-only/* * i740fb - framebuffer driver for Intel740 * Copyright (c) 2011 Ondrej Zary * * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru> * which was partially based on: * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org> * and Petr Vandrovec <VANDROVE@vc.cvut.cz> * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park, * Texas. * i740fb by Patrick LERDA, v0.9 */ #include <linux/aperture.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/pci_ids.h>#include <linux/i2c.h>#include <linux/i2c-algo-bit.h>#include <linux/console.h>#include <video/vga.h> #include "i740_reg.h" static char *mode_option;static int mtrr = 1; struct i740fb_par { unsigned char __iomem *regs; bool has_sgram; int wc_cookie; bool ddc_registered; struct i2c_adapter ddc_adapter; struct i2c_algo_bit_data ddc_algo; u32 pseudo_palette[16]; struct mutex open_lock; unsigned int ref_count; u8 crtc[VGA_CRT_C]; u8 atc[VGA_ATT_C]; u8 gdc[VGA_GFX_C]; u8 seq[VGA_SEQ_C]; u8 misc; u8 vss; /* i740 specific registers */ u8 display_cntl; u8 pixelpipe_cfg0; u8 pixelpipe_cfg1; u8 pixelpipe_cfg2; u8 video_clk2_m; u8 video_clk2_n; u8 video_clk2_mn_msbs; u8 video_clk2_div_sel; u8 pll_cntl; u8 address_mapping; u8 io_cntl; u8 bitblt_cntl; u8 ext_vert_total; u8 ext_vert_disp_end; u8 ext_vert_sync_start; u8 ext_vert_blank_start; u8 ext_horiz_total; u8 ext_horiz_blank; u8 ext_offset; u8 interlace_cntl; u32 lmi_fifo_watermark; u8 ext_start_addr; u8 ext_start_addr_hi;}; #define DACSPEED8 203#define DACSPEED16 163#define DACSPEED24_SG 136#define DACSPEED24_SD 128#define DACSPEED32 86 static const struct fb_fix_screeninfo i740fb_fix = { .id = "i740fb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .xpanstep = 8, .ypanstep = 1, .accel = FB_ACCEL_NONE,}; static inline void i740outb(struct i740fb_par *par, u16 port, u8 val){ vga_mm_w(par->regs, port, val);}static inline u8 i740inb(struct i740fb_par *par, u16 port){ return vga_mm_r(par->regs, port);}static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val){ vga_mm_w_fast(par->regs, port, reg, val);}static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg){ vga_mm_w(par->regs, port, reg); return vga_mm_r(par->regs, port+1);}static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg, u8 val, u8 mask){ vga_mm_w_fast(par->regs, port, reg, (val & mask) | (i740inreg(par, port, reg) & ~mask));} #define REG_DDC_DRIVE 0x62#define REG_DDC_STATE 0x63#define DDC_SCL (1 << 3)#define DDC_SDA (1 << 2) static void i740fb_ddc_setscl(void *data, int val){ struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL); i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL);} static void i740fb_ddc_setsda(void *data, int val){ struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA); i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA);} static int i740fb_ddc_getscl(void *data){ struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL); return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL);} static int i740fb_ddc_getsda(void *data){ struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA); return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA);} static int i740fb_setup_ddc_bus(struct fb_info *info){ struct i740fb_par *par = info->par; strscpy(par->ddc_adapter.name, info->fix.id, sizeof(par->ddc_adapter.name)); par->ddc_adapter.owner = THIS_MODULE; par->ddc_adapter.algo_data = &par->ddc_algo; par->ddc_adapter.dev.parent = info->device; par->ddc_algo.setsda = i740fb_ddc_setsda; par->ddc_algo.setscl = i740fb_ddc_setscl; par->ddc_algo.getsda = i740fb_ddc_getsda; par->ddc_algo.getscl = i740fb_ddc_getscl; par->ddc_algo.udelay = 10; par->ddc_algo.timeout = 20; par->ddc_algo.data = par; i2c_set_adapdata(&par->ddc_adapter, par); return i2c_bit_add_bus(&par->ddc_adapter);} static int i740fb_open(struct fb_info *info, int user){ struct i740fb_par *par = info->par; mutex_lock(&(par->open_lock)); par->ref_count++; mutex_unlock(&(par->open_lock)); return 0;} static int i740fb_release(struct fb_info *info, int user){ struct i740fb_par *par = info->par; mutex_lock(&(par->open_lock)); if (par->ref_count == 0) { fb_err(info, "release called with zero refcount\n"); mutex_unlock(&(par->open_lock)); return -EINVAL; } par->ref_count--; mutex_unlock(&(par->open_lock)); return 0;} static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp){ /* * Would like to calculate these values automatically, but a generic * algorithm does not seem possible. Note: These FIFO water mark * values were tested on several cards and seem to eliminate the * all of the snow and vertical banding, but fine adjustments will * probably be required for other cards. */ u32 wm; switch (bpp) { case 8: if (freq > 200) wm = 0x18120000; else if (freq > 175) wm = 0x16110000; else if (freq > 135) wm = 0x120E0000; else wm = 0x100D0000; break; case 15: case 16: if (par->has_sgram) { if (freq > 140) wm = 0x2C1D0000; else if (freq > 120) wm = 0x2C180000; else if (freq > 100) wm = 0x24160000; else if (freq > 90) wm = 0x18120000; else if (freq > 50) wm = 0x16110000; else if (freq > 32) wm = 0x13100000; else wm = 0x120E0000; } else { if (freq > 160) wm = 0x28200000; else if (freq > 140) wm = 0x2A1E0000; else if (freq > 130) wm = 0x2B1A0000; else if (freq > 120) wm = 0x2C180000; else if (freq > 100) wm = 0x24180000; else if (freq > 90) wm = 0x18120000; else if (freq > 50) wm = 0x16110000; else if (freq > 32) wm = 0x13100000; else wm = 0x120E0000; } break; case 24: if (par->has_sgram) { if (freq > 130) wm = 0x31200000; else if (freq > 120) wm = 0x2E200000; else if (freq > 100) wm = 0x2C1D0000; else if (freq > 80) wm = 0x25180000; else if (freq > 64) wm = 0x24160000; else if (freq > 49) wm = 0x18120000; else if (freq > 32) wm = 0x16110000; else wm = 0x13100000; } else { if (freq > 120) wm = 0x311F0000; else if (freq > 100) wm = 0x2C1D0000; else if (freq > 80) wm = 0x25180000; else if (freq > 64) wm = 0x24160000; else if (freq > 49) wm = 0x18120000; else if (freq > 32) wm = 0x16110000; else wm = 0x13100000; } break; case 32: if (par->has_sgram) { if (freq > 80) wm = 0x2A200000; else if (freq > 60) wm = 0x281A0000; else if (freq > 49) wm = 0x25180000; else if (freq > 32) wm = 0x18120000; else wm = 0x16110000; } else { if (freq > 80) wm = 0x29200000; else if (freq > 60) wm = 0x281A0000; else if (freq > 49) wm = 0x25180000; else if (freq > 32) wm = 0x18120000; else wm = 0x16110000; } break; } return wm;} /* clock calculation from i740fb by Patrick LERDA */ #define I740_RFREQ 1000000#define TARGET_MAX_N 30#define I740_FFIX (1 << 8)#define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX)#define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */#define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */ static void i740_calc_vclk(u32 freq, struct i740fb_par *par){ const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX); const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX); u32 err_best = 512 * I740_FFIX; u32 f_err, f_vco; int m_best = 0, n_best = 0, p_best = 0; int m, n; p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX))); f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX; freq = freq / I740_RFREQ_FIX; n = 2; do { n++; m = ((f_vco * n) / I740_REF_FREQ + 2) / 4; if (m < 3) m = 3; { u32 f_out = (((m * I740_REF_FREQ * 4) / n) + ((1 << p_best) / 2)) / (1 << p_best); f_err = (freq - f_out); if (abs(f_err) < err_max) { m_best = m; n_best = n; err_best = f_err; } } } while ((abs(f_err) >= err_target) && ((n <= TARGET_MAX_N) || (abs(err_best) > err_max))); if (abs(f_err) < err_target) { m_best = m; n_best = n; } par->video_clk2_m = (m_best - 2) & 0xFF; par->video_clk2_n = (n_best - 2) & 0xFF; par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS) | (((m_best - 2) >> 8) & VCO_M_MSBS)); par->video_clk2_div_sel = ((p_best << 4) | REF_DIV_1);} static int i740fb_decode_var(const struct fb_var_screeninfo *var, struct i740fb_par *par, struct fb_info *info){ /* * Get the video params out of 'var'. * If a value doesn't fit, round it up, if it's too big, return -EINVAL. */ u32 xres, right, hslen, left, xtotal; u32 yres, lower, vslen, upper, ytotal; u32 vxres, xoffset, vyres, yoffset; u32 bpp, base, dacspeed24, mem, freq; u8 r7; int i; dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n", var->xres, var->yres, var->xres_virtual, var->xres_virtual); dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n", var->xoffset, var->yoffset, var->bits_per_pixel, var->grayscale); dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n", var->activate, var->nonstd, var->vmode); dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n", var->pixclock, var->hsync_len, var->vsync_len); dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n", var->left_margin, var->right_margin, var->upper_margin, var->lower_margin); bpp = var->bits_per_pixel; switch (bpp) { case 1 ... 8: bpp = 8; if ((1000000 / var->pixclock) > DACSPEED8) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n", 1000000 / var->pixclock, DACSPEED8); return -EINVAL; } break; case 9 ... 15: bpp = 15; fallthrough; case 16: if ((1000000 / var->pixclock) > DACSPEED16) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", 1000000 / var->pixclock, DACSPEED16); return -EINVAL; } break; case 17 ... 24: bpp = 24; dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD; if ((1000000 / var->pixclock) > dacspeed24) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n", 1000000 / var->pixclock, dacspeed24); return -EINVAL; } break; case 25 ... 32: bpp = 32; if ((1000000 / var->pixclock) > DACSPEED32) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n", 1000000 / var->pixclock, DACSPEED32); return -EINVAL; } break; default: return -EINVAL; } xres = ALIGN(var->xres, 8); vxres = ALIGN(var->xres_virtual, 16); if (vxres < xres) vxres = xres; xoffset = ALIGN(var->xoffset, 8); if (xres + xoffset > vxres) xoffset = vxres - xres; left = ALIGN(var->left_margin, 8); right = ALIGN(var->right_margin, 8); hslen = ALIGN(var->hsync_len, 8); yres = var->yres; vyres = var->yres_virtual; if (yres > vyres) vyres = yres; yoffset = var->yoffset; if (yres + yoffset > vyres) yoffset = vyres - yres; lower = var->lower_margin; vslen = var->vsync_len; upper = var->upper_margin; mem = vxres * vyres * ((bpp + 1) / 8); if (mem > info->screen_size) { dev_err(info->device, "not enough video memory (%d KB requested, %ld KB available)\n", mem >> 10, info->screen_size >> 10); return -ENOMEM; } if (yoffset + yres > vyres) yoffset = vyres - yres; xtotal = xres + right + hslen + left; ytotal = yres + lower + vslen + upper; Lines 1–500 of 1316. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * i740fb - framebuffer driver for Intel740 * Copyright (c) 2011 Ondrej Zary * * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru> * which was partially based on: * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org> * and Petr Vandrovec <VANDROVE@vc.cvut.cz> * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park, * Texas. * i740fb by Patrick LERDA, v0.9 */ #include <linux/aperture.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> #include <linux/console.h> #include <video/vga.h> #include "i740_reg.h" static char *mode_option; static int mtrr = 1; struct i740fb_par { unsigned char __iomem *regs; bool has_sgram; int wc_cookie; bool ddc_registered; struct i2c_adapter ddc_adapter; struct i2c_algo_bit_data ddc_algo; u32 pseudo_palette[16]; struct mutex open_lock; unsigned int ref_count; u8 crtc[VGA_CRT_C]; u8 atc[VGA_ATT_C]; u8 gdc[VGA_GFX_C]; u8 seq[VGA_SEQ_C]; u8 misc; u8 vss; /* i740 specific registers */ u8 display_cntl; u8 pixelpipe_cfg0; u8 pixelpipe_cfg1; u8 pixelpipe_cfg2; u8 video_clk2_m; u8 video_clk2_n; u8 video_clk2_mn_msbs; u8 video_clk2_div_sel; u8 pll_cntl; u8 address_mapping; u8 io_cntl; u8 bitblt_cntl; u8 ext_vert_total; u8 ext_vert_disp_end; u8 ext_vert_sync_start; u8 ext_vert_blank_start; u8 ext_horiz_total; u8 ext_horiz_blank; u8 ext_offset; u8 interlace_cntl; u32 lmi_fifo_watermark; u8 ext_start_addr; u8 ext_start_addr_hi; }; #define DACSPEED8 203 #define DACSPEED16 163 #define DACSPEED24_SG 136 #define DACSPEED24_SD 128 #define DACSPEED32 86 static const struct fb_fix_screeninfo i740fb_fix = { .id = "i740fb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .xpanstep = 8, .ypanstep = 1, .accel = FB_ACCEL_NONE, }; static inline void i740outb(struct i740fb_par *par, u16 port, u8 val) { vga_mm_w(par->regs, port, val); } static inline u8 i740inb(struct i740fb_par *par, u16 port) { return vga_mm_r(par->regs, port); } static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val) { vga_mm_w_fast(par->regs, port, reg, val); } static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg) { vga_mm_w(par->regs, port, reg); return vga_mm_r(par->regs, port+1); } static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg, u8 val, u8 mask) { vga_mm_w_fast(par->regs, port, reg, (val & mask) | (i740inreg(par, port, reg) & ~mask)); } #define REG_DDC_DRIVE 0x62 #define REG_DDC_STATE 0x63 #define DDC_SCL (1 << 3) #define DDC_SDA (1 << 2) static void i740fb_ddc_setscl(void *data, int val) { struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL); i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL); } static void i740fb_ddc_setsda(void *data, int val) { struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA); i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA); } static int i740fb_ddc_getscl(void *data) { struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL); return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL); } static int i740fb_ddc_getsda(void *data) { struct i740fb_par *par = data; i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA); return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA); } static int i740fb_setup_ddc_bus(struct fb_info *info) { struct i740fb_par *par = info->par; strscpy(par->ddc_adapter.name, info->fix.id, sizeof(par->ddc_adapter.name)); par->ddc_adapter.owner = THIS_MODULE; par->ddc_adapter.algo_data = &par->ddc_algo; par->ddc_adapter.dev.parent = info->device; par->ddc_algo.setsda = i740fb_ddc_setsda; par->ddc_algo.setscl = i740fb_ddc_setscl; par->ddc_algo.getsda = i740fb_ddc_getsda; par->ddc_algo.getscl = i740fb_ddc_getscl; par->ddc_algo.udelay = 10; par->ddc_algo.timeout = 20; par->ddc_algo.data = par; i2c_set_adapdata(&par->ddc_adapter, par); return i2c_bit_add_bus(&par->ddc_adapter); } static int i740fb_open(struct fb_info *info, int user) { struct i740fb_par *par = info->par; mutex_lock(&(par->open_lock)); par->ref_count++; mutex_unlock(&(par->open_lock)); return 0; } static int i740fb_release(struct fb_info *info, int user) { struct i740fb_par *par = info->par; mutex_lock(&(par->open_lock)); if (par->ref_count == 0) { fb_err(info, "release called with zero refcount\n"); mutex_unlock(&(par->open_lock)); return -EINVAL; } par->ref_count--; mutex_unlock(&(par->open_lock)); return 0; } static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp) { /* * Would like to calculate these values automatically, but a generic * algorithm does not seem possible. Note: These FIFO water mark * values were tested on several cards and seem to eliminate the * all of the snow and vertical banding, but fine adjustments will * probably be required for other cards. */ u32 wm; switch (bpp) { case 8: if (freq > 200) wm = 0x18120000; else if (freq > 175) wm = 0x16110000; else if (freq > 135) wm = 0x120E0000; else wm = 0x100D0000; break; case 15: case 16: if (par->has_sgram) { if (freq > 140) wm = 0x2C1D0000; else if (freq > 120) wm = 0x2C180000; else if (freq > 100) wm = 0x24160000; else if (freq > 90) wm = 0x18120000; else if (freq > 50) wm = 0x16110000; else if (freq > 32) wm = 0x13100000; else wm = 0x120E0000; } else { if (freq > 160) wm = 0x28200000; else if (freq > 140) wm = 0x2A1E0000; else if (freq > 130) wm = 0x2B1A0000; else if (freq > 120) wm = 0x2C180000; else if (freq > 100) wm = 0x24180000; else if (freq > 90) wm = 0x18120000; else if (freq > 50) wm = 0x16110000; else if (freq > 32) wm = 0x13100000; else wm = 0x120E0000; } break; case 24: if (par->has_sgram) { if (freq > 130) wm = 0x31200000; else if (freq > 120) wm = 0x2E200000; else if (freq > 100) wm = 0x2C1D0000; else if (freq > 80) wm = 0x25180000; else if (freq > 64) wm = 0x24160000; else if (freq > 49) wm = 0x18120000; else if (freq > 32) wm = 0x16110000; else wm = 0x13100000; } else { if (freq > 120) wm = 0x311F0000; else if (freq > 100) wm = 0x2C1D0000; else if (freq > 80) wm = 0x25180000; else if (freq > 64) wm = 0x24160000; else if (freq > 49) wm = 0x18120000; else if (freq > 32) wm = 0x16110000; else wm = 0x13100000; } break; case 32: if (par->has_sgram) { if (freq > 80) wm = 0x2A200000; else if (freq > 60) wm = 0x281A0000; else if (freq > 49) wm = 0x25180000; else if (freq > 32) wm = 0x18120000; else wm = 0x16110000; } else { if (freq > 80) wm = 0x29200000; else if (freq > 60) wm = 0x281A0000; else if (freq > 49) wm = 0x25180000; else if (freq > 32) wm = 0x18120000; else wm = 0x16110000; } break; } return wm; } /* clock calculation from i740fb by Patrick LERDA */ #define I740_RFREQ 1000000 #define TARGET_MAX_N 30 #define I740_FFIX (1 << 8) #define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX) #define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */ #define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */ static void i740_calc_vclk(u32 freq, struct i740fb_par *par) { const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX); const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX); u32 err_best = 512 * I740_FFIX; u32 f_err, f_vco; int m_best = 0, n_best = 0, p_best = 0; int m, n; p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX))); f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX; freq = freq / I740_RFREQ_FIX; n = 2; do { n++; m = ((f_vco * n) / I740_REF_FREQ + 2) / 4; if (m < 3) m = 3; { u32 f_out = (((m * I740_REF_FREQ * 4) / n) + ((1 << p_best) / 2)) / (1 << p_best); f_err = (freq - f_out); if (abs(f_err) < err_max) { m_best = m; n_best = n; err_best = f_err; } } } while ((abs(f_err) >= err_target) && ((n <= TARGET_MAX_N) || (abs(err_best) > err_max))); if (abs(f_err) < err_target) { m_best = m; n_best = n; } par->video_clk2_m = (m_best - 2) & 0xFF; par->video_clk2_n = (n_best - 2) & 0xFF; par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS) | (((m_best - 2) >> 8) & VCO_M_MSBS)); par->video_clk2_div_sel = ((p_best << 4) | REF_DIV_1); } static int i740fb_decode_var(const struct fb_var_screeninfo *var, struct i740fb_par *par, struct fb_info *info) { /* * Get the video params out of 'var'. * If a value doesn't fit, round it up, if it's too big, return -EINVAL. */ u32 xres, right, hslen, left, xtotal; u32 yres, lower, vslen, upper, ytotal; u32 vxres, xoffset, vyres, yoffset; u32 bpp, base, dacspeed24, mem, freq; u8 r7; int i; dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n", var->xres, var->yres, var->xres_virtual, var->xres_virtual); dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n", var->xoffset, var->yoffset, var->bits_per_pixel, var->grayscale); dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n", var->activate, var->nonstd, var->vmode); dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n", var->pixclock, var->hsync_len, var->vsync_len); dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n", var->left_margin, var->right_margin, var->upper_margin, var->lower_margin); bpp = var->bits_per_pixel; switch (bpp) { case 1 ... 8: bpp = 8; if ((1000000 / var->pixclock) > DACSPEED8) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n", 1000000 / var->pixclock, DACSPEED8); return -EINVAL; } break; case 9 ... 15: bpp = 15; fallthrough; case 16: if ((1000000 / var->pixclock) > DACSPEED16) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", 1000000 / var->pixclock, DACSPEED16); return -EINVAL; } break; case 17 ... 24: bpp = 24; dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD; if ((1000000 / var->pixclock) > dacspeed24) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n", 1000000 / var->pixclock, dacspeed24); return -EINVAL; } break; case 25 ... 32: bpp = 32; if ((1000000 / var->pixclock) > DACSPEED32) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n", 1000000 / var->pixclock, DACSPEED32); return -EINVAL; } break; default: return -EINVAL; } xres = ALIGN(var->xres, 8); vxres = ALIGN(var->xres_virtual, 16); if (vxres < xres) vxres = xres; xoffset = ALIGN(var->xoffset, 8); if (xres + xoffset > vxres) xoffset = vxres - xres; left = ALIGN(var->left_margin, 8); right = ALIGN(var->right_margin, 8); hslen = ALIGN(var->hsync_len, 8); yres = var->yres; vyres = var->yres_virtual; if (yres > vyres) vyres = yres; yoffset = var->yoffset; if (yres + yoffset > vyres) yoffset = vyres - yres; lower = var->lower_margin; vslen = var->vsync_len; upper = var->upper_margin; mem = vxres * vyres * ((bpp + 1) / 8); if (mem > info->screen_size) { dev_err(info->device, "not enough video memory (%d KB requested, %ld KB available)\n", mem >> 10, info->screen_size >> 10); return -ENOMEM; } if (yoffset + yres > vyres) yoffset = vyres - yres; xtotal = xres + right + hslen + left; ytotal = yres + lower + vslen + upper;