162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/vt8623fb.c - fbdev driver for 362306a36Sopenharmony_ci * integrated graphic core in VIA VT8623 [CLE266] chipset 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 862306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 962306a36Sopenharmony_ci * more details. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Code is based on s3fb, some parts are from David Boucher's viafb 1262306a36Sopenharmony_ci * (http://davesdomain.org.uk/viafb/) 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/aperture.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/tty.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/fb.h> 2462306a36Sopenharmony_ci#include <linux/svga.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/pci.h> 2762306a36Sopenharmony_ci#include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ 2862306a36Sopenharmony_ci#include <video/vga.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct vt8623fb_info { 3162306a36Sopenharmony_ci char __iomem *mmio_base; 3262306a36Sopenharmony_ci int wc_cookie; 3362306a36Sopenharmony_ci struct vgastate state; 3462306a36Sopenharmony_ci struct mutex open_lock; 3562306a36Sopenharmony_ci unsigned int ref_count; 3662306a36Sopenharmony_ci u32 pseudo_palette[16]; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct svga_fb_format vt8623fb_formats[] = { 4462306a36Sopenharmony_ci { 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 4562306a36Sopenharmony_ci FB_TYPE_TEXT, FB_AUX_TEXT_SVGA_STEP8, FB_VISUAL_PSEUDOCOLOR, 16, 16}, 4662306a36Sopenharmony_ci { 4, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 4762306a36Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 16, 16}, 4862306a36Sopenharmony_ci { 4, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 1, 4962306a36Sopenharmony_ci FB_TYPE_INTERLEAVED_PLANES, 1, FB_VISUAL_PSEUDOCOLOR, 16, 16}, 5062306a36Sopenharmony_ci { 8, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 5162306a36Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 8}, 5262306a36Sopenharmony_ci/* {16, {10, 5, 0}, {5, 5, 0}, {0, 5, 0}, {0, 0, 0}, 0, 5362306a36Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 4, 4}, */ 5462306a36Sopenharmony_ci {16, {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, 0, 5562306a36Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 4, 4}, 5662306a36Sopenharmony_ci {32, {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 5762306a36Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 2, 2}, 5862306a36Sopenharmony_ci SVGA_FORMAT_END 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct svga_pll vt8623_pll = {2, 127, 2, 7, 0, 3, 6262306a36Sopenharmony_ci 60000, 300000, 14318}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* CRT timing register sets */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_total_regs[] = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END}; 6762306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_display_regs[] = {{0x01, 0, 7}, VGA_REGSET_END}; 6862306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END}; 6962306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END}; 7062306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_sync_start_regs[] = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END}; 7162306a36Sopenharmony_cistatic const struct vga_regset vt8623_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END}; 7462306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END}; 7562306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END}; 7662306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; 7762306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END}; 7862306a36Sopenharmony_cistatic const struct vga_regset vt8623_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic const struct vga_regset vt8623_offset_regs[] = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END}; 8162306a36Sopenharmony_cistatic const struct vga_regset vt8623_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END}; 8262306a36Sopenharmony_cistatic const struct vga_regset vt8623_fetch_count_regs[] = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END}; 8362306a36Sopenharmony_cistatic const struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct svga_timing_regs vt8623_timing_regs = { 8662306a36Sopenharmony_ci vt8623_h_total_regs, vt8623_h_display_regs, vt8623_h_blank_start_regs, 8762306a36Sopenharmony_ci vt8623_h_blank_end_regs, vt8623_h_sync_start_regs, vt8623_h_sync_end_regs, 8862306a36Sopenharmony_ci vt8623_v_total_regs, vt8623_v_display_regs, vt8623_v_blank_start_regs, 8962306a36Sopenharmony_ci vt8623_v_blank_end_regs, vt8623_v_sync_start_regs, vt8623_v_sync_end_regs, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Module parameters */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic char *mode_option = "640x480-8@60"; 9962306a36Sopenharmony_cistatic int mtrr = 1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciMODULE_AUTHOR("(c) 2006 Ondrej Zajicek <santiago@crfreenet.org>"); 10262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 10362306a36Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for integrated graphics core in VIA VT8623 [CLE266]"); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cimodule_param(mode_option, charp, 0644); 10662306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); 10762306a36Sopenharmony_cimodule_param_named(mode, mode_option, charp, 0); 10862306a36Sopenharmony_ciMODULE_PARM_DESC(mode, "Default video mode e.g. '648x480-8@60' (deprecated)"); 10962306a36Sopenharmony_cimodule_param(mtrr, int, 0444); 11062306a36Sopenharmony_ciMODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void vt8623fb_tilecursor(struct fb_info *info, struct fb_tilecursor *cursor) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci svga_tilecursor(par->state.vgabase, info, cursor); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct fb_tile_ops vt8623fb_tile_ops = { 12362306a36Sopenharmony_ci .fb_settile = svga_settile, 12462306a36Sopenharmony_ci .fb_tilecopy = svga_tilecopy, 12562306a36Sopenharmony_ci .fb_tilefill = svga_tilefill, 12662306a36Sopenharmony_ci .fb_tileblit = svga_tileblit, 12762306a36Sopenharmony_ci .fb_tilecursor = vt8623fb_tilecursor, 12862306a36Sopenharmony_ci .fb_get_tilemax = svga_get_tilemax, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* image data is MSB-first, fb structure is MSB-first too */ 13662306a36Sopenharmony_cistatic inline u32 expand_color(u32 c) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci return ((c & 1) | ((c & 2) << 7) | ((c & 4) << 14) | ((c & 8) << 21)) * 0xFF; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* vt8623fb_iplan_imageblit silently assumes that almost everything is 8-pixel aligned */ 14262306a36Sopenharmony_cistatic void vt8623fb_iplan_imageblit(struct fb_info *info, const struct fb_image *image) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 fg = expand_color(image->fg_color); 14562306a36Sopenharmony_ci u32 bg = expand_color(image->bg_color); 14662306a36Sopenharmony_ci const u8 *src1, *src; 14762306a36Sopenharmony_ci u8 __iomem *dst1; 14862306a36Sopenharmony_ci u32 __iomem *dst; 14962306a36Sopenharmony_ci u32 val; 15062306a36Sopenharmony_ci int x, y; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci src1 = image->data; 15362306a36Sopenharmony_ci dst1 = info->screen_base + (image->dy * info->fix.line_length) 15462306a36Sopenharmony_ci + ((image->dx / 8) * 4); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci for (y = 0; y < image->height; y++) { 15762306a36Sopenharmony_ci src = src1; 15862306a36Sopenharmony_ci dst = (u32 __iomem *) dst1; 15962306a36Sopenharmony_ci for (x = 0; x < image->width; x += 8) { 16062306a36Sopenharmony_ci val = *(src++) * 0x01010101; 16162306a36Sopenharmony_ci val = (val & fg) | (~val & bg); 16262306a36Sopenharmony_ci fb_writel(val, dst++); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci src1 += image->width / 8; 16562306a36Sopenharmony_ci dst1 += info->fix.line_length; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* vt8623fb_iplan_fillrect silently assumes that almost everything is 8-pixel aligned */ 17062306a36Sopenharmony_cistatic void vt8623fb_iplan_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci u32 fg = expand_color(rect->color); 17362306a36Sopenharmony_ci u8 __iomem *dst1; 17462306a36Sopenharmony_ci u32 __iomem *dst; 17562306a36Sopenharmony_ci int x, y; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci dst1 = info->screen_base + (rect->dy * info->fix.line_length) 17862306a36Sopenharmony_ci + ((rect->dx / 8) * 4); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci for (y = 0; y < rect->height; y++) { 18162306a36Sopenharmony_ci dst = (u32 __iomem *) dst1; 18262306a36Sopenharmony_ci for (x = 0; x < rect->width; x += 8) { 18362306a36Sopenharmony_ci fb_writel(fg, dst++); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci dst1 += info->fix.line_length; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* image data is MSB-first, fb structure is high-nibble-in-low-byte-first */ 19162306a36Sopenharmony_cistatic inline u32 expand_pixel(u32 c) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return (((c & 1) << 24) | ((c & 2) << 27) | ((c & 4) << 14) | ((c & 8) << 17) | 19462306a36Sopenharmony_ci ((c & 16) << 4) | ((c & 32) << 7) | ((c & 64) >> 6) | ((c & 128) >> 3)) * 0xF; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* vt8623fb_cfb4_imageblit silently assumes that almost everything is 8-pixel aligned */ 19862306a36Sopenharmony_cistatic void vt8623fb_cfb4_imageblit(struct fb_info *info, const struct fb_image *image) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u32 fg = image->fg_color * 0x11111111; 20162306a36Sopenharmony_ci u32 bg = image->bg_color * 0x11111111; 20262306a36Sopenharmony_ci const u8 *src1, *src; 20362306a36Sopenharmony_ci u8 __iomem *dst1; 20462306a36Sopenharmony_ci u32 __iomem *dst; 20562306a36Sopenharmony_ci u32 val; 20662306a36Sopenharmony_ci int x, y; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci src1 = image->data; 20962306a36Sopenharmony_ci dst1 = info->screen_base + (image->dy * info->fix.line_length) 21062306a36Sopenharmony_ci + ((image->dx / 8) * 4); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (y = 0; y < image->height; y++) { 21362306a36Sopenharmony_ci src = src1; 21462306a36Sopenharmony_ci dst = (u32 __iomem *) dst1; 21562306a36Sopenharmony_ci for (x = 0; x < image->width; x += 8) { 21662306a36Sopenharmony_ci val = expand_pixel(*(src++)); 21762306a36Sopenharmony_ci val = (val & fg) | (~val & bg); 21862306a36Sopenharmony_ci fb_writel(val, dst++); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci src1 += image->width / 8; 22162306a36Sopenharmony_ci dst1 += info->fix.line_length; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void vt8623fb_imageblit(struct fb_info *info, const struct fb_image *image) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if ((info->var.bits_per_pixel == 4) && (image->depth == 1) 22862306a36Sopenharmony_ci && ((image->width % 8) == 0) && ((image->dx % 8) == 0)) { 22962306a36Sopenharmony_ci if (info->fix.type == FB_TYPE_INTERLEAVED_PLANES) 23062306a36Sopenharmony_ci vt8623fb_iplan_imageblit(info, image); 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci vt8623fb_cfb4_imageblit(info, image); 23362306a36Sopenharmony_ci } else 23462306a36Sopenharmony_ci cfb_imageblit(info, image); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void vt8623fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci if ((info->var.bits_per_pixel == 4) 24062306a36Sopenharmony_ci && ((rect->width % 8) == 0) && ((rect->dx % 8) == 0) 24162306a36Sopenharmony_ci && (info->fix.type == FB_TYPE_INTERLEAVED_PLANES)) 24262306a36Sopenharmony_ci vt8623fb_iplan_fillrect(info, rect); 24362306a36Sopenharmony_ci else 24462306a36Sopenharmony_ci cfb_fillrect(info, rect); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void vt8623_set_pixclock(struct fb_info *info, u32 pixclock) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 25462306a36Sopenharmony_ci u16 m, n, r; 25562306a36Sopenharmony_ci u8 regval; 25662306a36Sopenharmony_ci int rv; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rv = svga_compute_pll(&vt8623_pll, 1000000000 / pixclock, &m, &n, &r, info->node); 25962306a36Sopenharmony_ci if (rv < 0) { 26062306a36Sopenharmony_ci fb_err(info, "cannot set requested pixclock, keeping old value\n"); 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Set VGA misc register */ 26562306a36Sopenharmony_ci regval = vga_r(par->state.vgabase, VGA_MIS_R); 26662306a36Sopenharmony_ci vga_w(par->state.vgabase, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Set clock registers */ 26962306a36Sopenharmony_ci vga_wseq(par->state.vgabase, 0x46, (n | (r << 6))); 27062306a36Sopenharmony_ci vga_wseq(par->state.vgabase, 0x47, m); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci udelay(1000); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* PLL reset */ 27562306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x40, 0x02, 0x02); 27662306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x40, 0x00, 0x02); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int vt8623fb_open(struct fb_info *info, int user) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mutex_lock(&(par->open_lock)); 28562306a36Sopenharmony_ci if (par->ref_count == 0) { 28662306a36Sopenharmony_ci void __iomem *vgabase = par->state.vgabase; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci memset(&(par->state), 0, sizeof(struct vgastate)); 28962306a36Sopenharmony_ci par->state.vgabase = vgabase; 29062306a36Sopenharmony_ci par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS | VGA_SAVE_CMAP; 29162306a36Sopenharmony_ci par->state.num_crtc = 0xA2; 29262306a36Sopenharmony_ci par->state.num_seq = 0x50; 29362306a36Sopenharmony_ci save_vga(&(par->state)); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci par->ref_count++; 29762306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int vt8623fb_release(struct fb_info *info, int user) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci mutex_lock(&(par->open_lock)); 30762306a36Sopenharmony_ci if (par->ref_count == 0) { 30862306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (par->ref_count == 1) 31362306a36Sopenharmony_ci restore_vga(&(par->state)); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci par->ref_count--; 31662306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int vt8623fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int rv, mem, step; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!var->pixclock) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Find appropriate format */ 32962306a36Sopenharmony_ci rv = svga_match_format (vt8623fb_formats, var, NULL); 33062306a36Sopenharmony_ci if (rv < 0) 33162306a36Sopenharmony_ci { 33262306a36Sopenharmony_ci fb_err(info, "unsupported mode requested\n"); 33362306a36Sopenharmony_ci return rv; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Do not allow to have real resoulution larger than virtual */ 33762306a36Sopenharmony_ci if (var->xres > var->xres_virtual) 33862306a36Sopenharmony_ci var->xres_virtual = var->xres; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (var->yres > var->yres_virtual) 34162306a36Sopenharmony_ci var->yres_virtual = var->yres; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Round up xres_virtual to have proper alignment of lines */ 34462306a36Sopenharmony_ci step = vt8623fb_formats[rv].xresstep - 1; 34562306a36Sopenharmony_ci var->xres_virtual = (var->xres_virtual+step) & ~step; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Check whether have enough memory */ 34862306a36Sopenharmony_ci mem = ((var->bits_per_pixel * var->xres_virtual) >> 3) * var->yres_virtual; 34962306a36Sopenharmony_ci if (mem > info->screen_size) 35062306a36Sopenharmony_ci { 35162306a36Sopenharmony_ci fb_err(info, "not enough framebuffer memory (%d kB requested, %d kB available)\n", 35262306a36Sopenharmony_ci mem >> 10, (unsigned int) (info->screen_size >> 10)); 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Text mode is limited to 256 kB of memory */ 35762306a36Sopenharmony_ci if ((var->bits_per_pixel == 0) && (mem > (256*1024))) 35862306a36Sopenharmony_ci { 35962306a36Sopenharmony_ci fb_err(info, "text framebuffer size too large (%d kB requested, 256 kB possible)\n", 36062306a36Sopenharmony_ci mem >> 10); 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci rv = svga_check_timings (&vt8623_timing_regs, var, info->node); 36562306a36Sopenharmony_ci if (rv < 0) 36662306a36Sopenharmony_ci { 36762306a36Sopenharmony_ci fb_err(info, "invalid timings requested\n"); 36862306a36Sopenharmony_ci return rv; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Interlaced mode not supported */ 37262306a36Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int vt8623fb_set_par(struct fb_info *info) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci u32 mode, offset_value, fetch_value, screen_size; 38262306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 38362306a36Sopenharmony_ci u32 bpp = info->var.bits_per_pixel; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (bpp != 0) { 38662306a36Sopenharmony_ci info->fix.ypanstep = 1; 38762306a36Sopenharmony_ci info->fix.line_length = (info->var.xres_virtual * bpp) / 8; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci info->flags &= ~FBINFO_MISC_TILEBLITTING; 39062306a36Sopenharmony_ci info->tileops = NULL; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ 39362306a36Sopenharmony_ci info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); 39462306a36Sopenharmony_ci info->pixmap.blit_y = ~(u32)0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci offset_value = (info->var.xres_virtual * bpp) / 64; 39762306a36Sopenharmony_ci fetch_value = ((info->var.xres * bpp) / 128) + 4; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (bpp == 4) 40062306a36Sopenharmony_ci fetch_value = (info->var.xres / 8) + 8; /* + 0 is OK */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci screen_size = info->var.yres_virtual * info->fix.line_length; 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci info->fix.ypanstep = 16; 40562306a36Sopenharmony_ci info->fix.line_length = 0; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci info->flags |= FBINFO_MISC_TILEBLITTING; 40862306a36Sopenharmony_ci info->tileops = &vt8623fb_tile_ops; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* supports 8x16 tiles only */ 41162306a36Sopenharmony_ci info->pixmap.blit_x = 1 << (8 - 1); 41262306a36Sopenharmony_ci info->pixmap.blit_y = 1 << (16 - 1); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci offset_value = info->var.xres_virtual / 16; 41562306a36Sopenharmony_ci fetch_value = (info->var.xres / 8) + 8; 41662306a36Sopenharmony_ci screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci info->var.xoffset = 0; 42062306a36Sopenharmony_ci info->var.yoffset = 0; 42162306a36Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Unlock registers */ 42462306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x10, 0x01, 0x01); 42562306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x80); 42662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x47, 0x00, 0x01); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Device, screen and sync off */ 42962306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 43062306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x30, 0x30); 43162306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x00, 0x80); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Set default values */ 43462306a36Sopenharmony_ci svga_set_default_gfx_regs(par->state.vgabase); 43562306a36Sopenharmony_ci svga_set_default_atc_regs(par->state.vgabase); 43662306a36Sopenharmony_ci svga_set_default_seq_regs(par->state.vgabase); 43762306a36Sopenharmony_ci svga_set_default_crt_regs(par->state.vgabase); 43862306a36Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, vt8623_line_compare_regs, 0xFFFFFFFF); 43962306a36Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, vt8623_start_address_regs, 0); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, vt8623_offset_regs, offset_value); 44262306a36Sopenharmony_ci svga_wseq_multi(par->state.vgabase, vt8623_fetch_count_regs, fetch_value); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Clear H/V Skew */ 44562306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x03, 0x00, 0x60); 44662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x05, 0x00, 0x60); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (info->var.vmode & FB_VMODE_DOUBLE) 44962306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x09, 0x80, 0x80); 45062306a36Sopenharmony_ci else 45162306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x09, 0x00, 0x80); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x1E, 0xF0, 0xF0); // DI/DVP bus 45462306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x2A, 0x0F, 0x0F); // DI/DVP bus 45562306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x16, 0x08, 0xBF); // FIFO read threshold 45662306a36Sopenharmony_ci vga_wseq(par->state.vgabase, 0x17, 0x1F); // FIFO depth 45762306a36Sopenharmony_ci vga_wseq(par->state.vgabase, 0x18, 0x4E); 45862306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x1A, 0x08, 0x08); // enable MMIO ? 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci vga_wcrt(par->state.vgabase, 0x32, 0x00); 46162306a36Sopenharmony_ci vga_wcrt(par->state.vgabase, 0x34, 0x00); 46262306a36Sopenharmony_ci vga_wcrt(par->state.vgabase, 0x6A, 0x80); 46362306a36Sopenharmony_ci vga_wcrt(par->state.vgabase, 0x6A, 0xC0); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci vga_wgfx(par->state.vgabase, 0x20, 0x00); 46662306a36Sopenharmony_ci vga_wgfx(par->state.vgabase, 0x21, 0x00); 46762306a36Sopenharmony_ci vga_wgfx(par->state.vgabase, 0x22, 0x00); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Set SR15 according to number of bits per pixel */ 47062306a36Sopenharmony_ci mode = svga_match_format(vt8623fb_formats, &(info->var), &(info->fix)); 47162306a36Sopenharmony_ci switch (mode) { 47262306a36Sopenharmony_ci case 0: 47362306a36Sopenharmony_ci fb_dbg(info, "text mode\n"); 47462306a36Sopenharmony_ci svga_set_textmode_vga_regs(par->state.vgabase); 47562306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0x00, 0xFE); 47662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x11, 0x60, 0x70); 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci case 1: 47962306a36Sopenharmony_ci fb_dbg(info, "4 bit pseudocolor\n"); 48062306a36Sopenharmony_ci vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); 48162306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0x20, 0xFE); 48262306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x70); 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case 2: 48562306a36Sopenharmony_ci fb_dbg(info, "4 bit pseudocolor, planar\n"); 48662306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0x00, 0xFE); 48762306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x70); 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci case 3: 49062306a36Sopenharmony_ci fb_dbg(info, "8 bit pseudocolor\n"); 49162306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0x22, 0xFE); 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case 4: 49462306a36Sopenharmony_ci fb_dbg(info, "5/6/5 truecolor\n"); 49562306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0xB6, 0xFE); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case 5: 49862306a36Sopenharmony_ci fb_dbg(info, "8/8/8 truecolor\n"); 49962306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x15, 0xAE, 0xFE); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci default: 50262306a36Sopenharmony_ci printk(KERN_ERR "vt8623fb: unsupported mode - bug\n"); 50362306a36Sopenharmony_ci return (-EINVAL); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci vt8623_set_pixclock(info, info->var.pixclock); 50762306a36Sopenharmony_ci svga_set_timings(par->state.vgabase, &vt8623_timing_regs, &(info->var), 1, 1, 50862306a36Sopenharmony_ci (info->var.vmode & FB_VMODE_DOUBLE) ? 2 : 1, 1, 50962306a36Sopenharmony_ci 1, info->node); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (screen_size > info->screen_size) 51262306a36Sopenharmony_ci screen_size = info->screen_size; 51362306a36Sopenharmony_ci memset_io(info->screen_base, 0x00, screen_size); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Device and screen back on */ 51662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80); 51762306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30); 51862306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int vt8623fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 52562306a36Sopenharmony_ci u_int transp, struct fb_info *fb) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci switch (fb->var.bits_per_pixel) { 52862306a36Sopenharmony_ci case 0: 52962306a36Sopenharmony_ci case 4: 53062306a36Sopenharmony_ci if (regno >= 16) 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci outb(0x0F, VGA_PEL_MSK); 53462306a36Sopenharmony_ci outb(regno, VGA_PEL_IW); 53562306a36Sopenharmony_ci outb(red >> 10, VGA_PEL_D); 53662306a36Sopenharmony_ci outb(green >> 10, VGA_PEL_D); 53762306a36Sopenharmony_ci outb(blue >> 10, VGA_PEL_D); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci case 8: 54062306a36Sopenharmony_ci if (regno >= 256) 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci outb(0xFF, VGA_PEL_MSK); 54462306a36Sopenharmony_ci outb(regno, VGA_PEL_IW); 54562306a36Sopenharmony_ci outb(red >> 10, VGA_PEL_D); 54662306a36Sopenharmony_ci outb(green >> 10, VGA_PEL_D); 54762306a36Sopenharmony_ci outb(blue >> 10, VGA_PEL_D); 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci case 16: 55062306a36Sopenharmony_ci if (regno >= 16) 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (fb->var.green.length == 5) 55462306a36Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = ((red & 0xF800) >> 1) | 55562306a36Sopenharmony_ci ((green & 0xF800) >> 6) | ((blue & 0xF800) >> 11); 55662306a36Sopenharmony_ci else if (fb->var.green.length == 6) 55762306a36Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = (red & 0xF800) | 55862306a36Sopenharmony_ci ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11); 55962306a36Sopenharmony_ci else 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci case 24: 56362306a36Sopenharmony_ci case 32: 56462306a36Sopenharmony_ci if (regno >= 16) 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* ((transp & 0xFF00) << 16) */ 56862306a36Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = ((red & 0xFF00) << 8) | 56962306a36Sopenharmony_ci (green & 0xFF00) | ((blue & 0xFF00) >> 8); 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci default: 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int vt8623fb_blank(int blank_mode, struct fb_info *info) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci switch (blank_mode) { 58462306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 58562306a36Sopenharmony_ci fb_dbg(info, "unblank\n"); 58662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30); 58762306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci case FB_BLANK_NORMAL: 59062306a36Sopenharmony_ci fb_dbg(info, "blank\n"); 59162306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x00, 0x30); 59262306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 59562306a36Sopenharmony_ci fb_dbg(info, "DPMS standby (hsync off)\n"); 59662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x10, 0x30); 59762306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 60062306a36Sopenharmony_ci fb_dbg(info, "DPMS suspend (vsync off)\n"); 60162306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x20, 0x30); 60262306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 60562306a36Sopenharmony_ci fb_dbg(info, "DPMS off (no sync)\n"); 60662306a36Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x36, 0x30, 0x30); 60762306a36Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int vt8623fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 61862306a36Sopenharmony_ci unsigned int offset; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Calculate the offset */ 62162306a36Sopenharmony_ci if (info->var.bits_per_pixel == 0) { 62262306a36Sopenharmony_ci offset = (var->yoffset / 16) * info->var.xres_virtual 62362306a36Sopenharmony_ci + var->xoffset; 62462306a36Sopenharmony_ci offset = offset >> 3; 62562306a36Sopenharmony_ci } else { 62662306a36Sopenharmony_ci offset = (var->yoffset * info->fix.line_length) + 62762306a36Sopenharmony_ci (var->xoffset * info->var.bits_per_pixel / 8); 62862306a36Sopenharmony_ci offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 1); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Set the offset */ 63262306a36Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, vt8623_start_address_regs, offset); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/* Frame buffer operations */ 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic const struct fb_ops vt8623fb_ops = { 64462306a36Sopenharmony_ci .owner = THIS_MODULE, 64562306a36Sopenharmony_ci .fb_open = vt8623fb_open, 64662306a36Sopenharmony_ci .fb_release = vt8623fb_release, 64762306a36Sopenharmony_ci .fb_check_var = vt8623fb_check_var, 64862306a36Sopenharmony_ci .fb_set_par = vt8623fb_set_par, 64962306a36Sopenharmony_ci .fb_setcolreg = vt8623fb_setcolreg, 65062306a36Sopenharmony_ci .fb_blank = vt8623fb_blank, 65162306a36Sopenharmony_ci .fb_pan_display = vt8623fb_pan_display, 65262306a36Sopenharmony_ci .fb_fillrect = vt8623fb_fillrect, 65362306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 65462306a36Sopenharmony_ci .fb_imageblit = vt8623fb_imageblit, 65562306a36Sopenharmony_ci .fb_get_caps = svga_get_caps, 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/* PCI probe */ 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int vt8623_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct pci_bus_region bus_reg; 66462306a36Sopenharmony_ci struct resource vga_res; 66562306a36Sopenharmony_ci struct fb_info *info; 66662306a36Sopenharmony_ci struct vt8623fb_info *par; 66762306a36Sopenharmony_ci unsigned int memsize1, memsize2; 66862306a36Sopenharmony_ci int rc; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Ignore secondary VGA device because there is no VGA arbitration */ 67162306a36Sopenharmony_ci if (! svga_primary_device(dev)) { 67262306a36Sopenharmony_ci dev_info(&(dev->dev), "ignoring secondary device\n"); 67362306a36Sopenharmony_ci return -ENODEV; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci rc = aperture_remove_conflicting_pci_devices(dev, "vt8623fb"); 67762306a36Sopenharmony_ci if (rc) 67862306a36Sopenharmony_ci return rc; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Allocate and fill driver data structure */ 68162306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct vt8623fb_info), &(dev->dev)); 68262306a36Sopenharmony_ci if (!info) 68362306a36Sopenharmony_ci return -ENOMEM; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci par = info->par; 68662306a36Sopenharmony_ci mutex_init(&par->open_lock); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN; 68962306a36Sopenharmony_ci info->fbops = &vt8623fb_ops; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* Prepare PCI device */ 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci rc = pci_enable_device(dev); 69462306a36Sopenharmony_ci if (rc < 0) { 69562306a36Sopenharmony_ci dev_err(info->device, "cannot enable PCI device\n"); 69662306a36Sopenharmony_ci goto err_enable_device; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci rc = pci_request_regions(dev, "vt8623fb"); 70062306a36Sopenharmony_ci if (rc < 0) { 70162306a36Sopenharmony_ci dev_err(info->device, "cannot reserve framebuffer region\n"); 70262306a36Sopenharmony_ci goto err_request_regions; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci info->fix.smem_start = pci_resource_start(dev, 0); 70662306a36Sopenharmony_ci info->fix.smem_len = pci_resource_len(dev, 0); 70762306a36Sopenharmony_ci info->fix.mmio_start = pci_resource_start(dev, 1); 70862306a36Sopenharmony_ci info->fix.mmio_len = pci_resource_len(dev, 1); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Map physical IO memory address into kernel space */ 71162306a36Sopenharmony_ci info->screen_base = pci_iomap_wc(dev, 0, 0); 71262306a36Sopenharmony_ci if (! info->screen_base) { 71362306a36Sopenharmony_ci rc = -ENOMEM; 71462306a36Sopenharmony_ci dev_err(info->device, "iomap for framebuffer failed\n"); 71562306a36Sopenharmony_ci goto err_iomap_1; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci par->mmio_base = pci_iomap(dev, 1, 0); 71962306a36Sopenharmony_ci if (! par->mmio_base) { 72062306a36Sopenharmony_ci rc = -ENOMEM; 72162306a36Sopenharmony_ci dev_err(info->device, "iomap for MMIO failed\n"); 72262306a36Sopenharmony_ci goto err_iomap_2; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci bus_reg.start = 0; 72662306a36Sopenharmony_ci bus_reg.end = 64 * 1024; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci vga_res.flags = IORESOURCE_IO; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci par->state.vgabase = (void __iomem *) (unsigned long) vga_res.start; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Find how many physical memory there is on card */ 73562306a36Sopenharmony_ci memsize1 = (vga_rseq(par->state.vgabase, 0x34) + 1) >> 1; 73662306a36Sopenharmony_ci memsize2 = vga_rseq(par->state.vgabase, 0x39) << 2; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if ((16 <= memsize1) && (memsize1 <= 64) && (memsize1 == memsize2)) 73962306a36Sopenharmony_ci info->screen_size = memsize1 << 20; 74062306a36Sopenharmony_ci else { 74162306a36Sopenharmony_ci dev_err(info->device, "memory size detection failed (%x %x), suppose 16 MB\n", memsize1, memsize2); 74262306a36Sopenharmony_ci info->screen_size = 16 << 20; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci info->fix.smem_len = info->screen_size; 74662306a36Sopenharmony_ci strcpy(info->fix.id, "VIA VT8623"); 74762306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 74862306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 74962306a36Sopenharmony_ci info->fix.ypanstep = 0; 75062306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 75162306a36Sopenharmony_ci info->pseudo_palette = (void*)par->pseudo_palette; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Prepare startup mode */ 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci kernel_param_lock(THIS_MODULE); 75662306a36Sopenharmony_ci rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); 75762306a36Sopenharmony_ci kernel_param_unlock(THIS_MODULE); 75862306a36Sopenharmony_ci if (! ((rc == 1) || (rc == 2))) { 75962306a36Sopenharmony_ci rc = -EINVAL; 76062306a36Sopenharmony_ci dev_err(info->device, "mode %s not found\n", mode_option); 76162306a36Sopenharmony_ci goto err_find_mode; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci rc = fb_alloc_cmap(&info->cmap, 256, 0); 76562306a36Sopenharmony_ci if (rc < 0) { 76662306a36Sopenharmony_ci dev_err(info->device, "cannot allocate colormap\n"); 76762306a36Sopenharmony_ci goto err_alloc_cmap; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci rc = register_framebuffer(info); 77162306a36Sopenharmony_ci if (rc < 0) { 77262306a36Sopenharmony_ci dev_err(info->device, "cannot register framebuffer\n"); 77362306a36Sopenharmony_ci goto err_reg_fb; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci fb_info(info, "%s on %s, %d MB RAM\n", 77762306a36Sopenharmony_ci info->fix.id, pci_name(dev), info->fix.smem_len >> 20); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Record a reference to the driver data */ 78062306a36Sopenharmony_ci pci_set_drvdata(dev, info); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (mtrr) 78362306a36Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 78462306a36Sopenharmony_ci info->fix.smem_len); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Error handling */ 78962306a36Sopenharmony_cierr_reg_fb: 79062306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 79162306a36Sopenharmony_cierr_alloc_cmap: 79262306a36Sopenharmony_cierr_find_mode: 79362306a36Sopenharmony_ci pci_iounmap(dev, par->mmio_base); 79462306a36Sopenharmony_cierr_iomap_2: 79562306a36Sopenharmony_ci pci_iounmap(dev, info->screen_base); 79662306a36Sopenharmony_cierr_iomap_1: 79762306a36Sopenharmony_ci pci_release_regions(dev); 79862306a36Sopenharmony_cierr_request_regions: 79962306a36Sopenharmony_ci/* pci_disable_device(dev); */ 80062306a36Sopenharmony_cierr_enable_device: 80162306a36Sopenharmony_ci framebuffer_release(info); 80262306a36Sopenharmony_ci return rc; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/* PCI remove */ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void vt8623_pci_remove(struct pci_dev *dev) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(dev); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (info) { 81262306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 81562306a36Sopenharmony_ci unregister_framebuffer(info); 81662306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci pci_iounmap(dev, info->screen_base); 81962306a36Sopenharmony_ci pci_iounmap(dev, par->mmio_base); 82062306a36Sopenharmony_ci pci_release_regions(dev); 82162306a36Sopenharmony_ci/* pci_disable_device(dev); */ 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci framebuffer_release(info); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci/* PCI suspend */ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int __maybe_unused vt8623_pci_suspend(struct device *dev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 83362306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci dev_info(info->device, "suspend\n"); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci console_lock(); 83862306a36Sopenharmony_ci mutex_lock(&(par->open_lock)); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (par->ref_count == 0) { 84162306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 84262306a36Sopenharmony_ci console_unlock(); 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci fb_set_suspend(info, 1); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 84962306a36Sopenharmony_ci console_unlock(); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/* PCI resume */ 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int __maybe_unused vt8623_pci_resume(struct device *dev) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 86062306a36Sopenharmony_ci struct vt8623fb_info *par = info->par; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci dev_info(info->device, "resume\n"); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci console_lock(); 86562306a36Sopenharmony_ci mutex_lock(&(par->open_lock)); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (par->ref_count == 0) 86862306a36Sopenharmony_ci goto fail; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci vt8623fb_set_par(info); 87162306a36Sopenharmony_ci fb_set_suspend(info, 0); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cifail: 87462306a36Sopenharmony_ci mutex_unlock(&(par->open_lock)); 87562306a36Sopenharmony_ci console_unlock(); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return 0; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic const struct dev_pm_ops vt8623_pci_pm_ops = { 88162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 88262306a36Sopenharmony_ci .suspend = vt8623_pci_suspend, 88362306a36Sopenharmony_ci .resume = vt8623_pci_resume, 88462306a36Sopenharmony_ci .freeze = NULL, 88562306a36Sopenharmony_ci .thaw = vt8623_pci_resume, 88662306a36Sopenharmony_ci .poweroff = vt8623_pci_suspend, 88762306a36Sopenharmony_ci .restore = vt8623_pci_resume, 88862306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 88962306a36Sopenharmony_ci}; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci/* List of boards that we are trying to support */ 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic const struct pci_device_id vt8623_devices[] = { 89462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_VIA, 0x3122)}, 89562306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vt8623_devices); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic struct pci_driver vt8623fb_pci_driver = { 90162306a36Sopenharmony_ci .name = "vt8623fb", 90262306a36Sopenharmony_ci .id_table = vt8623_devices, 90362306a36Sopenharmony_ci .probe = vt8623_pci_probe, 90462306a36Sopenharmony_ci .remove = vt8623_pci_remove, 90562306a36Sopenharmony_ci .driver.pm = &vt8623_pci_pm_ops, 90662306a36Sopenharmony_ci}; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci/* Cleanup */ 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void __exit vt8623fb_cleanup(void) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci pr_debug("vt8623fb: cleaning up\n"); 91362306a36Sopenharmony_ci pci_unregister_driver(&vt8623fb_pci_driver); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/* Driver Initialisation */ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int __init vt8623fb_init(void) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci#ifndef MODULE 92262306a36Sopenharmony_ci char *option = NULL; 92362306a36Sopenharmony_ci#endif 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (fb_modesetting_disabled("vt8623fb")) 92662306a36Sopenharmony_ci return -ENODEV; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci#ifndef MODULE 92962306a36Sopenharmony_ci if (fb_get_options("vt8623fb", &option)) 93062306a36Sopenharmony_ci return -ENODEV; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (option && *option) 93362306a36Sopenharmony_ci mode_option = option; 93462306a36Sopenharmony_ci#endif 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci pr_debug("vt8623fb: initializing\n"); 93762306a36Sopenharmony_ci return pci_register_driver(&vt8623fb_pci_driver); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* Modularization */ 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cimodule_init(vt8623fb_init); 94562306a36Sopenharmony_cimodule_exit(vt8623fb_cleanup); 946