/* * linux/drivers/video/stifb.c - * Low level Frame buffer driver for HP workstations with * STI (standard text interface) video firmware. * * Copyright (C) 2001-2006 Helge Deller <deller@gmx.de> * Portions Copyright (C) 2001 Thomas Bogendoerfer <tsbogend@alpha.franken.de> * * Based on: * - linux/drivers/video/artistfb.c -- Artist frame buffer driver * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> * - based on skeletonfb, which was * Created 28 Dec 1997 by Geert Uytterhoeven * - HP Xhp cfb-based X11 window driver for XFree86 * (c)Copyright 1992 Hewlett-Packard Co. * * * The following graphics display devices (NGLE family) are supported by this driver: * * HPA4070A known as "HCRX", a 1280x1024 color device with 8 planes * HPA4071A known as "HCRX24", a 1280x1024 color device with 24 planes, * optionally available with a hardware accelerator as HPA4071A_Z * HPA1659A known as "CRX", a 1280x1024 color device with 8 planes * HPA1439A known as "CRX24", a 1280x1024 color device with 24 planes, * optionally available with a hardware accelerator. * HPA1924A known as "GRX", a 1280x1024 grayscale device with 8 planes * HPA2269A known as "Dual CRX", a 1280x1024 color device with 8 planes, * implements support for two displays on a single graphics card. * HP710C internal graphics support optionally available on the HP9000s710 SPU, * supports 1280x1024 color displays with 8 planes. * HP710G same as HP710C, 1280x1024 grayscale only * HP710L same as HP710C, 1024x768 color only * HP712 internal graphics support on HP9000s712 SPU, supports 640x480, * 1024x768 or 1280x1024 color displays on 8 planes (Artist) * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details.
*/
/* TODO: * - 1bpp mode is completely untested * - add support for h/w acceleration * - add hardware cursor * - automatically disable double buffering (e.g. on RDI precisionbook laptop)
*/
/* on supported graphic devices you may: * #define FALLBACK_TO_1BPP to fall back to 1 bpp, or
* #undef FALLBACK_TO_1BPP to reject support for unsupported cards */ #undef FALLBACK_TO_1BPP
#undef DEBUG_STIFB_REGS /* debug sti register accesses */
staticvoid
SET_ATTR_SIZE(struct stifb_info *fb, int width, int height)
{ /* REG_6 seems to have special values when run on a RDI precisionbook parisc laptop (INTERNAL_EG_DX1024 or INTERNAL_EG_X1024). The values are: 0x2f0: internal (LCD) & external display enabled 0x2a0: external display only 0x000: zero on standard artist graphic cards
*/
WRITE_WORD(0x00000000, fb, REG_6);
WRITE_WORD((width<<16) | height, fb, REG_9);
WRITE_WORD(0x05000000, fb, REG_6);
WRITE_WORD(0x00040001, fb, REG_9);
}
/* Expect lutIndex to be 0 or 1 for image cmaps, 2 or 3 for overlay cmaps */ if (fb->info->var.bits_per_pixel == 8)
lutBltCtl.fields.lutOffset = 2 * 256; else
lutBltCtl.fields.lutOffset = 0 * 256;
/* Offset points to start of LUT. Adjust for within LUT */
lutBltCtl.fields.lutOffset += offsetWithinLut;
/* * In order to work around an ELK hardware problem (Buffy doesn't * always flush it's buffers when writing to the attribute * planes), at least 4 pixels must be written to the attribute * planes starting at (X == 1280) and (Y != to the last Y written * by BIF):
*/
staticvoid
hyperResetPlanes(struct stifb_info *fb, int enable)
{ unsignedint controlPlaneReg;
NGLE_LOCK(fb);
if (IS_24_DEVICE(fb)) if (fb->info->var.bits_per_pixel == 32)
controlPlaneReg = 0x04000F00; else
controlPlaneReg = 0x00000F00; /* 0x00000800 should be enough, but lets clear all 4 bits */ else
controlPlaneReg = 0x00000F00; /* 0x00000100 should be enough, but lets clear all 4 bits */
switch (enable) { case ENABLE: /* clear screen */ if (IS_24_DEVICE(fb))
ngleDepth24_ClearImagePlanes(fb); else
ngleDepth8_ClearImagePlanes(fb);
/* Paint attribute planes for default case.
* On Hyperdrive, this means all windows using overlay cmap 0. */
ngleResetAttrPlanes(fb, controlPlaneReg);
/************************************************** ** Also need to counteract ITE settings
**************************************************/
hyperUndoITE(fb); break;
case DISABLE: /* clear screen */ if (IS_24_DEVICE(fb))
ngleDepth24_ClearImagePlanes(fb); else
ngleDepth8_ClearImagePlanes(fb);
ngleResetAttrPlanes(fb, controlPlaneReg);
ngleClearOverlayPlanes(fb, 0xff, 0); break;
case -1: /* RESET */
hyperUndoITE(fb);
ngleResetAttrPlanes(fb, controlPlaneReg); break;
}
NGLE_UNLOCK(fb);
}
/* Return pointer to in-memory structure holding ELK device-dependent ROM values. */
staticvoid
ngleGetDeviceRomData(struct stifb_info *fb)
{ #if 0
XXX: FIXME: !!! int *pBytePerLongDevDepData;/* data byte == LSB */ int *pRomTable;
NgleDevRomData *pPackedDevRomData; int sizePackedDevRomData = sizeof(*pPackedDevRomData); char *pCard8; int i; char *mapOrigin = NULL;
int romTableIdx;
pPackedDevRomData = fb->ngle_rom;
SETUP_HW(fb); if (fb->id == S9000_ID_ARTIST) {
pPackedDevRomData->cursor_pipeline_delay = 4;
pPackedDevRomData->video_interleaves = 4;
} else { /* Get pointer to unpacked byte/long data in ROM */
pBytePerLongDevDepData = fb->sti->regions[NGLEDEVDEPROM_CRT_REGION];
/* Tomcat supports several resolutions: 1280x1024, 1024x768, 640x480 */ if (fb->id == S9000_ID_TOMCAT)
{ /* jump to the correct ROM table */
GET_ROMTABLE_INDEX(romTableIdx); while (romTableIdx > 0)
{
pCard8 = (Card8 *) pPackedDevRomData;
pRomTable = pBytePerLongDevDepData; /* Pack every fourth byte from ROM into structure */ for (i = 0; i < sizePackedDevRomData; i++)
{
*pCard8++ = (Card8) (*pRomTable++);
}
/* First write to Hyperbowl must happen twice (bug) */
WRITE_WORD(hyperbowl, fb, REG_40);
WRITE_WORD(hyperbowl, fb, REG_40);
WRITE_WORD(HYPERBOWL_MODE2_8_24, fb, REG_39);
WRITE_WORD(0x014c0148, fb, REG_42); /* Set lut 0 to be the direct color */
WRITE_WORD(0x404c4048, fb, REG_43);
WRITE_WORD(0x034c0348, fb, REG_44);
WRITE_WORD(0x444c4448, fb, REG_45);
} else {
hyperbowl = HYPERBOWL_MODE_FOR_8_OVER_88_LUT0_NO_TRANSPARENCIES;
/* First write to Hyperbowl must happen twice (bug) */
WRITE_WORD(hyperbowl, fb, REG_40);
WRITE_WORD(hyperbowl, fb, REG_40);
switch (fb->id) { case S9000_ID_A1439A:
CRX24_ENABLE_DISABLE_DISPLAY(fb, enable); break; case CRT_ID_VISUALIZE_EG: case S9000_ID_ARTIST:
ARTIST_ENABLE_DISABLE_DISPLAY(fb, enable); break; case S9000_ID_HCRX:
HYPER_ENABLE_DISABLE_DISPLAY(fb, enable); break; case S9000_ID_A1659A: case S9000_ID_TIMBER: case CRX24_OVERLAY_PLANES: default:
ENABLE_DISABLE_DISPLAY(fb, enable); break;
}
staticvoid __init
stifb_init_display(struct stifb_info *fb)
{ int id = fb->id;
SETUP_FB(fb);
/* HCRX specific initialization */
SETUP_HCRX(fb);
/* if (id == S9000_ID_HCRX) hyperInitSprite(fb); else ngleInitSprite(fb);
*/
/* Initialize the image planes. */ switch (id) { case S9000_ID_HCRX:
hyperResetPlanes(fb, ENABLE); break; case S9000_ID_A1439A:
rattlerSetupPlanes(fb); break; case S9000_ID_A1659A: case S9000_ID_ARTIST: case CRT_ID_VISUALIZE_EG:
elkSetupPlanes(fb); break;
}
/* Clear attribute planes on non HCRX devices. */ switch (id) { case S9000_ID_A1659A: case S9000_ID_A1439A: if (fb->info->var.bits_per_pixel == 32)
ngleSetupAttrPlanes(fb, BUFF1_CMAP3); else {
ngleSetupAttrPlanes(fb, BUFF1_CMAP0);
} if (id == S9000_ID_A1439A)
ngleClearOverlayPlanes(fb, 0xff, 0); break; case S9000_ID_ARTIST: case CRT_ID_VISUALIZE_EG: if (fb->info->var.bits_per_pixel == 32)
ngleSetupAttrPlanes(fb, BUFF1_CMAP3); else {
ngleSetupAttrPlanes(fb, ARTIST_CMAP0);
} break;
}
stifb_blank(0, fb->info); /* 0=enable screen */
SETUP_FB(fb);
}
/* ------------ Interfaces to hardware functions ------------ */
info = framebuffer_alloc(sizeof(*fb), sti->dev); if (!info) return -ENOMEM;
fb = info->par;
fb->info = info;
/* set struct to a known state */
fix = &info->fix;
var = &info->var;
fb->sti = sti;
dev_name = sti->sti_data->inq_outptr.dev_name; /* store upper 32bits of the graphics id */
fb->id = fb->sti->graphics_id[0];
/* only supported cards are allowed */ switch (fb->id) { case CRT_ID_VISUALIZE_EG: /* Visualize cards can run either in "double buffer" or "standard" mode. Depending on the mode, the card reports a different device name, e.g. "INTERNAL_EG_DX1024" in double buffer mode and "INTERNAL_EG_X1024" in standard mode. Since this driver only supports standard mode, we check if the device name contains the string "DX" and tell the
user how to reconfigure the card. */ if (strstr(dev_name, "DX")) {
printk(KERN_WARNING "WARNING: stifb framebuffer driver does not support '%s' in double-buffer mode.\n" "WARNING: Please disable the double-buffer mode in IPL menu (the PARISC-BIOS).\n",
dev_name); goto out_err0;
}
fallthrough; case S9000_ID_ARTIST: case S9000_ID_HCRX: case S9000_ID_TIMBER: case S9000_ID_A1659A: case S9000_ID_A1439A: break; default:
printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n",
dev_name, fb->id); goto out_err0;
}
/* default to 8 bpp on most graphic chips */
bpp = 8;
xres = sti_onscreen_x(fb->sti);
yres = sti_onscreen_y(fb->sti);
ngleGetDeviceRomData(fb);
/* get (virtual) io region base addr */
fix->mmio_start = REGION_BASE(fb,2);
fix->mmio_len = 0x400000;
/* Reject any device not in the NGLE family */ switch (fb->id) { case S9000_ID_A1659A: /* CRX/A1659A */ break; case S9000_ID_ELM: /* GRX, grayscale but else same as A1659A */
var->grayscale = 1;
fb->id = S9000_ID_A1659A; break; case S9000_ID_TIMBER: /* HP9000/710 Any (may be a grayscale device) */ if (strstr(dev_name, "GRAYSCALE") ||
strstr(dev_name, "Grayscale") ||
strstr(dev_name, "grayscale"))
var->grayscale = 1; break; case S9000_ID_TOMCAT: /* Dual CRX, behaves else like a CRX */ /* FIXME: TomCat supports two heads: * fb.iobase = REGION_BASE(fb_info,3); * fb.screen_base = ioremap(REGION_BASE(fb_info,2),xxx);
* for now we only support the left one ! */
xres = fb->ngle_rom.x_size_visible;
yres = fb->ngle_rom.y_size_visible;
fb->id = S9000_ID_A1659A; break; case S9000_ID_A1439A: /* CRX24/A1439A */
bpp = 32; break; case S9000_ID_HCRX: /* Hyperdrive/HCRX */
memset(&fb->ngle_rom, 0, sizeof(fb->ngle_rom)); if ((fb->sti->regions_phys[0] & 0xfc000000) ==
(fb->sti->regions_phys[2] & 0xfc000000))
sti_rom_address = F_EXTEND(fb->sti->regions_phys[0]); else
sti_rom_address = F_EXTEND(fb->sti->regions_phys[1]);
if (fb_get_options("stifb", &option)) return -ENODEV;
stifb_setup(option); #endif if (stifb_disabled) {
printk(KERN_INFO "stifb: disabled by \"stifb=off\" kernel parameter\n"); return -ENXIO;
}
def_sti = sti_get_rom(0); if (def_sti) { for (i = 1; i <= MAX_STI_ROMS; i++) {
sti = sti_get_rom(i); if (!sti) break; if (sti == def_sti) {
stifb_init_fb(sti, stifb_bpp_pref[i - 1]); break;
}
}
}
for (i = 1; i <= MAX_STI_ROMS; i++) {
sti = sti_get_rom(i); if (!sti) break; if (sti == def_sti) continue;
stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
} return 0;
}
/* * Cleanup
*/
staticvoid __exit
stifb_cleanup(void)
{ struct sti_struct *sti; int i;
for (i = 1; i <= MAX_STI_ROMS; i++) {
sti = sti_get_rom(i); if (!sti) break; if (sti->dev) { struct fb_info *info = dev_get_drvdata(sti->dev);
if (!info) continue;
unregister_framebuffer(info);
release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
release_mem_region(info->fix.smem_start, info->fix.smem_len); if (info->screen_base)
iounmap(info->screen_base);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
dev_set_drvdata(sti->dev, NULL);
}
}
}
MODULE_AUTHOR("Helge Deller , Thomas Bogendoerfer ");
MODULE_DESCRIPTION("Framebuffer driver for HP's NGLE series graphics cards in HP PARISC machines");
MODULE_LICENSE("GPL v2");