/* * Permedia2 framebuffer driver. * * 2.5/2.6 driver: * Copyright (c) 2003 Jim Hague (jim.hague@acm.org) * * based on 2.4 driver: * Copyright (c) 1998-2000 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com) * * and additional input from James Simmon's port of Hannu Mallat's tdfx * driver. * * I have a Creative Graphics Blaster Exxtreme card - pm2fb on x86. I * have no access to other pm2fb implementations. Sparc (and thus * hopefully other big-endian) devices now work, thanks to a lot of * testing work by Ron Murray. I have no access to CVision hardware, * and therefore for now I am omitting the CVision code. * * Multiple boards support has been on the TODO list for ages. * Don't expect this to change. * * 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. * *
*/
/* * The XFree GLINT driver will (I think to implement hardware cursor * support on TVP4010 and similar where there is no RAMDAC - see * comment in set_video) always request +ve sync regardless of what * the mode requires. This screws me because I have a Sun * fixed-frequency monitor which absolutely has to have -ve sync. So * these flags allow the user to specify that requests for +ve sync * should be silently turned in -ve sync.
*/ staticbool lowhsync; staticbool lowvsync; staticbool noaccel; staticbool nomtrr;
/* * The hardware state of the graphics card that isn't part of the * screeninfo.
*/ struct pm2fb_par
{
pm2type_t type; /* Board type */ unsignedchar __iomem *v_regs;/* virtual address of p_regs */
u32 memclock; /* memclock */
u32 video; /* video flags before blanking */
u32 mem_config; /* MemConfig reg at probe */
u32 mem_control; /* MemControl reg at probe */
u32 boot_address; /* BootAddress reg at probe */
u32 palette[16]; int wc_cookie;
};
/* * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo * if we don't use modedb.
*/ staticstruct fb_fix_screeninfo pm2fb_fix = {
.id = "",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 1,
.ypanstep = 1,
.ywrapstep = 0,
.accel = FB_ACCEL_3DLABS_PERMEDIA2,
};
staticvoid set_aperture(struct pm2fb_par *p, u32 depth)
{ /* * The hardware is little-endian. When used in big-endian * hosts, the on-chip aperture settings are used where * possible to translate from host to card byte order.
*/
WAIT_FIFO(p, 2); #ifdef __LITTLE_ENDIAN
pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD); #else switch (depth) { case 24: /* RGB->BGR */ /* * We can't use the aperture to translate host to * card byte order here, so we switch to BGR mode * in pm2fb_set_par().
*/ case 8: /* B->B */
pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_STANDARD); break; case 16: /* HL->LH */
pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_HALFWORDSWAP); break; case 32: /* RGBA->ABGR */
pm2_WR(p, PM2R_APERTURE_ONE, PM2F_APERTURE_BYTESWAP); break;
} #endif
/* We don't use aperture two, so this may be superflous */
pm2_WR(p, PM2R_APERTURE_TWO, PM2F_APERTURE_STANDARD);
}
/* * The hardware cursor needs +vsync to recognise vert retrace. * We may not be using the hardware cursor, but the X Glint * driver may well. So always set +hsync/+vsync and then set * the RAMDAC to invert the sync if necessary.
*/
vsync &= ~(PM2F_HSYNC_MASK | PM2F_VSYNC_MASK);
vsync |= PM2F_HSYNC_ACT_HIGH | PM2F_VSYNC_ACT_HIGH;
/* * pm2fb_check_var - Optional function. Validates a var passed in. * @var: frame buffer variable screen structure * @info: frame buffer structure that represents a single frame buffer * * Checks to see if the hardware supports the state requested by * var passed in. * * Returns negative errno on error, or zero on success.
*/ staticint pm2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
u32 lpitch;
if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) { if (lowhsync) {
DPRINTK("ignoring +hsync, using -hsync.\n");
video |= PM2F_HSYNC_ACT_LOW;
} else
video |= PM2F_HSYNC_ACT_HIGH;
} else
video |= PM2F_HSYNC_ACT_LOW;
if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) { if (lowvsync) {
DPRINTK("ignoring +vsync, using -vsync.\n");
video |= PM2F_VSYNC_ACT_LOW;
} else
video |= PM2F_VSYNC_ACT_HIGH;
} else
video |= PM2F_VSYNC_ACT_LOW;
if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
DPRINTK("interlaced not supported\n"); return -EINVAL;
} if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
video |= PM2F_LINE_DOUBLE; if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
video |= PM2F_VIDEO_ENABLE;
par->video = video;
/** * pm2fb_setcolreg - Sets a color register. * @regno: boolean, 0 copy local, 1 get_user() function * @red: frame buffer colormap structure * @green: The green value which can be up to 16 bits wide * @blue: The blue value which can be up to 16 bits wide. * @transp: If supported the alpha value which can be up to 16 bits wide. * @info: frame buffer info structure * * Set a single color register. The values supplied have a 16 bit * magnitude which needs to be scaled in this function for the hardware. * Pretty much a direct lift from tdfxfb.c. * * Returns negative errno on error, or zero on success.
*/ staticint pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
{ struct pm2fb_par *par = info->par;
if (regno >= info->cmap.len) /* no. of hw registers */ return -EINVAL; /* * Program hardware... do anything you want with transp
*/
/* grayscale works only partially under directcolor */ /* grayscale = 0.30*R + 0.59*G + 0.11*B */ if (info->var.grayscale)
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
/* Directcolor: * var->{color}.offset contains start of bitfield * var->{color}.length contains length of bitfield * {hardwarespecific} contains width of DAC * cmap[X] is programmed to * (X << red.offset) | (X << green.offset) | (X << blue.offset) * RAMDAC[X] is programmed to (red, green, blue) * * Pseudocolor: * uses offset = 0 && length = DAC register width. * var->{color}.offset is 0 * var->{color}.length contains width of DAC * cmap is not used * DAC[X] is programmed to (red, green, blue) * Truecolor: * does not use RAMDAC (usually has 3 of them). * var->{color}.offset contains start of bitfield * var->{color}.length contains length of bitfield * cmap is programmed to * (red << red.offset) | (green << green.offset) | * (blue << blue.offset) | (transp << transp.offset) * RAMDAC does not exist
*/ #define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF -(val)) >> 16) switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: case FB_VISUAL_PSEUDOCOLOR:
red = CNVT_TOHW(red, info->var.red.length);
green = CNVT_TOHW(green, info->var.green.length);
blue = CNVT_TOHW(blue, info->var.blue.length);
transp = CNVT_TOHW(transp, info->var.transp.length); break; case FB_VISUAL_DIRECTCOLOR: /* example here assumes 8 bit DAC. Might be different
* for your hardware */
red = CNVT_TOHW(red, 8);
green = CNVT_TOHW(green, 8);
blue = CNVT_TOHW(blue, 8); /* hey, there is bug in transp handling... */
transp = CNVT_TOHW(transp, 8); break;
} #undef CNVT_TOHW /* Truecolor has hardware independent palette */ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v;
/** * pm2fb_blank - Blanks the display. * @blank_mode: the blank mode we want. * @info: frame buffer structure that represents a single frame buffer * * Blank the screen if blank_mode != 0, else unblank. Return 0 if * blanking succeeded, != 0 if un-/blanking failed due to e.g. a * video mode which doesn't support it. Implements VESA suspend * and powerdown modes on hardware that supports disabling hsync/vsync: * blank_mode == 2: suspend vsync * blank_mode == 3: suspend hsync * blank_mode == 4: powerdown * * Returns negative errno on error, or zero on success. *
*/ staticint pm2fb_blank(int blank_mode, struct fb_info *info)
{ struct pm2fb_par *par = info->par;
u32 video = par->video;
DPRINTK("blank_mode %d\n", blank_mode);
switch (blank_mode) { case FB_BLANK_UNBLANK: /* Screen: On */
video |= PM2F_VIDEO_ENABLE; break; case FB_BLANK_NORMAL: /* Screen: Off */
video &= ~PM2F_VIDEO_ENABLE; break; case FB_BLANK_VSYNC_SUSPEND: /* VSync: Off */
video &= ~(PM2F_VSYNC_MASK | PM2F_BLANK_LOW); break; case FB_BLANK_HSYNC_SUSPEND: /* HSync: Off */
video &= ~(PM2F_HSYNC_MASK | PM2F_BLANK_LOW); break; case FB_BLANK_POWERDOWN: /* HSync: Off, VSync: Off */
video &= ~(PM2F_VSYNC_MASK | PM2F_HSYNC_MASK | PM2F_BLANK_LOW); break;
}
set_video(par, video); return 0;
}
staticint pm2vfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{ struct pm2fb_par *par = info->par;
u8 mode = PM2F_CURSORMODE_TYPE_X; int x = cursor->image.dx - info->var.xoffset; int y = cursor->image.dy - info->var.yoffset;
if (cursor->enable)
mode |= PM2F_CURSORMODE_CURSOR_ENABLE;
pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_MODE, mode);
if (!cursor->enable)
x = 2047; /* push it outside display */
pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_X_LOW, x & 0xff);
pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_X_HIGH, (x >> 8) & 0xf);
pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_Y_LOW, y & 0xff);
pm2v_RDAC_WR(par, PM2VI_RD_CURSOR_Y_HIGH, (y >> 8) & 0xf);
/* * If the cursor is not be changed this means either we want the * current cursor state (if enable is set) or we want to query what * we can do with the cursor (if enable is not set)
*/ if (!cursor->set) return 0;
if (!hwcursor) return -EINVAL; /* just to force soft_cursor() call */
/* Too large of a cursor or wrong bpp :-( */ if (cursor->image.width > 64 ||
cursor->image.height > 64 ||
cursor->image.depth > 1) return -EINVAL;
if (par->type == PM2_TYPE_PERMEDIA2V) return pm2vfb_cursor(info, cursor);
mode = 0x40; if (cursor->enable)
mode = 0x43;
pm2_RDAC_WR(par, PM2I_RD_CURSOR_CONTROL, mode);
/* * If the cursor is not be changed this means either we want the * current cursor state (if enable is set) or we want to query what * we can do with the cursor (if enable is not set)
*/ if (!cursor->set) return 0;
if (cursor->set & FB_CUR_SETPOS) { int x = cursor->image.dx - info->var.xoffset + 63; int y = cursor->image.dy - info->var.yoffset + 63;
#ifdefined(__BIG_ENDIAN) /* * PM2 has a 64k register file, mapped twice in 128k. Lower * map is little-endian, upper map is big-endian.
*/
pm2fb_fix.mmio_start += PM2_REGS_SIZE;
DPRINTK("Adjusting register base for big-endian.\n"); #endif
DPRINTK("Register base at 0x%lx\n", pm2fb_fix.mmio_start);
/* Registers - request region and map it. */ if (!request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len, "pm2fb regbase")) {
printk(KERN_WARNING "pm2fb: Can't reserve regbase.\n"); goto err_exit_neither;
}
default_par->v_regs =
ioremap(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len); if (!default_par->v_regs) {
printk(KERN_WARNING "pm2fb: Can't remap %s register area.\n",
pm2fb_fix.id);
release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len); goto err_exit_neither;
}
/* Stash away memory register info for use when we reset the board */
default_par->mem_control = pm2_RD(default_par, PM2R_MEM_CONTROL);
default_par->boot_address = pm2_RD(default_par, PM2R_BOOT_ADDRESS);
default_par->mem_config = pm2_RD(default_par, PM2R_MEM_CONFIG);
DPRINTK("MemControl 0x%x BootAddress 0x%x MemConfig 0x%x\n",
default_par->mem_control, default_par->boot_address,
default_par->mem_config);
if (default_par->mem_control == 0 &&
default_par->boot_address == 0x31 &&
default_par->mem_config == 0x259fffff) {
default_par->memclock = CVPPC_MEMCLOCK;
default_par->mem_control = 0;
default_par->boot_address = 0x20;
default_par->mem_config = 0xe6002021; if (pdev->subsystem_vendor == 0x1048 &&
pdev->subsystem_device == 0x0a31) {
DPRINTK("subsystem_vendor: %04x, " "subsystem_device: %04x\n",
pdev->subsystem_vendor, pdev->subsystem_device);
DPRINTK("We have not been initialized by VGA BIOS and " "are running on an Elsa Winner 2000 Office\n");
DPRINTK("Initializing card timings manually...\n");
default_par->memclock = 100000;
} if (pdev->subsystem_vendor == 0x3d3d &&
pdev->subsystem_device == 0x0100) {
DPRINTK("subsystem_vendor: %04x, " "subsystem_device: %04x\n",
pdev->subsystem_vendor, pdev->subsystem_device);
DPRINTK("We have not been initialized by VGA BIOS and " "are running on an 3dlabs reference board\n");
DPRINTK("Initializing card timings manually...\n");
default_par->memclock = 74894;
}
}
/* Now work out how big lfb is going to be. */ switch (default_par->mem_config & PM2F_MEM_CONFIG_RAM_MASK) { case PM2F_MEM_BANKS_1:
pm2fb_fix.smem_len = 0x200000; break; case PM2F_MEM_BANKS_2:
pm2fb_fix.smem_len = 0x400000; break; case PM2F_MEM_BANKS_3:
pm2fb_fix.smem_len = 0x600000; break; case PM2F_MEM_BANKS_4:
pm2fb_fix.smem_len = 0x800000; break;
}
pm2fb_fix.smem_start = pci_resource_start(pdev, 1);
/* Linear frame buffer - request region and map it. */ if (!request_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len, "pm2fb smem")) {
printk(KERN_WARNING "pm2fb: Can't reserve smem.\n"); goto err_exit_mmio;
}
info->screen_base =
ioremap_wc(pm2fb_fix.smem_start, pm2fb_fix.smem_len); if (!info->screen_base) {
printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n");
release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len); goto err_exit_mmio;
}
if (!nomtrr)
default_par->wc_cookie = arch_phys_wc_add(pm2fb_fix.smem_start,
pm2fb_fix.smem_len);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.