162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1995 Jay Estabrook 562306a36Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 662306a36Sopenharmony_ci * Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha 762306a36Sopenharmony_ci * Copyright (C) 2002 Richard Henderson 862306a36Sopenharmony_ci * Copyright (C) 2006, 2007 Maciej W. Rozycki 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1162306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 1262306a36Sopenharmony_ci * more details. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/aperture.h> 1662306a36Sopenharmony_ci#include <linux/bitrev.h> 1762306a36Sopenharmony_ci#include <linux/compiler.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/fb.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/mm.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/pci.h> 2862306a36Sopenharmony_ci#include <linux/selection.h> 2962306a36Sopenharmony_ci#include <linux/string.h> 3062306a36Sopenharmony_ci#include <linux/tc.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <asm/io.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <video/tgafb.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#ifdef CONFIG_TC 3762306a36Sopenharmony_ci#define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type) 3862306a36Sopenharmony_ci#else 3962306a36Sopenharmony_ci#define TGA_BUS_TC(dev) 0 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Local functions. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int tgafb_check_var(struct fb_var_screeninfo *, struct fb_info *); 4762306a36Sopenharmony_cistatic int tgafb_set_par(struct fb_info *); 4862306a36Sopenharmony_cistatic void tgafb_set_pll(struct tga_par *, int); 4962306a36Sopenharmony_cistatic int tgafb_setcolreg(unsigned, unsigned, unsigned, unsigned, 5062306a36Sopenharmony_ci unsigned, struct fb_info *); 5162306a36Sopenharmony_cistatic int tgafb_blank(int, struct fb_info *); 5262306a36Sopenharmony_cistatic void tgafb_init_fix(struct fb_info *); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void tgafb_imageblit(struct fb_info *, const struct fb_image *); 5562306a36Sopenharmony_cistatic void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *); 5662306a36Sopenharmony_cistatic void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *); 5762306a36Sopenharmony_cistatic int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int tgafb_register(struct device *dev); 6062306a36Sopenharmony_cistatic void tgafb_unregister(struct device *dev); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const char *mode_option; 6362306a36Sopenharmony_cistatic const char *mode_option_pci = "640x480@60"; 6462306a36Sopenharmony_cistatic const char *mode_option_tc = "1280x1024@72"; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic struct pci_driver tgafb_pci_driver; 6862306a36Sopenharmony_cistatic struct tc_driver tgafb_tc_driver; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Frame buffer operations 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic const struct fb_ops tgafb_ops = { 7562306a36Sopenharmony_ci .owner = THIS_MODULE, 7662306a36Sopenharmony_ci .fb_check_var = tgafb_check_var, 7762306a36Sopenharmony_ci .fb_set_par = tgafb_set_par, 7862306a36Sopenharmony_ci .fb_setcolreg = tgafb_setcolreg, 7962306a36Sopenharmony_ci .fb_blank = tgafb_blank, 8062306a36Sopenharmony_ci .fb_pan_display = tgafb_pan_display, 8162306a36Sopenharmony_ci .fb_fillrect = tgafb_fillrect, 8262306a36Sopenharmony_ci .fb_copyarea = tgafb_copyarea, 8362306a36Sopenharmony_ci .fb_imageblit = tgafb_imageblit, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#ifdef CONFIG_PCI 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * PCI registration operations 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic int tgafb_pci_register(struct pci_dev *, const struct pci_device_id *); 9262306a36Sopenharmony_cistatic void tgafb_pci_unregister(struct pci_dev *); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct pci_device_id const tgafb_pci_table[] = { 9562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) }, 9662306a36Sopenharmony_ci { } 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tgafb_pci_table); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct pci_driver tgafb_pci_driver = { 10162306a36Sopenharmony_ci .name = "tgafb", 10262306a36Sopenharmony_ci .id_table = tgafb_pci_table, 10362306a36Sopenharmony_ci .probe = tgafb_pci_register, 10462306a36Sopenharmony_ci .remove = tgafb_pci_unregister, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int tgafb_pci_register(struct pci_dev *pdev, 10862306a36Sopenharmony_ci const struct pci_device_id *ent) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = aperture_remove_conflicting_pci_devices(pdev, "tgafb"); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return tgafb_register(&pdev->dev); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void tgafb_pci_unregister(struct pci_dev *pdev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci tgafb_unregister(&pdev->dev); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#ifdef CONFIG_TC 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * TC registration operations 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic int tgafb_tc_register(struct device *); 13062306a36Sopenharmony_cistatic int tgafb_tc_unregister(struct device *); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct tc_device_id const tgafb_tc_table[] = { 13362306a36Sopenharmony_ci { "DEC ", "PMAGD-AA" }, 13462306a36Sopenharmony_ci { "DEC ", "PMAGD " }, 13562306a36Sopenharmony_ci { } 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tc, tgafb_tc_table); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct tc_driver tgafb_tc_driver = { 14062306a36Sopenharmony_ci .id_table = tgafb_tc_table, 14162306a36Sopenharmony_ci .driver = { 14262306a36Sopenharmony_ci .name = "tgafb", 14362306a36Sopenharmony_ci .bus = &tc_bus_type, 14462306a36Sopenharmony_ci .probe = tgafb_tc_register, 14562306a36Sopenharmony_ci .remove = tgafb_tc_unregister, 14662306a36Sopenharmony_ci }, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int tgafb_tc_register(struct device *dev) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int status = tgafb_register(dev); 15262306a36Sopenharmony_ci if (!status) 15362306a36Sopenharmony_ci get_device(dev); 15462306a36Sopenharmony_ci return status; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int tgafb_tc_unregister(struct device *dev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci put_device(dev); 16062306a36Sopenharmony_ci tgafb_unregister(dev); 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci#endif /* CONFIG_TC */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/** 16762306a36Sopenharmony_ci * tgafb_check_var - Optional function. Validates a var passed in. 16862306a36Sopenharmony_ci * @var: frame buffer variable screen structure 16962306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int 17262306a36Sopenharmony_citgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *)info->par; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!var->pixclock) 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (par->tga_type == TGA_TYPE_8PLANE) { 18062306a36Sopenharmony_ci if (var->bits_per_pixel != 8) 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci if (var->bits_per_pixel != 32) 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci var->red.length = var->green.length = var->blue.length = 8; 18762306a36Sopenharmony_ci if (var->bits_per_pixel == 32) { 18862306a36Sopenharmony_ci var->red.offset = 16; 18962306a36Sopenharmony_ci var->green.offset = 8; 19062306a36Sopenharmony_ci var->blue.offset = 0; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (var->xres_virtual != var->xres || var->yres_virtual != var->yres) 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len) 19662306a36Sopenharmony_ci return -EINVAL; 19762306a36Sopenharmony_ci if (var->nonstd) 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ) 20062306a36Sopenharmony_ci return -EINVAL; 20162306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Some of the acceleration routines assume the line width is 20562306a36Sopenharmony_ci a multiple of 8 bytes. */ 20662306a36Sopenharmony_ci if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 8) 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * tgafb_set_par - Optional function. Alters the hardware state. 21462306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int 21762306a36Sopenharmony_citgafb_set_par(struct fb_info *info) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci static unsigned int const deep_presets[4] = { 22062306a36Sopenharmony_ci 0x00004000, 22162306a36Sopenharmony_ci 0x0000440d, 22262306a36Sopenharmony_ci 0xffffffff, 22362306a36Sopenharmony_ci 0x0000441d 22462306a36Sopenharmony_ci }; 22562306a36Sopenharmony_ci static unsigned int const rasterop_presets[4] = { 22662306a36Sopenharmony_ci 0x00000003, 22762306a36Sopenharmony_ci 0x00000303, 22862306a36Sopenharmony_ci 0xffffffff, 22962306a36Sopenharmony_ci 0x00000303 23062306a36Sopenharmony_ci }; 23162306a36Sopenharmony_ci static unsigned int const mode_presets[4] = { 23262306a36Sopenharmony_ci 0x00000000, 23362306a36Sopenharmony_ci 0x00000300, 23462306a36Sopenharmony_ci 0xffffffff, 23562306a36Sopenharmony_ci 0x00000300 23662306a36Sopenharmony_ci }; 23762306a36Sopenharmony_ci static unsigned int const base_addr_presets[4] = { 23862306a36Sopenharmony_ci 0x00000000, 23962306a36Sopenharmony_ci 0x00000001, 24062306a36Sopenharmony_ci 0xffffffff, 24162306a36Sopenharmony_ci 0x00000001 24262306a36Sopenharmony_ci }; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 24562306a36Sopenharmony_ci int tga_bus_pci = dev_is_pci(par->dev); 24662306a36Sopenharmony_ci int tga_bus_tc = TGA_BUS_TC(par->dev); 24762306a36Sopenharmony_ci u32 htimings, vtimings, pll_freq; 24862306a36Sopenharmony_ci u8 tga_type; 24962306a36Sopenharmony_ci int i; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Encode video timings. */ 25262306a36Sopenharmony_ci htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB) 25362306a36Sopenharmony_ci | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB)); 25462306a36Sopenharmony_ci vtimings = (info->var.yres & TGA_VERT_ACTIVE); 25562306a36Sopenharmony_ci htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP; 25662306a36Sopenharmony_ci vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP; 25762306a36Sopenharmony_ci htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC; 25862306a36Sopenharmony_ci vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC; 25962306a36Sopenharmony_ci htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP; 26062306a36Sopenharmony_ci vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) 26362306a36Sopenharmony_ci htimings |= TGA_HORIZ_POLARITY; 26462306a36Sopenharmony_ci if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) 26562306a36Sopenharmony_ci vtimings |= TGA_VERT_POLARITY; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci par->htimings = htimings; 26862306a36Sopenharmony_ci par->vtimings = vtimings; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Store other useful values in par. */ 27362306a36Sopenharmony_ci par->xres = info->var.xres; 27462306a36Sopenharmony_ci par->yres = info->var.yres; 27562306a36Sopenharmony_ci par->pll_freq = pll_freq = 1000000000 / info->var.pixclock; 27662306a36Sopenharmony_ci par->bits_per_pixel = info->var.bits_per_pixel; 27762306a36Sopenharmony_ci info->fix.line_length = par->xres * (par->bits_per_pixel >> 3); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci tga_type = par->tga_type; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* First, disable video. */ 28262306a36Sopenharmony_ci TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Write the DEEP register. */ 28562306a36Sopenharmony_ci while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */ 28662306a36Sopenharmony_ci continue; 28762306a36Sopenharmony_ci mb(); 28862306a36Sopenharmony_ci TGA_WRITE_REG(par, deep_presets[tga_type] | 28962306a36Sopenharmony_ci (par->sync_on_green ? 0x0 : 0x00010000), 29062306a36Sopenharmony_ci TGA_DEEP_REG); 29162306a36Sopenharmony_ci while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */ 29262306a36Sopenharmony_ci continue; 29362306a36Sopenharmony_ci mb(); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Write some more registers. */ 29662306a36Sopenharmony_ci TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG); 29762306a36Sopenharmony_ci TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG); 29862306a36Sopenharmony_ci TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Calculate & write the PLL. */ 30162306a36Sopenharmony_ci tgafb_set_pll(par, pll_freq); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Write some more registers. */ 30462306a36Sopenharmony_ci TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG); 30562306a36Sopenharmony_ci TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Init video timing regs. */ 30862306a36Sopenharmony_ci TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG); 30962306a36Sopenharmony_ci TGA_WRITE_REG(par, vtimings, TGA_VERT_REG); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Initialise RAMDAC. */ 31262306a36Sopenharmony_ci if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) { 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Init BT485 RAMDAC registers. */ 31562306a36Sopenharmony_ci BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0), 31662306a36Sopenharmony_ci BT485_CMD_0); 31762306a36Sopenharmony_ci BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE); 31862306a36Sopenharmony_ci BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */ 31962306a36Sopenharmony_ci BT485_WRITE(par, 0x40, BT485_CMD_1); 32062306a36Sopenharmony_ci BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */ 32162306a36Sopenharmony_ci BT485_WRITE(par, 0xff, BT485_PIXEL_MASK); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Fill palette registers. */ 32462306a36Sopenharmony_ci BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE); 32562306a36Sopenharmony_ci TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = 0; i < 256 * 3; i += 4) { 32862306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8), 32962306a36Sopenharmony_ci TGA_RAMDAC_REG); 33062306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8), 33162306a36Sopenharmony_ci TGA_RAMDAC_REG); 33262306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8), 33362306a36Sopenharmony_ci TGA_RAMDAC_REG); 33462306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8), 33562306a36Sopenharmony_ci TGA_RAMDAC_REG); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci } else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) { 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Init BT459 RAMDAC registers. */ 34162306a36Sopenharmony_ci BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40); 34262306a36Sopenharmony_ci BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00); 34362306a36Sopenharmony_ci BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2, 34462306a36Sopenharmony_ci (par->sync_on_green ? 0xc0 : 0x40)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Fill the palette. */ 34962306a36Sopenharmony_ci BT459_LOAD_ADDR(par, 0x0000); 35062306a36Sopenharmony_ci TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci for (i = 0; i < 256 * 3; i += 4) { 35362306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG); 35462306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 35562306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 35662306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci } else { /* 24-plane or 24plusZ */ 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Init BT463 RAMDAC registers. */ 36262306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40); 36362306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08); 36462306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2, 36562306a36Sopenharmony_ci (par->sync_on_green ? 0xc0 : 0x40)); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff); 36862306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff); 36962306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff); 37062306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00); 37362306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00); 37462306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00); 37562306a36Sopenharmony_ci BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Fill the palette. */ 37862306a36Sopenharmony_ci BT463_LOAD_ADDR(par, 0x0000); 37962306a36Sopenharmony_ci TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci#ifdef CONFIG_HW_CONSOLE 38262306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 38362306a36Sopenharmony_ci int j = color_table[i]; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG); 38662306a36Sopenharmony_ci TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG); 38762306a36Sopenharmony_ci TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci for (i = 0; i < 512 * 3; i += 4) { 39062306a36Sopenharmony_ci#else 39162306a36Sopenharmony_ci for (i = 0; i < 528 * 3; i += 4) { 39262306a36Sopenharmony_ci#endif 39362306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG); 39462306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 39562306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 39662306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Fill window type table after start of vertical retrace. */ 40062306a36Sopenharmony_ci while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01)) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG); 40362306a36Sopenharmony_ci mb(); 40462306a36Sopenharmony_ci while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01)) 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE); 40962306a36Sopenharmony_ci TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 41262306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 41362306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG); 41462306a36Sopenharmony_ci TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Finally, enable video scan (and pray for the monitor... :-) */ 42062306a36Sopenharmony_ci TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci#define DIFFCHECK(X) \ 42662306a36Sopenharmony_cido { \ 42762306a36Sopenharmony_ci if (m <= 0x3f) { \ 42862306a36Sopenharmony_ci int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \ 42962306a36Sopenharmony_ci if (delta < 0) \ 43062306a36Sopenharmony_ci delta = -delta; \ 43162306a36Sopenharmony_ci if (delta < min_diff) \ 43262306a36Sopenharmony_ci min_diff = delta, vm = m, va = a, vr = r; \ 43362306a36Sopenharmony_ci } \ 43462306a36Sopenharmony_ci} while (0) 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void 43762306a36Sopenharmony_citgafb_set_pll(struct tga_par *par, int f) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci int n, shift, base, min_diff, target; 44062306a36Sopenharmony_ci int r,a,m,vm = 34, va = 1, vr = 30; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (r = 0 ; r < 12 ; r++) 44362306a36Sopenharmony_ci TGA_WRITE_REG(par, !r, TGA_CLOCK_REG); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (f > TGA_PLL_MAX_FREQ) 44662306a36Sopenharmony_ci f = TGA_PLL_MAX_FREQ; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (f >= TGA_PLL_MAX_FREQ / 2) 44962306a36Sopenharmony_ci shift = 0; 45062306a36Sopenharmony_ci else if (f >= TGA_PLL_MAX_FREQ / 4) 45162306a36Sopenharmony_ci shift = 1; 45262306a36Sopenharmony_ci else 45362306a36Sopenharmony_ci shift = 2; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG); 45662306a36Sopenharmony_ci TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci for (r = 0 ; r < 10 ; r++) 45962306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (f <= 120000) { 46262306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 46362306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci else if (f <= 200000) { 46662306a36Sopenharmony_ci TGA_WRITE_REG(par, 1, TGA_CLOCK_REG); 46762306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci else { 47062306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 47162306a36Sopenharmony_ci TGA_WRITE_REG(par, 1, TGA_CLOCK_REG); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci TGA_WRITE_REG(par, 1, TGA_CLOCK_REG); 47562306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 47662306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 47762306a36Sopenharmony_ci TGA_WRITE_REG(par, 1, TGA_CLOCK_REG); 47862306a36Sopenharmony_ci TGA_WRITE_REG(par, 0, TGA_CLOCK_REG); 47962306a36Sopenharmony_ci TGA_WRITE_REG(par, 1, TGA_CLOCK_REG); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci target = (f << shift) / TGA_PLL_BASE_FREQ; 48262306a36Sopenharmony_ci min_diff = TGA_PLL_MAX_FREQ; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci r = 7 / target; 48562306a36Sopenharmony_ci if (!r) r = 1; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci base = target * r; 48862306a36Sopenharmony_ci while (base < 449) { 48962306a36Sopenharmony_ci for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) { 49062306a36Sopenharmony_ci m = ((n + 3) / 7) - 1; 49162306a36Sopenharmony_ci a = 0; 49262306a36Sopenharmony_ci DIFFCHECK((m + 1) * 7); 49362306a36Sopenharmony_ci m++; 49462306a36Sopenharmony_ci DIFFCHECK((m + 1) * 7); 49562306a36Sopenharmony_ci m = (n / 6) - 1; 49662306a36Sopenharmony_ci if ((a = n % 6)) 49762306a36Sopenharmony_ci DIFFCHECK(n); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci r++; 50062306a36Sopenharmony_ci base += target; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci vr--; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (r = 0; r < 8; r++) 50662306a36Sopenharmony_ci TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG); 50762306a36Sopenharmony_ci for (r = 0; r < 8 ; r++) 50862306a36Sopenharmony_ci TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG); 50962306a36Sopenharmony_ci for (r = 0; r < 7 ; r++) 51062306a36Sopenharmony_ci TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG); 51162306a36Sopenharmony_ci TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/** 51662306a36Sopenharmony_ci * tgafb_setcolreg - Optional function. Sets a color register. 51762306a36Sopenharmony_ci * @regno: boolean, 0 copy local, 1 get_user() function 51862306a36Sopenharmony_ci * @red: frame buffer colormap structure 51962306a36Sopenharmony_ci * @green: The green value which can be up to 16 bits wide 52062306a36Sopenharmony_ci * @blue: The blue value which can be up to 16 bits wide. 52162306a36Sopenharmony_ci * @transp: If supported the alpha value which can be up to 16 bits wide. 52262306a36Sopenharmony_ci * @info: frame buffer info structure 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_cistatic int 52562306a36Sopenharmony_citgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, 52662306a36Sopenharmony_ci unsigned transp, struct fb_info *info) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 52962306a36Sopenharmony_ci int tga_bus_pci = dev_is_pci(par->dev); 53062306a36Sopenharmony_ci int tga_bus_tc = TGA_BUS_TC(par->dev); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (regno > 255) 53362306a36Sopenharmony_ci return 1; 53462306a36Sopenharmony_ci red >>= 8; 53562306a36Sopenharmony_ci green >>= 8; 53662306a36Sopenharmony_ci blue >>= 8; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) { 53962306a36Sopenharmony_ci BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE); 54062306a36Sopenharmony_ci TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG); 54162306a36Sopenharmony_ci TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); 54262306a36Sopenharmony_ci TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); 54362306a36Sopenharmony_ci TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); 54462306a36Sopenharmony_ci } else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) { 54562306a36Sopenharmony_ci BT459_LOAD_ADDR(par, regno); 54662306a36Sopenharmony_ci TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG); 54762306a36Sopenharmony_ci TGA_WRITE_REG(par, red, TGA_RAMDAC_REG); 54862306a36Sopenharmony_ci TGA_WRITE_REG(par, green, TGA_RAMDAC_REG); 54962306a36Sopenharmony_ci TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG); 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci if (regno < 16) { 55262306a36Sopenharmony_ci u32 value = (regno << 16) | (regno << 8) | regno; 55362306a36Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = value; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci BT463_LOAD_ADDR(par, regno); 55662306a36Sopenharmony_ci TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG); 55762306a36Sopenharmony_ci TGA_WRITE_REG(par, red, TGA_RAMDAC_REG); 55862306a36Sopenharmony_ci TGA_WRITE_REG(par, green, TGA_RAMDAC_REG); 55962306a36Sopenharmony_ci TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/** 56762306a36Sopenharmony_ci * tgafb_blank - Optional function. Blanks the display. 56862306a36Sopenharmony_ci * @blank: the blank mode we want. 56962306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_cistatic int 57262306a36Sopenharmony_citgafb_blank(int blank, struct fb_info *info) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 57562306a36Sopenharmony_ci u32 vhcr, vvcr, vvvr; 57662306a36Sopenharmony_ci unsigned long flags; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci local_irq_save(flags); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci vhcr = TGA_READ_REG(par, TGA_HORIZ_REG); 58162306a36Sopenharmony_ci vvcr = TGA_READ_REG(par, TGA_VERT_REG); 58262306a36Sopenharmony_ci vvvr = TGA_READ_REG(par, TGA_VALID_REG); 58362306a36Sopenharmony_ci vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci switch (blank) { 58662306a36Sopenharmony_ci case FB_BLANK_UNBLANK: /* Unblanking */ 58762306a36Sopenharmony_ci if (par->vesa_blanked) { 58862306a36Sopenharmony_ci TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG); 58962306a36Sopenharmony_ci TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG); 59062306a36Sopenharmony_ci par->vesa_blanked = 0; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG); 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci case FB_BLANK_NORMAL: /* Normal blanking */ 59662306a36Sopenharmony_ci TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK, 59762306a36Sopenharmony_ci TGA_VALID_REG); 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ 60162306a36Sopenharmony_ci TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG); 60262306a36Sopenharmony_ci TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG); 60362306a36Sopenharmony_ci par->vesa_blanked = 1; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ 60762306a36Sopenharmony_ci TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG); 60862306a36Sopenharmony_ci TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG); 60962306a36Sopenharmony_ci par->vesa_blanked = 1; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: /* Poweroff */ 61362306a36Sopenharmony_ci TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG); 61462306a36Sopenharmony_ci TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG); 61562306a36Sopenharmony_ci TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG); 61662306a36Sopenharmony_ci par->vesa_blanked = 1; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci local_irq_restore(flags); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/* 62662306a36Sopenharmony_ci * Acceleration. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void 63062306a36Sopenharmony_citgafb_mono_imageblit(struct fb_info *info, const struct fb_image *image) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 63362306a36Sopenharmony_ci u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask; 63462306a36Sopenharmony_ci unsigned long rincr, line_length, shift, pos, is8bpp; 63562306a36Sopenharmony_ci unsigned long i, j; 63662306a36Sopenharmony_ci const unsigned char *data; 63762306a36Sopenharmony_ci void __iomem *regs_base; 63862306a36Sopenharmony_ci void __iomem *fb_base; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci is8bpp = info->var.bits_per_pixel == 8; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci dx = image->dx; 64362306a36Sopenharmony_ci dy = image->dy; 64462306a36Sopenharmony_ci width = image->width; 64562306a36Sopenharmony_ci height = image->height; 64662306a36Sopenharmony_ci vxres = info->var.xres_virtual; 64762306a36Sopenharmony_ci vyres = info->var.yres_virtual; 64862306a36Sopenharmony_ci line_length = info->fix.line_length; 64962306a36Sopenharmony_ci rincr = (width + 7) / 8; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* A shift below cannot cope with. */ 65262306a36Sopenharmony_ci if (unlikely(width == 0)) 65362306a36Sopenharmony_ci return; 65462306a36Sopenharmony_ci /* Crop the image to the screen. */ 65562306a36Sopenharmony_ci if (dx > vxres || dy > vyres) 65662306a36Sopenharmony_ci return; 65762306a36Sopenharmony_ci if (dx + width > vxres) 65862306a36Sopenharmony_ci width = vxres - dx; 65962306a36Sopenharmony_ci if (dy + height > vyres) 66062306a36Sopenharmony_ci height = vyres - dy; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci regs_base = par->tga_regs_base; 66362306a36Sopenharmony_ci fb_base = par->tga_fb_base; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* Expand the color values to fill 32-bits. */ 66662306a36Sopenharmony_ci /* ??? Would be nice to notice colour changes elsewhere, so 66762306a36Sopenharmony_ci that we can do this only when necessary. */ 66862306a36Sopenharmony_ci fgcolor = image->fg_color; 66962306a36Sopenharmony_ci bgcolor = image->bg_color; 67062306a36Sopenharmony_ci if (is8bpp) { 67162306a36Sopenharmony_ci fgcolor |= fgcolor << 8; 67262306a36Sopenharmony_ci fgcolor |= fgcolor << 16; 67362306a36Sopenharmony_ci bgcolor |= bgcolor << 8; 67462306a36Sopenharmony_ci bgcolor |= bgcolor << 16; 67562306a36Sopenharmony_ci } else { 67662306a36Sopenharmony_ci if (fgcolor < 16) 67762306a36Sopenharmony_ci fgcolor = ((u32 *)info->pseudo_palette)[fgcolor]; 67862306a36Sopenharmony_ci if (bgcolor < 16) 67962306a36Sopenharmony_ci bgcolor = ((u32 *)info->pseudo_palette)[bgcolor]; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci __raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG); 68262306a36Sopenharmony_ci __raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Acquire proper alignment; set up the PIXELMASK register 68562306a36Sopenharmony_ci so that we only write the proper character cell. */ 68662306a36Sopenharmony_ci pos = dy * line_length; 68762306a36Sopenharmony_ci if (is8bpp) { 68862306a36Sopenharmony_ci pos += dx; 68962306a36Sopenharmony_ci shift = pos & 3; 69062306a36Sopenharmony_ci pos &= -4; 69162306a36Sopenharmony_ci } else { 69262306a36Sopenharmony_ci pos += dx * 4; 69362306a36Sopenharmony_ci shift = (pos & 7) >> 2; 69462306a36Sopenharmony_ci pos &= -8; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci data = (const unsigned char *) image->data; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Enable opaque stipple mode. */ 70062306a36Sopenharmony_ci __raw_writel((is8bpp 70162306a36Sopenharmony_ci ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE 70262306a36Sopenharmony_ci : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE), 70362306a36Sopenharmony_ci regs_base + TGA_MODE_REG); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (width + shift <= 32) { 70662306a36Sopenharmony_ci unsigned long bwidth; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Handle common case of imaging a single character, in 70962306a36Sopenharmony_ci a font less than or 32 pixels wide. */ 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Avoid a shift by 32; width > 0 implied. */ 71262306a36Sopenharmony_ci pixelmask = (2ul << (width - 1)) - 1; 71362306a36Sopenharmony_ci pixelmask <<= shift; 71462306a36Sopenharmony_ci __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG); 71562306a36Sopenharmony_ci wmb(); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci bwidth = (width + 7) / 8; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 72062306a36Sopenharmony_ci u32 mask = 0; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* The image data is bit big endian; we need 72362306a36Sopenharmony_ci little endian. */ 72462306a36Sopenharmony_ci for (j = 0; j < bwidth; ++j) 72562306a36Sopenharmony_ci mask |= bitrev8(data[j]) << (j * 8); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci __raw_writel(mask << shift, fb_base + pos); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci pos += line_length; 73062306a36Sopenharmony_ci data += rincr; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci wmb(); 73362306a36Sopenharmony_ci __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG); 73462306a36Sopenharmony_ci } else if (shift == 0) { 73562306a36Sopenharmony_ci unsigned long pos0 = pos; 73662306a36Sopenharmony_ci const unsigned char *data0 = data; 73762306a36Sopenharmony_ci unsigned long bincr = (is8bpp ? 8 : 8*4); 73862306a36Sopenharmony_ci unsigned long bwidth; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Handle another common case in which accel_putcs 74162306a36Sopenharmony_ci generates a large bitmap, which happens to be aligned. 74262306a36Sopenharmony_ci Allow the tail to be misaligned. This case is 74362306a36Sopenharmony_ci interesting because we've not got to hold partial 74462306a36Sopenharmony_ci bytes across the words being written. */ 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci wmb(); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci bwidth = (width / 8) & -4; 74962306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 75062306a36Sopenharmony_ci for (j = 0; j < bwidth; j += 4) { 75162306a36Sopenharmony_ci u32 mask = 0; 75262306a36Sopenharmony_ci mask |= bitrev8(data[j+0]) << (0 * 8); 75362306a36Sopenharmony_ci mask |= bitrev8(data[j+1]) << (1 * 8); 75462306a36Sopenharmony_ci mask |= bitrev8(data[j+2]) << (2 * 8); 75562306a36Sopenharmony_ci mask |= bitrev8(data[j+3]) << (3 * 8); 75662306a36Sopenharmony_ci __raw_writel(mask, fb_base + pos + j*bincr); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci pos += line_length; 75962306a36Sopenharmony_ci data += rincr; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci wmb(); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci pixelmask = (1ul << (width & 31)) - 1; 76462306a36Sopenharmony_ci if (pixelmask) { 76562306a36Sopenharmony_ci __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG); 76662306a36Sopenharmony_ci wmb(); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci pos = pos0 + bwidth*bincr; 76962306a36Sopenharmony_ci data = data0 + bwidth; 77062306a36Sopenharmony_ci bwidth = ((width & 31) + 7) / 8; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 77362306a36Sopenharmony_ci u32 mask = 0; 77462306a36Sopenharmony_ci for (j = 0; j < bwidth; ++j) 77562306a36Sopenharmony_ci mask |= bitrev8(data[j]) << (j * 8); 77662306a36Sopenharmony_ci __raw_writel(mask, fb_base + pos); 77762306a36Sopenharmony_ci pos += line_length; 77862306a36Sopenharmony_ci data += rincr; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci wmb(); 78162306a36Sopenharmony_ci __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci unsigned long pos0 = pos; 78562306a36Sopenharmony_ci const unsigned char *data0 = data; 78662306a36Sopenharmony_ci unsigned long bincr = (is8bpp ? 8 : 8*4); 78762306a36Sopenharmony_ci unsigned long bwidth; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Finally, handle the generic case of misaligned start. 79062306a36Sopenharmony_ci Here we split the write into 16-bit spans. This allows 79162306a36Sopenharmony_ci us to use only one pixel mask, instead of four as would 79262306a36Sopenharmony_ci be required by writing 24-bit spans. */ 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci pixelmask = 0xffff << shift; 79562306a36Sopenharmony_ci __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG); 79662306a36Sopenharmony_ci wmb(); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci bwidth = (width / 8) & -2; 79962306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 80062306a36Sopenharmony_ci for (j = 0; j < bwidth; j += 2) { 80162306a36Sopenharmony_ci u32 mask = 0; 80262306a36Sopenharmony_ci mask |= bitrev8(data[j+0]) << (0 * 8); 80362306a36Sopenharmony_ci mask |= bitrev8(data[j+1]) << (1 * 8); 80462306a36Sopenharmony_ci mask <<= shift; 80562306a36Sopenharmony_ci __raw_writel(mask, fb_base + pos + j*bincr); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci pos += line_length; 80862306a36Sopenharmony_ci data += rincr; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci wmb(); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci pixelmask = ((1ul << (width & 15)) - 1) << shift; 81362306a36Sopenharmony_ci if (pixelmask) { 81462306a36Sopenharmony_ci __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG); 81562306a36Sopenharmony_ci wmb(); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci pos = pos0 + bwidth*bincr; 81862306a36Sopenharmony_ci data = data0 + bwidth; 81962306a36Sopenharmony_ci bwidth = (width & 15) > 8; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 82262306a36Sopenharmony_ci u32 mask = bitrev8(data[0]); 82362306a36Sopenharmony_ci if (bwidth) 82462306a36Sopenharmony_ci mask |= bitrev8(data[1]) << 8; 82562306a36Sopenharmony_ci mask <<= shift; 82662306a36Sopenharmony_ci __raw_writel(mask, fb_base + pos); 82762306a36Sopenharmony_ci pos += line_length; 82862306a36Sopenharmony_ci data += rincr; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci wmb(); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Disable opaque stipple mode. */ 83662306a36Sopenharmony_ci __raw_writel((is8bpp 83762306a36Sopenharmony_ci ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE 83862306a36Sopenharmony_ci : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE), 83962306a36Sopenharmony_ci regs_base + TGA_MODE_REG); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic void 84362306a36Sopenharmony_citgafb_clut_imageblit(struct fb_info *info, const struct fb_image *image) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 84662306a36Sopenharmony_ci u32 color, dx, dy, width, height, vxres, vyres; 84762306a36Sopenharmony_ci u32 *palette = ((u32 *)info->pseudo_palette); 84862306a36Sopenharmony_ci unsigned long pos, line_length, i, j; 84962306a36Sopenharmony_ci const unsigned char *data; 85062306a36Sopenharmony_ci void __iomem *fb_base; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci dx = image->dx; 85362306a36Sopenharmony_ci dy = image->dy; 85462306a36Sopenharmony_ci width = image->width; 85562306a36Sopenharmony_ci height = image->height; 85662306a36Sopenharmony_ci vxres = info->var.xres_virtual; 85762306a36Sopenharmony_ci vyres = info->var.yres_virtual; 85862306a36Sopenharmony_ci line_length = info->fix.line_length; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* Crop the image to the screen. */ 86162306a36Sopenharmony_ci if (dx > vxres || dy > vyres) 86262306a36Sopenharmony_ci return; 86362306a36Sopenharmony_ci if (dx + width > vxres) 86462306a36Sopenharmony_ci width = vxres - dx; 86562306a36Sopenharmony_ci if (dy + height > vyres) 86662306a36Sopenharmony_ci height = vyres - dy; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci fb_base = par->tga_fb_base; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci pos = dy * line_length + (dx * 4); 87162306a36Sopenharmony_ci data = image->data; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Now copy the image, color_expanding via the palette. */ 87462306a36Sopenharmony_ci for (i = 0; i < height; i++) { 87562306a36Sopenharmony_ci for (j = 0; j < width; j++) { 87662306a36Sopenharmony_ci color = palette[*data++]; 87762306a36Sopenharmony_ci __raw_writel(color, fb_base + pos + j*4); 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci pos += line_length; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/** 88462306a36Sopenharmony_ci * tgafb_imageblit - REQUIRED function. Can use generic routines if 88562306a36Sopenharmony_ci * non acclerated hardware and packed pixel based. 88662306a36Sopenharmony_ci * Copies a image from system memory to the screen. 88762306a36Sopenharmony_ci * 88862306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 88962306a36Sopenharmony_ci * @image: structure defining the image. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_cistatic void 89262306a36Sopenharmony_citgafb_imageblit(struct fb_info *info, const struct fb_image *image) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci unsigned int is8bpp = info->var.bits_per_pixel == 8; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* If a mono image, regardless of FB depth, go do it. */ 89762306a36Sopenharmony_ci if (image->depth == 1) { 89862306a36Sopenharmony_ci tgafb_mono_imageblit(info, image); 89962306a36Sopenharmony_ci return; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* For copies that aren't pixel expansion, there's little we 90362306a36Sopenharmony_ci can do better than the generic code. */ 90462306a36Sopenharmony_ci /* ??? There is a DMA write mode; I wonder if that could be 90562306a36Sopenharmony_ci made to pull the data from the image buffer... */ 90662306a36Sopenharmony_ci if (image->depth == info->var.bits_per_pixel) { 90762306a36Sopenharmony_ci cfb_imageblit(info, image); 90862306a36Sopenharmony_ci return; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* If 24-plane FB and the image is 8-plane with CLUT, we can do it. */ 91262306a36Sopenharmony_ci if (!is8bpp && image->depth == 8) { 91362306a36Sopenharmony_ci tgafb_clut_imageblit(info, image); 91462306a36Sopenharmony_ci return; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Silently return... */ 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/** 92162306a36Sopenharmony_ci * tgafb_fillrect - REQUIRED function. Can use generic routines if 92262306a36Sopenharmony_ci * non acclerated hardware and packed pixel based. 92362306a36Sopenharmony_ci * Draws a rectangle on the screen. 92462306a36Sopenharmony_ci * 92562306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 92662306a36Sopenharmony_ci * @rect: structure defining the rectagle and operation. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_cistatic void 92962306a36Sopenharmony_citgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 93262306a36Sopenharmony_ci int is8bpp = info->var.bits_per_pixel == 8; 93362306a36Sopenharmony_ci u32 dx, dy, width, height, vxres, vyres, color; 93462306a36Sopenharmony_ci unsigned long pos, align, line_length, i, j; 93562306a36Sopenharmony_ci void __iomem *regs_base; 93662306a36Sopenharmony_ci void __iomem *fb_base; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci dx = rect->dx; 93962306a36Sopenharmony_ci dy = rect->dy; 94062306a36Sopenharmony_ci width = rect->width; 94162306a36Sopenharmony_ci height = rect->height; 94262306a36Sopenharmony_ci vxres = info->var.xres_virtual; 94362306a36Sopenharmony_ci vyres = info->var.yres_virtual; 94462306a36Sopenharmony_ci line_length = info->fix.line_length; 94562306a36Sopenharmony_ci regs_base = par->tga_regs_base; 94662306a36Sopenharmony_ci fb_base = par->tga_fb_base; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* Crop the rectangle to the screen. */ 94962306a36Sopenharmony_ci if (dx > vxres || dy > vyres || !width || !height) 95062306a36Sopenharmony_ci return; 95162306a36Sopenharmony_ci if (dx + width > vxres) 95262306a36Sopenharmony_ci width = vxres - dx; 95362306a36Sopenharmony_ci if (dy + height > vyres) 95462306a36Sopenharmony_ci height = vyres - dy; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci pos = dy * line_length + dx * (is8bpp ? 1 : 4); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* ??? We could implement ROP_XOR with opaque fill mode 95962306a36Sopenharmony_ci and a RasterOp setting of GXxor, but as far as I can 96062306a36Sopenharmony_ci tell, this mode is not actually used in the kernel. 96162306a36Sopenharmony_ci Thus I am ignoring it for now. */ 96262306a36Sopenharmony_ci if (rect->rop != ROP_COPY) { 96362306a36Sopenharmony_ci cfb_fillrect(info, rect); 96462306a36Sopenharmony_ci return; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* Expand the color value to fill 8 pixels. */ 96862306a36Sopenharmony_ci color = rect->color; 96962306a36Sopenharmony_ci if (is8bpp) { 97062306a36Sopenharmony_ci color |= color << 8; 97162306a36Sopenharmony_ci color |= color << 16; 97262306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG); 97362306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG); 97462306a36Sopenharmony_ci } else { 97562306a36Sopenharmony_ci if (color < 16) 97662306a36Sopenharmony_ci color = ((u32 *)info->pseudo_palette)[color]; 97762306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG); 97862306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG); 97962306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG); 98062306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG); 98162306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG); 98262306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG); 98362306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG); 98462306a36Sopenharmony_ci __raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* The DATA register holds the fill mask for block fill mode. 98862306a36Sopenharmony_ci Since we're not stippling, this is all ones. */ 98962306a36Sopenharmony_ci __raw_writel(0xffffffff, regs_base + TGA_DATA_REG); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Enable block fill mode. */ 99262306a36Sopenharmony_ci __raw_writel((is8bpp 99362306a36Sopenharmony_ci ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL 99462306a36Sopenharmony_ci : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL), 99562306a36Sopenharmony_ci regs_base + TGA_MODE_REG); 99662306a36Sopenharmony_ci wmb(); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* We can fill 2k pixels per operation. Notice blocks that fit 99962306a36Sopenharmony_ci the width of the screen so that we can take advantage of this 100062306a36Sopenharmony_ci and fill more than one line per write. */ 100162306a36Sopenharmony_ci if (width == line_length) { 100262306a36Sopenharmony_ci width *= height; 100362306a36Sopenharmony_ci height = 1; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* The write into the frame buffer must be aligned to 4 bytes, 100762306a36Sopenharmony_ci but we are allowed to encode the offset within the word in 100862306a36Sopenharmony_ci the data word written. */ 100962306a36Sopenharmony_ci align = (pos & 3) << 16; 101062306a36Sopenharmony_ci pos &= -4; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (width <= 2048) { 101362306a36Sopenharmony_ci u32 data; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci data = (width - 1) | align; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 101862306a36Sopenharmony_ci __raw_writel(data, fb_base + pos); 101962306a36Sopenharmony_ci pos += line_length; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci } else { 102262306a36Sopenharmony_ci unsigned long Bpp = (is8bpp ? 1 : 4); 102362306a36Sopenharmony_ci unsigned long nwidth = width & -2048; 102462306a36Sopenharmony_ci u32 fdata, ldata; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci fdata = (2048 - 1) | align; 102762306a36Sopenharmony_ci ldata = ((width & 2047) - 1) | align; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 103062306a36Sopenharmony_ci for (j = 0; j < nwidth; j += 2048) 103162306a36Sopenharmony_ci __raw_writel(fdata, fb_base + pos + j*Bpp); 103262306a36Sopenharmony_ci if (j < width) 103362306a36Sopenharmony_ci __raw_writel(ldata, fb_base + pos + j*Bpp); 103462306a36Sopenharmony_ci pos += line_length; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci wmb(); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci /* Disable block fill mode. */ 104062306a36Sopenharmony_ci __raw_writel((is8bpp 104162306a36Sopenharmony_ci ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE 104262306a36Sopenharmony_ci : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE), 104362306a36Sopenharmony_ci regs_base + TGA_MODE_REG); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/* 104762306a36Sopenharmony_ci * tgafb_copyarea - REQUIRED function. Can use generic routines if 104862306a36Sopenharmony_ci * non acclerated hardware and packed pixel based. 104962306a36Sopenharmony_ci * Copies on area of the screen to another area. 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 105262306a36Sopenharmony_ci * @area: structure defining the source and destination. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/* Handle the special case of copying entire lines, e.g. during scrolling. 105662306a36Sopenharmony_ci We can avoid a lot of needless computation in this case. In the 8bpp 105762306a36Sopenharmony_ci case we need to use the COPY64 registers instead of mask writes into 105862306a36Sopenharmony_ci the frame buffer to achieve maximum performance. */ 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cistatic inline void 106162306a36Sopenharmony_cicopyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy, 106262306a36Sopenharmony_ci u32 height, u32 width) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 106562306a36Sopenharmony_ci void __iomem *tga_regs = par->tga_regs_base; 106662306a36Sopenharmony_ci unsigned long dpos, spos, i, n64; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Set up the MODE and PIXELSHIFT registers. */ 106962306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG); 107062306a36Sopenharmony_ci __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG); 107162306a36Sopenharmony_ci wmb(); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci n64 = (height * width) / 64; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (sy < dy) { 107662306a36Sopenharmony_ci spos = (sy + height) * width; 107762306a36Sopenharmony_ci dpos = (dy + height) * width; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci for (i = 0; i < n64; ++i) { 108062306a36Sopenharmony_ci spos -= 64; 108162306a36Sopenharmony_ci dpos -= 64; 108262306a36Sopenharmony_ci __raw_writel(spos, tga_regs+TGA_COPY64_SRC); 108362306a36Sopenharmony_ci wmb(); 108462306a36Sopenharmony_ci __raw_writel(dpos, tga_regs+TGA_COPY64_DST); 108562306a36Sopenharmony_ci wmb(); 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci } else { 108862306a36Sopenharmony_ci spos = sy * width; 108962306a36Sopenharmony_ci dpos = dy * width; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci for (i = 0; i < n64; ++i) { 109262306a36Sopenharmony_ci __raw_writel(spos, tga_regs+TGA_COPY64_SRC); 109362306a36Sopenharmony_ci wmb(); 109462306a36Sopenharmony_ci __raw_writel(dpos, tga_regs+TGA_COPY64_DST); 109562306a36Sopenharmony_ci wmb(); 109662306a36Sopenharmony_ci spos += 64; 109762306a36Sopenharmony_ci dpos += 64; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Reset the MODE register to normal. */ 110262306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG); 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic inline void 110662306a36Sopenharmony_cicopyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy, 110762306a36Sopenharmony_ci u32 height, u32 width) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 111062306a36Sopenharmony_ci void __iomem *tga_regs = par->tga_regs_base; 111162306a36Sopenharmony_ci void __iomem *tga_fb = par->tga_fb_base; 111262306a36Sopenharmony_ci void __iomem *src; 111362306a36Sopenharmony_ci void __iomem *dst; 111462306a36Sopenharmony_ci unsigned long i, n16; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* Set up the MODE and PIXELSHIFT registers. */ 111762306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG); 111862306a36Sopenharmony_ci __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG); 111962306a36Sopenharmony_ci wmb(); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci n16 = (height * width) / 16; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (sy < dy) { 112462306a36Sopenharmony_ci src = tga_fb + (sy + height) * width * 4; 112562306a36Sopenharmony_ci dst = tga_fb + (dy + height) * width * 4; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci for (i = 0; i < n16; ++i) { 112862306a36Sopenharmony_ci src -= 64; 112962306a36Sopenharmony_ci dst -= 64; 113062306a36Sopenharmony_ci __raw_writel(0xffff, src); 113162306a36Sopenharmony_ci wmb(); 113262306a36Sopenharmony_ci __raw_writel(0xffff, dst); 113362306a36Sopenharmony_ci wmb(); 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci } else { 113662306a36Sopenharmony_ci src = tga_fb + sy * width * 4; 113762306a36Sopenharmony_ci dst = tga_fb + dy * width * 4; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci for (i = 0; i < n16; ++i) { 114062306a36Sopenharmony_ci __raw_writel(0xffff, src); 114162306a36Sopenharmony_ci wmb(); 114262306a36Sopenharmony_ci __raw_writel(0xffff, dst); 114362306a36Sopenharmony_ci wmb(); 114462306a36Sopenharmony_ci src += 64; 114562306a36Sopenharmony_ci dst += 64; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Reset the MODE register to normal. */ 115062306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci/* The (almost) general case of backward copy in 8bpp mode. */ 115462306a36Sopenharmony_cistatic inline void 115562306a36Sopenharmony_cicopyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy, 115662306a36Sopenharmony_ci u32 height, u32 width, u32 line_length, 115762306a36Sopenharmony_ci const struct fb_copyarea *area) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *) info->par; 116062306a36Sopenharmony_ci unsigned i, yincr; 116162306a36Sopenharmony_ci int depos, sepos, backward, last_step, step; 116262306a36Sopenharmony_ci u32 mask_last; 116362306a36Sopenharmony_ci unsigned n32; 116462306a36Sopenharmony_ci void __iomem *tga_regs; 116562306a36Sopenharmony_ci void __iomem *tga_fb; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* Do acceleration only if we are aligned on 8 pixels */ 116862306a36Sopenharmony_ci if ((dx | sx | width) & 7) { 116962306a36Sopenharmony_ci cfb_copyarea(info, area); 117062306a36Sopenharmony_ci return; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci yincr = line_length; 117462306a36Sopenharmony_ci if (dy > sy) { 117562306a36Sopenharmony_ci dy += height - 1; 117662306a36Sopenharmony_ci sy += height - 1; 117762306a36Sopenharmony_ci yincr = -yincr; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci backward = dy == sy && dx > sx && dx < sx + width; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Compute the offsets and alignments in the frame buffer. 118262306a36Sopenharmony_ci More than anything else, these control how we do copies. */ 118362306a36Sopenharmony_ci depos = dy * line_length + dx; 118462306a36Sopenharmony_ci sepos = sy * line_length + sx; 118562306a36Sopenharmony_ci if (backward) { 118662306a36Sopenharmony_ci depos += width; 118762306a36Sopenharmony_ci sepos += width; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* Next copy full words at a time. */ 119162306a36Sopenharmony_ci n32 = width / 32; 119262306a36Sopenharmony_ci last_step = width % 32; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* Finally copy the unaligned head of the span. */ 119562306a36Sopenharmony_ci mask_last = (1ul << last_step) - 1; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!backward) { 119862306a36Sopenharmony_ci step = 32; 119962306a36Sopenharmony_ci last_step = 32; 120062306a36Sopenharmony_ci } else { 120162306a36Sopenharmony_ci step = -32; 120262306a36Sopenharmony_ci last_step = -last_step; 120362306a36Sopenharmony_ci sepos -= 32; 120462306a36Sopenharmony_ci depos -= 32; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci tga_regs = par->tga_regs_base; 120862306a36Sopenharmony_ci tga_fb = par->tga_fb_base; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* Set up the MODE and PIXELSHIFT registers. */ 121162306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG); 121262306a36Sopenharmony_ci __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG); 121362306a36Sopenharmony_ci wmb(); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci for (i = 0; i < height; ++i) { 121662306a36Sopenharmony_ci unsigned long j; 121762306a36Sopenharmony_ci void __iomem *sfb; 121862306a36Sopenharmony_ci void __iomem *dfb; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci sfb = tga_fb + sepos; 122162306a36Sopenharmony_ci dfb = tga_fb + depos; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci for (j = 0; j < n32; j++) { 122462306a36Sopenharmony_ci if (j < 2 && j + 1 < n32 && !backward && 122562306a36Sopenharmony_ci !(((unsigned long)sfb | (unsigned long)dfb) & 63)) { 122662306a36Sopenharmony_ci do { 122762306a36Sopenharmony_ci __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC); 122862306a36Sopenharmony_ci wmb(); 122962306a36Sopenharmony_ci __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST); 123062306a36Sopenharmony_ci wmb(); 123162306a36Sopenharmony_ci sfb += 64; 123262306a36Sopenharmony_ci dfb += 64; 123362306a36Sopenharmony_ci j += 2; 123462306a36Sopenharmony_ci } while (j + 1 < n32); 123562306a36Sopenharmony_ci j--; 123662306a36Sopenharmony_ci continue; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci __raw_writel(0xffffffff, sfb); 123962306a36Sopenharmony_ci wmb(); 124062306a36Sopenharmony_ci __raw_writel(0xffffffff, dfb); 124162306a36Sopenharmony_ci wmb(); 124262306a36Sopenharmony_ci sfb += step; 124362306a36Sopenharmony_ci dfb += step; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (mask_last) { 124762306a36Sopenharmony_ci sfb += last_step - step; 124862306a36Sopenharmony_ci dfb += last_step - step; 124962306a36Sopenharmony_ci __raw_writel(mask_last, sfb); 125062306a36Sopenharmony_ci wmb(); 125162306a36Sopenharmony_ci __raw_writel(mask_last, dfb); 125262306a36Sopenharmony_ci wmb(); 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci sepos += yincr; 125662306a36Sopenharmony_ci depos += yincr; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* Reset the MODE register to normal. */ 126062306a36Sopenharmony_ci __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG); 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic void 126462306a36Sopenharmony_citgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci unsigned long dx, dy, width, height, sx, sy, vxres, vyres; 126762306a36Sopenharmony_ci unsigned long line_length, bpp; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci dx = area->dx; 127062306a36Sopenharmony_ci dy = area->dy; 127162306a36Sopenharmony_ci width = area->width; 127262306a36Sopenharmony_ci height = area->height; 127362306a36Sopenharmony_ci sx = area->sx; 127462306a36Sopenharmony_ci sy = area->sy; 127562306a36Sopenharmony_ci vxres = info->var.xres_virtual; 127662306a36Sopenharmony_ci vyres = info->var.yres_virtual; 127762306a36Sopenharmony_ci line_length = info->fix.line_length; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci /* The top left corners must be in the virtual screen. */ 128062306a36Sopenharmony_ci if (dx > vxres || sx > vxres || dy > vyres || sy > vyres) 128162306a36Sopenharmony_ci return; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* Clip the destination. */ 128462306a36Sopenharmony_ci if (dx + width > vxres) 128562306a36Sopenharmony_ci width = vxres - dx; 128662306a36Sopenharmony_ci if (dy + height > vyres) 128762306a36Sopenharmony_ci height = vyres - dy; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* The source must be completely inside the virtual screen. */ 129062306a36Sopenharmony_ci if (sx + width > vxres || sy + height > vyres) 129162306a36Sopenharmony_ci return; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci bpp = info->var.bits_per_pixel; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* Detect copies of the entire line. */ 129662306a36Sopenharmony_ci if (!(line_length & 63) && width * (bpp >> 3) == line_length) { 129762306a36Sopenharmony_ci if (bpp == 8) 129862306a36Sopenharmony_ci copyarea_line_8bpp(info, dy, sy, height, width); 129962306a36Sopenharmony_ci else 130062306a36Sopenharmony_ci copyarea_line_32bpp(info, dy, sy, height, width); 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* ??? The documentation is unclear to me exactly how the pixelshift 130462306a36Sopenharmony_ci register works in 32bpp mode. Since I don't have hardware to test, 130562306a36Sopenharmony_ci give up for now and fall back on the generic routines. */ 130662306a36Sopenharmony_ci else if (bpp == 32) 130762306a36Sopenharmony_ci cfb_copyarea(info, area); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci else 131062306a36Sopenharmony_ci copyarea_8bpp(info, dx, dy, sx, sy, height, 131162306a36Sopenharmony_ci width, line_length, area); 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci/* 131662306a36Sopenharmony_ci * Initialisation 131762306a36Sopenharmony_ci */ 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic void 132062306a36Sopenharmony_citgafb_init_fix(struct fb_info *info) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct tga_par *par = (struct tga_par *)info->par; 132362306a36Sopenharmony_ci int tga_bus_pci = dev_is_pci(par->dev); 132462306a36Sopenharmony_ci int tga_bus_tc = TGA_BUS_TC(par->dev); 132562306a36Sopenharmony_ci u8 tga_type = par->tga_type; 132662306a36Sopenharmony_ci const char *tga_type_name = NULL; 132762306a36Sopenharmony_ci unsigned memory_size; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci switch (tga_type) { 133062306a36Sopenharmony_ci case TGA_TYPE_8PLANE: 133162306a36Sopenharmony_ci if (tga_bus_pci) 133262306a36Sopenharmony_ci tga_type_name = "Digital ZLXp-E1"; 133362306a36Sopenharmony_ci if (tga_bus_tc) 133462306a36Sopenharmony_ci tga_type_name = "Digital ZLX-E1"; 133562306a36Sopenharmony_ci memory_size = 2097152; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci case TGA_TYPE_24PLANE: 133862306a36Sopenharmony_ci if (tga_bus_pci) 133962306a36Sopenharmony_ci tga_type_name = "Digital ZLXp-E2"; 134062306a36Sopenharmony_ci if (tga_bus_tc) 134162306a36Sopenharmony_ci tga_type_name = "Digital ZLX-E2"; 134262306a36Sopenharmony_ci memory_size = 8388608; 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci case TGA_TYPE_24PLUSZ: 134562306a36Sopenharmony_ci if (tga_bus_pci) 134662306a36Sopenharmony_ci tga_type_name = "Digital ZLXp-E3"; 134762306a36Sopenharmony_ci if (tga_bus_tc) 134862306a36Sopenharmony_ci tga_type_name = "Digital ZLX-E3"; 134962306a36Sopenharmony_ci memory_size = 16777216; 135062306a36Sopenharmony_ci break; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci if (!tga_type_name) { 135362306a36Sopenharmony_ci tga_type_name = "Unknown"; 135462306a36Sopenharmony_ci memory_size = 16777216; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci strscpy(info->fix.id, tga_type_name, sizeof(info->fix.id)); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 136062306a36Sopenharmony_ci info->fix.type_aux = 0; 136162306a36Sopenharmony_ci info->fix.visual = (tga_type == TGA_TYPE_8PLANE 136262306a36Sopenharmony_ci ? FB_VISUAL_PSEUDOCOLOR 136362306a36Sopenharmony_ci : FB_VISUAL_DIRECTCOLOR); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci info->fix.smem_start = (size_t) par->tga_fb_base; 136662306a36Sopenharmony_ci info->fix.smem_len = memory_size; 136762306a36Sopenharmony_ci info->fix.mmio_start = (size_t) par->tga_regs_base; 136862306a36Sopenharmony_ci info->fix.mmio_len = 512; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci info->fix.xpanstep = 0; 137162306a36Sopenharmony_ci info->fix.ypanstep = 0; 137262306a36Sopenharmony_ci info->fix.ywrapstep = 0; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_DEC_TGA; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* 137762306a36Sopenharmony_ci * These are needed by fb_set_logo_truepalette(), so we 137862306a36Sopenharmony_ci * set them here for 24-plane cards. 137962306a36Sopenharmony_ci */ 138062306a36Sopenharmony_ci if (tga_type != TGA_TYPE_8PLANE) { 138162306a36Sopenharmony_ci info->var.red.length = 8; 138262306a36Sopenharmony_ci info->var.green.length = 8; 138362306a36Sopenharmony_ci info->var.blue.length = 8; 138462306a36Sopenharmony_ci info->var.red.offset = 16; 138562306a36Sopenharmony_ci info->var.green.offset = 8; 138662306a36Sopenharmony_ci info->var.blue.offset = 0; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci /* We just use this to catch switches out of graphics mode. */ 139362306a36Sopenharmony_ci tgafb_set_par(info); /* A bit of overkill for BASE_ADDR reset. */ 139462306a36Sopenharmony_ci return 0; 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic int tgafb_register(struct device *dev) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci static const struct fb_videomode modedb_tc = { 140062306a36Sopenharmony_ci /* 1280x1024 @ 72 Hz, 76.8 kHz hsync */ 140162306a36Sopenharmony_ci "1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3, 140262306a36Sopenharmony_ci FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED 140362306a36Sopenharmony_ci }; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci static unsigned int const fb_offset_presets[4] = { 140662306a36Sopenharmony_ci TGA_8PLANE_FB_OFFSET, 140762306a36Sopenharmony_ci TGA_24PLANE_FB_OFFSET, 140862306a36Sopenharmony_ci 0xffffffff, 140962306a36Sopenharmony_ci TGA_24PLUSZ_FB_OFFSET 141062306a36Sopenharmony_ci }; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci const struct fb_videomode *modedb_tga = NULL; 141362306a36Sopenharmony_ci resource_size_t bar0_start = 0, bar0_len = 0; 141462306a36Sopenharmony_ci const char *mode_option_tga = NULL; 141562306a36Sopenharmony_ci int tga_bus_pci = dev_is_pci(dev); 141662306a36Sopenharmony_ci int tga_bus_tc = TGA_BUS_TC(dev); 141762306a36Sopenharmony_ci unsigned int modedbsize_tga = 0; 141862306a36Sopenharmony_ci void __iomem *mem_base; 141962306a36Sopenharmony_ci struct fb_info *info; 142062306a36Sopenharmony_ci struct tga_par *par; 142162306a36Sopenharmony_ci u8 tga_type; 142262306a36Sopenharmony_ci int ret = 0; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* Enable device in PCI config. */ 142562306a36Sopenharmony_ci if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) { 142662306a36Sopenharmony_ci printk(KERN_ERR "tgafb: Cannot enable PCI device\n"); 142762306a36Sopenharmony_ci return -ENODEV; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* Allocate the fb and par structures. */ 143162306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct tga_par), dev); 143262306a36Sopenharmony_ci if (!info) 143362306a36Sopenharmony_ci return -ENOMEM; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci par = info->par; 143662306a36Sopenharmony_ci dev_set_drvdata(dev, info); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* Request the mem regions. */ 143962306a36Sopenharmony_ci ret = -ENODEV; 144062306a36Sopenharmony_ci if (tga_bus_pci) { 144162306a36Sopenharmony_ci bar0_start = pci_resource_start(to_pci_dev(dev), 0); 144262306a36Sopenharmony_ci bar0_len = pci_resource_len(to_pci_dev(dev), 0); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci if (tga_bus_tc) { 144562306a36Sopenharmony_ci bar0_start = to_tc_dev(dev)->resource.start; 144662306a36Sopenharmony_ci bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci if (!request_mem_region (bar0_start, bar0_len, "tgafb")) { 144962306a36Sopenharmony_ci printk(KERN_ERR "tgafb: cannot reserve FB region\n"); 145062306a36Sopenharmony_ci goto err0; 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* Map the framebuffer. */ 145462306a36Sopenharmony_ci mem_base = ioremap(bar0_start, bar0_len); 145562306a36Sopenharmony_ci if (!mem_base) { 145662306a36Sopenharmony_ci printk(KERN_ERR "tgafb: Cannot map MMIO\n"); 145762306a36Sopenharmony_ci goto err1; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* Grab info about the card. */ 146162306a36Sopenharmony_ci tga_type = (readl(mem_base) >> 12) & 0x0f; 146262306a36Sopenharmony_ci par->dev = dev; 146362306a36Sopenharmony_ci par->tga_mem_base = mem_base; 146462306a36Sopenharmony_ci par->tga_fb_base = mem_base + fb_offset_presets[tga_type]; 146562306a36Sopenharmony_ci par->tga_regs_base = mem_base + TGA_REGS_OFFSET; 146662306a36Sopenharmony_ci par->tga_type = tga_type; 146762306a36Sopenharmony_ci if (tga_bus_pci) 146862306a36Sopenharmony_ci par->tga_chip_rev = (to_pci_dev(dev))->revision; 146962306a36Sopenharmony_ci if (tga_bus_tc) 147062306a36Sopenharmony_ci par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* Setup framebuffer. */ 147362306a36Sopenharmony_ci info->flags = FBINFO_HWACCEL_COPYAREA | 147462306a36Sopenharmony_ci FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT; 147562306a36Sopenharmony_ci info->fbops = &tgafb_ops; 147662306a36Sopenharmony_ci info->screen_base = par->tga_fb_base; 147762306a36Sopenharmony_ci info->pseudo_palette = par->palette; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* This should give a reasonable default video mode. */ 148062306a36Sopenharmony_ci if (tga_bus_pci) { 148162306a36Sopenharmony_ci mode_option_tga = mode_option_pci; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci if (tga_bus_tc) { 148462306a36Sopenharmony_ci mode_option_tga = mode_option_tc; 148562306a36Sopenharmony_ci modedb_tga = &modedb_tc; 148662306a36Sopenharmony_ci modedbsize_tga = 1; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci tgafb_init_fix(info); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci ret = fb_find_mode(&info->var, info, 149262306a36Sopenharmony_ci mode_option ? mode_option : mode_option_tga, 149362306a36Sopenharmony_ci modedb_tga, modedbsize_tga, NULL, 149462306a36Sopenharmony_ci tga_type == TGA_TYPE_8PLANE ? 8 : 32); 149562306a36Sopenharmony_ci if (ret == 0 || ret == 4) { 149662306a36Sopenharmony_ci printk(KERN_ERR "tgafb: Could not find valid video mode\n"); 149762306a36Sopenharmony_ci ret = -EINVAL; 149862306a36Sopenharmony_ci goto err1; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0)) { 150262306a36Sopenharmony_ci printk(KERN_ERR "tgafb: Could not allocate color map\n"); 150362306a36Sopenharmony_ci ret = -ENOMEM; 150462306a36Sopenharmony_ci goto err1; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci tgafb_set_par(info); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (register_framebuffer(info) < 0) { 151062306a36Sopenharmony_ci printk(KERN_ERR "tgafb: Could not register framebuffer\n"); 151162306a36Sopenharmony_ci ret = -EINVAL; 151262306a36Sopenharmony_ci goto err2; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (tga_bus_pci) { 151662306a36Sopenharmony_ci pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n", 151762306a36Sopenharmony_ci par->tga_chip_rev); 151862306a36Sopenharmony_ci pr_info("tgafb: at PCI bus %d, device %d, function %d\n", 151962306a36Sopenharmony_ci to_pci_dev(dev)->bus->number, 152062306a36Sopenharmony_ci PCI_SLOT(to_pci_dev(dev)->devfn), 152162306a36Sopenharmony_ci PCI_FUNC(to_pci_dev(dev)->devfn)); 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci if (tga_bus_tc) 152462306a36Sopenharmony_ci pr_info("tgafb: SFB+ detected, rev=0x%02x\n", 152562306a36Sopenharmony_ci par->tga_chip_rev); 152662306a36Sopenharmony_ci fb_info(info, "%s frame buffer device at 0x%lx\n", 152762306a36Sopenharmony_ci info->fix.id, (long)bar0_start); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return 0; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci err2: 153262306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 153362306a36Sopenharmony_ci err1: 153462306a36Sopenharmony_ci if (mem_base) 153562306a36Sopenharmony_ci iounmap(mem_base); 153662306a36Sopenharmony_ci release_mem_region(bar0_start, bar0_len); 153762306a36Sopenharmony_ci err0: 153862306a36Sopenharmony_ci framebuffer_release(info); 153962306a36Sopenharmony_ci return ret; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic void tgafb_unregister(struct device *dev) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci resource_size_t bar0_start = 0, bar0_len = 0; 154562306a36Sopenharmony_ci int tga_bus_pci = dev_is_pci(dev); 154662306a36Sopenharmony_ci int tga_bus_tc = TGA_BUS_TC(dev); 154762306a36Sopenharmony_ci struct fb_info *info = NULL; 154862306a36Sopenharmony_ci struct tga_par *par; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci info = dev_get_drvdata(dev); 155162306a36Sopenharmony_ci if (!info) 155262306a36Sopenharmony_ci return; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci par = info->par; 155562306a36Sopenharmony_ci unregister_framebuffer(info); 155662306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 155762306a36Sopenharmony_ci iounmap(par->tga_mem_base); 155862306a36Sopenharmony_ci if (tga_bus_pci) { 155962306a36Sopenharmony_ci bar0_start = pci_resource_start(to_pci_dev(dev), 0); 156062306a36Sopenharmony_ci bar0_len = pci_resource_len(to_pci_dev(dev), 0); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci if (tga_bus_tc) { 156362306a36Sopenharmony_ci bar0_start = to_tc_dev(dev)->resource.start; 156462306a36Sopenharmony_ci bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci release_mem_region(bar0_start, bar0_len); 156762306a36Sopenharmony_ci framebuffer_release(info); 156862306a36Sopenharmony_ci} 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic void tgafb_exit(void) 157162306a36Sopenharmony_ci{ 157262306a36Sopenharmony_ci tc_unregister_driver(&tgafb_tc_driver); 157362306a36Sopenharmony_ci pci_unregister_driver(&tgafb_pci_driver); 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci#ifndef MODULE 157762306a36Sopenharmony_cistatic int tgafb_setup(char *arg) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci char *this_opt; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (arg && *arg) { 158262306a36Sopenharmony_ci while ((this_opt = strsep(&arg, ","))) { 158362306a36Sopenharmony_ci if (!*this_opt) 158462306a36Sopenharmony_ci continue; 158562306a36Sopenharmony_ci if (!strncmp(this_opt, "mode:", 5)) 158662306a36Sopenharmony_ci mode_option = this_opt+5; 158762306a36Sopenharmony_ci else 158862306a36Sopenharmony_ci printk(KERN_ERR 158962306a36Sopenharmony_ci "tgafb: unknown parameter %s\n", 159062306a36Sopenharmony_ci this_opt); 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci return 0; 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci#endif /* !MODULE */ 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic int tgafb_init(void) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci int status; 160162306a36Sopenharmony_ci#ifndef MODULE 160262306a36Sopenharmony_ci char *option = NULL; 160362306a36Sopenharmony_ci#endif 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (fb_modesetting_disabled("tgafb")) 160662306a36Sopenharmony_ci return -ENODEV; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci#ifndef MODULE 160962306a36Sopenharmony_ci if (fb_get_options("tgafb", &option)) 161062306a36Sopenharmony_ci return -ENODEV; 161162306a36Sopenharmony_ci tgafb_setup(option); 161262306a36Sopenharmony_ci#endif 161362306a36Sopenharmony_ci status = pci_register_driver(&tgafb_pci_driver); 161462306a36Sopenharmony_ci if (!status) 161562306a36Sopenharmony_ci status = tc_register_driver(&tgafb_tc_driver); 161662306a36Sopenharmony_ci return status; 161762306a36Sopenharmony_ci} 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci/* 162062306a36Sopenharmony_ci * Modularisation 162162306a36Sopenharmony_ci */ 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cimodule_init(tgafb_init); 162462306a36Sopenharmony_cimodule_exit(tgafb_exit); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset"); 162762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1628