/* * linux/drivers/video/neofb.c -- NeoMagic Framebuffer Driver * * Copyright (c) 2001-2002 Denis Oliver Kropp <dok@directfb.org> * * * Card specific code is based on XFree86's neomagic driver. * Framebuffer framework code is based on code of cyber2000fb. * * 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. * * * 0.4.1 * - Cosmetic changes (dok) * * 0.4 * - Toshiba Libretto support, allow modes larger than LCD size if * LCD is disabled, keep BIOS settings if internal/external display * haven't been enabled explicitly * (Thomas J. Moore <dark@mama.indstate.edu>) * * 0.3.3 * - Porting over to new fbdev api. (jsimmons) * * 0.3.2 * - got rid of all floating point (dok) * * 0.3.1 * - added module license (dok) * * 0.3 * - hardware accelerated clear and move for 2200 and above (dok) * - maximum allowed dotclock is handled now (dok) * * 0.2.1 * - correct panning after X usage (dok) * - added module and kernel parameters (dok) * - no stretching if external display is enabled (dok) * * 0.2 * - initial version (dok) * * * TODO * - ioctl for internal/external switching * - blanking * - 32bit depth support, maybe impossible * - disable pan-on-sync, need specs * * BUGS * - white margin on bootup like with tdfxfb (colormap problem?) *
*/
if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 ||
info->fix.accel == FB_ACCEL_NEOMAGIC_NM2230 ||
info->fix.accel == FB_ACCEL_NEOMAGIC_NM2360 ||
info->fix.accel == FB_ACCEL_NEOMAGIC_NM2380) { /* NOT_DONE: We are trying the full range of the 2200 clock.
We should be able to try n up to 2047 */
par->VCLK3NumeratorLow = n_best;
par->VCLK3NumeratorHigh = (f_best << 7);
} else
par->VCLK3NumeratorLow = n_best | (f_best << 7);
if (par->ref_count == 1) {
restore_vga(&par->state);
}
par->ref_count--;
return 0;
}
staticint
neofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct neofb_par *par = info->par; int memlen, vramlen; int mode_ok = 0;
DBG("neofb_check_var");
if (!var->pixclock || PICOS2KHZ(var->pixclock) > par->maxClock) return -EINVAL;
/* Is the mode larger than the LCD panel? */ if (par->internal_display &&
((var->xres > par->NeoPanelWidth) ||
(var->yres > par->NeoPanelHeight))) {
printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel (%dx%d)\n",
var->xres, var->yres, par->NeoPanelWidth,
par->NeoPanelHeight); return -EINVAL;
}
/* Is the mode one of the acceptable sizes? */ if (!par->internal_display)
mode_ok = 1; else { switch (var->xres) { case 1280: if (var->yres == 1024)
mode_ok = 1; break; case 1024: if (var->yres == 768)
mode_ok = 1; break; case 800: if (var->yres == (par->libretto ? 480 : 600))
mode_ok = 1; break; case 640: if (var->yres == 480)
mode_ok = 1; break;
}
}
if (!mode_ok) {
printk(KERN_INFO "Mode (%dx%d) won't display properly on LCD\n",
var->xres, var->yres); return -EINVAL;
}
/* we must round yres/xres down, we already rounded y/xres_virtual up
if it was possible. We should return -EINVAL, but I disagree */ if (var->yres_virtual < var->yres)
var->yres = var->yres_virtual; if (var->xoffset + var->xres > var->xres_virtual)
var->xoffset = var->xres_virtual - var->xres; if (var->yoffset + var->yres > var->yres_virtual)
var->yoffset = var->yres_virtual - var->yres;
staticint neofb_set_par(struct fb_info *info)
{ struct neofb_par *par = info->par; unsignedchar temp; int i, clock_hi = 0; int lcd_stretch; int hoffset, voffset; int vsync_start, vtotal;
/* Fast write bursts on unless disabled. */ if (par->pci_burst)
par->SysIfaceCntl1 = 0x30; else
par->SysIfaceCntl1 = 0x00;
par->SysIfaceCntl2 = 0xc0; /* VESA Bios sets this to 0x80! */
/* Initialize: by default, we want display config register to be read */
par->PanelDispCntlRegRead = 1;
/* Enable any user specified display devices. */
par->PanelDispCntlReg1 = 0x00; if (par->internal_display)
par->PanelDispCntlReg1 |= 0x02; if (par->external_display)
par->PanelDispCntlReg1 |= 0x01;
/* If the user did not specify any display devices, then... */ if (par->PanelDispCntlReg1 == 0x00) { /* Default to internal (i.e., LCD) only. */
par->PanelDispCntlReg1 = vga_rgfx(NULL, 0x20) & 0x03;
}
/* If we are using a fixed mode, then tell the chip we are. */ switch (info->var.xres) { case 1280:
par->PanelDispCntlReg1 |= 0x60; break; case 1024:
par->PanelDispCntlReg1 |= 0x40; break; case 800:
par->PanelDispCntlReg1 |= 0x20; break; case 640: default: break;
}
/* Setup shadow register locking. */ switch (par->PanelDispCntlReg1 & 0x03) { case 0x01: /* External CRT only mode: */
par->GeneralLockReg = 0x00; /* We need to program the VCLK for external display only mode. */
par->ProgramVCLK = 1; break; case 0x02: /* Internal LCD only mode: */ case 0x03: /* Simultaneous internal/external (LCD/CRT) mode: */
par->GeneralLockReg = 0x01; /* Don't program the VCLK when using the LCD. */
par->ProgramVCLK = 0; break;
}
/* * If the screen is to be stretched, turn on stretching for the * various modes. * * OPTION_LCD_STRETCH means stretching should be turned off!
*/
par->PanelDispCntlReg2 = 0x00;
par->PanelDispCntlReg3 = 0x00;
if (par->lcd_stretch && (par->PanelDispCntlReg1 == 0x02) && /* LCD only */
(info->var.xres != par->NeoPanelWidth)) { switch (info->var.xres) { case 320: /* Needs testing. KEM -- 24 May 98 */ case 400: /* Needs testing. KEM -- 24 May 98 */ case 640: case 800: case 1024:
lcd_stretch = 1;
par->PanelDispCntlReg2 |= 0xC6; break; default:
lcd_stretch = 0; /* No stretching in these modes. */
}
} else
lcd_stretch = 0;
/* * If the screen is to be centerd, turn on the centering for the * various modes.
*/
par->PanelVertCenterReg1 = 0x00;
par->PanelVertCenterReg2 = 0x00;
par->PanelVertCenterReg3 = 0x00;
par->PanelVertCenterReg4 = 0x00;
par->PanelVertCenterReg5 = 0x00;
par->PanelHorizCenterReg1 = 0x00;
par->PanelHorizCenterReg2 = 0x00;
par->PanelHorizCenterReg3 = 0x00;
par->PanelHorizCenterReg4 = 0x00;
par->PanelHorizCenterReg5 = 0x00;
if (par->PanelDispCntlReg1 & 0x02) { if (info->var.xres == par->NeoPanelWidth) { /* * No centering required when the requested display width * equals the panel width.
*/
} else {
par->PanelDispCntlReg2 |= 0x01;
par->PanelDispCntlReg3 |= 0x10;
/* don't know what this is, but it's 0 from bootup anyway */
vga_wgfx(NULL, 0x15, 0x00);
/* was set to 0x01 by my bios in text and vesa modes */
vga_wgfx(NULL, 0x0A, par->GeneralLockReg);
/* * The color mode needs to be set before calling vgaHWRestore * to ensure the DAC is initialized properly. * * NOTE: Make sure we don't change bits make sure we don't change * any reserved bits.
*/
temp = vga_rgfx(NULL, 0x90); switch (info->fix.accel) { case FB_ACCEL_NEOMAGIC_NM2070:
temp &= 0xF0; /* Save bits 7:4 */
temp |= (par->ExtColorModeSelect & ~0xF0); break; case FB_ACCEL_NEOMAGIC_NM2090: case FB_ACCEL_NEOMAGIC_NM2093: case FB_ACCEL_NEOMAGIC_NM2097: case FB_ACCEL_NEOMAGIC_NM2160: case FB_ACCEL_NEOMAGIC_NM2200: case FB_ACCEL_NEOMAGIC_NM2230: case FB_ACCEL_NEOMAGIC_NM2360: case FB_ACCEL_NEOMAGIC_NM2380:
temp &= 0x70; /* Save bits 6:4 */
temp |= (par->ExtColorModeSelect & ~0x70); break;
}
vga_wgfx(NULL, 0x90, temp);
/* * In some rare cases a lockup might occur if we don't delay * here. (Reported by Miles Lane)
*/ //mdelay(200);
/* * Disable horizontal and vertical graphics and text expansions so * that vgaHWRestore works properly.
*/
temp = vga_rgfx(NULL, 0x25);
temp &= 0x39;
vga_wgfx(NULL, 0x25, temp);
/* * Sleep for 200ms to make sure that the two operations above have * had time to take effect.
*/
mdelay(200);
/*
* This function handles restoring the generic VGA registers. */
vgaHWRestore(info, par);
/* linear colormap for non palettized modes */ switch (info->var.bits_per_pixel) { case 8: /* PseudoColor, 256 */
info->fix.visual = FB_VISUAL_PSEUDOCOLOR; break; case 16: /* TrueColor, 64k */
info->fix.visual = FB_VISUAL_TRUECOLOR;
switch (info->fix.accel) { case FB_ACCEL_NEOMAGIC_NM2200: case FB_ACCEL_NEOMAGIC_NM2230: case FB_ACCEL_NEOMAGIC_NM2360: case FB_ACCEL_NEOMAGIC_NM2380:
neo2200_accel_init(info, &info->var); break; default: break;
} return 0;
}
/* * Pan or Wrap the Display
*/ staticint neofb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct neofb_par *par = info->par; struct vgastate *state = &par->state; int oldExtCRTDispAddr; int Base;
DBG("neofb_update_start");
Base = (var->yoffset * info->var.xres_virtual + var->xoffset) >> 2;
Base *= (info->var.bits_per_pixel + 7) / 8;
neoUnlock();
/* * These are the generic starting address registers.
*/
vga_wcrt(state->vgabase, 0x0C, (Base & 0x00FF00) >> 8);
vga_wcrt(state->vgabase, 0x0D, (Base & 0x00FF));
/* * Make sure we don't clobber some other bits that might already * have been set. NOTE: NM2200 has a writable bit 3, but it shouldn't * be needed.
*/
oldExtCRTDispAddr = vga_rgfx(NULL, 0x0E);
vga_wgfx(state->vgabase, 0x0E, (((Base >> 16) & 0x0f) | (oldExtCRTDispAddr & 0xf0)));
/* * (Un)Blank the display.
*/ staticint neofb_blank(int blank_mode, struct fb_info *info)
{ /* * 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 for monitors, and backlight control on LCDs. * blank_mode == 0: unblanked (backlight on) * blank_mode == 1: blank (backlight on) * blank_mode == 2: suspend vsync (backlight off) * blank_mode == 3: suspend hsync (backlight off) * blank_mode == 4: powerdown (backlight off) * * wms...Enable VESA DPMS compatible powerdown mode * run "setterm -powersave powerdown" to take advantage
*/ struct neofb_par *par = info->par; int seqflags, lcdflags, dpmsflags, reg, tmpdisp;
/* * Read back the register bits related to display configuration. They might * have been changed underneath the driver via Fn key stroke.
*/
neoUnlock();
tmpdisp = vga_rgfx(NULL, 0x20) & 0x03;
neoLock(&par->state);
/* In case we blank the screen, we want to store the possibly new * configuration in the driver. During un-blank, we re-apply this setting, * since the LCD bit will be cleared in order to switch off the backlight.
*/ if (par->PanelDispCntlRegRead) {
par->PanelDispCntlReg1 = tmpdisp;
}
par->PanelDispCntlRegRead = !blank_mode;
switch (blank_mode) { case FB_BLANK_POWERDOWN: /* powerdown - both sync lines down */
seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */
lcdflags = 0; /* LCD off */
dpmsflags = NEO_GR01_SUPPRESS_HSYNC |
NEO_GR01_SUPPRESS_VSYNC; #ifdef CONFIG_TOSHIBA /* Do we still need this ? */ /* attempt to turn off backlight on toshiba; also turns off external */
{
SMMRegisters regs;
regs.eax = 0xff00; /* HCI_SET */
regs.ebx = 0x0002; /* HCI_BACKLIGHT */
regs.ecx = 0x0000; /* HCI_DISABLE */
tosh_smm(®s);
} #endif break; case FB_BLANK_HSYNC_SUSPEND: /* hsync off */
seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */
lcdflags = 0; /* LCD off */
dpmsflags = NEO_GR01_SUPPRESS_HSYNC; break; case FB_BLANK_VSYNC_SUSPEND: /* vsync off */
seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */
lcdflags = 0; /* LCD off */
dpmsflags = NEO_GR01_SUPPRESS_VSYNC; break; case FB_BLANK_NORMAL: /* just blank screen (backlight stays on) */
seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */ /* * During a blank operation with the LID shut, we might store "LCD off" * by mistake. Due to timing issues, the BIOS may switch the lights * back on, and we turn it back off once we "unblank". * * So here is an attempt to implement ">=" - if we are in the process * of unblanking, and the LCD bit is unset in the driver but set in the * register, we must keep it.
*/
lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
dpmsflags = 0x00; /* no hsync/vsync suppression */ break; case FB_BLANK_UNBLANK: /* unblank */
seqflags = 0; /* Enable sequencer */
lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
dpmsflags = 0x00; /* no hsync/vsync suppression */ #ifdef CONFIG_TOSHIBA /* Do we still need this ? */ /* attempt to re-enable backlight/external on toshiba */
{
SMMRegisters regs;
staticvoid
neo2200_imageblit(struct fb_info *info, conststruct fb_image *image)
{ struct neofb_par *par = info->par; int s_pitch = (image->width * image->depth + 7) >> 3; int scan_align = info->pixmap.scan_align - 1; int buf_align = info->pixmap.buf_align - 1; int bltCntl_flags, d_pitch, data_len;
// The data is padded for the hardware
d_pitch = (s_pitch + scan_align) & ~scan_align;
data_len = ((d_pitch * image->height) + buf_align) & ~buf_align;
neo2200_sync(info);
if (image->depth == 1) { if (info->var.bits_per_pixel == 24 && image->width < 16) { /* FIXME. There is a bug with accelerated color-expanded * transfers in 24 bit mode if the image being transferred * is less than 16 bits wide. This is due to insufficient * padding when writing the image. We need to adjust
* struct fb_pixmap. Not yet done. */
cfb_imageblit(info, image); return;
}
bltCntl_flags = NEO_BC0_SRC_MONO;
} elseif (image->depth == info->var.bits_per_pixel) {
bltCntl_flags = 0;
} else { /* We don't currently support hardware acceleration if image
* depth is different from display */
cfb_imageblit(info, image); return;
}
switch (info->var.bits_per_pixel) { case 8:
writel(image->fg_color, &par->neo2200->fgColor);
writel(image->bg_color, &par->neo2200->bgColor); break; case 16: case 24:
writel(((u32 *) (info->pseudo_palette))[image->fg_color],
&par->neo2200->fgColor);
writel(((u32 *) (info->pseudo_palette))[image->bg_color],
&par->neo2200->bgColor); break;
}
// Eventually we will have i2c support.
info->monspecs.modedb = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL); if (!info->monspecs.modedb) return -ENOMEM;
info->monspecs.modedb_len = 1;
err = neo_map_video(info, dev, video_len); if (err) goto err_init_hw;
if (!fb_find_mode(&info->var, info, mode_option, NULL, 0,
info->monspecs.modedb, 16)) {
printk(KERN_ERR "neofb: Unable to find usable video mode.\n");
err = -EINVAL; goto err_map_video;
}
/* * Calculate the hsync and vsync frequencies. Note that * we split the 1e12 constant up so that we can preserve * the precision and fit the results into 32-bit registers. * (1953125000 * 512 = 1e12)
*/
h_sync = 1953125000 / info->var.pixclock;
h_sync =
h_sync * 512 / (info->var.xres + info->var.left_margin +
info->var.right_margin + info->var.hsync_len);
v_sync =
h_sync / (info->var.yres + info->var.upper_margin +
info->var.lower_margin + info->var.vsync_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.