// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/video/sstfb.c -- voodoo graphics frame buffer * * Copyright (c) 2000-2002 Ghozlane Toumi <gtoumi@laposte.net> * * Created 15 Jan 2000 by Ghozlane Toumi * * Contributions (and many thanks) : * * 03/2001 James Simmons <jsimmons@infradead.org> * 04/2001 Paul Mundt <lethal@chaoticdreams.org> * 05/2001 Urs Ganse <ursg@uni.de> * (initial work on voodoo2 port, interlace) * 09/2002 Helge Deller <deller@gmx.de> * (enable driver on big-endian machines (hppa), ioctl fixes) * 12/2002 Helge Deller <deller@gmx.de> * (port driver to new frambuffer infrastructure) * 01/2003 Helge Deller <deller@gmx.de> * (initial work on fb hardware acceleration for voodoo2) * 08/2006 Alan Cox <alan@redhat.com> * Remove never finished and bogus 24/32bit support * Clean up macro abuse * Minor tidying for format. * 12/2006 Helge Deller <deller@gmx.de> * add /sys/class/graphics/fbX/vgapass sysfs-interface * add module option "mode_option" to set initial screen mode * use fbdev default videomode database * remove debug functions from ioctl
*/
/* * The voodoo1 has the following memory mapped address space: * 0x000000 - 0x3fffff : registers (4MB) * 0x400000 - 0x7fffff : linear frame buffer (4MB) * 0x800000 - 0xffffff : texture memory (8MB)
*/
/* * misc notes, TODOs, toASKs, and deep thoughts
-TODO: at one time or another test that the mode is acceptable by the monitor -ASK: Can I choose different ordering for the color bitfields (rgba argb ...) which one should i use ? is there any preferred one ? It seems ARGB is the one ... -TODO: in set_var check the validity of timings (hsync vsync)... -TODO: check and recheck the use of sst_wait_idle : we don't flush the fifo via a nop command. so it's ok as long as the commands we pass don't go through the fifo. warning: issuing a nop command seems to need pci_fifo -FIXME: in case of failure in the init sequence, be sure we return to a safe state. - FIXME: Use accelerator for 2D scroll -FIXME: 4MB boards have banked memory (FbiInit2 bits 1 & 20)
*/
/* * debug info * SST_DEBUG : enable debugging * SST_DEBUG_REG : debug registers * 0 : no debug * 1 : dac calls, [un]set_bits, FbiInit * 2 : insane debug level (log every register read/write) * SST_DEBUG_FUNC : functions * 0 : no debug * 1 : function call / debug ioctl * 2 : variables * 3 : flood . you don't want to do that. trust me. * SST_DEBUG_VAR : debug display/var structs * 0 : no debug * 1 : dumps display, fb_var * * sstfb specific ioctls: * toggle vga (0x46db) : toggle vga_pass_through
*/
/* * wait for the fbi chip. ASK: what happens if the fbi is stuck ? * * the FBI is supposed to be ready if we receive 5 time * in a row a "idle" answer to our requests
*/
staticint __sst_wait_idle(u8 __iomem *vbase)
{ int count = 0;
/* if (doFBINOP) __sst_write(vbase, NOPCMD, 0); */
while(1) { if (__sst_read(vbase, STATUS) & STATUS_FBI_BUSY) {
f_dddprintk("status: busy\n"); /* FIXME basically, this is a busy wait. maybe not that good. oh well; * this is a small loop after all.
* Or maybe we should use mdelay() or udelay() here instead ? */
count = 0;
} else {
count++;
f_dddprintk("status: idle(%d)\n", count);
} if (count >= 5) return 1; /* XXX do something to avoid hanging the machine if the voodoo is out */
}
}
/* dac access */ /* dac_read should be remaped to FbiInit2 (via the pci reg init_enable) */ static u8 __sst_dac_read(u8 __iomem *vbase, u8 reg)
{
u8 ret;
/* compute the m,n,p , returns the real freq * (ics datasheet : N <-> N1 , P <-> N2) * * Fout= Fref * (M+2)/( 2^P * (N+2)) * we try to get close to the asked freq * with P as high, and M as low as possible * range: * ti/att : 0 <= M <= 255; 0 <= P <= 3; 0<= N <= 63 * ics : 1 <= M <= 127; 0 <= P <= 3; 1<= N <= 31 * we'll use the lowest limitation, should be precise enouth
*/ staticint sst_calc_pll(constint freq, int *freq_out, struct pll_timing *t)
{ int m, m2, n, p, best_err, fout; int best_n = -1; int best_m = -1;
best_err = freq;
p = 3; /* f * 2^P = vco should be less than VCOmax ~ 250 MHz for ics*/ while (((1 << p) * freq > VCO_MAX) && (p >= 0))
p--; if (p == -1) return -EINVAL; for (n = 1; n < 32; n++) { /* calc 2 * m so we can round it later*/
m2 = (2 * freq * (1 << p) * (n + 2) ) / DAC_FREF - 4 ;
m = (m2 % 2 ) ? m2/2+1 : m2/2 ; if (m >= 128) break;
fout = (DAC_FREF * (m + 2)) / ((1 << p) * (n + 2)); if ((abs(fout - freq) < best_err) && (m > 0)) {
best_n = n;
best_m = m;
best_err = abs(fout - freq); /* we get the lowest m , allowing 0.5% error in freq*/ if (200*best_err < freq) break;
}
} if (best_n == -1) /* unlikely, but who knows ? */ return -EINVAL;
t->p = p;
t->n = best_n;
t->m = best_m;
*freq_out = (DAC_FREF * (t->m + 2)) / ((1 << t->p) * (t->n + 2));
f_ddprintk ("m: %d, n: %d, p: %d, F: %dKhz\n",
t->m, t->n, t->p, *freq_out); return 0;
}
/** * sstfb_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 * * Limit to the abilities of a single chip as SLI is not supported * by this driver.
*/
staticint sstfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct sstfb_par *par = info->par; int hSyncOff = var->xres + var->right_margin + var->left_margin; int vSyncOff = var->yres + var->lower_margin + var->upper_margin; int vBackPorch = var->left_margin, yDim = var->yres; int vSyncOn = var->vsync_len; int tiles_in_X, real_length; unsignedint freq;
if (sst_calc_pll(PICOS2KHZ(var->pixclock), &freq, &par->pll)) {
printk(KERN_ERR "sstfb: Pixclock at %ld KHZ out of range\n",
PICOS2KHZ(var->pixclock)); return -EINVAL;
}
var->pixclock = KHZ2PICOS(freq);
/* it seems that the fbi uses tiles of 64x16 pixels to "map" the mem */ /* FIXME: i don't like this... looks wrong */
real_length = tiles_in_X * (IS_VOODOO2(par) ? 32 : 64 )
* ((var->bits_per_pixel == 16) ? 2 : 4);
if (real_length * yDim > info->fix.smem_len) {
printk(KERN_ERR "sstfb: Not enough video memory\n"); return -ENOMEM;
}
fbiinit1 = (sst_read(FBIINIT1) & VIDEO_MASK)
| EN_DATA_OE
| EN_BLANK_OE
| EN_HVSYNC_OE
| EN_DCLK_OE /* | (15 << TILES_IN_X_SHIFT) */
| SEL_INPUT_VCLK_2X /* | (2 << VCLK_2X_SEL_DEL_SHIFT)
| (2 << VCLK_DEL_SHIFT) */ /* try with vclk_in_delay =0 (bits 29:30) , vclk_out_delay =0 (bits(27:28) in (near) future set them accordingly to revision + resolution (cf glide) first understand what it stands for :) FIXME: there are some artefacts... check for the vclk_in_delay lets try with 6ns delay in both vclk_out & in... doh... they're still there :\
*/
ntiles = par->tiles_in_X; if (IS_VOODOO2(par)) {
fbiinit1 |= ((ntiles & 0x20) >> 5) << TILES_IN_X_MSB_SHIFT
| ((ntiles & 0x1e) >> 1) << TILES_IN_X_SHIFT; /* as the only value of importance for us in fbiinit6 is tiles in X (lsb), and as reading fbinit 6 will return crap (see FBIINIT6_DEFAULT) we just write our value. BTW due to the dac unable to read odd number of tiles, this
field is always null ... */
fbiinit6 = (ntiles & 0x1) << TILES_IN_X_LSB_SHIFT;
} else
fbiinit1 |= ntiles << TILES_IN_X_SHIFT;
/* set lfbmode : set mode + front buffer for reads/writes
+ disable pipeline */ switch (info->var.bits_per_pixel) { case 16:
lfbmode = LFB_565; break; default: return -EINVAL;
}
#ifdefined(__BIG_ENDIAN) /* Enable byte-swizzle functionality in hardware. * With this enabled, all our read- and write-accesses to * the voodoo framebuffer can be done in native format, and * the hardware will automatically convert it to little-endian.
* - tested on HP-PARISC, Helge Deller <deller@gmx.de> */
lfbmode |= ( LFB_WORD_SWIZZLE_WR | LFB_BYTE_SWIZZLE_WR |
LFB_WORD_SWIZZLE_RD | LFB_BYTE_SWIZZLE_RD ); #endif
if (clipping) {
sst_write(LFBMODE, lfbmode | EN_PXL_PIPELINE); /* * Set "clipping" dimensions. If clipping is disabled and * writes to offscreen areas of the framebuffer are performed, * the "behaviour is undefined" (_very_ undefined) - Urs
*/ /* btw, it requires enabling pixel pipeline in LFBMODE . off screen read/writes will just wrap and read/print pixels
on screen. Ugly but not that dangerous */
f_ddprintk("setting clipping dimensions 0..%d, 0..%d\n",
info->var.xres - 1, par->yDim - 1);
sst_write(CLIP_LEFT_RIGHT, info->var.xres);
sst_write(CLIP_LOWY_HIGHY, par->yDim);
sst_set_bits(FBZMODE, EN_CLIPPING | EN_RGB_WRITE);
} else { /* no clipping : direct access, no pipeline */
sst_write(LFBMODE, lfbmode);
} return 0;
}
/** * sstfb_setcolreg - Optional function. Sets a color register. * @regno: hardware colormap register * @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
*/ staticint sstfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{ struct sstfb_par *par = info->par;
u32 col;
/* checks for 4mb lfb, then 2, then defaults to 1 */ if (readl(fbbase_virt + 0x200000) == 0xdeadbeef)
*memsize = 0x400000; elseif (readl(fbbase_virt + 0x100000) == 0xdeadbeef)
*memsize = 0x200000; else
*memsize = 0x100000;
f_ddprintk("detected memsize: %dMB\n", *memsize >> 20); return 1;
}
/* * DAC detection routines
*/
/* fbi should be idle, and fifo emty and mem disabled */ /* supposed to detect AT&T ATT20C409 and Ti TVP3409 ramdacs */
staticint sst_detect_att(struct fb_info *info)
{ struct sstfb_par *par = info->par; int i, mir, dir;
for (i = 0; i < 3; i++) {
sst_dac_write(DACREG_WMA, 0); /* backdoor */
sst_dac_read(DACREG_RMR); /* read 4 times RMR */
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR); /* the fifth time, CR0 is read */
sst_dac_read(DACREG_RMR); /* the 6th, manufacturer id register */
mir = sst_dac_read(DACREG_RMR); /*the 7th, device ID register */
dir = sst_dac_read(DACREG_RMR);
f_ddprintk("mir: %#x, dir: %#x\n", mir, dir); if (mir == DACREG_MIR_ATT && dir == DACREG_DIR_ATT) { return 1;
}
} return 0;
}
staticint sst_detect_ti(struct fb_info *info)
{ struct sstfb_par *par = info->par; int i, mir, dir;
for (i = 0; i<3; i++) {
sst_dac_write(DACREG_WMA, 0); /* backdoor */
sst_dac_read(DACREG_RMR); /* read 4 times RMR */
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR); /* the fifth time, CR0 is read */
sst_dac_read(DACREG_RMR); /* the 6th, manufacturer id register */
mir = sst_dac_read(DACREG_RMR); /*the 7th, device ID register */
dir = sst_dac_read(DACREG_RMR);
f_ddprintk("mir: %#x, dir: %#x\n", mir, dir); if ((mir == DACREG_MIR_TI ) && (dir == DACREG_DIR_TI)) { return 1;
}
} return 0;
}
/* * try to detect ICS5342 ramdac * we get the 1st byte (M value) of preset f1,f7 and fB * why those 3 ? mmmh... for now, i'll do it the glide way... * and ask questions later. anyway, it seems that all the freq registers are * really at their default state (cf specs) so i ask again, why those 3 regs ? * mmmmh.. it seems that's much more ugly than i thought. we use f0 and fA for * pll programming, so in fact, we *hope* that the f1, f7 & fB won't be * touched... * is it really safe ? how can i reset this ramdac ? geee...
*/ staticint sst_detect_ics(struct fb_info *info)
{ struct sstfb_par *par = info->par; int m_clk0_1, m_clk0_7, m_clk1_b; int n_clk0_1, n_clk0_7, n_clk1_b; int i;
sst_dac_write(DACREG_WMA, 0);
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR);
sst_dac_read(DACREG_RMR);
sst_dac_write(DACREG_RMR, (cr0 & 0xf0)
| DACREG_CR0_EN_INDEXED
| DACREG_CR0_8BIT
| DACREG_CR0_PWDOWN ); /* so, now we are in indexed mode . dunno if its common, but
i find this way of doing things a little bit weird :p */
switch(bpp) { case 16:
sst_dac_write(DACREG_ICS_CMD, DACREG_ICS_CMD_16BPP); break; default:
dprintk("%s: bad depth '%u'\n", __func__, bpp); break;
}
}
/* * detect dac type * prerequisite : write to FbiInitx enabled, video and fbi and pci fifo reset, * dram refresh disabled, FbiInit remaped. * TODO: mmh.. maybe i should put the "prerequisite" in the func ...
*/
staticint sst_detect_dactype(struct fb_info *info, struct sstfb_par *par)
{ int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(dacs); i++) {
ret = dacs[i].detect(info); if (ret) break;
} if (!ret) return 0;
f_dprintk("%s found %s\n", __func__, dacs[i].name);
par->dac_sw = dacs[i]; return 1;
}
/* disable dram refresh */
sst_unset_bits(FBIINIT2, EN_DRAM_REFRESH);
sst_wait_idle(); /* remap fbinit2/3 to dac */
pci_write_config_dword(dev, PCI_INIT_ENABLE,
PCI_EN_INIT_WR | PCI_REMAP_DAC ); /* detect dac type */ if (!sst_detect_dactype(info, par)) {
printk(KERN_ERR "sstfb: unknown dac type.\n"); //FIXME watch it: we are not in a safe state, bad bad bad. return 0;
}
/* set graphic clock */
gfx_clock = spec->default_gfx_clock; if ((gfxclk >10 ) && (gfxclk < spec->max_gfxclk)) {
printk(KERN_INFO "sstfb: Using supplied graphic freq : %dMHz\n", gfxclk);
gfx_clock = gfxclk *1000;
} elseif (gfxclk) {
printk(KERN_WARNING "sstfb: %dMhz is way out of spec! Using default\n", gfxclk);
}
fix->type = FB_TYPE_PACKED_PIXELS;
fix->visual = FB_VISUAL_TRUECOLOR;
fix->accel = FB_ACCEL_NONE; /* FIXME */ /* * According to the specs, the linelength must be of 1024 *pixels* * and the 24bpp mode is in fact a 32 bpp mode (and both are in * fact dithered to 16bit).
*/
fix->line_length = 2048; /* default value, for 24 or 32bit: 4096 */
MODULE_AUTHOR("(c) 2000,2002 Ghozlane Toumi ");
MODULE_DESCRIPTION("FBDev driver for 3dfx Voodoo Graphics and Voodoo2 based video boards");
MODULE_LICENSE("GPL");
module_param(mem, int, 0);
MODULE_PARM_DESC(mem, "Size of frame buffer memory in MB (1, 2, 4 MB, default=autodetect)");
module_param(vgapass, bool, 0);
MODULE_PARM_DESC(vgapass, "Enable VGA PassThrough mode (0 or 1) (default=0)");
module_param(clipping, bool, 0);
MODULE_PARM_DESC(clipping, "Enable clipping (slower, safer) (0 or 1) (default=1)");
module_param(gfxclk, int, 0);
MODULE_PARM_DESC(gfxclk, "Force graphic chip frequency in MHz. DANGEROUS. (default=auto)");
module_param(slowpci, bool, 0);
MODULE_PARM_DESC(slowpci, "Uses slow PCI settings (0 or 1) (default=0)");
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "Initial video mode (default=" DEFAULT_VIDEO_MODE ")");
Messung V0.5
¤ Dauer der Verarbeitung: 0.56 Sekunden
(vorverarbeitet)
¤
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.