// 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
*/
#undef SST_DEBUG
/*
* Includes
*/
#include <linux/aperture.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <video/sstfb.h>
/* initialized by setup */
static bool vgapass; /* enable VGA passthrough cable */
static int mem; /* mem size in MB, 0 = autodetect */
static bool clipping = 1; /* use clipping (slower, safer) */
static int gfxclk; /* force FBI freq in Mhz . Dangerous */
static bool slowpci; /* slow PCI settings */
/*
Possible default video modes: 800x600@60, 640x480@75, 1024x768@76, 640x480@60
*/
#define DEFAULT_VIDEO_MODE "640x480@60"
static char *mode_option = DEFAULT_VIDEO_MODE;
enum {
ID_VOODOO1 = 0,
ID_VOODOO2 = 1,
};
#define IS_VOODOO2(par) ((par)->type == ID_VOODOO2)
static struct sst_spec voodoo_spec[] = {
{ .name = "Voodoo Graphics", .default_gfx_clock = 50000, .max_gfxclk = 60 },
{ .name = "Voodoo2", .default_gfx_clock = 75000, .max_gfxclk = 85 },
};
/*
* debug functions
*/
#if (SST_DEBUG_REG > 0)
static void sst_dbg_print_read_reg(u32 reg, u32 val) {
const char *regname;
switch (reg) {
case FBIINIT0: regname = "FbiInit0"; break;
case FBIINIT1: regname = "FbiInit1"; break;
case FBIINIT2: regname = "FbiInit2"; break;
case FBIINIT3: regname = "FbiInit3"; break;
case FBIINIT4: regname = "FbiInit4"; break;
case FBIINIT5: regname = "FbiInit5"; break;
case FBIINIT6: regname = "FbiInit6"; break;
default: regname = NULL; break;
}
if (regname == NULL)
r_ddprintk("sst_read(%#x): %#x\n", reg, val);
else
r_dprintk(" sst_read(%s): %#x\n", regname, val);
}
static void sst_dbg_print_write_reg(u32 reg, u32 val) {
const char *regname;
switch (reg) {
case FBIINIT0: regname = "FbiInit0"; break;
case FBIINIT1: regname = "FbiInit1"; break;
case FBIINIT2: regname = "FbiInit2"; break;
case FBIINIT3: regname = "FbiInit3"; break;
case FBIINIT4: regname = "FbiInit4"; break;
case FBIINIT5: regname = "FbiInit5"; break;
case FBIINIT6: regname = "FbiInit6"; break;
default: regname = NULL; break;
}
if (regname == NULL)
r_ddprintk("sst_write(%#x, %#x)\n", reg, val);
else
r_dprintk(" sst_write(%s, %#x)\n", regname, val);
}
#else /* (SST_DEBUG_REG > 0) */
# define sst_dbg_print_read_reg(reg, val) do {} while(0)
# define sst_dbg_print_write_reg(reg, val) do {} while(0)
#endif /* (SST_DEBUG_REG > 0) */
/*
* hardware access functions
*/
/* register access */
#define sst_read(reg) __sst_read(par->mmio_vbase, reg)
#define sst_write(reg,val) __sst_write(par->mmio_vbase, reg, val)
#define sst_set_bits(reg,val) __sst_set_bits(par->mmio_vbase, reg, val)
#define sst_unset_bits(reg,val) __sst_unset_bits(par->mmio_vbase, reg, val)
#define sst_dac_read(reg) __sst_dac_read(par->mmio_vbase, reg)
#define sst_dac_write(reg,val) __sst_dac_write(par->mmio_vbase, reg, val)
#define dac_i_read(reg) __dac_i_read(par->mmio_vbase, reg)
#define dac_i_write(reg,val) __dac_i_write(par->mmio_vbase, reg, val)
static inline u32 __sst_read(u8 __iomem *vbase, u32 reg)
{
u32 ret = readl(vbase + reg);
sst_dbg_print_read_reg(reg, ret);
return ret;
}
static inline void __sst_write(u8 __iomem *vbase, u32 reg, u32 val)
{
sst_dbg_print_write_reg(reg, val);
writel(val, vbase + reg);
}
static inline void __sst_set_bits(u8 __iomem *vbase, u32 reg, u32 val)
{
r_dprintk("sst_set_bits(%#x, %#x)\n", reg, val);
__sst_write(vbase, reg, __sst_read(vbase, reg) | val);
}
static inline void __sst_unset_bits(u8 __iomem *vbase, u32 reg, u32 val)
{
r_dprintk("sst_unset_bits(%#x, %#x)\n", reg, val);
__sst_write(vbase, reg, __sst_read(vbase, reg) & ~val);
}
/*
* 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
*/
#define sst_wait_idle() __sst_wait_idle(par->mmio_vbase)
static int __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 han
|