Commit 0e2e212c authored by James Simmons's avatar James Simmons

Anothe rattempt at commting.

parent 864d65fb
Intel 810/815 Framebuffer driver
Tony Daplas <adaplas@pol.net>
http://i810fb.sourceforge.net
March 17, 2002
First Released: July 2001
================================================================
A. Introduction
This is a framebuffer driver for various Intel 810/815 compatible
graphics devices. These would include:
Intel 810
Intel 810E
Intel 810-DC100
Intel 815 Internal graphics only, 100Mhz FSB
Intel 815 Internal graphics only
Intel 815 Internal graphics and AGP
B. Features
- Choice of using Discrete Video Timings, VESA Generalized Timing
Formula, or a framebuffer specific database to set the video mode
- Supports a variable range of horizontal and vertical resolution, and
vertical refresh rates if the VESA Generalized Timing Formula is
enabled.
- Supports color depths of 8, 16, 24 and 32 bits per pixel
- Supports pseudocolor, directcolor, or truecolor visuals
- Full and optimized hardware acceleration at 8, 16 and 24 bpp
- Robust video state save and restore
- MTRR support
- Utilizes user-entered monitor specifications to automatically
calculate required video mode parameters.
- Can concurrently run with xfree86 running with native i810 drivers
- Hardware Cursor Support
C. List of available options
a. "video=i810fb"
enables the i810 driver
Recommendation: required
b. "xres:<value>"
select horizontal resolution in pixels
Recommendation: user preference
(default = 640)
c. "yres:<value>"
select vertical resolution in scanlines. If Discrete Video Timings
is enabled, this will be ignored and computed as 3*xres/4.
Recommendation: user preference
(default = 480)
d. "vyres:<value>"
select virtual vertical resolution in scanlines. If (0) or none
is specified, this will be computed against maximum available memory.
Recommendation: do not set
(default = 480)
e. "vram:<value>"
select amount of system RAM in MB to allocate for the video memory
Recommendation: 1 - 4 MB.
(default = 4)
f. "bpp:<value>"
select desired pixel depth
Recommendation: 8
(default = 8)
g. "hsync1/hsync2:<value>"
select the minimum and maximum Horizontal Sync Frequency of the
monitor in KHz. If a using a fixed frequency monitor, hsync1 must
be equal to hsync2.
Recommendation: check monitor manual for correct values
default (29/30)
h. "vsync1/vsync2:<value>"
select the minimum and maximum Vertical Sync Frequency of the monitor
in Hz. You can also use this option to lock your monitor's refresh
rate.
Recommendation: check monitor manual for correct values
(default = 60/60)
IMPORTANT: If you need to clamp your timings, try to give some
leeway for computational errors (over/underflows). Example: if
using vsync1/vsync2 = 60/60, make sure hsync1/hsync2 has at least
a 1 unit difference, and vice versa.
i. "voffset:<value>"
select at what offset in MB of the logical memory to allocate the
framebuffer memory. The intent is to avoid the memory blocks
used by standard graphics applications (XFree86). The default
offset (16 MB for a 64MB aperture, 8 MB for a 32MB aperture) will
avoid XFree86's usage and allows up to 7MB/15MB of framebuffer
memory. Depending on your usage, adjust the value up or down,
(0 for maximum usage, 31/63 MB for the least amount). Note, an
arbitrary setting may conflict with XFree86.
Recommendation: do not set
(default = 8 or 16 MB)
j. "accel"
enable text acceleration. This can be enabled/reenabled anytime
by using 'fbset -accel true/false'.
Recommendation: enable
(default = not set)
k. "mtrr"
enable MTRR. This allows data transfers to the framebuffer memory
to occur in bursts which can significantly increase performance.
Not very helpful with the i810/i815 because of 'shared memory'.
Recommendation: do not set
(default = not set)
l. "extvga"
if specified, secondary/external VGA output will always be enabled.
Useful if the BIOS turns off the VGA port when no monitor is attached.
The external VGA monitor can then be attached without rebooting.
Recommendation: do not set
(default = not set)
m. "sync"
Forces the hardware engine to do a "sync" or wait for the hardware
to finish before starting another instruction. This will produce a
more stable setup, but will be slower.
Recommendation: do not set
(default = not set)
n. "dcolor"
Use directcolor visual instead of truecolor for pixel depths greater
than 8 bpp. Useful for color tuning, such as gamma control.
Recommendation: do not set
(default = not set)
D. Kernel booting
Separate each option/option-pair by commas (,) and the option from its value
with a colon (:) as in the following:
video=i810fb:option1,option2:value2
Sample Usage
------------
In /etc/lilo.conf, add the line:
append="video=i810fb:vram:2,xres:1024,yres:768,bpp:8,hsync1:30,hsync2:55, \
vsync1:50,vsync2:85,accel,mtrr"
This will initialize the framebuffer to 1024x768 at 8bpp. The framebuffer
will use 2 MB of System RAM. MTRR support will be enabled. The refresh rate
will be computed based on the hsync1/hsync2 and vsync1/vsync2 values.
IMPORTANT:
You must include hsync1, hsync2, vsync1 and vsync2 to enable video modes
better than 640x480 at 60Hz.
E. Module options
The module parameters are essentially similar to the kernel
parameters. The main difference is that you need to include a Boolean value
(1 for TRUE, and 0 for FALSE) for those options which don't need a value.
Example, to enable MTRR, include "mtrr=1".
Sample Usage
------------
Using the same setup as described above, load the module like this:
modprobe i810fb vram=2 xres=1024 bpp=8 hsync1=30 hsync2=55 vsync1=50 \
vsync2=85 accel=1 mtrr=1
Or just add the following to /etc/modules.conf
options i810fb vram=2 xres=1024 bpp=16 hsync1=30 hsync2=55 vsync1=50 \
vsync2=85 accel=1 mtrr=1
and just do a
modprobe i810fb
F. Setup
a. Do your usual method of configuring the kernel.
make menuconfig/xconfig/config
b. Under "Code Maturity Options", enable "Prompt for experimental/
incomplete code/drivers".
c. Enable agpgart support for the Intel 810/815 on-board graphics.
This is required. The option is under "Character Devices"
d. Under "Graphics Support", select "Intel 810/815" either statically
or as a module. Choose "use VESA GTF for video timings" if you
need to maximize the capability of your display. To be on the
safe side, you can leave this unselected.
e. If you want a framebuffer console, enable it under "Console
Drivers"
f. Compile your kernel.
g. Load the driver as described in section D and E.
Optional:
h. If you are going to run XFree86 with its native drivers, the
standard XFree86 4.1.0 and 4.2.0 drivers should work as is.
However, there's a bug in the XFree86 i810 drivers. It attempts
to use XAA even when switched to the console. This will crash
your server. I have a fix at this site:
http://i810fb.sourceforge.net.
You can either use the patch, or just replace
/usr/X11R6/lib/modules/drivers/i810_drv.o
with the one provided at the website.
i. Try the DirectFB (http://www.directfb.org) + the i810 gfxdriver
patch to see the chipset in action (or inaction :-).
G. Acknowledgment:
1. Geert Uytterhoeven - his excellent howto and the virtual
framebuffer driver code made this possible.
2. Jeff Hartmann for his agpgart code.
3. The X developers. Insights were provided just by reading the
XFree86 source code.
4. Intel(c). For this value-oriented chipset driver and for
providing documentation.
5. Matt Sottek. His inputs and ideas helped in making some
optimizations possible.
H. Home Page:
A more complete, and probably updated information is provided at
http://i810fb.sourceforge.net.
###########################
Tony
......@@ -471,6 +471,44 @@ config FB_RIVA
module will be called rivafb.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
config FB_I810
tristate "Intel 810/815 support (EXPERIMENTAL)"
depends on FB && AGP && AGP_INTEL && EXPERIMENTAL && PCI
help
This driver supports the on-board graphics built in to the Intel 810
and 815 chipsets. Say Y if you have and plan to use such a board.
The driver is also available as a module ( = code which can be
inserted and removed from the running kernel whenever you want).
The module will be called i810fb.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
For more information, please read
<file:Documentation/fb/intel810.txt>
config FB_I810_GTF
bool "use VESA Generalized Timing Formula"
depends on FB_I810
help
If you say Y, then the VESA standard, Generalized Timing Formula
or GTF, will be used to calculate the required video timing values
per video mode. Since the GTF allows nondiscrete timings
(nondiscrete being a range of values as opposed to discrete being a
set of values), you'll be able to use any combination of horizontal
and vertical resolutions, and vertical refresh rates without having
to specify your own timing parameters. This is especially useful
to maximize the performance of an aging display, or if you just
have a display with nonstandard dimensions. A VESA compliant
monitor is recommended, but can still work with non-compliant ones.
If you need or want this, then select this option. The timings may
not be compliant with Intel's recommended values. Use at your own
risk.
If you say N, the driver will revert to discrete video timings
using a set recommended by Intel in their documentation.
If unsure, say N.
config FB_MATROX
tristate "Matrox acceleration"
depends on FB && PCI
......
......@@ -12,8 +12,7 @@ export-objs := fbmem.o fbcmap.o fbmon.o modedb.o softcursor.o cfbfillrect.o
obj-$(CONFIG_VT) += console/
# Add fbmon.o back into obj-$(CONFIG_FB) in 2.5.x
obj-$(CONFIG_FB) += fbmem.o fbcmap.o modedb.o softcursor.o
obj-$(CONFIG_FB) += fbmem.o fbmon.o fbcmap.o modedb.o softcursor.o
# Only include macmodes.o if we have FB support and are PPC
ifeq ($(CONFIG_FB),y)
obj-$(CONFIG_PPC) += macmodes.o
......@@ -42,7 +41,7 @@ obj-$(CONFIG_FB_SGIVW) += sgivwfb.o cfbfillrect.o cfbcopyarea.o cfbim
obj-$(CONFIG_FB_3DFX) += tdfxfb.o
obj-$(CONFIG_FB_MAC) += macfb.o macmodes.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_HP300) += hpfb.o cfbfillrect.o cfbimgblt.o
obj-$(CONFIG_FB_OF) += offb.o cfbfillrect.o cfbimgblit.o cfbcopyarea.o
obj-$(CONFIG_FB_OF) += offb.o cfbfillrect.o cfbimgblt.o cfbcopyarea.o
obj-$(CONFIG_FB_IMSTT) += imsttfb.o
obj-$(CONFIG_FB_RETINAZ3) += retz3fb.o
obj-$(CONFIG_FB_CLGEN) += clgenfb.o
......@@ -74,6 +73,8 @@ obj-$(CONFIG_FB_RIVA) += riva/ cfbfillrect.o cfbcopyarea.o \
cfbimgblt.o vgastate.o
obj-$(CONFIG_FB_SIS) += sis/
obj-$(CONFIG_FB_ATY) += aty/ cfbimgblt.o
obj-$(CONFIG_FB_I810) += i810/ cfbfillrect.o cfbcopyarea.o \
cfbimgblt.o vgastate.o
obj-$(CONFIG_FB_SUN3) += sun3fb.o
obj-$(CONFIG_FB_BWTWO) += bwtwofb.o
......
......@@ -976,7 +976,7 @@ static int atyfb_release(struct fb_info *info, int user)
var.yres_virtual =
var.yres;
}
gen_set_var(&var, -1, info);
fb_set_var(&var, -1, info);
}
}
}
......
......@@ -103,10 +103,10 @@ static inline void color_imageblit(struct fb_image *image, struct fb_info *p, u8
{
/* Draw the penguin */
int i, n;
unsigned long bitmask = SHIFT_LOW(~0UL, BITS_PER_LONG - p->var.bits_per_pixel);
int bpp = p->var.bits_per_pixel;
unsigned long *palette = (unsigned long *) p->pseudo_palette;
unsigned long *dst, *dst2, color = 0, val, shift;
unsigned long null_bits = BITS_PER_LONG - p->var.bits_per_pixel;
unsigned long null_bits = BITS_PER_LONG - bpp;
u8 *src = image->data;
dst2 = (unsigned long *) dst1;
......@@ -125,9 +125,10 @@ static inline void color_imageblit(struct fb_image *image, struct fb_info *p, u8
while (n--) {
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR )
color = palette[*src] & bitmask;
color = palette[*src];
else
color = *src & bitmask;
color = *src;
color <<= LEFT_POS(bpp);
val |= SHIFT_HIGH(color, shift);
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
......@@ -136,7 +137,7 @@ static inline void color_imageblit(struct fb_image *image, struct fb_info *p, u8
else
val = SHIFT_LOW(color, BITS_PER_LONG - shift);
}
shift += p->var.bits_per_pixel;
shift += bpp;
shift &= (BITS_PER_LONG - 1);
src++;
}
......
......@@ -929,11 +929,11 @@ static void fbcon_set_display(int con, int init, int logo)
struct display *q = &fb_display[i];
struct vc_data *tmp = vc_cons[i].d;
if (fontwidthvalid(p, vc->vc_font.width)) {
if (!fontwidthvalid(p, vc->vc_font.width)) {
/* If we are not the first console on this
fb, copy the font from that console */
tmp->vc_font.width = vc->vc_font.width;
tmp->vc_font.height = vc->vc_font.height;
vc->vc_font.width = tmp->vc_font.width;
vc->vc_font.height = tmp->vc_font.height;
p->fontdata = q->fontdata;
p->userfont = q->userfont;
if (p->userfont) {
......
......@@ -141,6 +141,8 @@ extern int pvr2fb_init(void);
extern int pvr2fb_setup(char*);
extern int sstfb_init(void);
extern int sstfb_setup(char*);
extern int i810fb_init(void);
extern int i810fb_setup(char*);
static struct {
const char *name;
......@@ -235,6 +237,9 @@ static struct {
#ifdef CONFIG_FB_TRIDENT
{ "trident", tridentfb_init, tridentfb_setup },
#endif
#ifdef CONFIG_FB_I810
{ "i810fb", i810fb_init, i810fb_setup },
#endif
/*
* Generic drivers that are used as fallbacks
......
/*
* linux/drivers/video/fbmon.c
*
* Copyright (C) 1999 James Simmons
* Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
*
* 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.
*
* Notes:
* This code handles the different types of monitors that are out their.
* Most video cards for example can support a mode like 800x600 but fix
* frequency monitors can't. So the code here checks if the monitor can
* support the mode as well as the card can. Fbmonospecs takes on
* different meaning with different types of monitors. For multifrequency
* monitors fbmonospecs represents the range of frequencies the monitor
* can support. Only one fbmonospec needs to be allocated. The fbmonospecs
* pointer in fb_info points to this one. If you specific a mode that has
* timing greater than the allowed range then setting the video mode will
* fail. With multifrequency monitors you can set any mode you like as long
* as you have a programmable clock on the video card.
* With fixed frequency monitors you have only a SET of very narrow
* allowed frequency ranges. So for a fixed fequency monitor you have a
* array of fbmonospecs. The fbmonospecs in fb_info represents the
* monitor frequency for the CURRENT mode. If you change the mode and ask
* for fbmonospecs you will NOT get the same values as before. Note this
* is not true for multifrequency monitors where you do get the same
* fbmonospecs each time. Also the values in each fbmonospecs represent the
* very narrow frequency band for range. Well you can't have exactly the
* same frequencies from fixed monitor. So some tolerance is excepted.
* By DEFAULT all monitors are assumed fixed frequency since they are so
* easy to fry or screw up a mode with. Just try setting a 800x600 mode on
* one. After you boot you can run a simple program the tells what kind of
* monitor you have. If you have a multifrequency monitor then you can set
* any mode size you like as long as your video card has a programmable clock.
* By default also besides assuming you have a fixed frequency monitor it
* assumes the monitor only supports lower modes. This way for example you
* can't set a 1280x1024 mode on a fixed frequency monitor that can only
* support up to 1024x768.
*
*/
#include <linux/tty.h>
#include <linux/fb.h>
#include <linux/module.h>
int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
const struct fb_info *fb_info)
/*
* EDID parser
*
* portions of this file were based on the EDID parser by
* John Fremlin <vii@users.sourceforge.net> and Ani Joshi <ajoshi@unixbox.com>
*/
#define EDID_LENGTH 0x80
#define EDID_HEADER 0x00
#define EDID_HEADER_END 0x07
#define ID_MANUFACTURER_NAME 0x08
#define ID_MANUFACTURER_NAME_END 0x09
#define ID_MODEL 0x0a
#define ID_SERIAL_NUMBER 0x0c
#define MANUFACTURE_WEEK 0x10
#define MANUFACTURE_YEAR 0x11
#define EDID_STRUCT_VERSION 0x12
#define EDID_STRUCT_REVISION 0x13
#define DPMS_FLAGS 0x18
#define ESTABLISHED_TIMING_1 0x23
#define ESTABLISHED_TIMING_2 0x24
#define MANUFACTURERS_TIMINGS 0x25
#define DETAILED_TIMING_DESCRIPTIONS_START 0x36
#define DETAILED_TIMING_DESCRIPTION_SIZE 18
#define NO_DETAILED_TIMING_DESCRIPTIONS 4
#define DETAILED_TIMING_DESCRIPTION_1 0x36
#define DETAILED_TIMING_DESCRIPTION_2 0x48
#define DETAILED_TIMING_DESCRIPTION_3 0x5a
#define DETAILED_TIMING_DESCRIPTION_4 0x6c
#define DESCRIPTOR_DATA 5
#define UPPER_NIBBLE( x ) \
(((128|64|32|16) & (x)) >> 4)
#define LOWER_NIBBLE( x ) \
((1|2|4|8) & (x))
#define COMBINE_HI_8LO( hi, lo ) \
( (((unsigned)hi) << 8) | (unsigned)lo )
#define COMBINE_HI_4LO( hi, lo ) \
( (((unsigned)hi) << 4) | (unsigned)lo )
#define PIXEL_CLOCK_LO (unsigned)block[ 0 ]
#define PIXEL_CLOCK_HI (unsigned)block[ 1 ]
#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*1000
#define H_ACTIVE_LO (unsigned)block[ 2 ]
#define H_BLANKING_LO (unsigned)block[ 3 ]
#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] )
#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO )
#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] )
#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO )
#define V_ACTIVE_LO (unsigned)block[ 5 ]
#define V_BLANKING_LO (unsigned)block[ 6 ]
#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] )
#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO )
#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] )
#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO )
#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ]
#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ]
#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] )
#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] )
#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2))
#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2)
#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4)
#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6)
#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO )
#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO )
#define H_SYNC_WIDTH COMBINE_HI_4LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO )
#define H_SYNC_OFFSET COMBINE_HI_4LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO )
#define H_SIZE_LO (unsigned)block[ 12 ]
#define V_SIZE_LO (unsigned)block[ 13 ]
#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] )
#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] )
#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO )
#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO )
#define H_BORDER (unsigned)block[ 15 ]
#define V_BORDER (unsigned)block[ 16 ]
#define FLAGS (unsigned)block[ 17 ]
#define INTERLACED (FLAGS&128)
#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */
#define SYNC_SEPARATE (3<<3)
#define HSYNC_POSITIVE (FLAGS & 4)
#define VSYNC_POSITIVE (FLAGS & 2)
const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00
};
const unsigned char edid_v1_descriptor_flag[] = { 0x00, 0x00 };
static int edid_checksum(unsigned char *edid)
{
#if 0
/*
* long long divisions .... $#%%#$
*/
unsigned long long hpicos, vpicos;
const unsigned long long _1e12 = 1000000000000ULL;
const struct fb_monspecs *monspecs = &fb_info->monspecs;
hpicos = (unsigned long long)htotal*(unsigned long long)pixclock;
vpicos = (unsigned long long)vtotal*(unsigned long long)hpicos;
if (!vpicos)
return 0;
if (monspecs->hfmin == 0)
return 1;
if (hpicos*monspecs->hfmin > _1e12 || hpicos*monspecs->hfmax < _1e12 ||
vpicos*monspecs->vfmin > _1e12 || vpicos*monspecs->vfmax < _1e12)
return 0;
#endif
return 1;
unsigned char i, csum = 0;
for (i = 0; i < EDID_LENGTH; i++)
csum += edid[i];
if (csum == 0x00) {
/* checksum passed, everything's good */
return 1;
} else {
printk("EDID checksum failed, aborting\n");
return 0;
}
}
int fbmon_dpms(const struct fb_info *fb_info)
static int edid_check_header(unsigned char *edid)
{
return fb_info->monspecs.dpms;
if ((edid[0] != 0x00) || (edid[1] != 0xff) || (edid[2] != 0xff) ||
(edid[3] != 0xff) || (edid[4] != 0xff) || (edid[5] != 0xff) ||
(edid[6] != 0xff)) {
printk
("EDID header doesn't match EDID v1 header, aborting\n");
return 0;
}
return 1;
}
EXPORT_SYMBOL(fbmon_valid_timings);
static char *edid_get_vendor(unsigned char *block)
{
static char sign[4];
unsigned short h;
h = COMBINE_HI_8LO(block[0], block[1]);
sign[0] = ((h >> 10) & 0x1f) + 'A' - 1;
sign[1] = ((h >> 5) & 0x1f) + 'A' - 1;
sign[2] = (h & 0x1f) + 'A' - 1;
sign[3] = 0;
return sign;
}
static char *edid_get_monitor(unsigned char *block)
{
static char name[13];
unsigned i;
const unsigned char *ptr = block + DESCRIPTOR_DATA;
for (i = 0; i < 13; i++, ptr++) {
if (*ptr == 0xa) {
name[i] = 0x00;
return name;
}
name[i] = *ptr;
}
return name;
}
static int edid_is_timing_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00))
return 0;
else
return 1;
}
static int edid_is_monitor_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfc))
return 1;
else
return 0;
}
static void parse_timing_block(unsigned char *block,
struct fb_var_screeninfo *var)
{
var->xres = var->xres_virtual = H_ACTIVE;
var->yres = var->yres_virtual = V_ACTIVE;
var->height = var->width = -1;
var->right_margin = H_SYNC_OFFSET;
var->left_margin = (H_ACTIVE + H_BLANKING) -
(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
var->upper_margin = V_BLANKING - V_SYNC_OFFSET - V_SYNC_WIDTH;
var->lower_margin = V_SYNC_OFFSET;
var->hsync_len = H_SYNC_WIDTH;
var->vsync_len = V_SYNC_WIDTH;
var->pixclock = PIXEL_CLOCK;
var->pixclock /= 1000;
var->pixclock = KHZ2PICOS(var->pixclock);
if (HSYNC_POSITIVE)
var->sync |= FB_SYNC_HOR_HIGH_ACT;
if (VSYNC_POSITIVE)
var->sync |= FB_SYNC_VERT_HIGH_ACT;
}
int parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
{
int i;
unsigned char *block, *vendor, *monitor;
if (!(edid_checksum(edid)))
return 0;
if (!(edid_check_header(edid)))
return 0;
printk("EDID ver %d rev %d\n", (int) edid[EDID_STRUCT_VERSION],
(int) edid[EDID_STRUCT_REVISION]);
vendor = edid_get_vendor(edid + ID_MANUFACTURER_NAME);
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
if (edid_is_monitor_block(block)) {
monitor = edid_get_monitor(block);
}
}
printk("EDID: detected %s %s\n", vendor, monitor);
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
if (edid_is_timing_block(block)) {
parse_timing_block(block, var);
}
}
return 1;
}
char *get_EDID(struct pci_dev *pdev)
{
#ifdef CONFIG_ALL_PPC
static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", NULL };
unsigned char *pedid = NULL;
struct device_node *dp;
int i;
dp = pci_device_to_OF_node(pdev);
while (dp != NULL) {
for (i = 0; propnames[i] != NULL; ++i) {
pedid = (unsigned char *) get_property(dp, propnames[i], NULL);
if (pedid != NULL)
return pedid;
}
dp = dp->child;
}
return pedid;
#else
return NULL;
#endif
}
EXPORT_SYMBOL(parse_edid);
EXPORT_SYMBOL(get_EDID);
/*-*- linux-c -*-
* linux/drivers/video/i810.h -- Intel 810 General Definitions/Declarations
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
*
* 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.
*/
#ifndef __I810_H__
#define __I810_H__
#include <linux/list.h>
#include <linux/agp_backend.h>
#include <linux/fb.h>
#include <video/vga.h>
/* Fence */
#define TILEWALK_X (0 << 12)
#define TILEWALK_Y (1 << 12)
/* Raster ops */
#define COLOR_COPY_ROP 0xF0
#define PAT_COPY_ROP 0xCC
#define CLEAR_ROP 0x00
#define WHITE_ROP 0xFF
#define INVERT_ROP 0x55
#define XOR_ROP 0x5A
/* 2D Engine definitions */
#define SOLIDPATTERN 0x80000000
#define NONSOLID 0x00000000
#define BPP8 (0 << 24)
#define BPP16 (1 << 24)
#define BPP24 (2 << 24)
#define PIXCONF8 (2 << 16)
#define PIXCONF15 (4 << 16)
#define PIXCONF16 (5 << 16)
#define PIXCONF24 (6 << 16)
#define PIXCONF32 (7 << 16)
#define DYN_COLOR_EN (1 << 26)
#define DYN_COLOR_DIS (0 << 26)
#define INCREMENT 0x00000000
#define DECREMENT (0x01 << 30)
#define ARB_ON 0x00000001
#define ARB_OFF 0x00000000
#define SYNC_FLIP 0x00000000
#define ASYNC_FLIP 0x00000040
#define OPTYPE_MASK 0xE0000000
#define PARSER_MASK 0x001F8000
#define D2_MASK 0x001FC000 /* 2D mask */
/* Instruction type */
/* There are more but pertains to 3D */
#define PARSER 0x00000000
#define BLIT (0x02 << 29)
#define RENDER (0x03 << 29)
/* Parser */
#define NOP 0x00 /* No operation, padding */
#define BP_INT (0x01 << 23) /* Breakpoint interrupt */
#define USR_INT (0x02 << 23) /* User interrupt */
#define WAIT_FOR_EVNT (0x03 << 23) /* Wait for event */
#define FLUSH (0x04 << 23)
#define CONTEXT_SEL (0x05 << 23)
#define REPORT_HEAD (0x07 << 23)
#define ARB_ON_OFF (0x08 << 23)
#define OVERLAY_FLIP (0x11 << 23)
#define LOAD_SCAN_INC (0x12 << 23)
#define LOAD_SCAN_EX (0x13 << 23)
#define FRONT_BUFFER (0x14 << 23)
#define DEST_BUFFER (0x15 << 23)
#define Z_BUFFER (0x16 << 23)
#define STORE_DWORD_IMM (0x20 << 23)
#define STORE_DWORD_IDX (0x21 << 23)
#define BATCH_BUFFER (0x30 << 23)
/* Blit */
#define SETUP_BLIT 0x00
#define SETUP_MONO_PATTERN_SL_BLT (0x10 << 22)
#define PIXEL_BLT (0x20 << 22)
#define SCANLINE_BLT (0x21 << 22)
#define TEXT_BLT (0x22 << 22)
#define TEXT_IMM_BLT (0x30 << 22)
#define COLOR_BLT (0x40 << 22)
#define MONO_PAT_BLIT (0x42 << 22)
#define SOURCE_COPY_BLIT (0x43 << 22)
#define MONO_SOURCE_COPY_BLIT (0x44 << 22)
#define SOURCE_COPY_IMMEDIATE (0x60 << 22)
#define MONO_SOURCE_COPY_IMMEDIATE (0x61 << 22)
#define VERSION_MAJOR 0
#define VERSION_MINOR 9
#define VERSION_TEENIE 0
#define BRANCH_VERSION ""
/* mvo: intel i815 */
#ifndef PCI_DEVICE_ID_INTEL_82815_100
#define PCI_DEVICE_ID_INTEL_82815_100 0x1102
#endif
#ifndef PCI_DEVICE_ID_INTEL_82815_NOAGP
#define PCI_DEVICE_ID_INTEL_82815_NOAGP 0x1112
#endif
#ifndef PCI_DEVICE_ID_INTEL_82815_FULL_CTRL
#define PCI_DEVICE_ID_INTEL_82815_FULL_CTRL 0x1130
#endif
/* General Defines */
#define I810_PAGESIZE 4096
#define MAX_DMA_SIZE (1024 * 4096)
#define SAREA_SIZE 4096
#define PCI_I810_MISCC 0x72
#define MMIO_SIZE (512*1024)
#define GTT_SIZE (16*1024)
#define RINGBUFFER_SIZE (64*1024)
#define PIXMAP_SIZE (4 * 4096)
#define CURSOR_SIZE 4096
#define OFF 0
#define ON 1
#define MAX_KEY 256
#define WAIT_COUNT 10000000
#define IRING_PAD 8
#define FONTDATAMAX 8192
/* Masks (AND ops) and OR's */
#define FB_START_MASK (0x3f << (32 - 6))
#define MMIO_ADDR_MASK (0x1FFF << (32 - 13))
#define FREQ_MASK 0x1EF
#define SCR_OFF 0x20
#define DRAM_ON 0x08
#define DRAM_OFF 0xE7
#define PG_ENABLE_MASK 0x01
#define RING_SIZE_MASK (RINGBUFFER_SIZE - 1);
/* defines for restoring registers partially */
#define ADDR_MAP_MASK (0x07 << 5)
#define DISP_CTRL ~0
#define PIXCONF_0 (0x64 << 8)
#define PIXCONF_2 (0xF3 << 24)
#define PIXCONF_1 (0xF0 << 16)
#define MN_MASK 0x3FF03FF
#define P_OR (0x7 << 4)
#define DAC_BIT (1 << 16)
#define INTERLACE_BIT (1 << 7)
#define IER_MASK (3 << 13)
#define IMR_MASK (3 << 13)
/* Power Management */
#define DPMS_MASK 0xF0000
#define POWERON 0x00000
#define STANDBY 0x20000
#define SUSPEND 0x80000
#define POWERDOWN 0xA0000
#define EMR_MASK ~0x3F
#define FW_BLC_MASK ~(0x3F|(7 << 8)|(0x3F << 12)|(7 << 20))
/* Ringbuffer */
#define RBUFFER_START_MASK 0xFFFFF000
#define RBUFFER_SIZE_MASK 0x001FF000
#define RBUFFER_HEAD_MASK 0x001FFFFC
#define RBUFFER_TAIL_MASK 0x001FFFF8
/* Video Timings */
#define REF_FREQ 24000000
#define TARGET_N_MAX 30
#define FLYBACK 550
#define V_FRONTPORCH 1
#define H_OFFSET 40
#define H_SCALEFACTOR 20
#define H_BLANKSCALE 128
#define H_GRADIENT 600
#define MAX_PIXELCLOCK 230000000
#define MIN_PIXELCLOCK 15000000
#define VFMAX 60
#define VFMIN 60
#define HFMAX 30000
#define HFMIN 29000
/* Cursor */
#define CURSOR_ENABLE_MASK 0x1000
#define CURSOR_MODE_64_TRANS 4
#define CURSOR_MODE_64_XOR 5
#define CURSOR_MODE_64_3C 6
#define COORD_INACTIVE 0
#define COORD_ACTIVE (1 << 4)
#define EXTENDED_PALETTE 1
/* AGP Memory Types*/
#define AGP_NORMAL_MEMORY 0
#define AGP_DCACHE_MEMORY 1
#define AGP_PHYSICAL_MEMORY 2
/* Allocated resource Flags */
#define FRAMEBUFFER_REQ 1
#define MMIO_REQ 2
#define PCI_DEVICE_ENABLED 4
#define HAS_FONTCACHE 8
/* driver flags */
#define HAS_MTRR 1
#define HAS_ACCELERATION 2
#define ALWAYS_SYNC 4
#define LOCKUP 8
struct gtt_data {
agp_memory *i810_fb_memory;
agp_memory *i810_iring_memory;
agp_memory *i810_cursor_memory;
agp_memory *i810_pixmap_memory;
};
struct mode_registers {
u32 pixclock, M, N, P;
u8 cr00, cr01, cr02, cr03;
u8 cr04, cr05, cr06, cr07;
u8 cr09, cr10, cr11, cr12;
u8 cr13, cr15, cr16, cr30;
u8 cr31, cr32, cr33, cr35, cr39;
u32 bpp8_100, bpp16_100;
u32 bpp24_100, bpp8_133;
u32 bpp16_133, bpp24_133;
u8 msr;
};
struct heap_data {
unsigned long physical;
__u8 *virtual;
u32 offset;
u32 size;
};
struct state_registers {
u32 dclk_1d, dclk_2d, dclk_0ds;
u32 pixconf, fw_blc, pgtbl_ctl;
u32 fence0, hws_pga, dplystas;
u16 bltcntl, hwstam, ier, iir, imr;
u8 cr00, cr01, cr02, cr03, cr04;
u8 cr05, cr06, cr07, cr08, cr09;
u8 cr10, cr11, cr12, cr13, cr14;
u8 cr15, cr16, cr17, cr80, gr10;
u8 cr30, cr31, cr32, cr33, cr35;
u8 cr39, cr41, cr70, sr01, msr;
};
struct i810fb_par {
struct mode_registers regs;
struct state_registers hw_state;
struct gtt_data i810_gtt;
struct fb_ops i810fb_ops;
struct pci_dev *dev;
struct heap_data aperture;
struct heap_data fb;
struct heap_data iring;
struct heap_data cursor_heap;
struct heap_data pixmap;
struct vgastate state;
drm_agp_t *drm_agp;
atomic_t use_count;
u32 pseudo_palette[17];
unsigned long mmio_start_phys;
u8 *mmio_start_virtual;
u32 cursor_reset;
u8 red[64];
u8 green[64];
u8 blue[64];
u32 pixmap_offset;
u32 pitch;
u32 pixconf;
u32 watermark;
u32 mem_freq;
u32 res_flags;
u32 dev_flags;
u32 cur_tail;
u32 depth;
u32 blit_bpp;
u32 ovract;
int mtrr_reg;
u16 bltcntl;
u8 interlace;
};
/*
* Register I/O
*/
#define i810_readb(where, mmio) readb(mmio + where)
#define i810_readw(where, mmio) readw(mmio + where)
#define i810_readl(where, mmio) readl(mmio + where)
#define i810_writeb(where, mmio, val) writeb(val, mmio + where)
#define i810_writew(where, mmio, val) writew(val, mmio + where)
#define i810_writel(where, mmio, val) writel(val, mmio + where)
#endif /* __I810_H__ */
/*-*- linux-c -*-
* linux/drivers/video/i810_accel.c -- Hardware Acceleration
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/fb.h>
#include "i810_regs.h"
#include "i810.h"
static u32 i810fb_rop[] = {
COLOR_COPY_ROP, /* ROP_COPY */
XOR_ROP /* ROP_XOR */
};
/* Macros */
#define PUT_RING(n) { \
i810_writel(par->cur_tail, par->iring.virtual, n); \
par->cur_tail += 4; \
par->cur_tail &= RING_SIZE_MASK; \
}
extern inline void flush_cache(void);
extern int reinit_agp(struct fb_info *info);
/************************************************************/
/* BLT Engine Routines */
static inline void i810_report_error(u8 *mmio)
{
printk("IIR : 0x%04x\n"
"EIR : 0x%04x\n"
"PGTBL_ER: 0x%04x\n"
"IPEIR : 0x%04x\n"
"IPEHR : 0x%04x\n",
i810_readw(IIR, mmio),
i810_readb(EIR, mmio),
i810_readl(PGTBL_ER, mmio),
i810_readl(IPEIR, mmio),
i810_readl(IPEHR, mmio));
}
/**
* wait_for_space - check ring buffer free space
* @space: amount of ringbuffer space needed in bytes
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* The function waits until a free space from the ringbuffer
* is available
*/
static inline int wait_for_space(struct i810fb_par *par, u32 space)
{
u32 head, count = WAIT_COUNT, tail;
u8 *mmio = par->mmio_start_virtual;
tail = par->cur_tail;
while (count--) {
head = i810_readl(IRING + 4, mmio) & RBUFFER_HEAD_MASK;
if ((tail == head) ||
(tail > head &&
(par->iring.size - tail + head) >= space) ||
(tail < head && (head - tail) >= space)) {
return 0;
}
}
printk("ringbuffer lockup!!!\n");
i810_report_error(mmio);
par->dev_flags |= LOCKUP;
return 1;
}
/**
* wait_for_engine_idle - waits for all hardware engines to finish
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* This waits for lring(0), iring(1), and batch(3), etc to finish and
* waits until ringbuffer is empty.
*/
static inline int wait_for_engine_idle(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
int count = WAIT_COUNT;
while((i810_readw(INSTDONE, mmio) & 0x7B) != 0x7B && --count);
if (count) return 0;
printk("accel engine lockup!!!\n");
printk("INSTDONE: 0x%04x\n", i810_readl(INSTDONE, mmio));
i810_report_error(mmio);
par->dev_flags |= LOCKUP;
return 1;
}
/* begin_iring - prepares the ringbuffer
* @space: length of sequence in dwords
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Checks/waits for sufficent space in ringbuffer of size
* space. Returns the tail of the buffer
*/
static inline u32 begin_iring(struct i810fb_par *par, u32 space)
{
if (par->dev_flags & ALWAYS_SYNC)
wait_for_engine_idle(par);
return wait_for_space(par, space);
}
/**
* end_iring - advances the buffer
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* This advances the tail of the ringbuffer, effectively
* beginning the execution of the graphics instruction sequence.
*/
static inline void end_iring(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
i810_writel(IRING, mmio, par->cur_tail);
}
/**
* source_copy_blit - BLIT transfer operation
* @dwidth: width of rectangular graphics data
* @dheight: height of rectangular graphics data
* @dpitch: bytes per line of destination buffer
* @xdir: direction of copy (left to right or right to left)
* @src: address of first pixel to read from
* @dest: address of first pixel to write to
* @from: source address
* @where: destination address
* @rop: raster operation
* @blit_bpp: pixel format which can be different from the
* framebuffer's pixelformat
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* This is a BLIT operation typically used when doing
* a 'Copy and Paste'
*/
static inline void source_copy_blit(int dwidth, int dheight, int dpitch,
int xdir, int src, int dest, int rop,
int blit_bpp, struct i810fb_par *par)
{
if (begin_iring(par, 24 + IRING_PAD)) return;
PUT_RING(BLIT | SOURCE_COPY_BLIT | 4);
PUT_RING(xdir | rop << 16 | dpitch | DYN_COLOR_EN | blit_bpp);
PUT_RING(dheight << 16 | dwidth);
PUT_RING(dest);
PUT_RING(dpitch);
PUT_RING(src);
end_iring(par);
}
/**
* color_blit - solid color BLIT operation
* @width: width of destination
* @height: height of destination
* @pitch: pixels per line of the buffer
* @dest: address of first pixel to write to
* @where: destination
* @rop: raster operation
* @what: color to transfer
* @blit_bpp: pixel format which can be different from the
* framebuffer's pixelformat
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* A BLIT operation which can be used for color fill/rectangular fill
*/
static inline void color_blit(int width, int height, int pitch, int dest,
int rop, int what, int blit_bpp,
struct i810fb_par *par)
{
if (begin_iring(par, 24 + IRING_PAD)) return;
PUT_RING(BLIT | COLOR_BLT | 3);
PUT_RING(rop << 16 | pitch | SOLIDPATTERN | DYN_COLOR_EN | blit_bpp);
PUT_RING(height << 16 | width);
PUT_RING(dest);
PUT_RING(what);
PUT_RING(NOP);
end_iring(par);
}
/**
* mono_src_copy_imm_blit - color expand from system memory to framebuffer
* @dwidth: width of destination
* @dheight: height of destination
* @dpitch: pixels per line of the buffer
* @dsize: size of bitmap in double words
* @dest: address of first byte of pixel;
* @rop: raster operation
* @blit_bpp: pixelformat to use which can be different from the
* framebuffer's pixelformat
* @src: address of image data
* @bg: backgound color
* @fg: forground color
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* A color expand operation where the source data is placed in the
* ringbuffer itself. Useful for drawing text.
*
* REQUIREMENT:
* The end of a scanline must be padded to the next word.
*/
static inline void mono_src_copy_imm_blit(int dwidth, int dheight, int dpitch,
int dsize, int blit_bpp, int rop,
int dest, const u32 *src, int bg,
int fg, struct i810fb_par *par)
{
u32 i, *s = (u32 *) src;
if (begin_iring(par, 24 + (dsize << 2) + IRING_PAD)) return;
PUT_RING(BLIT | MONO_SOURCE_COPY_IMMEDIATE | (4 + dsize));
PUT_RING(DYN_COLOR_EN | blit_bpp | rop << 16 | dpitch);
PUT_RING(dheight << 16 | dwidth);
PUT_RING(dest);
PUT_RING(bg);
PUT_RING(fg);
for (i = dsize; i--; )
PUT_RING(*s++);
end_iring(par);
}
/**
* mono_src_copy_blit - color expand from video memory to framebuffer
* @dwidth: width of destination
* @dheight: height of destination
* @dpitch: pixels per line of the buffer
* @qsize: size of bitmap in quad words
* @dest: address of first byte of pixel;
* @rop: raster operation
* @blit_bpp: pixelformat to use which can be different from the
* framebuffer's pixelformat
* @src: address of image data
* @bg: backgound color
* @fg: forground color
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* A color expand operation where the source data is in video memory.
* Useful for drawing text.
*
* REQUIREMENT:
* The end of a scanline must be padded to the next word.
*/
static inline void mono_src_copy_blit(int dwidth, int dheight, int dpitch,
int qsize, int blit_bpp, int rop,
int dest, int src, int bg,
int fg, struct i810fb_par *par)
{
if (begin_iring(par, 32 + IRING_PAD)) return;
PUT_RING(BLIT | MONO_SOURCE_COPY_BLIT | 6);
PUT_RING(DYN_COLOR_EN | blit_bpp | rop << 16 | dpitch | 1 << 27);
PUT_RING(dheight << 16 | dwidth);
PUT_RING(dest);
PUT_RING(qsize - 1);
PUT_RING(src);
PUT_RING(bg);
PUT_RING(fg);
end_iring(par);
}
static u32 get_buffer_offset(u32 size, struct i810fb_par *par)
{
u32 offset;
if (par->pixmap_offset + size > par->pixmap.size) {
wait_for_engine_idle(par);
par->pixmap_offset = 0;
}
offset = par->pixmap_offset;
par->pixmap_offset += size;
return offset;
}
/**
* i810fb_iring_enable - enables/disables the ringbuffer
* @mode: enable or disable
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Enables or disables the ringbuffer, effectively enabling or
* disabling the instruction/acceleration engine.
*/
static inline void i810fb_iring_enable(struct i810fb_par *par, u32 mode)
{
u32 tmp;
u8 *mmio = par->mmio_start_virtual;
tmp = i810_readl(IRING + 12, mmio);
if (mode == OFF)
tmp &= ~1;
else
tmp |= 1;
flush_cache();
i810_writel(IRING + 12, mmio, tmp);
}
void i810fb_fillrect(struct fb_info *p, struct fb_fillrect *rect)
{
struct i810fb_par *par = (struct i810fb_par *) p->par;
u32 dx, dy, width, height, dest, rop = 0, color = 0;
if (!p->var.accel_flags || par->dev_flags & LOCKUP) {
cfb_fillrect(p, rect);
return;
}
if (par->depth == 4) {
wait_for_engine_idle(par);
cfb_fillrect(p, rect);
return;
}
if (par->depth == 1)
color = rect->color;
else
color = ((u32 *) (p->pseudo_palette))[rect->color];
rop = i810fb_rop[rect->rop];
dx = rect->dx * par->depth;
width = rect->width * par->depth;
dy = rect->dy;
height = rect->height;
dest = p->fix.smem_start + (dy * p->fix.line_length) + dx;
color_blit(width, height, p->fix.line_length, dest, rop, color,
par->blit_bpp, par);
}
void i810fb_copyarea(struct fb_info *p, struct fb_copyarea *region)
{
struct i810fb_par *par = (struct i810fb_par *) p->par;
u32 sx, sy, dx, dy, pitch, width, height, src, dest, xdir;
if (!p->var.accel_flags || par->dev_flags & LOCKUP) {
cfb_copyarea(p, region);
return;
}
if (par->depth == 4) {
wait_for_engine_idle(par);
cfb_copyarea(p, region);
return;
}
dx = region->dx * par->depth;
sx = region->sx * par->depth;
width = region->width * par->depth;
sy = region->sy;
dy = region->dy;
height = region->height;
if (dx <= sx) {
xdir = INCREMENT;
}
else {
xdir = DECREMENT;
sx += width - 1;
dx += width - 1;
}
if (dy <= sy) {
pitch = p->fix.line_length;
}
else {
pitch = (-(p->fix.line_length)) & 0xFFFF;
sy += height - 1;
dy += height - 1;
}
src = p->fix.smem_start + (sy * p->fix.line_length) + sx;
dest = p->fix.smem_start + (dy * p->fix.line_length) + dx;
source_copy_blit(width, height, pitch, xdir, src, dest,
PAT_COPY_ROP, par->blit_bpp, par);
}
/*
* Blitting is done at 8x8 pixel-array at a time. If map is not
* monochrome or not a multiple of 8x8 pixels, cfb_imageblit will
* be called instead.
*/
void i810fb_imageblit(struct fb_info *p, struct fb_image *image)
{
struct i810fb_par *par = (struct i810fb_par *) p->par;
u32 fg = 0, bg = 0, s_pitch, d_pitch, size, offset, dst, i, j;
u8 *s_addr, *d_addr;
if (!p->var.accel_flags || par->dev_flags & LOCKUP) {
cfb_imageblit(p, image);
return;
}
if (par->depth == 4 || image->depth != 1) {
wait_for_engine_idle(par);
cfb_imageblit(p, image);
return;
}
switch (p->var.bits_per_pixel) {
case 8:
fg = image->fg_color;
bg = image->bg_color;
break;
case 16:
fg = ((u32 *)(p->pseudo_palette))[image->fg_color];
bg = ((u32 *)(p->pseudo_palette))[image->bg_color];
break;
case 24:
fg = ((u32 *)(p->pseudo_palette))[image->fg_color];
bg = ((u32 *)(p->pseudo_palette))[image->bg_color];
break;
}
dst = p->fix.smem_start + (image->dy * p->fix.line_length) +
(image->dx * par->depth);
s_pitch = image->width/8;
d_pitch = (s_pitch + 1) & ~1;
size = d_pitch * image->height;
if (image->width & 15) {
offset = get_buffer_offset(size, par);
d_addr = par->pixmap.virtual + offset;
s_addr = image->data;
for (i = image->height; i--; ) {
for (j = 0; j < s_pitch; j++)
i810_writeb(j, d_addr, s_addr[j]);
s_addr += s_pitch;
d_addr += d_pitch;
}
mono_src_copy_blit(image->width * par->depth, image->height,
p->fix.line_length, size/8, par->blit_bpp,
PAT_COPY_ROP, dst,
par->pixmap.physical + offset,
bg, fg, par);
}
/*
* immediate blit if width is a multiple of 16 (hardware requirement)
*/
else {
mono_src_copy_imm_blit(image->width * par->depth,
image->height, p->fix.line_length,
size/4, par->blit_bpp,
PAT_COPY_ROP, dst, (u32 *) image->data,
bg, fg, par);
}
}
int i810fb_sync(struct fb_info *p)
{
struct i810fb_par *par = (struct i810fb_par *) p->par;
if (!p->var.accel_flags)
return 0;
return wait_for_engine_idle(par);
}
/**
* i810fb_init_ringbuffer - initialize the ringbuffer
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Initializes the ringbuffer by telling the device the
* size and location of the ringbuffer. It also sets
* the head and tail pointers = 0
*/
void i810fb_init_ringbuffer(struct i810fb_par *par)
{
u32 tmp1, tmp2;
u8 *mmio = par->mmio_start_virtual;
wait_for_engine_idle(par);
i810fb_iring_enable(par, OFF);
i810_writel(IRING, mmio, 0);
i810_writel(IRING + 4, mmio, 0);
par->cur_tail = 0;
tmp2 = i810_readl(IRING + 8, mmio) & ~RBUFFER_START_MASK;
tmp1 = par->iring.physical;
i810_writel(IRING + 8, mmio, tmp2 | tmp1);
tmp1 = i810_readl(IRING + 12, mmio);
tmp1 &= ~RBUFFER_SIZE_MASK;
tmp2 = (par->iring.size - I810_PAGESIZE) & RBUFFER_SIZE_MASK;
i810_writel(IRING + 12, mmio, tmp1 | tmp2);
i810fb_iring_enable(par, ON);
}
/*-*- linux-c -*-
* linux/drivers/video/i810_dvt.c -- Intel 810 Discrete Video Timings (Intel)
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
*
* 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.
*/
#include <linux/kernel.h>
#include "i810_regs.h"
#include "i810.h"
struct mode_registers std_modes[] = {
/* 640x480 @ 60Hz */
{ 25000, 0x0013, 0x0003, 0x40, 0x5F, 0x4F, 0x50, 0x82, 0x51, 0x9D,
0x0B, 0x10, 0x40, 0xE9, 0x0B, 0xDF, 0x50, 0xE7, 0x04, 0x02,
0x01, 0x01, 0x01, 0x00, 0x01, 0x22002000, 0x22004000, 0x22006000,
0x22002000, 0x22004000, 0x22006000, 0xC0 },
/* 640x480 @ 70Hz */
{ 28000, 0x0053, 0x0010, 0x40, 0x61, 0x4F, 0x4F, 0x85, 0x52, 0x9A,
0xF2, 0x10, 0x40, 0xE0, 0x03, 0xDF, 0x50, 0xDF, 0xF3, 0x01,
0x01, 0x01, 0x01, 0x00, 0x01, 0x22002000, 0x22004000, 0x22005000,
0x22002000, 0x22004000, 0x22005000, 0xC0 },
/* 640x480 @ 72Hz */
{ 31000, 0x0013, 0x0002, 0x40, 0x63, 0x4F, 0x4F, 0x87, 0x52, 0x97,
0x06, 0x0F, 0x40, 0xE8, 0x0B, 0xDF, 0x50, 0xDF, 0x07, 0x02,
0x01, 0x01, 0x01, 0x00, 0x01, 0x22003000, 0x22005000, 0x22007000,
0x22003000, 0x22005000, 0x22007000, 0xC0 },
/* 640x480 @ 75Hz */
{ 31000, 0x0013, 0x0002, 0x40, 0x64, 0x4F, 0x4F, 0x88, 0x51, 0x99,
0xF2, 0x10, 0x40, 0xE0, 0x03, 0xDF, 0x50, 0xDF, 0xF3, 0x01,
0x01, 0x01, 0x01, 0x00, 0x01, 0x22003000, 0x22005000, 0x22007000,
0x22003000, 0x22005000, 0x22007000, 0xC0 },
/* 640x480 @ 85Hz */
{ 36000, 0x0010, 0x0001, 0x40, 0x63, 0x4F, 0x4F, 0x87, 0x56, 0x9D,
0xFB, 0x10, 0x40, 0xE0, 0x03, 0xDF, 0x50, 0xDF, 0xFC, 0x01,
0x01, 0x01, 0x01, 0x00, 0x01, 0x22003000, 0x22005000, 0x22107000,
0x22003000, 0x22005000, 0x22107000, 0xC0 },
/* 800x600 @ 56Hz */
{ 36000, 0x0010, 0x0001, 0x40, 0x7B, 0x63, 0x63, 0x9F, 0x66, 0x8F,
0x6F, 0x10, 0x40, 0x58, 0x0A, 0x57, 0xC8, 0x57, 0x70, 0x02,
0x02, 0x02, 0x02, 0x00, 0x01, 0x22003000, 0x22005000, 0x22107000,
0x22003000, 0x22005000, 0x22107000, 0x00 },
/* 800x600 @ 60Hz */
{ 40000, 0x0008, 0x0001, 0x30, 0x7F, 0x63, 0x63, 0x83, 0x68, 0x18,
0x72, 0x10, 0x40, 0x58, 0x0C, 0x57, 0xC8, 0x57, 0x73, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x22003000, 0x22006000, 0x22108000,
0x22003000, 0x22006000, 0x22108000, 0x00 },
/* 800x600 @ 70Hz */
{ 45000, 0x0054, 0x0015, 0x30, 0x7D, 0x63, 0x63, 0x81, 0x68, 0x12,
0x6f, 0x10, 0x40, 0x58, 0x0b, 0x57, 0x64, 0x57, 0x70, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x22004000, 0x22007000, 0x2210A000,
0x22004000, 0x22007000, 0x2210A000, 0x00 },
/* 800x600 @ 72Hz */
{ 50000, 0x0017, 0x0004, 0x30, 0x7D, 0x63, 0x63, 0x81, 0x6A, 0x19,
0x98, 0x10, 0x40, 0x7C, 0x02, 0x57, 0xC8, 0x57, 0x99, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x22004000, 0x22007000, 0x2210A000,
0x22004000, 0x22007000, 0x2210A000, 0x00 },
/* 800x600 @ 75Hz */
{ 49000, 0x001F, 0x0006, 0x30, 0x7F, 0x63, 0x63, 0x83, 0x65, 0x0F,
0x6F, 0x10, 0x40, 0x58, 0x0B, 0x57, 0xC8, 0x57, 0x70, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x22004000, 0x22007000, 0x2210B000,
0x22004000, 0x22007000, 0x2210B000, 0x00 },
/* 800x600 @ 85Hz */
{ 56000, 0x0049, 0x000E, 0x30, 0x7E, 0x63, 0x63, 0x82, 0x67, 0x0F,
0x75, 0x10, 0x40, 0x58, 0x0B, 0x57, 0xC8, 0x57, 0x76, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x22004000, 0x22108000, 0x2210b000,
0x22004000, 0x22108000, 0x2210b000, 0x00 },
/* 1024x768 @ 60Hz */
{ 65000, 0x003F, 0x000A, 0x30, 0xA3, 0x7F, 0x7F, 0x87, 0x83, 0x94,
0x24, 0x10, 0x40, 0x02, 0x08, 0xFF, 0x80, 0xFF, 0x25, 0x03,
0x02, 0x03, 0x02, 0x00, 0x00, 0x22005000, 0x22109000, 0x2220D000,
0x22005000, 0x22109000, 0x2220D000, 0xC0 },
/* 1024x768 @ 70Hz */
{ 75000, 0x0017, 0x0002, 0x30, 0xA1, 0x7F, 0x7F, 0x85, 0x82, 0x93,
0x24, 0x10, 0x40, 0x02, 0x08, 0xFF, 0x80, 0xFF, 0x25, 0x03,
0x02, 0x03, 0x02, 0x00, 0x00, 0x22005000, 0x2210A000, 0x2220F000,
0x22005000, 0x2210A000, 0x2220F000, 0xC0 },
/* 1024x768 @ 75Hz */
{ 78000, 0x0050, 0x0017, 0x20, 0x9F, 0x7F, 0x7F, 0x83, 0x81, 0x8D,
0x1E, 0x10, 0x40, 0x00, 0x03, 0xFF, 0x80, 0xFF, 0x1F, 0x03,
0x02, 0x03, 0x02, 0x00, 0x00, 0x22006000, 0x2210B000, 0x22210000,
0x22006000, 0x2210B000, 0x22210000, 0x00 },
/* 1024x768 @ 85Hz */
{ 94000, 0x003D, 0x000E, 0x20, 0xA7, 0x7F, 0x7F, 0x8B, 0x85, 0x91,
0x26, 0x10, 0x40, 0x00, 0x03, 0xFF, 0x80, 0xFF, 0x27, 0x03,
0x02, 0x03, 0x02, 0x00, 0x00, 0x22007000, 0x2220E000, 0x22212000,
0x22007000, 0x2220E000, 0x22212000, 0x00 },
/* 1152x864 @ 60Hz */
{ 80000, 0x0008, 0x0001, 0x20, 0xB3, 0x8F, 0x8F, 0x97, 0x93, 0x9f,
0x87, 0x10, 0x40, 0x60, 0x03, 0x5F, 0x90, 0x5f, 0x88, 0x03,
0x03, 0x03, 0x03, 0x00, 0x00, 0x2220C000, 0x22210000, 0x22415000,
0x2220C000, 0x22210000, 0x22415000, 0x00 },
/* 1152x864 @ 70Hz */
{ 96000, 0x000a, 0x0001, 0x20, 0xbb, 0x8F, 0x8F, 0x9f, 0x98, 0x87,
0x82, 0x10, 0x40, 0x60, 0x03, 0x5F, 0x90, 0x5F, 0x83, 0x03,
0x03, 0x03, 0x03, 0x00, 0x00, 0x22107000, 0x22210000, 0x22415000,
0x22107000, 0x22210000, 0x22415000, 0x00 },
/* 1152x864 @ 72Hz */
{ 99000, 0x001f, 0x0006, 0x20, 0xbb, 0x8F, 0x8F, 0x9f, 0x98, 0x87,
0x83, 0x10, 0x40, 0x60, 0x03, 0x5F, 0x90, 0x5F, 0x84, 0x03,
0x03, 0x03, 0x03, 0x00, 0x00, 0x22107000, 0x22210000, 0x22415000,
0x22107000, 0x22210000, 0x22415000, 0x00 },
/* 1152x864 @ 75Hz */
{ 108000, 0x0010, 0x0002, 0x20, 0xC3, 0x8F, 0x8F, 0x87, 0x97, 0x07,
0x82, 0x10, 0x40, 0x60, 0x03, 0x5F, 0x90, 0x5F, 0x83, 0x03,
0x03, 0x03, 0x03, 0x00, 0x01, 0x22107000, 0x22210000, 0x22415000,
0x22107000, 0x22210000, 0x22415000, 0x00 },
/* 1152x864 @ 85Hz */
{ 121000, 0x006D, 0x0014, 0x20, 0xc0, 0x8F, 0x8F, 0x84, 0x97, 0x07,
0x93, 0x10, 0x40, 0x60, 0x03, 0x5F, 0x90, 0x5F, 0x94, 0x03,
0x03, 0x03, 0x03, 0x00, 0x01, 0x2220C000, 0x22210000, 0x22415000,
0x2220C000, 0x22210000, 0x22415000, 0x0 },
/* 1280x960 @ 60Hz */
{ 108000, 0x0010, 0x0002, 0x20, 0xDC, 0x9F, 0x9F, 0x80, 0xAB, 0x99,
0xE6, 0x10, 0x40, 0xC0, 0x03, 0xBF, 0xA0, 0xBF, 0xE7, 0x03,
0x03, 0x03, 0x03, 0x00, 0x01, 0x2210A000, 0x22210000, 0x22415000,
0x2210A000, 0x22210000, 0x22415000, 0x00 },
/* 1280x960 @ 75Hz */
{ 129000, 0x0029, 0x0006, 0x20, 0xD3, 0x9F, 0x9F, 0x97, 0xaa, 0x1b,
0xE8, 0x10, 0x40, 0xC0, 0x03, 0xBF, 0xA0, 0xBF, 0xE9, 0x03,
0x03, 0x03, 0x03, 0x00, 0x01, 0x2210A000, 0x22210000, 0x2241B000,
0x2210A000, 0x22210000, 0x2241B000, 0x00 },
/* 1280x960 @ 85Hz */
{ 148000, 0x0042, 0x0009, 0x20, 0xD3, 0x9F, 0x9F, 0x97, 0xA7, 0x1B,
0xF1, 0x10, 0x40, 0xC0, 0x03, 0xBF, 0xA0, 0xBF, 0xF2, 0x03,
0x03, 0x03, 0x03, 0x00, 0x01, 0x2210A000, 0x22220000, 0x2241D000,
0x2210A000, 0x22220000, 0x2241D000, 0x00 },
/* 1600x1200 @ 60Hz */
{ 162000, 0x0019, 0x0006, 0x10, 0x09, 0xC7, 0xC7, 0x8D, 0xcf, 0x07,
0xE0, 0x10, 0x40, 0xB0, 0x03, 0xAF, 0xC8, 0xAF, 0xE1, 0x04,
0x04, 0x04, 0x04, 0x01, 0x00, 0x2210b000, 0x22416000, 0x44419000,
0x2210b000, 0x22416000, 0x44419000, 0x00 },
/* 1600x1200 @ 65 Hz */
{ 175000, 0x005d, 0x0018, 0x10, 0x09, 0xC7, 0xC7, 0x8D, 0xcf, 0x07,
0xE0, 0x10, 0x40, 0xB0, 0x03, 0xAF, 0xC8, 0xAF, 0xE1, 0x04,
0x04, 0x04, 0x04, 0x01, 0x00, 0x2210c000, 0x22416000, 0x44419000,
0x2210c000, 0x22416000, 0x44419000, 0x00 },
/* 1600x1200 @ 70 Hz */
{ 189000, 0x003D, 0x000e, 0x10, 0x09, 0xC7, 0xC7, 0x8d, 0xcf, 0x07,
0xE0, 0x10, 0x40, 0xb0, 0x03, 0xAF, 0xC8, 0xaf, 0xE1, 0x04,
0x04, 0x04, 0x04, 0x01, 0x00, 0x2220e000, 0x22416000, 0x44419000,
0x2220e000, 0x22416000, 0x44419000, 0x00 },
/* 1600x1200 @ 72 Hz */
{ 195000, 0x003f, 0x000e, 0x10, 0x0b, 0xC7, 0xC7, 0x8f, 0xd5, 0x0b,
0xE1, 0x10, 0x40, 0xb0, 0x03, 0xAF, 0xC8, 0xaf, 0xe2, 0x04, 0x04,
0x04, 0x04, 0x01, 0x00, 0x2220e000, 0x22416000, 0x44419000,
0x2220e000, 0x22416000, 0x44419000, 0x00 },
/* 1600x1200 @ 75 Hz */
{ 202000, 0x0024, 0x0007, 0x10, 0x09, 0xC7, 0xC7, 0x8d, 0xcf, 0x07,
0xE0, 0x10, 0x40, 0xb0, 0x03, 0xAF, 0xC8, 0xaf, 0xE1, 0x04, 0x04,
0x04, 0x04, 0x01, 0x00, 0x2220e000, 0x22416000, 0x44419000,
0x2220e000, 0x22416000, 0x44419000, 0x00 },
/* 1600x1200 @ 85 Hz */
{ 229000, 0x0029, 0x0007, 0x10, 0x09, 0xC7, 0xC7, 0x8d, 0xcf, 0x07,
0xE0, 0x10, 0x40, 0xb0, 0x03, 0xAF, 0xC8, 0xaf, 0xE1, 0x04, 0x04,
0x04, 0x04, 0x01, 0x00, 0x22210000, 0x22416000, 0x0,
0x22210000, 0x22416000, 0x0, 0x00 },
};
void round_off_xres(u32 *xres)
{
if (*xres < 800)
*xres = 640;
if (*xres < 1024 && *xres >= 800)
*xres = 800;
if (*xres < 1152 && *xres >= 1024)
*xres = 1024;
if (*xres < 1280 && *xres >= 1152)
*xres = 1152;
if (*xres < 1600 && *xres >= 1280)
*xres = 1280;
if (*xres >= 1600)
*xres = 1600;
}
inline void round_off_yres(u32 *xres, u32 *yres)
{
*yres = (*xres * 3) >> 2;
}
void i810fb_encode_registers(const struct fb_var_screeninfo *var,
struct i810fb_par *par, u32 xres, u32 yres)
{
u32 diff = 0, diff_best = 0xFFFFFFFF, i = 0, i_best = 0;
u8 hfl;
hfl = (u8) ((xres >> 3) - 1);
for (i = 0; i < ARRAY_SIZE(std_modes); i++) {
if (std_modes[i].cr01 == hfl) {
if (std_modes[i].pixclock <= par->regs.pixclock)
diff = par->regs.pixclock -
std_modes[i].pixclock;
if (diff < diff_best) {
i_best = i;
diff_best = diff;
}
}
}
par->regs = std_modes[i_best];
/* overlay */
par->ovract = ((xres + var->right_margin + var->hsync_len +
var->left_margin - 32) | ((xres - 32) << 16));
}
void i810fb_fill_var_timings(struct fb_var_screeninfo *var)
{
struct i810fb_par par;
u32 total, xres, yres;
xres = var->xres;
yres = var->yres;
par.regs.pixclock = 1000000000/var->pixclock;
i810fb_encode_registers(var, &par, xres, yres);
total = ((par.regs.cr00 | (par.regs.cr35 & 1) << 8) + 3) << 3;
var->pixclock = 1000000000/par.regs.pixclock;
var->right_margin = (par.regs.cr04 << 3) - xres;
var->hsync_len = ((par.regs.cr05 & 0x1F) -
(par.regs.cr04 & 0x1F)) << 3;
var->left_margin = (total - (xres + var->right_margin +
var->hsync_len));
var->sync = FB_SYNC_ON_GREEN;
if (~(par.regs.msr & (1 << 6)))
var->sync |= FB_SYNC_HOR_HIGH_ACT;
if (~(par.regs.msr & (1 << 7)))
var->sync |= FB_SYNC_VERT_HIGH_ACT;
total = ((par.regs.cr06 | (par.regs.cr30 & 0x0F) << 8)) + 2;
var->lower_margin = (par.regs.cr10 |
(par.regs.cr32 & 0x0F) << 8) - yres;
var->vsync_len = (par.regs.cr11 & 0x0F) - (var->lower_margin & 0x0F);
var->upper_margin = total - (yres + var->lower_margin +
var->vsync_len);
}
u32 i810_get_watermark(struct fb_var_screeninfo *var,
struct i810fb_par *par)
{
struct mode_registers *params = &par->regs;
u32 wmark = 0;
if (par->mem_freq == 100) {
switch (var->bits_per_pixel) {
case 8:
wmark = params->bpp8_100;
break;
case 16:
wmark = params->bpp16_100;
break;
case 24:
case 32:
wmark = params->bpp24_100;
}
}
else {
switch (var->bits_per_pixel) {
case 8:
wmark = params->bpp8_133;
break;
case 16:
wmark = params->bpp16_133;
break;
case 24:
case 32:
wmark = params->bpp24_133;
}
}
return wmark;
}
/*-*- linux-c -*-
* linux/drivers/video/i810_main.h -- Intel 810 Nondiscrete Video Timings
* (VESA GTF)
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
*
* 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.
*/
#include <linux/kernel.h>
#include "i810_regs.h"
#include "i810.h"
/*
* FIFO and Watermark tables - based almost wholly on i810_wmark.c in
* XFree86 v4.03 by Precision Insight. Slightly modified for integer
* operation, instead of float
*/
struct wm_info {
u32 freq;
u32 wm;
};
struct wm_info i810_wm_8_100[] = {
{ 15, 0x0070c000 }, { 19, 0x0070c000 }, { 25, 0x22003000 },
{ 28, 0x22003000 }, { 31, 0x22003000 }, { 36, 0x22007000 },
{ 40, 0x22007000 }, { 45, 0x22007000 }, { 49, 0x22008000 },
{ 50, 0x22008000 }, { 56, 0x22008000 }, { 65, 0x22008000 },
{ 75, 0x22008000 }, { 78, 0x22008000 }, { 80, 0x22008000 },
{ 94, 0x22008000 }, { 96, 0x22107000 }, { 99, 0x22107000 },
{ 108, 0x22107000 }, { 121, 0x22107000 }, { 128, 0x22107000 },
{ 132, 0x22109000 }, { 135, 0x22109000 }, { 157, 0x2210b000 },
{ 162, 0x2210b000 }, { 175, 0x2210b000 }, { 189, 0x2220e000 },
{ 195, 0x2220e000 }, { 202, 0x2220e000 }, { 204, 0x2220e000 },
{ 218, 0x2220f000 }, { 229, 0x22210000 }, { 234, 0x22210000 },
};
struct wm_info i810_wm_16_100[] = {
{ 15, 0x0070c000 }, { 19, 0x0020c000 }, { 25, 0x22006000 },
{ 28, 0x22006000 }, { 31, 0x22007000 }, { 36, 0x22007000 },
{ 40, 0x22007000 }, { 45, 0x22007000 }, { 49, 0x22009000 },
{ 50, 0x22009000 }, { 56, 0x22108000 }, { 65, 0x2210e000 },
{ 75, 0x2210e000 }, { 78, 0x2210e000 }, { 80, 0x22210000 },
{ 94, 0x22210000 }, { 96, 0x22210000 }, { 99, 0x22210000 },
{ 108, 0x22210000 }, { 121, 0x22210000 }, { 128, 0x22210000 },
{ 132, 0x22314000 }, { 135, 0x22314000 }, { 157, 0x22415000 },
{ 162, 0x22416000 }, { 175, 0x22416000 }, { 189, 0x22416000 },
{ 195, 0x22416000 }, { 202, 0x22416000 }, { 204, 0x22416000 },
{ 218, 0x22416000 }, { 229, 0x22416000 },
};
struct wm_info i810_wm_24_100[] = {
{ 15, 0x0020c000 }, { 19, 0x0040c000 }, { 25, 0x22009000 },
{ 28, 0x22009000 }, { 31, 0x2200a000 }, { 36, 0x2210c000 },
{ 40, 0x2210c000 }, { 45, 0x2210c000 }, { 49, 0x22111000 },
{ 50, 0x22111000 }, { 56, 0x22111000 }, { 65, 0x22214000 },
{ 75, 0x22214000 }, { 78, 0x22215000 }, { 80, 0x22216000 },
{ 94, 0x22218000 }, { 96, 0x22418000 }, { 99, 0x22418000 },
{ 108, 0x22418000 }, { 121, 0x22418000 }, { 128, 0x22419000 },
{ 132, 0x22519000 }, { 135, 0x4441d000 }, { 157, 0x44419000 },
{ 162, 0x44419000 }, { 175, 0x44419000 }, { 189, 0x44419000 },
{ 195, 0x44419000 }, { 202, 0x44419000 }, { 204, 0x44419000 },
};
struct wm_info i810_wm_8_133[] = {
{ 15, 0x0070c000 }, { 19, 0x0070c000 }, { 25, 0x22003000 },
{ 28, 0x22003000 }, { 31, 0x22003000 }, { 36, 0x22007000 },
{ 40, 0x22007000 }, { 45, 0x22007000 }, { 49, 0x22008000 },
{ 50, 0x22008000 }, { 56, 0x22008000 }, { 65, 0x22008000 },
{ 75, 0x22008000 }, { 78, 0x22008000 }, { 80, 0x22008000 },
{ 94, 0x22008000 }, { 96, 0x22107000 }, { 99, 0x22107000 },
{ 108, 0x22107000 }, { 121, 0x22107000 }, { 128, 0x22107000 },
{ 132, 0x22109000 }, { 135, 0x22109000 }, { 157, 0x2210b000 },
{ 162, 0x2210b000 }, { 175, 0x2210b000 }, { 189, 0x2220e000 },
{ 195, 0x2220e000 }, { 202, 0x2220e000 }, { 204, 0x2220e000 },
{ 218, 0x2220f000 }, { 229, 0x22210000 }, { 234, 0x22210000 },
};
struct wm_info i810_wm_16_133[] = {
{ 15, 0x0020c000 }, { 19, 0x0020c000 }, { 25, 0x22006000 },
{ 28, 0x22006000 }, { 31, 0x22007000 }, { 36, 0x22007000 },
{ 40, 0x22007000 }, { 45, 0x22007000 }, { 49, 0x22009000 },
{ 50, 0x22009000 }, { 56, 0x22108000 }, { 65, 0x2210e000 },
{ 75, 0x2210e000 }, { 78, 0x2210e000 }, { 80, 0x22210000 },
{ 94, 0x22210000 }, { 96, 0x22210000 }, { 99, 0x22210000 },
{ 108, 0x22210000 }, { 121, 0x22210000 }, { 128, 0x22210000 },
{ 132, 0x22314000 }, { 135, 0x22314000 }, { 157, 0x22415000 },
{ 162, 0x22416000 }, { 175, 0x22416000 }, { 189, 0x22416000 },
{ 195, 0x22416000 }, { 202, 0x22416000 }, { 204, 0x22416000 },
{ 218, 0x22416000 }, { 229, 0x22416000 },
};
struct wm_info i810_wm_24_133[] = {
{ 15, 0x0020c000 }, { 19, 0x00408000 }, { 25, 0x22009000 },
{ 28, 0x22009000 }, { 31, 0x2200a000 }, { 36, 0x2210c000 },
{ 40, 0x2210c000 }, { 45, 0x2210c000 }, { 49, 0x22111000 },
{ 50, 0x22111000 }, { 56, 0x22111000 }, { 65, 0x22214000 },
{ 75, 0x22214000 }, { 78, 0x22215000 }, { 80, 0x22216000 },
{ 94, 0x22218000 }, { 96, 0x22418000 }, { 99, 0x22418000 },
{ 108, 0x22418000 }, { 121, 0x22418000 }, { 128, 0x22419000 },
{ 132, 0x22519000 }, { 135, 0x4441d000 }, { 157, 0x44419000 },
{ 162, 0x44419000 }, { 175, 0x44419000 }, { 189, 0x44419000 },
{ 195, 0x44419000 }, { 202, 0x44419000 }, { 204, 0x44419000 },
};
void round_off_xres(u32 *xres) { }
void round_off_yres(u32 *xres, u32 *yres) { }
/**
* i810fb_encode_registers - encode @var to hardware register values
* @var: pointer to var structure
* @par: pointer to hardware par structure
*
* DESCRIPTION:
* Timing values in @var will be converted to appropriate
* register values of @par.
*/
void i810fb_encode_registers(const struct fb_var_screeninfo *var,
struct i810fb_par *par, u32 xres, u32 yres)
{
int n, blank_s, blank_e;
u8 *mmio = par->mmio_start_virtual, msr = 0;
/* Horizontal */
/* htotal */
n = ((xres + var->right_margin + var->hsync_len +
var->left_margin) >> 3) - 5;
par->regs.cr00 = (u8) n;
par->regs.cr35 = (u8) ((n >> 8) & 1);
/* xres */
par->regs.cr01 = (u8) ((xres >> 3) - 1);
/* hblank */
blank_e = (xres + var->right_margin + var->hsync_len +
var->left_margin) >> 3;
blank_e--;
blank_s = blank_e - 127;
if (blank_s < (xres >> 3))
blank_s = xres >> 3;
par->regs.cr02 = (u8) blank_s;
par->regs.cr03 = (u8) (blank_e & 0x1F);
par->regs.cr05 = (u8) ((blank_e & (1 << 5)) << 2);
par->regs.cr39 = (u8) ((blank_e >> 6) & 1);
/* hsync */
par->regs.cr04 = (u8) ((xres + var->right_margin) >> 3);
par->regs.cr05 |= (u8) (((xres + var->right_margin +
var->hsync_len) >> 3) & 0x1F);
/* Vertical */
/* vtotal */
n = yres + var->lower_margin + var->vsync_len + var->upper_margin - 2;
par->regs.cr06 = (u8) (n & 0xFF);
par->regs.cr30 = (u8) ((n >> 8) & 0x0F);
/* vsync */
n = yres + var->lower_margin;
par->regs.cr10 = (u8) (n & 0xFF);
par->regs.cr32 = (u8) ((n >> 8) & 0x0F);
par->regs.cr11 = i810_readb(CR11, mmio) & ~0x0F;
par->regs.cr11 |= (u8) ((yres + var->lower_margin +
var->vsync_len) & 0x0F);
/* yres */
n = yres - 1;
par->regs.cr12 = (u8) (n & 0xFF);
par->regs.cr31 = (u8) ((n >> 8) & 0x0F);
/* vblank */
blank_e = yres + var->lower_margin + var->vsync_len +
var->upper_margin;
blank_e--;
blank_s = blank_e - 127;
if (blank_s < yres)
blank_s = yres;
par->regs.cr15 = (u8) (blank_s & 0xFF);
par->regs.cr33 = (u8) ((blank_s >> 8) & 0x0F);
par->regs.cr16 = (u8) (blank_e & 0xFF);
par->regs.cr09 = 0;
/* sync polarity */
if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
msr |= 1 << 6;
if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
msr |= 1 << 7;
par->regs.msr = msr;
/* interlace */
if (var->vmode & FB_VMODE_INTERLACED)
par->interlace = (1 << 7) | ((u8) (var->yres >> 4));
else
par->interlace = 0;
if (var->vmode & FB_VMODE_DOUBLE)
par->regs.cr09 |= 1 << 7;
/* overlay */
par->ovract = ((var->xres + var->right_margin + var->hsync_len +
var->left_margin - 32) | ((var->xres - 32) << 16));
}
void i810fb_fill_var_timings(struct fb_var_screeninfo *var) { }
/**
* i810_get_watermark - gets watermark
* @var: pointer to fb_var_screeninfo
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Get's the required watermark based on
* pixelclock and RAMBUS frequency.
*
* RETURNS:
* watermark
*/
u32 i810_get_watermark(const struct fb_var_screeninfo *var,
struct i810fb_par *par)
{
struct wm_info *wmark = 0;
u32 i, size = 0, pixclock, wm_best = 0, min, diff;
if (par->mem_freq == 100) {
switch (var->bits_per_pixel) {
case 8:
wmark = i810_wm_8_100;
size = ARRAY_SIZE(i810_wm_8_100);
break;
case 16:
wmark = i810_wm_16_100;
size = ARRAY_SIZE(i810_wm_16_100);
break;
case 24:
case 32:
wmark = i810_wm_24_100;
size = ARRAY_SIZE(i810_wm_24_100);
}
}
else {
switch(var->bits_per_pixel) {
case 8:
wmark = i810_wm_8_133;
size = ARRAY_SIZE(i810_wm_8_133);
break;
case 16:
wmark = i810_wm_16_133;
size = ARRAY_SIZE(i810_wm_16_133);
break;
case 24:
case 32:
wmark = i810_wm_24_133;
size = ARRAY_SIZE(i810_wm_24_133);
}
}
pixclock = 1000000/var->pixclock;
min = ~0;
for (i = 0; i < size; i++) {
if (pixclock <= wmark[i].freq)
diff = wmark[i].freq - pixclock;
else
diff = pixclock - wmark[i].freq;
if (diff < min) {
wm_best = wmark[i].wm;
min = diff;
}
}
return wm_best;
}
/*-*- linux-c -*-
* linux/drivers/video/i810_main.c -- Intel 810 frame buffer device
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
* Contributors:
* Michael Vogt <mvogt@acm.org> - added support for Intel 815 chipsets
* and enabling the power-on state of
* external VGA connectors for
* secondary displays
*
* Fredrik Andersson <krueger@shell.linux.se> - alpha testing of
* the VESA GTF
*
* Brad Corrion <bcorrion@web-co.com> - alpha testing of customized
* timings support
*
* The code framework is a modification of vfb.c by Geert Uytterhoeven.
* DotClock and PLL calculations are partly based on i810_driver.c
* in xfree86 v4.0.3 by Precision Insight.
* Watermark calculation and tables are based on i810_wmark.c
* in xfre86 v4.0.3 by Precision Insight. Slight modifications
* only to allow for integer operations instead of floating point.
*
* 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.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/resource.h>
#include <linux/selection.h>
#include <linux/console.h>
#include <linux/vt_kern.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <asm/div64.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
#include <asm/page.h>
#include "i810_regs.h"
#include "i810.h"
#include "i810_main.h"
/*------------------------------------------------------------*/
/**************************************************************
* Hardware Low Level Routines *
**************************************************************/
/**
* i810_screen_off - turns off/on display
* @mmio: address of register space
* @mode: on or off
*
* DESCRIPTION:
* Blanks/unblanks the display
*/
static void i810_screen_off(u8 *mmio, u8 mode)
{
u32 count = WAIT_COUNT;
u8 val;
i810_writeb(SR_INDEX, mmio, SR01);
val = i810_readb(SR_DATA, mmio);
val = (mode == OFF) ? val | SCR_OFF :
val & ~SCR_OFF;
while((i810_readw(DISP_SL, mmio) & 0xFFF) && count--);
i810_writeb(SR_INDEX, mmio, SR01);
i810_writeb(SR_DATA, mmio, val);
}
/**
* i810_dram_off - turns off/on dram refresh
* @mmio: address of register space
* @mode: on or off
*
* DESCRIPTION:
* Turns off DRAM refresh. Must be off for only 2 vsyncs
* before data becomes corrupt
*/
static void i810_dram_off(u8 *mmio, u8 mode)
{
u8 val;
val = i810_readb(DRAMCH, mmio);
val &= DRAM_OFF;
val = (mode == OFF) ? val : val | DRAM_ON;
i810_writeb(DRAMCH, mmio, val);
}
/**
* i810_protect_regs - allows rw/ro mode of certain VGA registers
* @mmio: address of register space
* @mode: protect/unprotect
*
* DESCRIPTION:
* The IBM VGA standard allows protection of certain VGA registers.
* This will protect or unprotect them.
*/
static void i810_protect_regs(u8 *mmio, int mode)
{
u8 reg;
i810_writeb(CR_INDEX_CGA, mmio, CR11);
reg = i810_readb(CR_DATA_CGA, mmio);
reg = (mode == OFF) ? reg & ~0x80 :
reg | 0x80;
i810_writeb(CR_INDEX_CGA, mmio, CR11);
i810_writeb(CR_DATA_CGA, mmio, reg);
}
/**
* i810_load_pll - loads values for the hardware PLL clock
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Loads the P, M, and N registers.
*/
static void i810_load_pll(struct i810fb_par *par)
{
u32 tmp1, tmp2;
u8 *mmio = par->mmio_start_virtual;
tmp1 = par->regs.M | par->regs.N << 16;
tmp2 = i810_readl(DCLK_2D, mmio);
tmp2 &= ~MN_MASK;
i810_writel(DCLK_2D, mmio, tmp1 | tmp2);
tmp1 = par->regs.P;
tmp2 = i810_readl(DCLK_0DS, mmio);
tmp2 &= ~(P_OR << 16);
i810_writel(DCLK_0DS, mmio, (tmp1 << 16) | tmp2);
i810_writeb(MSR_WRITE, mmio, par->regs.msr | 0xC8 | 1);
}
/**
* i810_load_vga - load standard VGA registers
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Load values to VGA registers
*/
static void i810_load_vga(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
/* interlace */
i810_writeb(CR_INDEX_CGA, mmio, CR70);
i810_writeb(CR_DATA_CGA, mmio, par->interlace);
i810_writeb(CR_INDEX_CGA, mmio, CR00);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr00);
i810_writeb(CR_INDEX_CGA, mmio, CR01);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr01);
i810_writeb(CR_INDEX_CGA, mmio, CR02);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr02);
i810_writeb(CR_INDEX_CGA, mmio, CR03);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr03);
i810_writeb(CR_INDEX_CGA, mmio, CR04);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr04);
i810_writeb(CR_INDEX_CGA, mmio, CR05);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr05);
i810_writeb(CR_INDEX_CGA, mmio, CR06);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr06);
i810_writeb(CR_INDEX_CGA, mmio, CR09);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr09);
i810_writeb(CR_INDEX_CGA, mmio, CR10);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr10);
i810_writeb(CR_INDEX_CGA, mmio, CR11);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr11);
i810_writeb(CR_INDEX_CGA, mmio, CR12);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr12);
i810_writeb(CR_INDEX_CGA, mmio, CR15);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr15);
i810_writeb(CR_INDEX_CGA, mmio, CR16);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr16);
}
/**
* i810_load_vgax - load extended VGA registers
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Load values to extended VGA registers
*/
static void i810_load_vgax(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
i810_writeb(CR_INDEX_CGA, mmio, CR30);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr30);
i810_writeb(CR_INDEX_CGA, mmio, CR31);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr31);
i810_writeb(CR_INDEX_CGA, mmio, CR32);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr32);
i810_writeb(CR_INDEX_CGA, mmio, CR33);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr33);
i810_writeb(CR_INDEX_CGA, mmio, CR35);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr35);
i810_writeb(CR_INDEX_CGA, mmio, CR39);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr39);
}
/**
* i810_load_2d - load grahics registers
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Load values to graphics registers
*/
static void i810_load_2d(struct i810fb_par *par)
{
u32 tmp;
u8 tmp8, *mmio = par->mmio_start_virtual;;
i810_writel(FW_BLC, mmio, par->watermark);
tmp = i810_readl(PIXCONF, mmio);
tmp |= 1 | 1 << 20;
i810_writel(PIXCONF, mmio, tmp);
i810_writel(OVRACT, mmio, par->ovract);
i810_writeb(GR_INDEX, mmio, GR10);
tmp8 = i810_readb(GR_DATA, mmio);
tmp8 |= 2;
i810_writeb(GR_INDEX, mmio, GR10);
i810_writeb(GR_DATA, mmio, tmp8);
}
/**
* i810_hires - enables high resolution mode
* @mmio: address of register space
*/
static void i810_hires(u8 *mmio)
{
u8 val;
i810_writeb(CR_INDEX_CGA, mmio, CR80);
val = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR80);
i810_writeb(CR_DATA_CGA, mmio, val | 1);
}
/**
* i810_load_pitch - loads the characters per line of the display
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Loads the characters per line
*/
static void i810_load_pitch(struct i810fb_par *par)
{
u32 tmp, pitch;
u8 val, *mmio = par->mmio_start_virtual;
pitch = par->pitch >> 3;
i810_writeb(SR_INDEX, mmio, SR01);
val = i810_readb(SR_DATA, mmio);
val &= 0xE0;
val |= 1 | 1 << 2;
i810_writeb(SR_INDEX, mmio, SR01);
i810_writeb(SR_DATA, mmio, val);
tmp = pitch & 0xFF;
i810_writeb(CR_INDEX_CGA, mmio, CR13);
i810_writeb(CR_DATA_CGA, mmio, (u8) tmp);
tmp = pitch >> 8;
i810_writeb(CR_INDEX_CGA, mmio, CR41);
val = i810_readb(CR_DATA_CGA, mmio) & ~0x0F;
i810_writeb(CR_INDEX_CGA, mmio, CR41);
i810_writeb(CR_DATA_CGA, mmio, (u8) tmp | val);
}
/**
* i810_load_color - loads the color depth of the display
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Loads the color depth of the display and the graphics engine
*/
static void i810_load_color(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
u32 reg1;
u16 reg2;
reg1 = i810_readl(PIXCONF, mmio) & ~(0xF0000 | 1 << 27);
reg2 = i810_readw(BLTCNTL, mmio) & ~0x30;
reg1 |= 0x8000 | par->pixconf;
reg2 |= par->bltcntl;
i810_writel(PIXCONF, mmio, reg1);
i810_writew(BLTCNTL, mmio, reg2);
}
/**
* i810_load_regs - loads all registers for the mode
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Loads registers
*/
static void i810_load_regs(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
i810_screen_off(mmio, OFF);
i810_protect_regs(mmio, OFF);
i810_dram_off(mmio, OFF);
i810_load_pll(par);
i810_load_vga(par);
i810_load_vgax(par);
i810_dram_off(mmio, ON);
i810_load_2d(par);
i810_hires(mmio);
i810_screen_off(mmio, ON);
i810_protect_regs(mmio, ON);
i810_load_color(par);
i810_load_pitch(par);
}
static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue,
u8 *mmio)
{
i810_writeb(CLUT_INDEX_WRITE, mmio, regno);
i810_writeb(CLUT_DATA, mmio, red);
i810_writeb(CLUT_DATA, mmio, green);
i810_writeb(CLUT_DATA, mmio, blue);
}
static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue,
u8 *mmio)
{
i810_writeb(CLUT_INDEX_READ, mmio, regno);
*red = i810_readb(CLUT_DATA, mmio);
*green = i810_readb(CLUT_DATA, mmio);
*blue = i810_readb(CLUT_DATA, mmio);
}
/************************************************************
* VGA State Restore *
************************************************************/
static void i810_restore_pll(struct i810fb_par *par)
{
u32 tmp1, tmp2;
u8 *mmio = par->mmio_start_virtual;
tmp1 = par->hw_state.dclk_2d;
tmp2 = i810_readl(DCLK_2D, mmio);
tmp1 &= ~MN_MASK;
tmp2 &= MN_MASK;
i810_writel(DCLK_2D, mmio, tmp1 | tmp2);
tmp1 = par->hw_state.dclk_1d;
tmp2 = i810_readl(DCLK_1D, mmio);
tmp1 &= ~MN_MASK;
tmp2 &= MN_MASK;
i810_writel(DCLK_1D, mmio, tmp1 | tmp2);
i810_writel(DCLK_0DS, mmio, par->hw_state.dclk_0ds);
}
static void i810_restore_dac(struct i810fb_par *par)
{
u32 tmp1, tmp2;
u8 *mmio = par->mmio_start_virtual;
tmp1 = par->hw_state.pixconf;
tmp2 = i810_readl(PIXCONF, mmio);
tmp1 &= DAC_BIT;
tmp2 &= ~DAC_BIT;
i810_writel(PIXCONF, mmio, tmp1 | tmp2);
}
static void i810_restore_vgax(struct i810fb_par *par)
{
u8 i, j, *mmio = par->mmio_start_virtual;
for (i = 0; i < 4; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR30+i);
i810_writeb(CR_DATA_CGA, mmio, *(&(par->hw_state.cr30) + i));
}
i810_writeb(CR_INDEX_CGA, mmio, CR35);
i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr35);
i810_writeb(CR_INDEX_CGA, mmio, CR39);
i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr39);
i810_writeb(CR_INDEX_CGA, mmio, CR41);
i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr39);
/*restore interlace*/
i810_writeb(CR_INDEX_CGA, mmio, CR70);
i = par->hw_state.cr70;
i &= INTERLACE_BIT;
j = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR70);
i810_writeb(CR_DATA_CGA, mmio, j | i);
i810_writeb(CR_INDEX_CGA, mmio, CR80);
i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr80);
i810_writeb(MSR_WRITE, mmio, par->hw_state.msr);
i810_writeb(SR_INDEX, mmio, SR01);
i = (par->hw_state.sr01) & ~0xE0 ;
j = i810_readb(SR_DATA, mmio) & 0xE0;
i810_writeb(SR_INDEX, mmio, SR01);
i810_writeb(SR_DATA, mmio, i | j);
}
static void i810_restore_vga(struct i810fb_par *par)
{
u8 i, *mmio = par->mmio_start_virtual;
for (i = 0; i < 10; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR00 + i);
i810_writeb(CR_DATA_CGA, mmio, *((&par->hw_state.cr00) + i));
}
for (i = 0; i < 8; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR10 + i);
i810_writeb(CR_DATA_CGA, mmio, *((&par->hw_state.cr10) + i));
}
}
static void i810_restore_addr_map(struct i810fb_par *par)
{
u8 tmp, *mmio = par->mmio_start_virtual;
i810_writeb(GR_INDEX, mmio, GR10);
tmp = i810_readb(GR_DATA, mmio);
tmp &= ADDR_MAP_MASK;
tmp |= par->hw_state.gr10;
i810_writeb(GR_INDEX, mmio, GR10);
i810_writeb(GR_DATA, mmio, tmp);
}
static void i810_restore_2d(struct i810fb_par *par)
{
u32 tmp_long;
u16 tmp_word;
u8 *mmio = par->mmio_start_virtual;
tmp_word = i810_readw(BLTCNTL, mmio);
tmp_word &= ~(3 << 4);
tmp_word |= par->hw_state.bltcntl;
i810_writew(BLTCNTL, mmio, tmp_word);
i810_dram_off(mmio, OFF);
i810_writel(PIXCONF, mmio, par->hw_state.pixconf);
i810_dram_off(mmio, ON);
tmp_word = i810_readw(HWSTAM, mmio);
tmp_word &= 3 << 13;
tmp_word |= par->hw_state.hwstam;
i810_writew(HWSTAM, mmio, tmp_word);
tmp_long = i810_readl(FW_BLC, mmio);
tmp_long &= FW_BLC_MASK;
tmp_long |= par->hw_state.fw_blc;
i810_writel(FW_BLC, mmio, tmp_long);
i810_writel(HWS_PGA, mmio, par->hw_state.hws_pga);
i810_writew(IER, mmio, par->hw_state.ier);
i810_writew(IMR, mmio, par->hw_state.imr);
i810_writel(DPLYSTAS, mmio, par->hw_state.dplystas);
}
static void i810_restore_vga_state(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
i810_screen_off(mmio, OFF);
i810_protect_regs(mmio, OFF);
i810_dram_off(mmio, OFF);
i810_restore_pll(par);
i810_restore_dac(par);
i810_restore_vga(par);
i810_restore_vgax(par);
i810_restore_addr_map(par);
i810_dram_off(mmio, ON);
i810_restore_2d(par);
i810_screen_off(mmio, ON);
i810_protect_regs(mmio, ON);
}
/***********************************************************************
* VGA State Save *
***********************************************************************/
static void i810_save_vgax(struct i810fb_par *par)
{
u8 i, *mmio = par->mmio_start_virtual;
for (i = 0; i < 4; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR30 + i);
*(&(par->hw_state.cr30) + i) = i810_readb(CR_DATA_CGA, mmio);
}
i810_writeb(CR_INDEX_CGA, mmio, CR35);
par->hw_state.cr35 = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR39);
par->hw_state.cr39 = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR41);
par->hw_state.cr41 = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR70);
par->hw_state.cr70 = i810_readb(CR_DATA_CGA, mmio);
par->hw_state.msr = i810_readb(MSR_READ, mmio);
i810_writeb(CR_INDEX_CGA, mmio, CR80);
par->hw_state.cr80 = i810_readb(CR_DATA_CGA, mmio);
i810_writeb(SR_INDEX, mmio, SR01);
par->hw_state.sr01 = i810_readb(SR_DATA, mmio);
}
static void i810_save_vga(struct i810fb_par *par)
{
u8 i, *mmio = par->mmio_start_virtual;
for (i = 0; i < 10; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR00 + i);
*((&par->hw_state.cr00) + i) = i810_readb(CR_DATA_CGA, mmio);
}
for (i = 0; i < 8; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR10 + i);
*((&par->hw_state.cr10) + i) = i810_readb(CR_DATA_CGA, mmio);
}
}
static void i810_save_2d(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
par->hw_state.dclk_2d = i810_readl(DCLK_2D, mmio);
par->hw_state.dclk_1d = i810_readl(DCLK_1D, mmio);
par->hw_state.dclk_0ds = i810_readl(DCLK_0DS, mmio);
par->hw_state.pixconf = i810_readl(PIXCONF, mmio);
par->hw_state.fw_blc = i810_readl(FW_BLC, mmio);
par->hw_state.bltcntl = i810_readw(BLTCNTL, mmio);
par->hw_state.hwstam = i810_readw(HWSTAM, mmio);
par->hw_state.hws_pga = i810_readl(HWS_PGA, mmio);
par->hw_state.ier = i810_readw(IER, mmio);
par->hw_state.imr = i810_readw(IMR, mmio);
par->hw_state.dplystas = i810_readl(DPLYSTAS, mmio);
}
static void i810_save_vga_state(struct i810fb_par *par)
{
i810_save_vga(par);
i810_save_vgax(par);
i810_save_2d(par);
}
/************************************************************
* Helpers *
************************************************************/
/**
* get_line_length - calculates buffer pitch in bytes
* @par: pointer to i810fb_par structure
* @xres_virtual: virtual resolution of the frame
* @bpp: bits per pixel
*
* DESCRIPTION:
* Calculates buffer pitch in bytes.
*/
u32 get_line_length(struct i810fb_par *par, int xres_virtual, int bpp)
{
u32 length;
length = xres_virtual*bpp;
length = (length+31)&-32;
length >>= 3;
return length;
}
/**
* i810_calc_dclk - calculates the P, M, and N values of a pixelclock value
* @freq: target pixelclock in picoseconds
* @m: where to write M register
* @n: where to write N register
* @p: where to write P register
*
* DESCRIPTION:
* Based on the formula Freq_actual = (4*M*Freq_ref)/(N^P)
* Repeatedly computes the Freq until the actual Freq is equal to
* the target Freq or until the loop count is zero. In the latter
* case, the actual frequency nearest the target will be used.
*/
static void i810_calc_dclk(u32 freq, u32 *m, u32 *n, u32 *p)
{
u32 m_reg, n_reg, p_divisor, n_target_max;
u32 m_target, n_target, p_target, n_best, m_best, mod;
u32 f_out, target_freq, diff = 0, mod_min, diff_min;
diff_min = mod_min = 0xFFFFFFFF;
n_best = m_best = m_target = f_out = 0;
target_freq = freq;
n_target_max = 30;
/*
* find P such that target freq is 16x reference freq (Hz).
*/
p_divisor = 1;
p_target = 0;
while(!((1000000 * p_divisor)/(16 * 24 * target_freq)) &&
p_divisor <= 32) {
p_divisor <<= 1;
p_target++;
}
n_reg = m_reg = n_target = 3;
while ((diff_min || mod_min) && (n_target < n_target_max)) {
f_out = (p_divisor * n_reg * 1000000)/(4 * 24 * m_reg);
mod = (p_divisor * n_reg * 1000000) % (4 * 24 * m_reg);
m_target = m_reg;
n_target = n_reg;
if (f_out <= target_freq) {
n_reg++;
diff = target_freq - f_out;
}
else {
m_reg++;
diff = f_out - target_freq;
}
if (diff_min > diff) {
diff_min = diff;
n_best = n_target;
m_best = m_target;
}
if (!diff && mod_min > mod) {
mod_min = mod;
n_best = n_target;
m_best = m_target;
}
}
if (m) *m = (m_best - 2) & 0x3FF;
if (n) *n = (n_best - 2) & 0x3FF;
if (p) *p = (p_target << 4);
}
/**
* i810_get_vblank - get vertical blank time
* @hfreq: horizontal freq
*
* DESCRIPTION:
* vblank = right_margin + vsync_len + left_margin
* given: right_margin = 1 (V_FRONTPORCH)
* vsync_len = 3
* flyback = 550
*
* flyback * hfreq
* left_margin = --------------- - vsync_len
* 1000000
* and flyback is set to 550
*/
static u32 i810_get_vblank(u32 hfreq)
{
u32 vblank;
vblank = (hfreq * FLYBACK)/1000;
vblank = (vblank + 500)/1000;
return (vblank + V_FRONTPORCH);
}
/**
* i810_get_hblank - get horizontal blank time
* @hfreq: horizontal freq
* @xres: horizontal resolution in pixels
*
* DESCRIPTION:
* duty cycle is the percent of htotal assigned to inactive display
* duty cycle = C - (M/Hfreq)
* where: C = ((offset - scale factor) * blank_scale)
* -------------------------------------- + scale factor
* 256
* M = blank_scale * gradient
*
* xres * duty_cycle
* hblank = ------------------
* 100 - duty_cycle
*/
static u32 i810_get_hblank(u32 hfreq, u32 xres)
{
u32 c_val, m_val, duty_cycle, hblank;
c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
H_SCALEFACTOR) * 1000;
m_val = (H_BLANKSCALE * H_GRADIENT)/256;
m_val = (m_val * 1000000)/hfreq;
duty_cycle = c_val - m_val;
hblank = (xres * duty_cycle)/(100000 - duty_cycle);
hblank = (hblank + 4) & ~7;
return (hblank);
}
/**
* i810_estimate_hfreq - estimate hsync
* @vfreq: vertical refresh rate
* @yres: vertical resolution
*
* DESCRIPTION:
* Based on:
*
* (yres + front_port) * vfreq * 1000000
* hfreq = -------------------------------------
* (1000000 - (vfreq * FLYBACK)
*
*/
static u32 i810_estimate_hfreq(u32 vfreq, u32 yres)
{
u64 hfreq;
u32 divisor;
divisor = 1000000 - (vfreq * FLYBACK);
hfreq = (u64) (yres + V_FRONTPORCH) *
(u64) (vfreq) * 1000000;
do_div(hfreq, divisor);
return ((u32) hfreq);
}
/**
* i810_calculate_timings - calculate video timings
* @info: pointer to fb_info structure
* @var: pointer to current var
*
* DESCRIPTION:
* htotal: calculated using GTF
* vtotal: calculated using GTF
* hsync pulse: 8% of htotal
* vsync pulse: 3
* left margin : (htotal - xres)/2 - hsync
* right margin: sync + left margin
* upper margin: 1
* lower margin: vtotal - (yres + vsync + upper margin)
*
* Calculates necessary timing information based on
* monitor specifications. This will use the
* VESA generalized timing formula. New values are
* written to @var.
*/
static int i810_calculate_timings(struct fb_info *info,
struct fb_var_screeninfo *var,
u32 xres, u32 yres)
{
u64 num = 1000000000000;
u32 htotal = 0, vtotal, hfreq, vfreq, hblank, vblank;
u32 dclk, interlace = 0, dscan = 0;
u32 max_pixclock = 0;
switch (var->bits_per_pixel) {
case 8:
max_pixclock = 234000000;
break;
case 16:
max_pixclock = 229000000;
break;
case 24:
case 32:
max_pixclock = 204000000;
break;
default:
max_pixclock = 0;
}
if (var->vmode & FB_VMODE_INTERLACED) {
yres >>= 1;
interlace = 1;
}
if (var->vmode & FB_VMODE_DOUBLE) {
yres <<= 1;
dscan = 1;
}
hfreq = info->monspecs.hfmax;
vblank = i810_get_vblank(hfreq);
vtotal = yres + vblank;
vfreq = hfreq/vtotal;
if (vfreq > info->monspecs.vfmax) {
vfreq = info->monspecs.vfmax;
hfreq = i810_estimate_hfreq(vfreq, yres);
vblank = i810_get_vblank(hfreq);
vtotal = yres + vblank;
}
hblank = i810_get_hblank(hfreq, xres);
htotal = xres + hblank;
dclk = htotal * hfreq;
while(dclk > max_pixclock &&
hfreq > info->monspecs.hfmin &&
vfreq > info->monspecs.vfmin) {
hfreq -= 1000;
vblank = i810_get_vblank(hfreq);
vtotal = yres + vblank;
vfreq = hfreq/vtotal;
hblank = i810_get_hblank(hfreq, xres);
htotal = xres + hblank;
dclk = hfreq * htotal;
}
if (vfreq < info->monspecs.vfmin) {
printk("i810fb: required vertical refresh, %dHz, "
"for %dx%d is out of range\n",
vfreq, xres, yres);
return -1;
}
if (hfreq < info->monspecs.hfmin) {
printk("i810fb: required horizontal sync frequency, %dKHz, "
"for %dx%d is out of range\n", hfreq/1000, xres, yres);
return -1;
}
if (dclk < MIN_PIXELCLOCK) {
printk("i810fb: required pixelclock, %dMHz, for %dx%d"
" is out of range\n", dclk/1000000, xres, yres);
return -1;
}
do_div(num, dclk);
var->pixclock = (u32) num;
var->hsync_len = ((htotal * 8)/100 + 4) & ~7;
var->right_margin = ((hblank >> 1) - var->hsync_len + 4) & ~7;
var->left_margin = (hblank-var->right_margin-var->hsync_len + 4) & ~7;
var->vsync_len = (3 << interlace) >> dscan;
var->lower_margin = (1 << interlace) >> dscan;
var->upper_margin = ((vblank << interlace) >> dscan) -
(var->vsync_len + var->lower_margin);
if (yres > 480 || (yres == 200 && vfreq == 60 && hfreq/100 == 157))
var->sync |= FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT;
else {
if (yres == 400 && vfreq == 70 && hfreq/100 == 315)
var->sync |= FB_SYNC_VERT_HIGH_ACT;
if (yres == 350 && vfreq == 60 && hfreq/100 == 218)
var->sync |= FB_SYNC_HOR_HIGH_ACT;
}
return 0;
}
/**
* i810_check_custom_timings - validates user entered timings
* @info: pointer to fb_info
* @var: pointer to current fb_var_screeninfo
*
* DESCRIPTION:
* Validates user entered timings in @var.
*/
static int i810_check_custom_timings(struct fb_info *info,
struct fb_var_screeninfo *var,
u32 xres, u32 yres)
{
u64 num = 1000000000000;
u32 hfreq, vfreq, htotal, vtotal, pixclock;
u32 max_pixclock = 0;
if (!var->pixclock)
return -EINVAL;
do_div(num, var->pixclock);
pixclock = (u32) num;
htotal = xres + var->right_margin + var->hsync_len + var->left_margin;
vtotal = yres + var->lower_margin + var->vsync_len + var->upper_margin;
if (var->vmode & FB_VMODE_INTERLACED)
vtotal >>= 1;
if (var->vmode & FB_VMODE_DOUBLE)
vtotal <<= 1;
hfreq = pixclock/htotal;
vfreq = hfreq/vtotal;
switch (var->bits_per_pixel) {
case 8:
max_pixclock = 234000000;
break;
case 16:
max_pixclock = 229000000;
break;
case 24:
case 32:
max_pixclock = 204000000;
break;
default:
max_pixclock = 0;
}
if (pixclock < MIN_PIXELCLOCK || pixclock > max_pixclock ||
hfreq < info->monspecs.hfmin || hfreq > info->monspecs.hfmax ||
vfreq < info->monspecs.vfmin || vfreq > info->monspecs.vfmax)
return -EINVAL;
return 0;
}
/*************************************************************
* Hardware Cursor Routines *
*************************************************************/
/**
* i810_enable_cursor - show or hide the hardware cursor
* @mmio: address of register space
* @mode: show (1) or hide (0)
*
* Description:
* Shows or hides the hardware cursor
*/
void i810_enable_cursor(u8 *mmio, int mode)
{
u32 temp;
temp = i810_readl(PIXCONF, mmio);
temp = (mode == ON) ? temp | CURSOR_ENABLE_MASK :
temp & ~CURSOR_ENABLE_MASK;
i810_writel(PIXCONF, mmio, temp);
}
static void i810_reset_cursor_image(struct i810fb_par *par)
{
u8 *addr = par->cursor_heap.virtual;
int i, j;
for (i = 64; i--; ) {
for (j = 0; j < 8; j++) {
i810_writeb(j, addr, 0xff);
i810_writeb(j+8, addr, 0x00);
}
addr +=16;
}
}
static void i810_load_cursor_image(int width, int height, u8 *data,
struct i810fb_par *par)
{
u8 *addr = par->cursor_heap.virtual;
int i, j, w = (width + 7)/8;
for (i = height; i--; ) {
for (j = 0; j < w; j++) {
i810_writeb(j+0, addr, 0x00);
i810_writeb(j+8, addr, *data++);
}
addr += 16;
}
}
static void i810_load_cursor_colors(int fg, int bg,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u8 *mmio = par->mmio_start_virtual, temp;
u8 red, green, blue, trans;
i810fb_getcolreg(bg, &red, &green, &blue, &trans, info);
temp = i810_readb(PIXCONF1, mmio);
i810_writeb(PIXCONF1, mmio, temp | EXTENDED_PALETTE);
i810_write_dac(4, red, green, blue, mmio);
i810_writeb(PIXCONF1, mmio, temp);
i810fb_getcolreg(fg, &red, &green, &blue, &trans, info);
temp = i810_readb(PIXCONF1, mmio);
i810_writeb(PIXCONF1, mmio, temp | EXTENDED_PALETTE);
i810_write_dac(5, red, green, blue, mmio);
i810_writeb(PIXCONF1, mmio, temp);
}
/**
* i810_init_cursor - initializes the cursor
* @par: pointer to i810fb_par structure
*
* DESCRIPTION:
* Initializes the cursor registers
*/
static void i810_init_cursor(struct i810fb_par *par)
{
u8 *mmio = par->mmio_start_virtual;
i810_enable_cursor(mmio, OFF);
i810_writel(CURBASE, mmio, par->cursor_heap.physical);
i810_writew(CURCNTR, mmio, COORD_ACTIVE | CURSOR_MODE_64_XOR);
}
/*********************************************************************
* Framebuffer hook helpers *
*********************************************************************/
/**
* i810_round_off - Round off values to capability of hardware
* @var: pointer to fb_var_screeninfo structure
*
* DESCRIPTION:
* @var contains user-defined information for the mode to be set.
* This will try modify those values to ones nearest the
* capability of the hardware
*/
static void i810_round_off(struct fb_var_screeninfo *var)
{
u32 xres, yres, vxres, vyres;
/*
* Presently supports only these configurations
*/
xres = var->xres;
yres = var->yres;
vxres = var->xres_virtual;
vyres = var->yres_virtual;
var->bits_per_pixel += 7;
var->bits_per_pixel &= ~7;
if (var->bits_per_pixel < 8)
var->bits_per_pixel = 8;
if (var->bits_per_pixel > 32)
var->bits_per_pixel = 32;
round_off_xres(&xres);
if (xres < 40)
xres = 40;
if (xres > 2048)
xres = 2048;
xres = (xres + 7) & ~7;
if (vxres < xres)
vxres = xres;
round_off_yres(&xres, &yres);
if (yres < 1)
yres = 1;
if (yres >= 2048)
yres = 2048;
if (vyres < yres)
vyres = yres;
if (var->bits_per_pixel == 32)
var->accel_flags = 0;
/* round of horizontal timings to nearest 8 pixels */
var->left_margin = (var->left_margin + 4) & ~7;
var->right_margin = (var->right_margin + 4) & ~7;
var->hsync_len = (var->hsync_len + 4) & ~7;
if (var->vmode & FB_VMODE_INTERLACED) {
if (!((yres + var->upper_margin + var->vsync_len +
var->lower_margin) & 1))
var->upper_margin++;
}
var->xres = xres;
var->yres = yres;
var->xres_virtual = vxres;
var->yres_virtual = vyres;
}
/**
* set_color_bitfields - sets rgba fields
* @var: pointer to fb_var_screeninfo
*
* DESCRIPTION:
* The length, offset and ordering for each color field
* (red, green, blue) will be set as specified
* by the hardware
*/
static void set_color_bitfields(struct fb_var_screeninfo *var)
{
switch (var->bits_per_pixel) {
case 8:
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 0;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
break;
case 16:
if (var->green.length == 5) {
/* RGB 555 */
var->red.offset = 10;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 5;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.offset = 15;
var->transp.length = 1;
}
else {
/* RGB 565 */
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.offset = 0;
var->transp.length = 0;
}
break;
case 24: /* RGB 888 */
case 32: /* RGBA 8888 */
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
if (var->bits_per_pixel == 24) {
var->transp.offset = 0;
var->transp.length = 0;
}
else {
var->transp.offset = 24;
var->transp.length = 8;
break;
}
}
var->red.msb_right = 0;
var->green.msb_right = 0;
var->blue.msb_right = 0;
var->transp.msb_right = 0;
}
/**
* i810_check_params - check if contents in var are valid
* @var: pointer to fb_var_screeninfo
* @info: pointer to fb_info
*
* DESCRIPTION:
* This will check if the framebuffer size is sufficient
* for the current mode and if the user's monitor has the
* required specifications to display the current mode.
*/
static int i810_check_params(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
int line_length, vidmem;
u32 xres, yres, vxres, vyres;
xres = var->xres;
yres = var->yres;
vxres = var->xres_virtual;
vyres = var->yres_virtual;
/*
* Memory limit
*/
line_length = get_line_length(par, vxres,
var->bits_per_pixel);
vidmem = line_length*vyres;
if (vidmem > par->fb.size) {
vyres = par->fb.size/line_length;
if (vyres < yres) {
vyres = yres;
vxres = par->fb.size/vyres;
vxres /= var->bits_per_pixel >> 3;
line_length = get_line_length(par, vxres,
var->bits_per_pixel);
vidmem = line_length * yres;
if (vxres < xres) {
printk("i810fb: required video memory, "
"%d bytes, for %dx%d-%d (virtual) "
"is out of range\n",
vidmem, vxres, vyres,
var->bits_per_pixel);
return -ENOMEM;
}
}
}
/*
* Monitor limit
*/
if (i810_check_custom_timings(info, var, xres, yres)) {
if (i810_calculate_timings(info, var, xres, yres)) {
return -EINVAL;
}
}
var->xres = xres;
var->yres = yres;
var->xres_virtual = vxres;
var->yres_virtual = vyres;
return 0;
}
/**
* encode_fix - fill up fb_fix_screeninfo structure
* @fix: pointer to fb_fix_screeninfo
* @info: pointer to fb_info
*
* DESCRIPTION:
* This will set up parameters that are unmodifiable by the user.
*/
static int encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
memset(fix, 0, sizeof(struct fb_fix_screeninfo));
strcpy(fix->id, i810fb_name);
fix->smem_start = par->fb.physical;
fix->smem_len = par->fb.size;
fix->type = FB_TYPE_PACKED_PIXELS;
fix->type_aux = 0;
fix->xpanstep = 8;
fix->ypanstep = 1;
switch (info->var.bits_per_pixel) {
case 8:
fix->visual = FB_VISUAL_PSEUDOCOLOR;
break;
case 16:
case 24:
case 32:
if (info->var.nonstd)
fix->visual = FB_VISUAL_DIRECTCOLOR;
else
fix->visual = FB_VISUAL_TRUECOLOR;
break;
default:
return -EINVAL;
}
fix->ywrapstep = 0;
fix->line_length = get_line_length(par, info->var.xres_virtual,
info->var.bits_per_pixel);
fix->mmio_start = par->mmio_start_phys;
fix->mmio_len = MMIO_SIZE;
fix->accel = FB_ACCEL_I810;
return 0;
}
/**
* decode_var - modify par according to contents of var
* @var: pointer to fb_var_screeninfo
* @par: pointer to i810fb_par
* @info: pointer to fb_info
*
* DESCRIPTION:
* Based on the contents of @var, @par will be dynamically filled up.
* @par contains all information necessary to modify the hardware.
*/
static void decode_var(const struct fb_var_screeninfo *var,
struct i810fb_par *par, struct fb_info *info)
{
u32 xres, yres, vxres, vyres;
xres = var->xres;
yres = var->yres;
vxres = var->xres_virtual;
vyres = var->yres_virtual;
switch (var->bits_per_pixel) {
case 8:
par->pixconf = PIXCONF8;
par->bltcntl = 0;
par->depth = 1;
par->blit_bpp = BPP8;
break;
case 16:
if (var->green.length == 5)
par->pixconf = PIXCONF15;
else
par->pixconf = PIXCONF16;
par->bltcntl = 16;
par->depth = 2;
par->blit_bpp = BPP16;
break;
case 24:
par->pixconf = PIXCONF24;
par->bltcntl = 32;
par->depth = 3;
par->blit_bpp = BPP24;
break;
case 32:
par->pixconf = PIXCONF32;
par->bltcntl = 0;
par->depth = 4;
par->blit_bpp = 3 << 24;
break;
}
if (var->nonstd && var->bits_per_pixel != 8)
par->pixconf |= 1 << 27;
i810_calc_dclk(var->pixclock, &par->regs.M,
&par->regs.N, &par->regs.P);
i810fb_encode_registers(var, par, xres, yres);
par->watermark = i810_get_watermark(var, par);
par->pitch = get_line_length(par, vxres, var->bits_per_pixel);
}
/**
* i810fb_getcolreg - gets red, green and blue values of the hardware DAC
* @regno: DAC index
* @red: red
* @green: green
* @blue: blue
* @transp: transparency (alpha)
* @info: pointer to fb_info
*
* DESCRIPTION:
* Gets the red, green and blue values of the hardware DAC as pointed by @regno
* and writes them to @red, @green and @blue respectively
*/
static int i810fb_getcolreg(u8 regno, u8 *red, u8 *green, u8 *blue,
u8 *transp, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u8 *mmio = par->mmio_start_virtual, temp;
if (regno > 255) return 1;
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
if ((info->var.green.length == 5 && regno > 31) ||
(info->var.green.length == 6 && regno > 63))
return 1;
}
temp = i810_readb(PIXCONF1, mmio);
i810_writeb(PIXCONF1, mmio, temp & ~EXTENDED_PALETTE);
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR &&
info->var.green.length == 5)
i810_read_dac(regno * 8, red, green, blue, mmio);
else if (info->fix.visual == FB_VISUAL_DIRECTCOLOR &&
info->var.green.length == 6) {
u8 tmp;
i810_read_dac(regno * 8, red, &tmp, blue, mmio);
i810_read_dac(regno * 4, &tmp, green, &tmp, mmio);
}
else
i810_read_dac(regno, red, green, blue, mmio);
*transp = 0;
i810_writeb(PIXCONF1, mmio, temp);
return 0;
}
/******************************************************************
* Framebuffer device-specific hooks *
******************************************************************/
static int i810fb_open(struct fb_info *info, int user)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u32 count = atomic_read(&par->use_count);
if (count == 0) {
memset(&par->state, 0, sizeof(struct vgastate));
par->state.flags = VGA_SAVE_CMAP;
par->state.vgabase = (caddr_t) par->mmio_start_virtual;
save_vga(&par->state);
i810_save_vga_state(par);
}
atomic_inc(&par->use_count);
return 0;
}
static int i810fb_release(struct fb_info *info, int user)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u32 count;
count = atomic_read(&par->use_count);
if (count == 0)
return -EINVAL;
if (count == 1) {
i810_restore_vga_state(par);
restore_vga(&par->state);
}
atomic_dec(&par->use_count);
return 0;
}
static int i810fb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u8 *mmio = par->mmio_start_virtual, temp;
int i;
if (regno > 255) return 1;
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
if ((info->var.green.length == 5 && regno > 31) ||
(info->var.green.length == 6 && regno > 63))
return 1;
}
if (info->var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;
temp = i810_readb(PIXCONF1, mmio);
i810_writeb(PIXCONF1, mmio, temp & ~EXTENDED_PALETTE);
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR &&
info->var.green.length == 5) {
for (i = 0; i < 8; i++)
i810_write_dac((u8) (regno * 8) + i, (u8) red,
(u8) green, (u8) blue, mmio);
}
else if (info->fix.visual == FB_VISUAL_DIRECTCOLOR &&
info->var.green.length == 6) {
if (!regno) {
memset(par->red, 0, 64);
memset(par->green, 0, 64);
memset(par->blue, 0, 64);
}
par->red[regno] = (u8) red;
par->green[regno] = (u8) green;
par->blue[regno] = (u8) blue;
if (regno < 32) {
for (i = 0; i < 8; i++)
i810_write_dac((u8) (regno * 8) + i,
(u8) red, par->green[regno*2],
(u8) blue, mmio);
}
for (i = 0; i < 4; i++)
i810_write_dac((u8) (regno*4) + i, par->red[regno/2],
(u8) green, par->blue[regno/2], mmio);
}
else {
i810_write_dac((u8) regno, (u8) red, (u8) green,
(u8) blue, mmio);
}
i810_writeb(PIXCONF1, mmio, temp);
if (regno < 16) {
switch (info->var.bits_per_pixel) {
case 16:
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
if (info->var.green.length == 5)
((u32 *)info->pseudo_palette)[regno] =
(regno << 10) | (regno << 5) |
regno;
else
((u32 *)info->pseudo_palette)[regno] =
(regno << 11) | (regno << 5) |
regno;
}
else {
if (info->var.green.length == 5) {
/* RGB 555 */
((u32 *)info->pseudo_palette)[regno] =
((red & 0xf800) >> 1) |
((green & 0xf800) >> 6) |
((blue & 0xf800) >> 11);
}
else {
/* RGB 565 */
((u32 *)info->pseudo_palette)[regno] =
(red & 0xf800) |
((green & 0xf800) >> 5) |
((blue & 0xf800) >> 11);
}
}
break;
case 24: /* RGB 888 */
case 32: /* RGBA 8888 */
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR)
((u32 *)info->pseudo_palette)[regno] =
(regno << 16) | (regno << 8) |
regno;
else
((u32 *)info->pseudo_palette)[regno] =
((red & 0xff00) << 8) |
(green & 0xff00) |
((blue & 0xff00) >> 8);
break;
}
}
return 0;
}
static int i810fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u32 total;
u8 *mmio = par->mmio_start_virtual;
int xoffset = var->xoffset, yoffset = var->yoffset;
if (xoffset < 0 ||
xoffset+var->xres > var->xres_virtual ||
yoffset < 0 ||
yoffset+var->yres > var->yres_virtual)
return -EINVAL;
total = xoffset * par->depth + yoffset * info->fix.line_length;
i810_writel(DPLYBASE, mmio, par->fb.physical + total);
return 0;
}
static int i810fb_blank (int blank_mode, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u8 *mmio = par->mmio_start_virtual;
int mode = 0, pwr, scr_off = 0;
pwr = i810_readl(PWR_CLKC, mmio);
switch(blank_mode) {
case VESA_NO_BLANKING:
mode = POWERON;
pwr |= 1;
scr_off = ON;
break;
case VESA_VSYNC_SUSPEND:
mode = STANDBY;
pwr |= 1;
scr_off = OFF;
break;
case VESA_HSYNC_SUSPEND:
mode = SUSPEND;
pwr |= 1;
scr_off = OFF;
break;
case VESA_POWERDOWN:
mode = POWERDOWN;
pwr &= ~1;
scr_off = OFF;
break;
default:
return -EINVAL;
}
i810_screen_off(mmio, scr_off);
i810_writel(HVSYNC, mmio, mode);
i810_writel(PWR_CLKC, mmio, pwr);
return 0;
}
static int i810fb_set_par(struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
i810_load_regs(par);
i810_init_cursor(par);
par->cursor_reset = 1;
encode_fix(&info->fix, info);
return 0;
}
static int i810fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
int err;
if (IS_DVT) {
var->vmode &= ~FB_VMODE_MASK;
var->vmode |= FB_VMODE_NONINTERLACED;
}
if (var->vmode & FB_VMODE_DOUBLE) {
var->vmode &= ~FB_VMODE_MASK;
var->vmode |= FB_VMODE_NONINTERLACED;
}
i810_round_off(var);
if ((err = i810_check_params(var, info)))
return err;
i810fb_fill_var_timings(var);
set_color_bitfields(var);
decode_var(&info->var, par, info);
return 0;
}
static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
static u8 data[64 * 8];
struct i810fb_par *par = (struct i810fb_par *)info->par;
u8 *mmio = par->mmio_start_virtual;
u16 flags = cursor->set;
if (!info->var.accel_flags || par->dev_flags & LOCKUP) {
return soft_cursor(info, cursor);
}
if (cursor->image.width > 64 || cursor->image.height > 64 ||
(cursor->dest == NULL && cursor->rop == ROP_XOR))
return 1;
if ((i810_readl(CURBASE, mmio) & 0xf) != par->cursor_heap.physical) {
i810_init_cursor(par);
par->cursor_reset = 1;
}
if (par->cursor_reset) {
flags = FB_CUR_SETALL;
par->cursor_reset = 0;
}
i810_enable_cursor(mmio, OFF);
if (flags & FB_CUR_SETPOS) {
u32 tmp;
tmp = cursor->image.dx - info->var.xoffset;
tmp |= (cursor->image.dy - info->var.yoffset) << 16;
i810_writel(CURPOS, mmio, tmp);
}
if (flags & FB_CUR_SETSIZE) {
i810_reset_cursor_image(par);
}
if (flags & FB_CUR_SETCMAP) {
i810_load_cursor_colors(cursor->image.fg_color,
cursor->image.bg_color,
info);
}
if (flags & (FB_CUR_SETDEST | FB_CUR_SETSHAPE)) {
int size = ((cursor->image.width + 7)/8) *
cursor->image.height;
int i;
switch (cursor->rop) {
case ROP_XOR:
for (i = 0; i < size; i++) {
data[i] = (cursor->image.data[i] &
cursor->mask[i]) ^
cursor->dest[i];
}
break;
case ROP_COPY:
default:
for (i = 0; i < size; i++) {
data[i] = cursor->image.data[i] &
cursor->mask[i];
}
break;
}
i810_load_cursor_image(cursor->image.width,
cursor->image.height, data,
par);
}
if (cursor->enable)
i810_enable_cursor(mmio, ON);
return 0;
}
static struct fb_ops i810fb_ops __initdata = {
.owner = THIS_MODULE,
.fb_open = i810fb_open,
.fb_release = i810fb_release,
.fb_check_var = i810fb_check_var,
.fb_set_par = i810fb_set_par,
.fb_setcolreg = i810fb_setcolreg,
.fb_blank = i810fb_blank,
.fb_pan_display = i810fb_pan_display,
.fb_fillrect = i810fb_fillrect,
.fb_copyarea = i810fb_copyarea,
.fb_imageblit = i810fb_imageblit,
.fb_cursor = i810fb_cursor,
};
/***********************************************************************
* AGP resource allocation *
***********************************************************************/
static void __devinit i810_fix_pointers(struct i810fb_par *par)
{
par->fb.physical = par->aperture.physical+(par->fb.offset << 12);
par->fb.virtual = par->aperture.virtual+(par->fb.offset << 12);
par->iring.physical = par->aperture.physical +
(par->iring.offset << 12);
par->iring.virtual = par->aperture.virtual +
(par->iring.offset << 12);
par->cursor_heap.virtual = par->aperture.virtual+
(par->cursor_heap.offset << 12);
par->pixmap.virtual = par->aperture.virtual +
(par->pixmap.offset << 12);
par->pixmap.physical = par->aperture.physical +
(par->pixmap.offset << 12);
}
static void __devinit i810_fix_offsets(struct i810fb_par *par)
{
if (vram + 1 > par->aperture.size >> 20)
vram = (par->aperture.size >> 20) - 1;
if (v_offset_default > (par->aperture.size >> 20))
v_offset_default = (par->aperture.size >> 20);
if (vram + v_offset_default + 1 > par->aperture.size >> 20)
v_offset_default = (par->aperture.size >> 20) - (vram + 1);
par->fb.size = vram << 20;
par->fb.offset = v_offset_default << 20;
par->fb.offset >>= 12;
par->iring.offset = par->fb.offset + (par->fb.size >> 12);
par->iring.size = RINGBUFFER_SIZE;
par->pixmap.offset = par->iring.offset + (RINGBUFFER_SIZE >> 12);
par->pixmap.size = PIXMAP_SIZE;
par->cursor_heap.offset = par->pixmap.offset + (PIXMAP_SIZE >> 12);
par->cursor_heap.size = 4096;
}
static int __devinit i810_alloc_agp_mem(struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
int size;
i810_fix_offsets(par);
size = par->fb.size + par->iring.size + par->pixmap.size;
par->drm_agp = (drm_agp_t *) inter_module_get("drm_agp");
if (!par->drm_agp) {
printk("i810fb: cannot acquire agp\n");
return -ENODEV;
}
par->drm_agp->acquire();
if (!(par->i810_gtt.i810_fb_memory =
par->drm_agp->allocate_memory(size >> 12, AGP_NORMAL_MEMORY))) {
printk("i810fb_alloc_fbmem: can't allocate framebuffer "
"memory\n");
par->drm_agp->release();
return -ENOMEM;
}
if (par->drm_agp->bind_memory(par->i810_gtt.i810_fb_memory,
par->fb.offset)) {
printk("i810fb_alloc_fbmem: can't bind framebuffer memory\n");
par->drm_agp->release();
return -EBUSY;
}
if (!(par->i810_gtt.i810_cursor_memory =
par->drm_agp->allocate_memory(par->cursor_heap.size >> 12,
AGP_PHYSICAL_MEMORY))) {
printk("i810fb_alloc_cursormem: can't allocate"
"cursor memory\n");
par->drm_agp->release();
return -ENOMEM;
}
if (par->drm_agp->bind_memory(par->i810_gtt.i810_cursor_memory,
par->cursor_heap.offset)) {
printk("i810fb_alloc_cursormem: cannot bind cursor memory\n");
par->drm_agp->release();
return -EBUSY;
}
par->cursor_heap.physical = par->i810_gtt.i810_cursor_memory->physical;
i810_fix_pointers(par);
par->drm_agp->release();
return 0;
}
/***************************************************************
* Initialization *
***************************************************************/
/**
* i810_init_monspecs
* @info: pointer to device specific info structure
*
* DESCRIPTION:
* Sets the the user monitor's horizontal and vertical
* frequency limits
*/
static void __devinit i810_init_monspecs(struct fb_info *info)
{
if (!hsync1)
hsync1 = HFMIN;
if (!hsync2)
hsync2 = HFMAX;
info->monspecs.hfmax = hsync2;
info->monspecs.hfmin = hsync1;
if (hsync2 < hsync1)
info->monspecs.hfmin = hsync2;
if (!vsync1)
vsync1 = VFMIN;
if (!vsync2)
vsync2 = VFMAX;
if (IS_DVT && vsync1 < 60)
vsync1 = 60;
info->monspecs.vfmax = vsync2;
info->monspecs.vfmin = vsync1;
if (vsync2 < vsync1)
info->monspecs.vfmin = vsync2;
}
/**
* i810_init_defaults - initializes default values to use
* @par: pointer to i810fb_par structure
* @info: pointer to current fb_info structure
*/
static void __devinit i810_init_defaults(struct i810fb_par *par,
struct fb_info *info)
{
if (voffset) {
v_offset_default = voffset;
}
else {
if (par->aperture.size > 32 * 1024 * 1024)
v_offset_default = 16;
else
v_offset_default = 8;
}
if (!vram)
vram = 1;
if (accel)
par->dev_flags |= HAS_ACCELERATION;
if (sync)
par->dev_flags |= ALWAYS_SYNC;
if (bpp < 8)
bpp = 8;
if (!vyres)
vyres = (vram << 20)/(xres*bpp >> 3);
par->i810fb_ops = i810fb_ops;
i810fb_default.xres = xres;
i810fb_default.yres = yres;
i810fb_default.yres_virtual = vyres;
i810fb_default.bits_per_pixel = bpp;
if (dcolor)
i810fb_default.nonstd = 1;
if (par->dev_flags & HAS_ACCELERATION)
i810fb_default.accel_flags = 1;
i810_init_monspecs(info);
}
/**
* i810_init_device - initialize device
* @par: pointer to i810fb_par structure
*/
static void __devinit i810_init_device(struct i810fb_par *par)
{
u8 reg, *mmio = par->mmio_start_virtual;
if (mtrr) set_mtrr(par);
i810_init_cursor(par);
/* mvo: enable external vga-connector (for laptops) */
if (ext_vga) {
i810_writel(HVSYNC, mmio, 0);
i810_writel(PWR_CLKC, mmio, 3);
}
pci_read_config_byte(par->dev, 0x50, &reg);
reg &= FREQ_MASK;
par->mem_freq = (reg) ? 133 : 100;
i810fb_init_ringbuffer(par);
}
static int __devinit
i810_allocate_pci_resource(struct i810fb_par *par,
const struct pci_device_id *entry)
{
int err;
if ((err = pci_enable_device(par->dev))) {
printk("i810fb_init: cannot enable device\n");
return err;
}
par->res_flags |= PCI_DEVICE_ENABLED;
if (pci_resource_len(par->dev, 0) > 512 * 1024) {
par->aperture.physical = pci_resource_start(par->dev, 0);
par->aperture.size = pci_resource_len(par->dev, 0);
par->mmio_start_phys = pci_resource_start(par->dev, 1);
}
else {
par->aperture.physical = pci_resource_start(par->dev, 1);
par->aperture.size = pci_resource_len(par->dev, 1);
par->mmio_start_phys = pci_resource_start(par->dev, 0);
}
if (!par->aperture.size) {
printk("i810fb_init: device is disabled\n");
return -ENOMEM;
}
if (!request_mem_region(par->aperture.physical,
par->aperture.size,
i810_pci_list[entry->driver_data])) {
printk("i810fb_init: cannot request framebuffer region\n");
return -ENODEV;
}
par->res_flags |= FRAMEBUFFER_REQ;
par->aperture.virtual = ioremap(par->aperture.physical,
par->aperture.size);
if (!par->aperture.virtual) {
printk("i810fb_init: cannot remap framebuffer region\n");
return -ENODEV;
}
if (!request_mem_region(par->mmio_start_phys,
MMIO_SIZE,
i810_pci_list[entry->driver_data])) {
printk("i810fb_init: cannot request mmio region\n");
return -ENODEV;
}
par->res_flags |= MMIO_REQ;
par->mmio_start_virtual = ioremap_nocache(par->mmio_start_phys,
MMIO_SIZE);
if (!par->mmio_start_virtual) {
printk("i810fb_init: cannot remap mmio region\n");
return -ENODEV;
}
return 0;
}
int __init i810fb_setup(char *options)
{
char *this_opt, *suffix = NULL;
i810_init = 1;
if (!options || !*options)
return 0;
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!strncmp(this_opt, "mtrr", 4))
mtrr = 1;
else if (!strncmp(this_opt, "accel", 5))
accel = 1;
else if (!strncmp(this_opt, "ext_vga", 7))
ext_vga = 1;
else if (!strncmp(this_opt, "sync", 4))
sync = 1;
else if (!strncmp(this_opt, "vram:", 5))
vram = (simple_strtoul(this_opt+5, NULL, 0));
else if (!strncmp(this_opt, "voffset:", 8))
voffset = (simple_strtoul(this_opt+8, NULL, 0));
else if (!strncmp(this_opt, "xres:", 5))
xres = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "yres:", 5))
yres = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "vyres:", 6))
vyres = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "bpp:", 4))
bpp = simple_strtoul(this_opt+4, NULL, 0);
else if (!strncmp(this_opt, "hsync1:", 7)) {
hsync1 = simple_strtoul(this_opt+7, &suffix, 0);
if (strncmp(suffix, "H", 1))
hsync1 *= 1000;
}
else if (!strncmp(this_opt, "hsync2:", 7)) {
hsync2 = simple_strtoul(this_opt+7, &suffix, 0);
if (strncmp(suffix, "H", 1))
hsync2 *= 1000;
}
else if (!strncmp(this_opt, "vsync1:", 7))
vsync1 = simple_strtoul(this_opt+7, NULL, 0);
else if (!strncmp(this_opt, "vsync2:", 7))
vsync2 = simple_strtoul(this_opt+7, NULL, 0);
else if (!strncmp(this_opt, "dcolor", 6))
dcolor = 1;
}
return 0;
}
static int __devinit i810fb_init_pci (struct pci_dev *dev,
const struct pci_device_id *entry)
{
struct fb_info *info;
struct i810fb_par *par = NULL;
int err, vfreq, hfreq, pixclock;
if (!i810_init)
return -EINVAL;
if (!(info = kmalloc(sizeof(struct fb_info), GFP_KERNEL))) {
i810fb_release_resource(info, par);
return -ENOMEM;
}
memset(info, 0, sizeof(struct fb_info));
if(!(par = kmalloc(sizeof(struct i810fb_par), GFP_KERNEL))) {
i810fb_release_resource(info, par);
return -ENOMEM;
}
memset(par, 0, sizeof(struct i810fb_par));
par->dev = dev;
info->par = par;
if ((err = i810_allocate_pci_resource(par, entry))) {
i810fb_release_resource(info, par);
return err;
}
i810_init_defaults(par, info);
if ((err = i810_alloc_agp_mem(info))) {
i810fb_release_resource(info, par);
return err;
}
i810_init_device(par);
info->screen_base = par->fb.virtual;
info->node = NODEV;
info->fbops = &par->i810fb_ops;
info->pseudo_palette = par->pseudo_palette;
info->flags = FBINFO_FLAG_DEFAULT;
fb_alloc_cmap(&info->cmap, 256, 0);
info->var = i810fb_default;
if ((err = info->fbops->fb_check_var(&info->var, info))) {
i810fb_release_resource(info, par);
return err;
}
encode_fix(&info->fix, info);
err = register_framebuffer(info);
if (err < 0) {
i810fb_release_resource(info, par);
printk("i810fb_init: cannot register framebuffer device\n");
return err;
}
pci_set_drvdata(dev, info);
pixclock = 1000000000/(info->var.pixclock);
pixclock *= 1000;
hfreq = pixclock/(info->var.xres + info->var.left_margin +
info->var.hsync_len + info->var.right_margin);
vfreq = hfreq/(info->var.yres + info->var.upper_margin +
info->var.vsync_len + info->var.lower_margin);
printk("fb: %s v%d.%d.%d%s, Tony Daplas\n"
" Video RAM : %dK\n"
" Mode : %dx%d-%dbpp@%dHz\n"
" Acceleration : %sabled\n"
" MTRR : %sabled\n"
" External VGA : %sabled\n"
" Video Timings : %s\n",
i810_pci_list[entry->driver_data],
VERSION_MAJOR, VERSION_MINOR, VERSION_TEENIE, BRANCH_VERSION,
(int) par->fb.size>>10, info->var.xres,
info->var.yres, info->var.bits_per_pixel, vfreq,
(par->dev_flags & HAS_ACCELERATION) ? "en" : "dis",
(par->dev_flags & HAS_MTRR) ? "en" : "dis",
(ext_vga) ? "en" : "dis", (IS_DVT) ?
"Intel(R) DVT" : "VESA GTF (US)");
return 0;
}
/***************************************************************
* Deinitialization *
***************************************************************/
static void i810fb_release_resource(struct fb_info *info,
struct i810fb_par *par)
{
if (par) {
unset_mtrr(par);
if (par->drm_agp) {
drm_agp_t *agp = par->drm_agp;
struct gtt_data *gtt = &par->i810_gtt;
if (par->i810_gtt.i810_cursor_memory)
agp->free_memory(gtt->i810_cursor_memory);
if (par->i810_gtt.i810_fb_memory)
agp->free_memory(gtt->i810_fb_memory);
inter_module_put("drm_agp");
par->drm_agp = NULL;
}
if (par->mmio_start_virtual)
iounmap(par->mmio_start_virtual);
if (par->aperture.virtual)
iounmap(par->aperture.virtual);
if (par->res_flags & FRAMEBUFFER_REQ)
release_mem_region(par->aperture.physical,
par->aperture.size);
if (par->res_flags & MMIO_REQ)
release_mem_region(par->mmio_start_phys, MMIO_SIZE);
if (par->res_flags & PCI_DEVICE_ENABLED)
pci_disable_device(par->dev);
kfree(par);
}
if (info)
kfree(info);
}
static void __devexit i810fb_remove_pci(struct pci_dev *dev)
{
struct fb_info *info = pci_get_drvdata(dev);
struct i810fb_par *par = (struct i810fb_par *) info->par;
unregister_framebuffer(info);
i810fb_release_resource(info, par);
pci_set_drvdata(dev, NULL);
printk("cleanup_module: unloaded i810 framebuffer device\n");
}
#ifndef MODULE
int __init i810fb_init(void)
{
if (agp_init()) {
printk("i810fb_init: cannot initialize agpgart\n");
return -ENODEV;
}
if (agp_intel_init()) {
printk("i810fb_init: cannot initialize intel agpgart\n");
return -ENODEV;
}
return (pci_module_init(&i810fb_driver));
}
#endif
/*********************************************************************
* Modularization *
*********************************************************************/
#ifdef MODULE
int __init i810fb_init(void)
{
i810_init = 1;
hsync1 *= 1000;
hsync2 *= 1000;
return (pci_module_init(&i810fb_driver));
}
MODULE_PARM(vram, "i");
MODULE_PARM_DESC(vram, "System RAM to allocate to framebuffer in MiB"
" (default=4)");
MODULE_PARM(voffset, "i");
MODULE_PARM_DESC(voffset, "at what offset to place start of framebuffer "
"memory (0 to maximum aperture size), in MiB (default = 48)");
MODULE_PARM(bpp, "i");
MODULE_PARM_DESC(bpp, "Color depth for display in bits per pixel"
" (default = 8)");
MODULE_PARM(xres, "i");
MODULE_PARM_DESC(xres, "Hozizontal resolution in pixels (default = 640)");
MODULE_PARM(yres, "i");
MODULE_PARM_DESC(yres, "Vertical resolution in scanlines (default = 480)");
MODULE_PARM(vyres, "i");
MODULE_PARM_DESC(vyres, "Virtual vertical resolution in scanlines"
" (default = 480)");
MODULE_PARM(hsync1, "i");
MODULE_PARM_DESC(hsync1, "Mimimum horizontal frequency of monitor in KHz"
" (default = 31)");
MODULE_PARM(hsync2, "i");
MODULE_PARM_DESC(hsync2, "Maximum horizontal frequency of monitor in KHz"
" (default = 31)");
MODULE_PARM(vsync1, "i");
MODULE_PARM_DESC(vsync1, "Minimum vertical frequency of monitor in Hz"
" (default = 50)");
MODULE_PARM(vsync2, "i");
MODULE_PARM_DESC(vsync2, "Maximum vertical frequency of monitor in Hz"
" (default = 60)");
MODULE_PARM(accel, "i");
MODULE_PARM_DESC(accel, "Use Acceleration (BLIT) engine (default = 0)");
MODULE_PARM(mtrr, "i");
MODULE_PARM_DESC(mtrr, "Use MTRR (default = 0)");
MODULE_PARM(ext_vga, "i");
MODULE_PARM_DESC(ext_vga, "Enable external VGA connector (default = 0)");
MODULE_PARM(sync, "i");
MODULE_PARM_DESC(sync, "wait for accel engine to finish drawing"
" (default = 0)");
MODULE_PARM(dcolor, "i");
MODULE_PARM_DESC(dcolor, "use DirectColor visuals"
" (default = 0 = TrueColor)");
MODULE_AUTHOR("Tony A. Daplas");
MODULE_DESCRIPTION("Framebuffer device for the Intel 810/815 and"
" compatible cards");
MODULE_LICENSE("GPL");
static void __exit i810fb_exit(void)
{
pci_unregister_driver(&i810fb_driver);
}
module_init(i810fb_init);
module_exit(i810fb_exit);
#endif /* MODULE */
/*-*- linux-c -*-
* linux/drivers/video/i810fb_main.h -- Intel 810 frame buffer device
* main header file
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
*
* 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.
*/
#ifndef __I810_MAIN_H__
#define __I810_MAIN_H__
/* PCI */
static const char *i810_pci_list[] __devinitdata = {
"Intel(R) 810 Framebuffer Device" ,
"Intel(R) 810-DC100 Framebuffer Device" ,
"Intel(R) 810E Framebuffer Device" ,
"Intel(R) 815 (Internal Graphics 100Mhz FSB) Framebuffer Device" ,
"Intel(R) 815 (Internal Graphics only) Framebuffer Device" ,
"Intel(R) 815 (Internal Graphics with AGP) Framebuffer Device"
};
static struct pci_device_id i810fb_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
/* mvo: added i815 PCI-ID */
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_NOAGP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }
};
static int __devinit i810fb_init_pci (struct pci_dev *dev,
const struct pci_device_id *entry);
static void __devexit i810fb_remove_pci(struct pci_dev *dev);
static struct pci_driver i810fb_driver = {
.name = "i810fb",
.id_table = i810fb_pci_tbl,
.probe = i810fb_init_pci,
.remove = __devexit_p(i810fb_remove_pci),
};
static int i810_init __devinitdata = 0;
static int vram __devinitdata = 4;
static int bpp __devinitdata = 8;
static int mtrr __devinitdata = 0;
static int accel __devinitdata = 0;
static int hsync1 __devinitdata = 0;
static int hsync2 __devinitdata = 0;
static int vsync1 __devinitdata = 0;
static int vsync2 __devinitdata = 0;
static int xres __devinitdata = 640;
static int yres __devinitdata = 480;
static int vyres __devinitdata = 0;
static int sync __devinitdata = 0;
static int ext_vga __devinitdata = 0;
static int dcolor __devinitdata = 0;
/* "use once" vars */
static char i810fb_name[16] = "i810fb";
static struct fb_var_screeninfo i810fb_default __devinitdata = {
/* 640x480, 8 bpp */
.xres = 640,
.yres = 480,
.xres_virtual = 640,
.yres_virtual = 480,
.xoffset = 0,
.yoffset = 0,
.bits_per_pixel = 8,
.grayscale = 0,
.red = {0, 8, 0},
.green = {0, 8, 0},
.blue = {0, 8, 0},
.transp = {0, 0, 0},
.nonstd = 0,
.activate = 0,
.height = -1,
.width = -1,
.accel_flags = 0,
.pixclock = 20000,
.left_margin = 64,
.right_margin = 64,
.upper_margin = 32,
.lower_margin = 32,
.hsync_len = 64,
.vsync_len = 2,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
};
/*
* voffset - framebuffer offset in MiB from aperture start address. In order for
* the driver to work with X, we must try to use memory holes left untouched by X. The
* following table lists where X's different surfaces start at.
*
* ---------------------------------------------
* : : 64 MiB : 32 MiB :
* ----------------------------------------------
* : FrontBuffer : 0 : 0 :
* : DepthBuffer : 48 : 16 :
* : BackBuffer : 56 : 24 :
* ----------------------------------------------
*
* So for chipsets with 64 MiB Aperture sizes, 32 MiB for v_offset is okay, allowing up to
* 15 + 1 MiB of Framebuffer memory. For 32 MiB Aperture sizes, a v_offset of 8 MiB should
* work, allowing 7 + 1 MiB of Framebuffer memory.
* Note, the size of the hole may change depending on how much memory you allocate to X,
* and how the memory is split up between these surfaces.
*
* Note: Anytime the DepthBuffer or FrontBuffer is overlapped, X would still run but with
* DRI disabled. But if the Frontbuffer is overlapped, X will fail to load.
*
* Experiment with v_offset to find out which works best for you.
*/
static u32 v_offset_default __devinitdata; /* For 32 MiB Aper size, 8 should be the default */
static u32 voffset __devinitdata = 0;
static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor);
/* Chipset Specific Functions */
static int i810fb_set_par (struct fb_info *info);
static int i810fb_getcolreg (u8 regno, u8 *red, u8 *green, u8 *blue,
u8 *transp, struct fb_info *info);
static int i810fb_setcolreg (unsigned regno, unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info);
static int i810fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
static int i810fb_blank (int blank_mode, struct fb_info *info);
/* Initialization */
static void i810fb_release_resource (struct fb_info *info, struct i810fb_par *par);
extern int __init agp_intel_init(void);
extern int __init agp_init(void);
/* Video Timings */
extern void round_off_xres (u32 *xres);
extern void round_off_yres (u32 *xres, u32 *yres);
extern u32 i810_get_watermark (const struct fb_var_screeninfo *var,
struct i810fb_par *par);
extern void i810fb_encode_registers(const struct fb_var_screeninfo *var,
struct i810fb_par *par, u32 xres, u32 yres);
extern void i810fb_fill_var_timings(struct fb_var_screeninfo *var);
/* Accelerated Functions */
extern void i810fb_fillrect (struct fb_info *p, struct fb_fillrect *rect);
extern void i810fb_copyarea (struct fb_info *p, struct fb_copyarea *region);
extern void i810fb_imageblit(struct fb_info *p, struct fb_image *image);
extern int i810fb_sync (struct fb_info *p);
extern void i810fb_init_ringbuffer (struct i810fb_par *par);
/* Conditionals */
#if defined(__i386__)
inline void flush_cache(void)
{
asm volatile ("wbinvd":::"memory");
}
#else
#define flush_cache() do { } while(0)
#endif
#ifdef CONFIG_MTRR
#define KERNEL_HAS_MTRR 1
static inline void __devinit set_mtrr(struct i810fb_par *par)
{
par->mtrr_reg = mtrr_add((u32) par->aperture.physical,
par->aperture.size, MTRR_TYPE_WRCOMB, 1);
if (par->mtrr_reg < 0) {
printk("set_mtrr: unable to set MTRR/n");
return;
}
par->dev_flags |= HAS_MTRR;
}
static inline void unset_mtrr(struct i810fb_par *par)
{
if (par->dev_flags & HAS_MTRR)
mtrr_del(par->mtrr_reg, (u32) par->aperture.physical,
par->aperture.size);
}
#else
#define KERNEL_HAS_MTRR 0
#define set_mtrr(x) printk("set_mtrr: MTRR is disabled in the kernel\n")
#define unset_mtrr(x) do { } while (0)
#endif /* CONFIG_MTRR */
#ifdef CONFIG_FB_I810_GTF
#define IS_DVT (0)
#else
#define IS_DVT (1)
#endif
#endif /* __I810_MAIN_H__ */
/*-*- linux-c -*-
* linux/drivers/video/i810_regs.h -- Intel 810/815 Register List
*
* Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
* All Rights Reserved
*
*
* 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.
*/
/*
* Intel 810 Chipset Family PRM 15 3.1
* GC Register Memory Address Map
*
* Based on:
* Intel (R) 810 Chipset Family
* Programmer s Reference Manual
* November 1999
* Revision 1.0
* Order Number: 298026-001 R
*
* All GC registers are memory-mapped. In addition, the VGA and extended VGA registers
* are I/O mapped.
*/
#ifndef __I810_REGS_H__
#define __I810_REGS_H__
/* Instruction and Interrupt Control Registers (01000h 02FFFh) */
#define FENCE 0x02000
#define PGTBL_CTL 0x02020
#define PGTBL_ER 0x02024
#define LRING 0x02030
#define IRING 0x02040
#define HWS_PGA 0x02080
#define IPEIR 0x02088
#define IPEHR 0x0208C
#define INSTDONE 0x02090
#define NOPID 0x02094
#define HWSTAM 0x02098
#define IER 0x020A0
#define IIR 0x020A4
#define IMR 0x020A8
#define ISR 0x020AC
#define EIR 0x020B0
#define EMR 0x020B4
#define ESR 0x020B8
#define INSTPM 0x020C0
#define INSTPS 0x020C4
#define BBP_PTR 0x020C8
#define ABB_SRT 0x020CC
#define ABB_END 0x020D0
#define DMA_FADD 0x020D4
#define FW_BLC 0x020D8
#define MEM_MODE 0x020DC
/* Memory Control Registers (03000h 03FFFh) */
#define DRT 0x03000
#define DRAMCL 0x03001
#define DRAMCH 0x03002
/* Span Cursor Registers (04000h 04FFFh) */
#define UI_SC_CTL 0x04008
/* I/O Control Registers (05000h 05FFFh) */
#define HVSYNC 0x05000
#define GPIOA 0x05010
#define GPIOB 0x05014
/* Clock Control and Power Management Registers (06000h 06FFFh) */
#define DCLK_0D 0x06000
#define DCLK_1D 0x06004
#define DCLK_2D 0x06008
#define LCD_CLKD 0x0600C
#define DCLK_0DS 0x06010
#define PWR_CLKC 0x06014
/* Graphics Translation Table Range Definition (10000h 1FFFFh) */
#define GTT 0x10000
/* Overlay Registers (30000h 03FFFFh) */
#define OVOADDR 0x30000
#define DOVOSTA 0x30008
#define GAMMA 0x30010
#define OBUF_0Y 0x30100
#define OBUF_1Y 0x30104
#define OBUF_0U 0x30108
#define OBUF_0V 0x3010C
#define OBUF_1U 0x30110
#define OBUF_1V 0x30114
#define OVOSTRIDE 0x30118
#define YRGB_VPH 0x3011C
#define UV_VPH 0x30120
#define HORZ_PH 0x30124
#define INIT_PH 0x30128
#define DWINPOS 0x3012C
#define DWINSZ 0x30130
#define SWID 0x30134
#define SWIDQW 0x30138
#define SHEIGHT 0x3013F
#define YRGBSCALE 0x30140
#define UVSCALE 0x30144
#define OVOCLRCO 0x30148
#define OVOCLRC1 0x3014C
#define DCLRKV 0x30150
#define DLCRKM 0x30154
#define SCLRKVH 0x30158
#define SCLRKVL 0x3015C
#define SCLRKM 0x30160
#define OVOCONF 0x30164
#define OVOCMD 0x30168
#define AWINPOS 0x30170
#define AWINZ 0x30174
/* BLT Engine Status (40000h 4FFFFh) (Software Debug) */
#define BR00 0x40000
#define BRO1 0x40004
#define BR02 0x40008
#define BR03 0x4000C
#define BR04 0x40010
#define BR05 0x40014
#define BR06 0x40018
#define BR07 0x4001C
#define BR08 0x40020
#define BR09 0x40024
#define BR10 0x40028
#define BR11 0x4002C
#define BR12 0x40030
#define BR13 0x40034
#define BR14 0x40038
#define BR15 0x4003C
#define BR16 0x40040
#define BR17 0x40044
#define BR18 0x40048
#define BR19 0x4004C
#define SSLADD 0x40074
#define DSLH 0x40078
#define DSLRADD 0x4007C
/* LCD/TV-Out and HW DVD Registers (60000h 6FFFFh) */
/* LCD/TV-Out */
#define HTOTAL 0x60000
#define HBLANK 0x60004
#define HSYNC 0x60008
#define VTOTAL 0x6000C
#define VBLANK 0x60010
#define VSYNC 0x60014
#define LCDTV_C 0x60018
#define OVRACT 0x6001C
#define BCLRPAT 0x60020
/* Display and Cursor Control Registers (70000h 7FFFFh) */
#define DISP_SL 0x70000
#define DISP_SLC 0x70004
#define PIXCONF 0x70008
#define PIXCONF1 0x70009
#define BLTCNTL 0x7000C
#define SWF 0x70014
#define DPLYBASE 0x70020
#define DPLYSTAS 0x70024
#define CURCNTR 0x70080
#define CURBASE 0x70084
#define CURPOS 0x70088
/* VGA Registers */
/* SMRAM Registers */
#define SMRAM 0x10
/* Graphics Control Registers */
#define GR_INDEX 0x3CE
#define GR_DATA 0x3CF
#define GR10 0x10
#define GR11 0x11
/* CRT Controller Registers */
#define CR_INDEX_MDA 0x3B4
#define CR_INDEX_CGA 0x3D4
#define CR_DATA_MDA 0x3B5
#define CR_DATA_CGA 0x3D5
#define CR30 0x30
#define CR31 0x31
#define CR32 0x32
#define CR33 0x33
#define CR35 0x35
#define CR39 0x39
#define CR40 0x40
#define CR41 0x41
#define CR42 0x42
#define CR70 0x70
#define CR80 0x80
#define CR81 0x82
/* Extended VGA Registers */
/* General Control and Status Registers */
#define ST00 0x3C2
#define ST01_MDA 0x3BA
#define ST01_CGA 0x3DA
#define FRC_READ 0x3CA
#define FRC_WRITE_MDA 0x3BA
#define FRC_WRITE_CGA 0x3DA
#define MSR_READ 0x3CC
#define MSR_WRITE 0x3C2
/* Sequencer Registers */
#define SR_INDEX 0x3C4
#define SR_DATA 0x3C5
#define SR01 0x01
#define SR02 0x02
#define SR03 0x03
#define SR04 0x04
#define SR07 0x07
/* Graphics Controller Registers */
#define GR00 0x00
#define GR01 0x01
#define GR02 0x02
#define GR03 0x03
#define GR04 0x04
#define GR05 0x05
#define GR06 0x06
#define GR07 0x07
#define GR08 0x08
/* Attribute Controller Registers */
#define ATTR_WRITE 0x3C0
#define ATTR_READ 0x3C1
/* VGA Color Palette Registers */
/* CLUT */
#define CLUT_DATA 0x3C9 /* DACDATA */
#define CLUT_INDEX_READ 0x3C7 /* DACRX */
#define CLUT_INDEX_WRITE 0x3C8 /* DACWX */
#define DACMASK 0x3C6
/* CRT Controller Registers */
#define CR00 0x00
#define CR01 0x01
#define CR02 0x02
#define CR03 0x03
#define CR04 0x04
#define CR05 0x05
#define CR06 0x06
#define CR07 0x07
#define CR08 0x08
#define CR09 0x09
#define CR0A 0x0A
#define CR0B 0x0B
#define CR0C 0x0C
#define CR0D 0x0D
#define CR0E 0x0E
#define CR0F 0x0F
#define CR10 0x10
#define CR11 0x11
#define CR12 0x12
#define CR13 0x13
#define CR14 0x14
#define CR15 0x15
#define CR16 0x16
#define CR17 0x17
#define CR18 0x18
#endif /* __I810_REGS_H__ */
......@@ -393,6 +393,7 @@ static void __init offb_init_fb(const char *name, const char *full_name,
struct fb_fix_screeninfo *fix;
struct fb_var_screeninfo *var;
struct fb_info *info;
int size;
if (!request_mem_region(res_start, res_size, "offb"))
return;
......@@ -421,7 +422,7 @@ static void __init offb_init_fb(const char *name, const char *full_name,
var = &info->var;
strcpy(fix->id, "OFfb ");
strncat(fix->id, name, sizeof(fix->id));
strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
fix->id[sizeof(fix->id) - 1] = '\0';
var->xres = var->xres_virtual = width;
......@@ -522,8 +523,6 @@ static void __init offb_init_fb(const char *name, const char *full_name,
var->sync = 0;
var->vmode = FB_VMODE_NONINTERLACED;
strcpy(fix->id, "OFfb ");
strncat(fix->id, full_name, sizeof(fix->id));
info->node = NODEV;
info->fbops = &offb_ops;
info->screen_base = ioremap(address, fix->smem_len);
......
/*
* linux/drivers/video/riva/fbdev.c - nVidia RIVA 128/TNT/TNT2 fb driver
*
* Maintained by Ani Joshi <ajoshi@shell.unixbox.com>
*
* Copyright 1999-2000 Jeff Garzik
*
* Contributors:
*
* Ani Joshi: Lots of debugging and cleanup work, really helped
* get the driver going
*
* Ferenc Bakonyi: Bug fixes, cleanup, modularization
*
* Jindrich Makovicka: Accel code help, hw cursor, mtrr
*
* Paul Richards: Bug fixes and updates.
*
* Initial template from skeletonfb.c, created 28 Dec 1997 by Geert Uytterhoeven
* Includes riva_hw.c from nVidia, see copyright below.
* KGI code provided the basis for state storage, init, and mode switching.
*
* 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.
*
* Known bugs and issues:
* restoring text mode fails
* doublescan modes are broken
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
#include "rivafb.h"
#include "nvreg.h"
#ifndef CONFIG_PCI /* sanity check */
#error This driver requires PCI support.
#endif
/* version number of this driver */
#define RIVAFB_VERSION "0.9.4"
/* ------------------------------------------------------------------------- *
*
* various helpful macros and constants
*
* ------------------------------------------------------------------------- */
#undef RIVAFBDEBUG
#ifdef RIVAFBDEBUG
#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
#else
#define DPRINTK(fmt, args...)
#endif
#ifndef RIVA_NDEBUG
#define assert(expr) \
if(!(expr)) { \
printk( "Assertion failed! %s,%s,%s,line=%d\n",\
#expr,__FILE__,__FUNCTION__,__LINE__); \
BUG(); \
}
#else
#define assert(expr)
#endif
#define PFX "rivafb: "
/* macro that allows you to set overflow bits */
#define SetBitField(value,from,to) SetBF(to,GetBF(value,from))
#define SetBit(n) (1<<(n))
#define Set8Bits(value) ((value)&0xff)
/* HW cursor parameters */
#define MAX_CURS 32
/* ------------------------------------------------------------------------- *
*
* prototypes
*
* ------------------------------------------------------------------------- */
static int rivafb_blank(int blank, struct fb_info *info);
extern inline void wait_for_idle(struct riva_par *par);
/* ------------------------------------------------------------------------- *
*
* card identification
*
* ------------------------------------------------------------------------- */
enum riva_chips {
CH_RIVA_128 = 0,
CH_RIVA_TNT,
CH_RIVA_TNT2,
CH_RIVA_UTNT2,
CH_RIVA_VTNT2,
CH_RIVA_UVTNT2,
CH_RIVA_ITNT2,
CH_GEFORCE_SDR,
CH_GEFORCE_DDR,
CH_QUADRO,
CH_GEFORCE2_MX,
CH_GEFORCE2_MX2,
CH_GEFORCE2_GO,
CH_QUADRO2_MXR,
CH_GEFORCE2_GTS,
CH_GEFORCE2_GTS2,
CH_GEFORCE2_ULTRA,
CH_QUADRO2_PRO,
CH_GEFORCE4_MX_460,
CH_GEFORCE4_MX_440,
CH_GEFORCE4_MX_420,
CH_GEFORCE4_440_GO,
CH_GEFORCE4_420_GO,
CH_GEFORCE4_420_GO_M32,
CH_QUADRO4_500XGL,
CH_GEFORCE4_440_GO_M64,
CH_QUADRO4_200,
CH_QUADRO4_550XGL,
CH_QUADRO4_500_GOGL,
CH_IGEFORCE2,
CH_GEFORCE3,
CH_GEFORCE3_1,
CH_GEFORCE3_2,
CH_QUADRO_DDC,
CH_GEFORCE4_TI_4600,
CH_GEFORCE4_TI_4400,
CH_GEFORCE4_TI_4200,
CH_QUADRO4_900XGL,
CH_QUADRO4_750XGL,
CH_QUADRO4_700XGL
};
/* directly indexed by riva_chips enum, above */
static struct riva_chip_info {
const char *name;
unsigned arch_rev;
} riva_chip_info[] __devinitdata = {
{ "RIVA-128", NV_ARCH_03 },
{ "RIVA-TNT", NV_ARCH_04 },
{ "RIVA-TNT2", NV_ARCH_04 },
{ "RIVA-UTNT2", NV_ARCH_04 },
{ "RIVA-VTNT2", NV_ARCH_04 },
{ "RIVA-UVTNT2", NV_ARCH_04 },
{ "RIVA-ITNT2", NV_ARCH_04 },
{ "GeForce-SDR", NV_ARCH_10},
{ "GeForce-DDR", NV_ARCH_10},
{ "Quadro", NV_ARCH_10},
{ "GeForce2-MX", NV_ARCH_10},
{ "GeForce2-MX", NV_ARCH_10},
{ "GeForce2-GO", NV_ARCH_10},
{ "Quadro2-MXR", NV_ARCH_10},
{ "GeForce2-GTS", NV_ARCH_10},
{ "GeForce2-GTS", NV_ARCH_10},
{ "GeForce2-ULTRA", NV_ARCH_10},
{ "Quadro2-PRO", NV_ARCH_10},
{ "GeForce4-MX-460", NV_ARCH_20 },
{ "GeForce4-MX-440", NV_ARCH_20 },
{ "GeForce4-MX-420", NV_ARCH_20 },
{ "GeForce4-440-GO", NV_ARCH_20 },
{ "GeForce4-420-GO", NV_ARCH_20 },
{ "GeForce4-420-GO-M32", NV_ARCH_20 },
{ "Quadro4-500-XGL", NV_ARCH_20 },
{ "GeForce4-440-GO-M64", NV_ARCH_20 },
{ "Quadro4-200", NV_ARCH_20 },
{ "Quadro4-550-XGL", NV_ARCH_20 },
{ "Quadro4-500-GOGL", NV_ARCH_20 },
{ "GeForce2", NV_ARCH_20 },
{ "GeForce3", NV_ARCH_20 },
{ "GeForce3 Ti 200", NV_ARCH_20 },
{ "GeForce3 Ti 500", NV_ARCH_20 },
{ "Quadro DDC", NV_ARCH_20 },
{ "GeForce4 Ti 4600", NV_ARCH_20 },
{ "GeForce4 Ti 4400", NV_ARCH_20 },
{ "GeForce4 Ti 4200", NV_ARCH_20 },
{ "Quadro4-900-XGL", NV_ARCH_20 },
{ "Quadro4-750-XGL", NV_ARCH_20 },
{ "Quadro4-700-XGL", NV_ARCH_20 }
};
static struct pci_device_id rivafb_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_128 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_TNT },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_TNT2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_UTNT2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_ITNT2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE_SDR },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE_DDR },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_MX },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_MX2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_GO },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO2_MXR },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_GTS },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_GTS2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_ULTRA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO2_PRO },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_MX_460 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_MX_440 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_MX_420 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_440_GO },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_420_GO },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_420_GO_M32 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_500XGL },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_440_GO_M64 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_200 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_550XGL },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_500_GOGL },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_IGEFORCE2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_IGEFORCE2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_1 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO_DDC },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_TI_4600 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_TI_4400 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE4_TI_4200 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_900XGL },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_750XGL },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO4_700XGL },
{ 0, } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl);
/* ------------------------------------------------------------------------- *
*
* global variables
*
* ------------------------------------------------------------------------- */
/* command line data, set in rivafb_setup() */
static u32 pseudo_palette[17];
#ifdef CONFIG_MTRR
static char nomtrr __initdata = 0;
#endif
#ifndef MODULE
static char *mode_option __initdata = NULL;
#endif
static struct fb_fix_screeninfo rivafb_fix = {
id: "nVidia",
type: FB_TYPE_PACKED_PIXELS,
xpanstep: 1,
ypanstep: 1,
};
static struct fb_var_screeninfo rivafb_default_var = {
xres: 640,
yres: 480,
xres_virtual: 640,
yres_virtual: 480,
xoffset: 0,
yoffset: 0,
bits_per_pixel: 8,
grayscale: 0,
red: {0, 6, 0},
green: {0, 6, 0},
blue: {0, 6, 0},
transp: {0, 0, 0},
nonstd: 0,
activate: 0,
height: -1,
width: -1,
accel_flags: FB_ACCELF_TEXT,
pixclock: 39721,
left_margin: 40,
right_margin: 24,
upper_margin: 32,
lower_margin: 11,
hsync_len: 96,
vsync_len: 2,
sync: 0,
vmode: FB_VMODE_NONINTERLACED
};
/* from GGI */
static const struct riva_regs reg_template = {
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* ATTR */
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x01, 0x0F, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* CRT */
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, /* 0x10 */
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, /* 0x40 */
},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, /* GRA */
0xFF},
{0x03, 0x01, 0x0F, 0x00, 0x0E}, /* SEQ */
0xEB /* MISC */
};
/* ------------------------------------------------------------------------- *
*
* MMIO access macros
*
* ------------------------------------------------------------------------- */
static inline void CRTCout(struct riva_par *par, unsigned char index,
unsigned char val)
{
VGA_WR08(par->riva.PCIO, 0x3d4, index);
VGA_WR08(par->riva.PCIO, 0x3d5, val);
}
static inline unsigned char CRTCin(struct riva_par *par,
unsigned char index)
{
VGA_WR08(par->riva.PCIO, 0x3d4, index);
return (VGA_RD08(par->riva.PCIO, 0x3d5));
}
static inline void GRAout(struct riva_par *par, unsigned char index,
unsigned char val)
{
VGA_WR08(par->riva.PVIO, 0x3ce, index);
VGA_WR08(par->riva.PVIO, 0x3cf, val);
}
static inline unsigned char GRAin(struct riva_par *par,
unsigned char index)
{
VGA_WR08(par->riva.PVIO, 0x3ce, index);
return (VGA_RD08(par->riva.PVIO, 0x3cf));
}
static inline void SEQout(struct riva_par *par, unsigned char index,
unsigned char val)
{
VGA_WR08(par->riva.PVIO, 0x3c4, index);
VGA_WR08(par->riva.PVIO, 0x3c5, val);
}
static inline unsigned char SEQin(struct riva_par *par,
unsigned char index)
{
VGA_WR08(par->riva.PVIO, 0x3c4, index);
return (VGA_RD08(par->riva.PVIO, 0x3c5));
}
static inline void ATTRout(struct riva_par *par, unsigned char index,
unsigned char val)
{
VGA_WR08(par->riva.PCIO, 0x3c0, index);
VGA_WR08(par->riva.PCIO, 0x3c0, val);
}
static inline unsigned char ATTRin(struct riva_par *par,
unsigned char index)
{
VGA_WR08(par->riva.PCIO, 0x3c0, index);
return (VGA_RD08(par->riva.PCIO, 0x3c1));
}
static inline void MISCout(struct riva_par *par, unsigned char val)
{
VGA_WR08(par->riva.PVIO, 0x3c2, val);
}
static inline unsigned char MISCin(struct riva_par *par)
{
return (VGA_RD08(par->riva.PVIO, 0x3cc));
}
/* ------------------------------------------------------------------------- *
*
* cursor stuff
*
* ------------------------------------------------------------------------- */
/**
* rivafb_load_cursor_image - load cursor image to hardware
* @data: address to monochrome bitmap (1 = foreground color, 0 = background)
* @mask: address to mask (1 = write image pixel, 0 = do not write pixel)
* @par: pointer to private data
* @w: width of cursor image in pixels
* @h: height of cursor image in scanlines
* @bg: background color (ARGB1555) - alpha bit determines opacity
* @fg: foreground color (ARGB1555)
*
* DESCRIPTiON:
* Loads cursor image based on a monochrome source and mask bitmap. The
* mask bit determines if the image pixel is to be written to the framebuffer
* or not. The imaage bits determines the color of the pixel, 0 for
* background, 1 for foreground. Only the affected region (as determined
* by @w and @h parameters) will be updated.
*
* CALLED FROM:
* rivafb_cursor()
*/
static void rivafb_load_cursor_image(u8 *data, u8 *mask, struct riva_par *par,
int w, int h, u16 bg, u16 fg)
{
int i, j, k = 0;
u32 b, m, tmp;
for (i = 0; i < h; i++) {
b = *((u32 *)data)++;
m = *((u32 *)mask)++;
for (j = 0; j < w/2; j++) {
tmp = 0;
#if defined (__BIG_ENDIAN__)
if (m & (1 << 31))
tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
b <<= 1;
m <<= 1;
if (m & (1 << 31))
tmp |= (b & (1 << 31)) ? fg : bg;
b <<= 1;
m <<= 1;
#else
if (m & 1)
tmp = (b & 1) ? fg : bg;
b >>= 1;
m >>= 1;
if (m & 1)
tmp |= (b & 1) ? fg << 16 : bg << 16;
b >>= 1;
m >>= 1;
#endif
writel(tmp, par->riva.CURSOR + k++);
}
k += (MAX_CURS - w)/2;
}
}
/* ------------------------------------------------------------------------- *
*
* general utility functions
*
* ------------------------------------------------------------------------- */
/**
* riva_wclut - set CLUT entry
* @chip: pointer to RIVA_HW_INST object
* @regnum: register number
* @red: red component
* @green: green component
* @blue: blue component
*
* DESCRIPTION:
* Sets color register @regnum.
*
* CALLED FROM:
* rivafb_setcolreg()
*/
static void riva_wclut(RIVA_HW_INST *chip,
unsigned char regnum, unsigned char red,
unsigned char green, unsigned char blue)
{
VGA_WR08(chip->PDIO, 0x3c8, regnum);
VGA_WR08(chip->PDIO, 0x3c9, red);
VGA_WR08(chip->PDIO, 0x3c9, green);
VGA_WR08(chip->PDIO, 0x3c9, blue);
}
/**
* riva_save_state - saves current chip state
* @par: pointer to riva_par object containing info for current riva board
* @regs: pointer to riva_regs object
*
* DESCRIPTION:
* Saves current chip state to @regs.
*
* CALLED FROM:
* rivafb_init_one()
*/
/* from GGI */
static void riva_save_state(struct riva_par *par, struct riva_regs *regs)
{
int i;
par->riva.LockUnlock(&par->riva, 0);
par->riva.UnloadStateExt(&par->riva, &regs->ext);
regs->misc_output = MISCin(par);
for (i = 0; i < NUM_CRT_REGS; i++) {
regs->crtc[i] = CRTCin(par, i);
}
for (i = 0; i < NUM_ATC_REGS; i++) {
regs->attr[i] = ATTRin(par, i);
}
for (i = 0; i < NUM_GRC_REGS; i++) {
regs->gra[i] = GRAin(par, i);
}
for (i = 0; i < NUM_SEQ_REGS; i++) {
regs->seq[i] = SEQin(par, i);
}
}
/**
* riva_load_state - loads current chip state
* @par: pointer to riva_par object containing info for current riva board
* @regs: pointer to riva_regs object
*
* DESCRIPTION:
* Loads chip state from @regs.
*
* CALLED FROM:
* riva_load_video_mode()
* rivafb_init_one()
* rivafb_remove_one()
*/
/* from GGI */
static void riva_load_state(struct riva_par *par, struct riva_regs *regs)
{
RIVA_HW_STATE *state = &regs->ext;
int i;
CRTCout(par, 0x11, 0x00);
par->riva.LockUnlock(&par->riva, 0);
par->riva.LoadStateExt(&par->riva, state);
MISCout(par, regs->misc_output);
for (i = 0; i < NUM_CRT_REGS; i++) {
switch (i) {
case 0x19:
case 0x20 ... 0x40:
break;
default:
CRTCout(par, i, regs->crtc[i]);
}
}
for (i = 0; i < NUM_ATC_REGS; i++) {
ATTRout(par, i, regs->attr[i]);
}
for (i = 0; i < NUM_GRC_REGS; i++) {
GRAout(par, i, regs->gra[i]);
}
for (i = 0; i < NUM_SEQ_REGS; i++) {
SEQout(par, i, regs->seq[i]);
}
}
/**
* riva_load_video_mode - calculate timings
* @info: pointer to fb_info object containing info for current riva board
* @video_mode: video mode to set
*
* DESCRIPTION:
* Calculate some timings and then send em off to riva_load_state().
*
* CALLED FROM:
* rivafb_set_var()
*/
static void riva_load_video_mode(struct fb_info *info)
{
int bpp, width, hDisplaySize, hDisplay, hStart,
hEnd, hTotal, height, vDisplay, vStart, vEnd, vTotal, dotClock;
int hBlankStart, hBlankEnd, vBlankStart, vBlankEnd;
struct riva_par *par = (struct riva_par *) info->par;
struct riva_regs newmode;
/* time to calculate */
rivafb_blank(1, info);
bpp = info->var.bits_per_pixel;
if (bpp == 16 && info->var.green.length == 5)
bpp = 15;
width = info->var.xres_virtual;
hDisplaySize = info->var.xres;
hDisplay = (hDisplaySize / 8) - 1;
hStart = (hDisplaySize + info->var.right_margin) / 8 + 2;
hEnd = (hDisplaySize + info->var.right_margin +
info->var.hsync_len) / 8 - 1;
hTotal = (hDisplaySize + info->var.right_margin +
info->var.hsync_len + info->var.left_margin) / 8 - 5;
hBlankStart = hDisplay;
hBlankEnd = hTotal + 4;
height = info->var.yres_virtual;
vDisplay = info->var.yres - 1;
vStart = info->var.yres + info->var.lower_margin - 1;
vEnd = info->var.yres + info->var.lower_margin +
info->var.vsync_len - 1;
vTotal = info->var.yres + info->var.lower_margin +
info->var.vsync_len + info->var.upper_margin + 2;
vBlankStart = vDisplay;
vBlankEnd = vTotal + 1;
dotClock = PICOS2KHZ(info->var.pixclock);
memcpy(&newmode, &reg_template, sizeof(struct riva_regs));
newmode.crtc[0x0] = Set8Bits (hTotal - 4);
if (par->FlatPanel) {
vStart = vTotal - 3;
vEnd = vTotal - 2;
vBlankStart = vStart;
hStart = hTotal - 3;
hEnd = hTotal - 2;
hBlankEnd = hTotal + 4;
}
newmode.crtc[0x0] = Set8Bits (hTotal);
newmode.crtc[0x1] = Set8Bits (hDisplay);
newmode.crtc[0x2] = Set8Bits (hBlankStart);
newmode.crtc[0x3] = SetBitField (hBlankEnd, 4: 0, 4:0) | SetBit (7);
newmode.crtc[0x4] = Set8Bits (hStart);
newmode.crtc[0x5] = SetBitField (hBlankEnd, 5: 5, 7:7)
| SetBitField (hEnd, 4: 0, 4:0);
newmode.crtc[0x6] = SetBitField (vTotal, 7: 0, 7:0);
newmode.crtc[0x7] = SetBitField (vTotal, 8: 8, 0:0)
| SetBitField (vDisplay, 8: 8, 1:1)
| SetBitField (vStart, 8: 8, 2:2)
| SetBitField (vBlankStart, 8: 8, 3:3)
| SetBit (4)
| SetBitField (vTotal, 9: 9, 5:5)
| SetBitField (vDisplay, 9: 9, 6:6)
| SetBitField (vStart, 9: 9, 7:7);
newmode.crtc[0x9] = SetBitField (vBlankStart, 9: 9, 5:5)
| SetBit (6);
newmode.crtc[0x10] = Set8Bits (vStart);
newmode.crtc[0x11] = SetBitField (vEnd, 3: 0, 3:0)
| SetBit (5);
newmode.crtc[0x12] = Set8Bits (vDisplay);
newmode.crtc[0x13] = (width / 8) * ((bpp + 1) / 8);
newmode.crtc[0x15] = Set8Bits (vBlankStart);
newmode.crtc[0x16] = Set8Bits (vBlankEnd + 1);
newmode.ext.screen = SetBitField(hBlankEnd,6:6,4:4)
| SetBitField(vBlankStart,10:10,3:3)
| SetBitField(vStart,10:10,2:2)
| SetBitField(vDisplay,10:10,1:1)
| SetBitField(vTotal,10:10,0:0);
newmode.ext.horiz = SetBitField(hTotal,8:8,0:0)
| SetBitField(hDisplay,8:8,1:1)
| SetBitField(hBlankStart,8:8,2:2)
| SetBitField(hStart,8:8,3:3);
newmode.ext.extra = SetBitField(vTotal,11:11,0:0)
| SetBitField(vDisplay,11:11,2:2)
| SetBitField(vStart,11:11,4:4)
| SetBitField(vBlankStart,11:11,6:6);
newmode.ext.interlace = 0xff; /* interlace off */
if(par->riva.Architecture >= NV_ARCH_10)
par->riva.CURSOR = (U032 *)(info->screen_base + par->riva.CursorStart);
par->riva.CalcStateExt(&par->riva, &newmode.ext, bpp, width,
hDisplaySize, height, dotClock);
newmode.ext.scale = par->riva.PRAMDAC[0x00000848/4] & 0xfff000ff;
if(par->FlatPanel == 1) {
newmode.ext.pixel |= (1 << 7);
newmode.ext.scale |= (1 << 8) ;
}
if(par->SecondCRTC) {
newmode.ext.head = par->riva.PCRTC0[0x00000860/4] & ~0x00001000;
newmode.ext.head2 = par->riva.PCRTC0[0x00002860/4] | 0x00001000;
newmode.ext.crtcOwner = 3;
newmode.ext.pllsel |= 0x20000800;
newmode.ext.vpll2 = newmode.ext.vpll;
} else if(par->riva.twoHeads) {
newmode.ext.head = par->riva.PCRTC0[0x00000860/4] | 0x00001000;
newmode.ext.head2 = par->riva.PCRTC0[0x00002860/4] & ~0x00001000;
newmode.ext.crtcOwner = 0;
newmode.ext.vpll2 = par->riva.PRAMDAC0[0x00000520/4];
}
newmode.ext.cursorConfig = 0x02000100;
par->current_state = newmode;
riva_load_state(par, &par->current_state);
rivafb_blank(0, info);
}
/**
* rivafb_do_maximize -
* @info: pointer to fb_info object containing info for current riva board
* @var:
* @nom:
* @den:
*
* DESCRIPTION:
* .
*
* RETURNS:
* -EINVAL on failure, 0 on success
*
*
* CALLED FROM:
* rivafb_set_var()
*/
static int rivafb_do_maximize(struct fb_info *info,
struct fb_var_screeninfo *var,
int nom, int den)
{
static struct {
int xres, yres;
} modes[] = {
{1600, 1280},
{1280, 1024},
{1024, 768},
{800, 600},
{640, 480},
{-1, -1}
};
int i;
/* use highest possible virtual resolution */
if (var->xres_virtual == -1 && var->yres_virtual == -1) {
printk(KERN_WARNING PFX
"using maximum available virtual resolution\n");
for (i = 0; modes[i].xres != -1; i++) {
if (modes[i].xres * nom / den * modes[i].yres <
info->fix.smem_len / 2)
break;
}
if (modes[i].xres == -1) {
printk(KERN_ERR PFX
"could not find a virtual resolution that fits into video memory!!\n");
DPRINTK("EXIT - EINVAL error\n");
return -EINVAL;
}
var->xres_virtual = modes[i].xres;
var->yres_virtual = modes[i].yres;
printk(KERN_INFO PFX
"virtual resolution set to maximum of %dx%d\n",
var->xres_virtual, var->yres_virtual);
} else if (var->xres_virtual == -1) {
var->xres_virtual = (info->fix.smem_len * den /
(nom * var->yres_virtual * 2)) & ~15;
printk(KERN_WARNING PFX
"setting virtual X resolution to %d\n", var->xres_virtual);
} else if (var->yres_virtual == -1) {
var->xres_virtual = (var->xres_virtual + 15) & ~15;
var->yres_virtual = info->fix.smem_len * den /
(nom * var->xres_virtual * 2);
printk(KERN_WARNING PFX
"setting virtual Y resolution to %d\n", var->yres_virtual);
} else {
var->xres_virtual = (var->xres_virtual + 15) & ~15;
if (var->xres_virtual * nom / den * var->yres_virtual > info->fix.smem_len) {
printk(KERN_ERR PFX
"mode %dx%dx%d rejected...resolution too high to fit into video memory!\n",
var->xres, var->yres, var->bits_per_pixel);
DPRINTK("EXIT - EINVAL error\n");
return -EINVAL;
}
}
if (var->xres_virtual * nom / den >= 8192) {
printk(KERN_WARNING PFX
"virtual X resolution (%d) is too high, lowering to %d\n",
var->xres_virtual, 8192 * den / nom - 16);
var->xres_virtual = 8192 * den / nom - 16;
}
if (var->xres_virtual < var->xres) {
printk(KERN_ERR PFX
"virtual X resolution (%d) is smaller than real\n", var->xres_virtual);
return -EINVAL;
}
if (var->yres_virtual < var->yres) {
printk(KERN_ERR PFX
"virtual Y resolution (%d) is smaller than real\n", var->yres_virtual);
return -EINVAL;
}
return 0;
}
/* acceleration routines */
inline void wait_for_idle(struct riva_par *par)
{
while (par->riva.Busy(&par->riva));
}
/* set copy ROP, no mask */
static void riva_setup_ROP(struct riva_par *par)
{
RIVA_FIFO_FREE(par->riva, Patt, 5);
par->riva.Patt->Shape = 0;
par->riva.Patt->Color0 = 0xffffffff;
par->riva.Patt->Color1 = 0xffffffff;
par->riva.Patt->Monochrome[0] = 0xffffffff;
par->riva.Patt->Monochrome[1] = 0xffffffff;
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = 0xCC;
}
void riva_setup_accel(struct riva_par *par)
{
RIVA_FIFO_FREE(par->riva, Clip, 2);
par->riva.Clip->TopLeft = 0x0;
par->riva.Clip->WidthHeight = 0x80008000;
riva_setup_ROP(par);
}
/**
* rivafb_fillrect - hardware accelerated color fill function
* @info: pointer to fb_info structure
* @rect: pointer to fb_fillrect structure
*
* DESCRIPTION:
* This function fills up a region of framebuffer memory with a solid
* color with a choice of two different ROP's, copy or invert.
*
* CALLED FROM:
* framebuffer hook
*/
static void rivafb_fillrect(struct fb_info *info, struct fb_fillrect *rect)
{
struct riva_par *par = (struct riva_par *) info->par;
u_int color, rop = 0;
if (info->var.bits_per_pixel == 8)
color = rect->color;
else
color = par->riva_palette[rect->color];
switch (rect->rop) {
case ROP_XOR:
rop = 0x66;
break;
case ROP_COPY:
default:
rop = 0xCC;
break;
}
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = rop;
RIVA_FIFO_FREE(par->riva, Bitmap, 1);
par->riva.Bitmap->Color1A = color;
RIVA_FIFO_FREE(par->riva, Bitmap, 2);
par->riva.Bitmap->UnclippedRectangle[0].TopLeft =
(rect->dx << 16) | rect->dy;
par->riva.Bitmap->UnclippedRectangle[0].WidthHeight =
(rect->width << 16) | rect->height;
}
/**
* rivafb_copyarea - hardware accelerated blit function
* @info: pointer to fb_info structure
* @region: pointer to fb_copyarea structure
*
* DESCRIPTION:
* This copies an area of pixels from one location to another
*
* CALLED FROM:
* framebuffer hook
*/
static void rivafb_copyarea(struct fb_info *info, struct fb_copyarea *region)
{
struct riva_par *par = (struct riva_par *) info->par;
RIVA_FIFO_FREE(par->riva, Blt, 3);
par->riva.Blt->TopLeftSrc = (region->sy << 16) | region->sx;
par->riva.Blt->TopLeftDst = (region->dy << 16) | region->dx;
par->riva.Blt->WidthHeight = (region->height << 16) | region->width;
wait_for_idle(par);
}
static u8 byte_rev[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
/**
* rivafb_imageblit: hardware accelerated color expand function
* @info: pointer to fb_info structure
* @image: pointer to fb_image structure
*
* DESCRIPTION:
* If the source is a monochrome bitmap, the function fills up a a region
* of framebuffer memory with pixels whose color is determined by the bit
* setting of the bitmap, 1 - foreground, 0 - background.
*
* If the source is not a monochrome bitmap, color expansion is not done.
* In this case, it is channeled to a software function.
*
* CALLED FROM:
* framebuffer hook
*/
static void rivafb_imageblit(struct fb_info *info, struct fb_image *image)
{
struct riva_par *par = (struct riva_par *) info->par;
u8 *cdat = image->data, *dat;
int w, h, dx, dy;
volatile u32 *d;
u32 fgx = 0, bgx = 0, size, width, mod;
int i, j;
if (image->depth != 1) {
wait_for_idle(par);
cfb_imageblit(info, image);
return;
}
w = image->width;
h = image->height;
dx = image->dx;
dy = image->dy;
width = (w + 7)/8;
size = width * h;
dat = cdat;
for (i = 0; i < size; i++) {
*dat = byte_rev[*dat];
dat++;
}
switch (info->var.bits_per_pixel) {
case 8:
fgx = image->fg_color | ~((1 << 8) - 1);
bgx = image->bg_color | ~((1 << 8) - 1);
break;
case 16:
/* set alpha bit */
if (info->var.green.length == 5) {
fgx = 1 << 15;
bgx = fgx;
}
/* Fall through... */
case 32:
fgx |= par->riva_palette[image->fg_color];
bgx |= par->riva_palette[image->bg_color];
break;
}
RIVA_FIFO_FREE(par->riva, Bitmap, 7);
par->riva.Bitmap->ClipE.TopLeft = (dy << 16) | (dx & 0xFFFF);
par->riva.Bitmap->ClipE.BottomRight = (((dy + h) << 16) |
((dx + w) & 0xffff));
par->riva.Bitmap->Color0E = bgx;
par->riva.Bitmap->Color1E = fgx;
par->riva.Bitmap->WidthHeightInE = (h << 16) | ((w + 31) & ~31);
par->riva.Bitmap->WidthHeightOutE = (h << 16) | ((w + 31) & ~31);
par->riva.Bitmap->PointE = (dy << 16) | (dx & 0xFFFF);
d = &par->riva.Bitmap->MonochromeData01E;
mod = width % 4;
if (width >= 4) {
while (h--) {
size = width / 4;
while (size >= 16) {
RIVA_FIFO_FREE(par->riva, Bitmap, 16);
for (i = 0; i < 16; i++)
d[i] = *((u32 *)cdat)++;
size -= 16;
}
if (size) {
RIVA_FIFO_FREE(par->riva, Bitmap, size);
for (i = 0; i < size; i++)
d[i] = *((u32 *) cdat)++;
}
if (mod) {
u32 tmp;
RIVA_FIFO_FREE(par->riva, Bitmap, 1);
for (i = 0; i < mod; i++)
((u8 *)&tmp)[i] = *cdat++;
d[i] = tmp;
}
}
}
else {
u32 k, tmp;
for (i = h; i > 0; i-=16) {
if (i >= 16)
size = 16;
else
size = i;
RIVA_FIFO_FREE(par->riva, Bitmap, size);
for (j = 0; j < size; j++) {
for (k = 0; k < width; k++)
((u8 *)&tmp)[k] = *cdat++;
d[j] = tmp;
}
}
}
}
/**
* rivafb_cursor - hardware cursor function
* @info: pointer to info structure
* @cursor: pointer to fbcursor structure
*
* DESCRIPTION:
* A cursor function that supports displaying a cursor image via hardware.
* Within the kernel, copy and invert rops are supported. If exported
* to user space, only the copy rop will be supported.
*
* CALLED FROM
* framebuffer hook
*/
static int rivafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
static u8 data[MAX_CURS*MAX_CURS/8], mask[MAX_CURS*MAX_CURS/8];
struct riva_par *par = (struct riva_par *) info->par;
int i, j, d_idx = 0, s_idx = 0;
u16 flags = cursor->set, fg, bg;
/*
* Can't do invert if one of the operands (dest) is missing,
* ie, only opaque cursor supported. This should be
* standard for GUI apps.
*/
if (cursor->dest == NULL && cursor->rop == ROP_XOR)
return 1;
if (par->cursor_reset) {
flags = FB_CUR_SETALL;
par->cursor_reset = 0;
}
par->riva.ShowHideCursor(&par->riva, 0);
if (flags & FB_CUR_SETPOS) {
u32 xx, yy, temp;
yy = cursor->image.dy - info->var.yoffset;
xx = cursor->image.dx - info->var.xoffset;
temp = xx & 0xFFFF;
temp |= yy << 16;
par->riva.PRAMDAC[0x0000300/4] = temp;
}
if (flags & FB_CUR_SETSIZE) {
memset(data, 0, MAX_CURS * MAX_CURS/8);
memset(mask, 0, MAX_CURS * MAX_CURS/8);
memset_io(par->riva.CURSOR, 0, MAX_CURS * MAX_CURS * 2);
}
if (flags & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETDEST)) {
int bg_idx = cursor->image.bg_color;
int fg_idx = cursor->image.fg_color;
switch (cursor->rop) {
case ROP_XOR:
for (i = 0; i < cursor->image.height; i++) {
for (j = 0; j < (cursor->image.width + 7)/8;
j++) {
d_idx = i * MAX_CURS/8 + j;
data[d_idx] = byte_rev[((u8 *)cursor->image.data)[s_idx] ^
((u8 *)cursor->dest)[s_idx]];
mask[d_idx] = byte_rev[((u8 *)cursor->mask)[s_idx]];
s_idx++;
}
}
break;
case ROP_COPY:
default:
for (i = 0; i < cursor->image.height; i++) {
for (j = 0; j < (cursor->image.width + 7)/8; j++) {
d_idx = i * MAX_CURS/8 + j;
data[d_idx] = byte_rev[((u8 *)cursor->image.data)[s_idx]];
mask[d_idx] = byte_rev[((u8 *)cursor->mask)[s_idx]];
s_idx++;
}
}
break;
}
bg = ((par->cmap[bg_idx].red & 0xf8) << 7) |
((par->cmap[bg_idx].green & 0xf8) << 2) |
((par->cmap[bg_idx].blue & 0xf8) >> 3) | 1 << 15;
fg = ((par->cmap[fg_idx].red & 0xf8) << 7) |
((par->cmap[fg_idx].green & 0xf8) << 2) |
((par->cmap[fg_idx].blue & 0xf8) >> 3) | 1 << 15;
par->riva.LockUnlock(&par->riva, 0);
rivafb_load_cursor_image(data, mask, par, cursor->image.width,
cursor->image.height, bg, fg);
}
if (cursor->enable)
par->riva.ShowHideCursor(&par->riva, 1);
return 0;
}
static int rivafb_sync(struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
wait_for_idle(par);
return 0;
}
/* ------------------------------------------------------------------------- *
*
* internal fb_ops helper functions
*
* ------------------------------------------------------------------------- */
/**
* riva_get_cmap_len - query current color map length
* @var: standard kernel fb changeable data
*
* DESCRIPTION:
* Get current color map length.
*
* RETURNS:
* Length of color map
*
* CALLED FROM:
* riva_getcolreg()
* rivafb_setcolreg()
* rivafb_get_cmap()
* rivafb_set_cmap()
*/
static int riva_get_cmap_len(const struct fb_var_screeninfo *var)
{
int rc = 16; /* reasonable default */
assert(var != NULL);
switch (var->green.length) {
case 5:
rc = 32; /* fix for 15 bpp depths on Riva 128 based cards */
break;
case 6:
rc = 64; /* directcolor... 16 entries SW palette */
break; /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
default:
rc = 256; /* pseudocolor... 256 entries HW palette */
break;
}
return rc;
}
/* ------------------------------------------------------------------------- *
*
* framebuffer operations
*
* ------------------------------------------------------------------------- */
static int rivafb_open(struct fb_info *info, int user)
{
struct riva_par *par = (struct riva_par *) info->par;
int cnt = atomic_read(&par->ref_count);
if (!cnt) {
memset(&par->state, 0, sizeof(struct vgastate));
par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONT0;
/* save the DAC for Riva128 */
if (par->riva.Architecture == NV_ARCH_03)
par->state.flags |= VGA_SAVE_CMAP;
save_vga(&par->state);
RivaGetConfig(&par->riva, par->Chipset);
riva_save_state(par, &par->initial_state);
}
atomic_inc(&par->ref_count);
return 0;
}
static int rivafb_release(struct fb_info *info, int user)
{
struct riva_par *par = (struct riva_par *) info->par;
int cnt = atomic_read(&par->ref_count);
if (!cnt)
return -EINVAL;
if (cnt == 1) {
riva_load_state(par, &par->initial_state);
par->riva.LockUnlock(&par->riva, 1);
restore_vga(&par->state);
}
atomic_dec(&par->ref_count);
return 0;
}
static int rivafb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
int nom, den; /* translating from pixels->bytes */
if (par->riva.Architecture == NV_ARCH_03 &&
var->bits_per_pixel == 16)
var->bits_per_pixel = 15;
switch (var->bits_per_pixel) {
case 1 ... 8:
var->bits_per_pixel = 8;
nom = 1;
den = 1;
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 0;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
break;
case 9 ... 15:
var->green.length = 5;
/* fall through */
case 16:
var->bits_per_pixel = 16;
nom = 2;
den = 1;
if (var->green.length == 5) {
/* 0rrrrrgg gggbbbbb */
var->red.offset = 10;
var->green.offset = 5;
var->blue.offset = 0;
var->red.length = 5;
var->green.length = 5;
var->blue.length = 5;
} else {
/* rrrrrggg gggbbbbb */
var->red.offset = 11;
var->green.offset = 5;
var->blue.offset = 0;
var->red.length = 5;
var->green.length = 6;
var->blue.length = 5;
}
break;
case 17 ... 32:
var->bits_per_pixel = 32;
nom = 4;
den = 1;
var->red.offset = 16;
var->green.offset = 8;
var->blue.offset = 0;
var->red.length = 8;
var->green.length = 8;
var->blue.length = 8;
break;
default:
printk(KERN_ERR PFX
"mode %dx%dx%d rejected...color depth not supported.\n",
var->xres, var->yres, var->bits_per_pixel);
DPRINTK("EXIT, returning -EINVAL\n");
return -EINVAL;
}
if (rivafb_do_maximize(info, var, nom, den) < 0)
return -EINVAL;
if (var->xoffset < 0)
var->xoffset = 0;
if (var->yoffset < 0)
var->yoffset = 0;
/* truncate xoffset and yoffset to maximum if too high */
if (var->xoffset > var->xres_virtual - var->xres)
var->xoffset = var->xres_virtual - var->xres - 1;
if (var->yoffset > var->yres_virtual - var->yres)
var->yoffset = var->yres_virtual - var->yres - 1;
var->red.msb_right =
var->green.msb_right =
var->blue.msb_right =
var->transp.offset = var->transp.length = var->transp.msb_right = 0;
return 0;
}
static int rivafb_set_par(struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
riva_load_video_mode(info);
if (info->var.accel_flags) {
riva_setup_accel(par);
info->fbops->fb_fillrect = rivafb_fillrect;
info->fbops->fb_copyarea = rivafb_copyarea;
info->fbops->fb_imageblit = rivafb_imageblit;
info->fbops->fb_cursor = rivafb_cursor;
info->fbops->fb_sync = rivafb_sync;
}
else {
info->fbops->fb_fillrect = cfb_fillrect;
info->fbops->fb_copyarea = cfb_copyarea;
info->fbops->fb_imageblit = cfb_imageblit;
info->fbops->fb_cursor = soft_cursor;
info->fbops->fb_sync = NULL;
}
info->fix.line_length = (info->var.xres_virtual * (info->var.bits_per_pixel >> 3));
info->fix.visual = (info->var.bits_per_pixel == 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
return 0;
}
/**
* rivafb_pan_display
* @var: standard kernel fb changeable data
* @con: TODO
* @info: pointer to rivafb_info object containing info for current riva board
*
* DESCRIPTION:
* Pan (or wrap, depending on the `vmode' field) the display using the
* `xoffset' and `yoffset' fields of the `var' structure.
* If the values don't fit, return -EINVAL.
*
* This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
*/
static int rivafb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
unsigned int base;
if (var->xoffset > (var->xres_virtual - var->xres))
return -EINVAL;
if (var->yoffset > (var->yres_virtual - var->yres))
return -EINVAL;
if (var->vmode & FB_VMODE_YWRAP) {
if (var->yoffset < 0
|| var->yoffset >= info->var.yres_virtual
|| var->xoffset) return -EINVAL;
} else {
if (var->xoffset + info->var.xres > info->var.xres_virtual ||
var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
}
base = var->yoffset * info->fix.line_length + var->xoffset;
par->riva.SetStartAddress(&par->riva, base);
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
if (var->vmode & FB_VMODE_YWRAP)
info->var.vmode |= FB_VMODE_YWRAP;
else
info->var.vmode &= ~FB_VMODE_YWRAP;
/*
* HACK: The hardware cursor occasionally disappears during fast scrolling.
* We just reset the cursor each time we change the start address.
* This also has a beneficial side effect of restoring the cursor
* image when switching from X.
*/
par->cursor_reset = 1;
DPRINTK("EXIT, returning 0\n");
return 0;
}
static int rivafb_blank(int blank, struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
unsigned char tmp, vesa;
tmp = SEQin(par, 0x01) & ~0x20; /* screen on/off */
vesa = CRTCin(par, 0x1a) & ~0xc0; /* sync on/off */
if (blank) {
tmp |= 0x20;
switch (blank - 1) {
case VESA_NO_BLANKING:
break;
case VESA_VSYNC_SUSPEND:
vesa |= 0x80;
break;
case VESA_HSYNC_SUSPEND:
vesa |= 0x40;
break;
case VESA_POWERDOWN:
vesa |= 0xc0;
break;
}
}
SEQout(par, 0x01, tmp);
CRTCout(par, 0x1a, vesa);
DPRINTK("EXIT\n");
return 0;
}
/**
* rivafb_setcolreg
* @regno: register index
* @red: red component
* @green: green component
* @blue: blue component
* @transp: transparency
* @info: pointer to rivafb_info object containing info for current riva board
*
* DESCRIPTION:
* Set a single color register. The values supplied have a 16 bit
* magnitude.
*
* RETURNS:
* Return != 0 for invalid regno.
*
* CALLED FROM:
* rivafb_set_cmap()
* fbcmap.c:fb_set_cmap()
* fbgen.c:fbgen_get_cmap()
* fbgen.c:do_install_cmap()
* fbgen.c:fbgen_set_var()
* fbgen.c:fbgen_switch()
* fbgen.c:fbgen_blank()
* fbgen.c:fbgen_blank()
*/
static int rivafb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
RIVA_HW_INST *chip = &par->riva;
int i;
if (regno >= riva_get_cmap_len(&info->var))
return -EINVAL;
if (info->var.grayscale) {
/* gray = 0.30*R + 0.59*G + 0.11*B */
red = green = blue =
(red * 77 + green * 151 + blue * 28) >> 8;
}
if (!regno) {
for (i = 0; i < 256; i++) {
par->cmap[i].red = 0;
par->cmap[i].green = 0;
par->cmap[i].blue = 0;
}
}
par->cmap[regno].red = (u8) red;
par->cmap[regno].green = (u8) green;
par->cmap[regno].blue = (u8) blue;
if (info->var.green.length == 5) {
/* RGB555: all components have 32 entries, 8 indices apart */
for (i = 0; i < 8; i++)
riva_wclut(chip, (regno*8)+i, (u8) red, (u8) green, (u8) blue);
}
else if (info->var.green.length == 6) {
/*
* RGB 565: red and blue have 32 entries, 8 indices apart, while
* green has 64 entries, 4 indices apart
*/
if (regno < 32) {
for (i = 0; i < 8; i++) {
riva_wclut(chip, (regno*8)+i, (u8) red,
par->cmap[regno*2].green,
(u8) blue);
}
}
for (i = 0; i < 4; i++) {
riva_wclut(chip, (regno*4)+i, par->cmap[regno/2].red,
(u8) green, par->cmap[regno/2].blue);
}
}
else {
riva_wclut(chip, regno, (u8) red, (u8) green, (u8) blue);
}
if (regno < 16) {
switch (info->var.bits_per_pixel) {
case 16:
if (info->var.green.length == 5) {
/* 0rrrrrgg gggbbbbb */
((u32 *)(info->pseudo_palette))[regno] =
(regno << 10) | (regno << 5) | regno;
par->riva_palette[regno] =
((red & 0xf800) >> 1) |
((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11);
} else {
/* rrrrrggg gggbbbbb */
((u32 *)(info->pseudo_palette))[regno] =
(regno << 11) | (regno << 6) | regno;
par->riva_palette[regno] = ((red & 0xf800) >> 0) |
((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11);
}
break;
case 32:
((u32 *)(info->pseudo_palette))[regno] =
(regno << 16) | (regno << 8) | regno;
par->riva_palette[regno] =
((red & 0xff00) << 8) |
((green & 0xff00)) | ((blue & 0xff00) >> 8);
break;
default:
/* do nothing */
break;
}
}
return 0;
}
/* ------------------------------------------------------------------------- *
*
* initialization helper functions
*
* ------------------------------------------------------------------------- */
/* kernel interface */
static struct fb_ops riva_fb_ops = {
.owner = THIS_MODULE,
.fb_open = rivafb_open,
.fb_release = rivafb_release,
.fb_check_var = rivafb_check_var,
.fb_set_par = rivafb_set_par,
.fb_setcolreg = rivafb_setcolreg,
.fb_pan_display=rivafb_pan_display,
.fb_blank = rivafb_blank,
.fb_fillrect = rivafb_fillrect,
.fb_copyarea = rivafb_copyarea,
.fb_imageblit = rivafb_imageblit,
.fb_cursor = rivafb_cursor,
.fb_sync = rivafb_sync,
};
static int __devinit riva_set_fbinfo(struct fb_info *info)
{
unsigned int cmap_len;
info->node = NODEV;
info->flags = FBINFO_FLAG_DEFAULT;
info->fbops = &riva_fb_ops;
info->var = rivafb_default_var;
info->fix = rivafb_fix;
/* FIXME: set monspecs to what??? */
info->display_fg = NULL;
info->pseudo_palette = pseudo_palette;
cmap_len = riva_get_cmap_len(&info->var);
fb_alloc_cmap(&info->cmap, cmap_len, 0);
#ifndef MODULE
if (mode_option)
fb_find_mode(&info->var, info, mode_option,
NULL, 0, NULL, 8);
#endif
info->fix.line_length = (info->var.xres_virtual * (info->var.bits_per_pixel >> 3));
info->fix.visual = (info->var.bits_per_pixel == 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
return 0;
}
/* ------------------------------------------------------------------------- *
*
* PCI bus
*
* ------------------------------------------------------------------------- */
static void __devinit rivafb_get_mem_len(struct riva_par *par,
struct fb_fix_screeninfo *fix)
{
RIVA_HW_INST *chip = &par->riva;
switch (chip->Architecture) {
case NV_ARCH_03:
if (chip->PFB[0x00000000/4] & 0x00000020) {
if (((chip->PMC[0x00000000/4] & 0xF0) == 0x20)
&& ((chip->PMC[0x00000000/4] & 0x0F) >= 0x02)) {
/*
* SDRAM 128 ZX.
*/
switch (chip->PFB[0x00000000/4] & 0x03) {
case 2:
fix->smem_len = 1024 * 1024 * 4;
break;
case 1:
fix->smem_len = 1024 * 1024 * 2;
break;
default:
fix->smem_len = 1024 * 1024 * 8;
break;
}
}
else {
fix->smem_len = 1024 * 1024 * 8;
}
}
else {
/*
* SGRAM 128.
*/
switch (chip->PFB[0x00000000/4] & 0x00000003) {
case 0:
fix->smem_len = 1024 * 1024 * 8;
break;
case 2:
fix->smem_len = 1024 * 1024 * 4;
break;
default:
fix->smem_len = 1024 * 1024 * 2;
break;
}
}
break;
case NV_ARCH_04:
if (chip->PFB[0x00000000/4] & 0x00000100) {
fix->smem_len = (((chip->PFB[0x00000000/4] >> 12)
& 0x0F) * 1024 * 2 + 1024 * 2) * 1024;
}
else {
switch (chip->PFB[0x00000000/4] & 0x00000003) {
case 0:
fix->smem_len = 1024 * 1024 * 32;
break;
case 1:
fix->smem_len = 1024 * 1024 * 4;
break;
case 2:
fix->smem_len = 1024 * 1024 * 8;
break;
case 3:
default:
fix->smem_len = 1024 * 1024 * 16;
break;
}
}
break;
case NV_ARCH_10:
case NV_ARCH_20:
switch ((chip->PFB[0x0000020C/4] >> 20) & 0x000000FF) {
case 0x02:
fix->smem_len = 1024 * 1024 * 2;
break;
case 0x04:
fix->smem_len = 1024 * 1024 * 4;
break;
case 0x08:
fix->smem_len = 1024 * 1024 * 8;
break;
case 0x10:
fix->smem_len = 1024 * 1024 * 16;
break;
case 0x20:
fix->smem_len = 1024 * 1024 * 32;
break;
case 0x40:
fix->smem_len = 1024 * 1024 * 64;
break;
case 0x80:
fix->smem_len = 1024 * 1024 * 128;
break;
default:
fix->smem_len = 1024 * 1024 * 16;
break;
}
}
}
static int __devinit rivafb_init_one(struct pci_dev *pd,
const struct pci_device_id *ent)
{
struct riva_chip_info *rci = &riva_chip_info[ent->driver_data];
struct riva_par *default_par;
struct fb_info *info;
assert(pd != NULL);
assert(rci != NULL);
info = kmalloc(sizeof(struct fb_info), GFP_KERNEL);
if (!info)
goto err_out;
default_par = kmalloc(sizeof(struct riva_par), GFP_KERNEL);
if (!default_par)
goto err_out_kfree;
memset(info, 0, sizeof(struct fb_info));
memset(default_par, 0, sizeof(struct riva_par));
strcat(rivafb_fix.id, rci->name);
default_par->riva.Architecture = rci->arch_rev;
default_par->Chipset = pd->device;
printk(KERN_INFO PFX "nVidia device/chipset %X\n",default_par->Chipset);
/*default_par->FlatPanel = flatpanel;
if (flatpanel == 1)
printk(KERN_INFO PFX "flatpanel support enabled\n");
default_par->forceCRTC = forceCRTC;
*/
rivafb_fix.mmio_len = pci_resource_len(pd, 0);
rivafb_fix.smem_len = pci_resource_len(pd, 1);
rivafb_fix.mmio_start = pci_resource_start(pd, 0);
rivafb_fix.smem_start = pci_resource_start(pd, 1);
if (!request_mem_region(rivafb_fix.mmio_start,
rivafb_fix.mmio_len, "rivafb")) {
printk(KERN_ERR PFX "cannot reserve MMIO region\n");
goto err_out_kfree;
}
default_par->ctrl_base = ioremap(rivafb_fix.mmio_start,
rivafb_fix.mmio_len);
if (!default_par->ctrl_base) {
printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
goto err_out_free_base0;
}
info->screen_base = ioremap(rivafb_fix.smem_start,
rivafb_fix.smem_len);
if (!info->screen_base) {
printk(KERN_ERR PFX "cannot ioremap FB base\n");
goto err_out_iounmap_ctrl;
}
default_par->riva.EnableIRQ = 0;
default_par->riva.PRAMDAC = (unsigned *)(default_par->ctrl_base +
0x00680000);
default_par->riva.PFB = (unsigned *)(default_par->ctrl_base +
0x00100000);
default_par->riva.PFIFO = (unsigned *)(default_par->ctrl_base +
0x00002000);
default_par->riva.PGRAPH = (unsigned *)(default_par->ctrl_base +
0x00400000);
default_par->riva.PEXTDEV = (unsigned *)(default_par->ctrl_base +
0x00101000);
default_par->riva.PTIMER = (unsigned *)(default_par->ctrl_base +
0x00009000);
default_par->riva.PMC = (unsigned *)(default_par->ctrl_base +
0x00000000);
default_par->riva.FIFO = (unsigned *)(default_par->ctrl_base +
0x00800000);
default_par->riva.PCIO = (U008 *)(default_par->ctrl_base + 0x00601000);
default_par->riva.PDIO = (U008 *)(default_par->ctrl_base + 0x00681000);
default_par->riva.PVIO = (U008 *)(default_par->ctrl_base + 0x000C0000);
default_par->riva.IO = (MISCin(default_par) & 0x01) ? 0x3D0 : 0x3B0;
switch (default_par->riva.Architecture) {
case NV_ARCH_03:
default_par->riva.PRAMIN = (unsigned *)(info->screen_base +
0x00C00000);
default_par->dclk_max = 256000000;
rivafb_fix.accel = FB_ACCEL_NV3;
break;
case NV_ARCH_04:
case NV_ARCH_10:
case NV_ARCH_20:
default_par->riva.PCRTC = (unsigned *)(default_par->ctrl_base
+ 0x00600000);
default_par->riva.PRAMIN = (unsigned *)(default_par->ctrl_base
+ 0x00710000);
default_par->dclk_max = 250000000;
rivafb_fix.accel = FB_ACCEL_NV4;
break;
}
rivafb_get_mem_len(default_par, &rivafb_fix);
info->par = default_par;
if (!request_mem_region(rivafb_fix.smem_start,
rivafb_fix.smem_len, "rivafb")) {
printk(KERN_ERR PFX "cannot reserve FB region\n");
goto err_out_free_base0;
}
info->screen_base = ioremap(rivafb_fix.smem_start,
rivafb_fix.smem_len);
if (!info->screen_base) {
printk(KERN_ERR PFX "cannot ioremap FB base\n");
goto err_out_iounmap_ctrl;
}
#ifdef CONFIG_MTRR
if (!nomtrr) {
default_par->mtrr.vram = mtrr_add(rivafb_fix.smem_start,
rivafb_fix.smem_len,
MTRR_TYPE_WRCOMB, 1);
if (default_par->mtrr.vram < 0) {
printk(KERN_ERR PFX "unable to setup MTRR\n");
} else {
default_par->mtrr.vram_valid = 1;
/* let there be speed */
printk(KERN_INFO PFX "RIVA MTRR set to ON\n");
}
}
#endif /* CONFIG_MTRR */
if (riva_set_fbinfo(info) < 0) {
printk(KERN_ERR PFX "error setting initial video mode\n");
goto err_out_cursor;
}
if (register_framebuffer(info) < 0) {
printk(KERN_ERR PFX
"error registering riva framebuffer\n");
goto err_out_load_state;
}
pci_set_drvdata(pd,info);
printk(KERN_INFO PFX
"PCI nVidia NV%x framebuffer ver %s (%s, %dMB @ 0x%lX)\n",
default_par->riva.Architecture,
RIVAFB_VERSION,
info->fix.id,
info->fix.smem_len / (1024 * 1024),
info->fix.smem_start);
return 0;
err_out_load_state:
err_out_cursor:
/* err_out_iounmap_fb: */
iounmap(info->screen_base);
err_out_iounmap_ctrl:
iounmap(default_par->ctrl_base);
err_out_free_base1:
release_mem_region(info->fix.smem_start, info->fix.smem_len);
err_out_free_base0:
release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
err_out_kfree:
kfree(info);
err_out:
return -ENODEV;
}
static void __devexit rivafb_remove_one(struct pci_dev *pd)
{
struct fb_info *info = pci_get_drvdata(pd);
struct riva_par *par = (struct riva_par *) info->par;
if (!info)
return;
unregister_framebuffer(info);
#ifdef CONFIG_MTRR
if (par->mtrr.vram_valid)
mtrr_del(par->mtrr.vram, info->fix.smem_start,
info->fix.smem_len);
#endif /* CONFIG_MTRR */
iounmap(par->ctrl_base);
iounmap(info->screen_base);
release_mem_region(info->fix.mmio_start,
info->fix.mmio_len);
release_mem_region(info->fix.smem_start,
info->fix.smem_len);
kfree(info);
pci_set_drvdata(pd, NULL);
}
/* ------------------------------------------------------------------------- *
*
* initialization
*
* ------------------------------------------------------------------------- */
#ifndef MODULE
int __init rivafb_setup(char *options)
{
char *this_opt;
if (!options || !*options)
return 0;
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
#ifdef CONFIG_MTRR
if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = 1;
} else
#endif
mode_option = this_opt;
}
return 0;
}
#endif /* !MODULE */
static struct pci_driver rivafb_driver = {
name: "rivafb",
id_table: rivafb_pci_tbl,
probe: rivafb_init_one,
remove: __devexit_p(rivafb_remove_one),
};
/* ------------------------------------------------------------------------- *
*
* modularization
*
* ------------------------------------------------------------------------- */
int __init rivafb_init(void)
{
pci_register_driver(&rivafb_driver);
return 0;
}
#ifdef MODULE
static void __exit rivafb_exit(void)
{
pci_unregister_driver(&rivafb_driver);
}
module_init(rivafb_init);
module_exit(rivafb_exit);
MODULE_PARM(flatpanel, "i");
MODULE_PARM_DESC(flatpanel, "Enables experimental flat panel support for some chipsets. (0 or 1=enabled) (default=0)");
MODULE_PARM(forceCRTC, "i");
MODULE_PARM_DESC(forceCRTC, "Forces usage of a particular CRTC in case autodetection fails. (0 or 1) (default=autodetect)");
MODULE_PARM(noaccel, "i");
MODULE_PARM_DESC(noaccel, "Disables hardware acceleration (0 or 1=disabled) (default=0)");
#ifdef CONFIG_MTRR
MODULE_PARM(nomtrr, "i");
MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) (default=0)");
#endif
#endif /* MODULE */
MODULE_AUTHOR("Ani Joshi, maintainer");
MODULE_DESCRIPTION("Framebuffer driver for nVidia Riva 128, TNT, TNT2, GeForce Series");
MODULE_LICENSE("GPL");
......@@ -97,6 +97,7 @@
#define FB_ACCEL_SIS_GLAMOUR 36 /* SiS 300/630/540 */
#define FB_ACCEL_3DLABS_PERMEDIA3 37 /* 3Dlabs Permedia 3 */
#define FB_ACCEL_ATI_RADEON 38 /* ATI Radeon family */
#define FB_ACCEL_I810 39 /* Intel 810/815 */
#define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */
#define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */
......@@ -432,9 +433,11 @@ struct fb_info {
#define fb_readb(addr) (*(volatile u8 *) (addr))
#define fb_readw(addr) (*(volatile u16 *) (addr))
#define fb_readl(addr) (*(volatile u32 *) (addr))
#define fb_readq(addr) (*(volatile u64 *) (addr))
#define fb_writeb(b,addr) (*(volatile u8 *) (addr) = (b))
#define fb_writew(b,addr) (*(volatile u16 *) (addr) = (b))
#define fb_writel(b,addr) (*(volatile u32 *) (addr) = (b))
#define fb_writeq(b,addr) (*(volatile u64 *) (addr) = (b))
#define fb_memset memset
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment