blob: 7c13170051098ec55e6ad4397d4cbe846b9b0be7 [file] [log] [blame]
/*
* linux/drivers/video/fbcon-sti.c -- Low level frame buffer
* operations for generic HP video boards using STI (standard
* text interface) firmware
*
* Based on linux/drivers/video/fbcon-artist.c
* Created 5 Apr 1997 by Geert Uytterhoeven
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
*
* 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/tty.h>
#include <linux/console.h>
#include <linux/string.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <asm/gsc.h> /* for gsc_read/write */
#include <asm/types.h>
#include <video/fbcon.h>
#include <video/fbcon-mfb.h>
#include "sti/sticore.h"
/* Translate an address as it would be found in a 2048x2048x1 bit frame
* buffer into a logical address Artist actually expects. Addresses fed
* into Artist look like this:
* fixed Y X
* FFFF FFFF LLLL LLLL LLLC CCCC CCCC CC00
*
* our "RAM" addresses look like this:
*
* FFFF FFFF 0000 0LLL LLLL LLLL CCCC CCCC [CCC]
*
* */
static inline u32
ram2log(void * addr)
{
#if 1
return (unsigned long) addr;
#else
u32 a = (unsigned long) addr;
u32 r;
#if 1
r = a & 0xff000000; /* fixed part */
r += ((a & 0x000000ff) << 5);
r += ((a & 0x00ffff00) << 3);
#else
r = a & 0xff000000; /* fixed part */
r += ((a & 0x000000ff) << 5);
r += ((a & 0x0007ff00) << 5);
#endif
return r;
#endif
}
/* All those functions need better names. */
static void
memcpy_fromhp_tohp(void *dest, void *src, int count)
{
unsigned long d = ram2log(dest);
unsigned long s = ram2log(src);
count += 3;
count &= ~3; /* XXX */
while(count) {
count --;
gsc_writel(~gsc_readl(s), d);
d += 32*4;
s += 32*4;
}
}
static void
memset_tohp(void *dest, u32 word, int count)
{
unsigned long d = ram2log(dest);
count += 3;
count &= ~3;
while(count) {
count--;
gsc_writel(word, d);
d += 32;
}
}
static u8
readb_hp(void *src)
{
unsigned long s = ram2log(src);
return ~gsc_readb(s);
}
static void
writeb_hp(u8 b, void *dst)
{
unsigned long d = ram2log(dst);
if((d&0xf0000000) != 0xf0000000) {
printk("writeb_hp %02x %p (%08lx) (%p)\n",
b, dst, d, __builtin_return_address(0));
return;
}
gsc_writeb(b, d);
}
static void
fbcon_sti_setup(struct display *p)
{
if (p->line_length)
p->next_line = p->line_length;
else
p->next_line = p->var.xres_virtual>>3;
p->next_plane = 0;
}
static void
fbcon_sti_bmove(struct display *p, int sy, int sx,
int dy, int dx,
int height, int width)
{
#if 0 /* Unfortunately, still broken */
sti_bmove(default_sti /* FIXME */, sy, sx, dy, dx, height, width);
#else
u8 *src, *dest;
u_int rows;
if (sx == 0 && dx == 0 && width == p->next_line) {
src = p->screen_base+sy*fontheight(p)*width;
dest = p->screen_base+dy*fontheight(p)*width;
memcpy_fromhp_tohp(dest, src, height*fontheight(p)*width);
} else if (dy <= sy) {
src = p->screen_base+sy*fontheight(p)*p->next_line+sx;
dest = p->screen_base+dy*fontheight(p)*p->next_line+dx;
for (rows = height*fontheight(p); rows--;) {
memcpy_fromhp_tohp(dest, src, width);
src += p->next_line;
dest += p->next_line;
}
} else {
src = p->screen_base+((sy+height)*fontheight(p)-1)*p->next_line+sx;
dest = p->screen_base+((dy+height)*fontheight(p)-1)*p->next_line+dx;
for (rows = height*fontheight(p); rows--;) {
memcpy_fromhp_tohp(dest, src, width);
src -= p->next_line;
dest -= p->next_line;
}
}
#endif
}
static void
fbcon_sti_clear(struct vc_data *conp,
struct display *p, int sy, int sx,
int height, int width)
{
u8 *dest;
u_int rows;
int inverse = conp ? attr_reverse(p,conp->vc_video_erase_char) : 0;
dest = p->screen_base+sy*fontheight(p)*p->next_line+sx;
if (sx == 0 && width == p->next_line) {
if (inverse)
memset_tohp(dest, ~0, height*fontheight(p)*width);
else
memset_tohp(dest, 0, height*fontheight(p)*width);
} else
for (rows = height*fontheight(p); rows--; dest += p->next_line)
if (inverse)
memset_tohp(dest, 0xffffffff, width);
else
memset_tohp(dest, 0x00000000, width);
}
static void fbcon_sti_putc(struct vc_data *conp,
struct display *p, int c,
int yy, int xx)
{
u8 *dest, *cdat;
u_int rows, bold, revs, underl;
u8 d;
dest = p->screen_base+yy*fontheight(p)*p->next_line+xx;
cdat = p->fontdata+(c&p->charmask)*fontheight(p);
bold = attr_bold(p,c);
revs = attr_reverse(p,c);
underl = attr_underline(p,c);
for (rows = fontheight(p); rows--; dest += p->next_line) {
d = *cdat++;
if (underl && !rows)
d = 0xff;
else if (bold)
d |= d>>1;
if (revs)
d = ~d;
writeb_hp (d, dest);
}
}
static void fbcon_sti_putcs(struct vc_data *conp,
struct display *p,
const unsigned short *s,
int count, int yy, int xx)
{
u8 *dest, *dest0, *cdat;
u_int rows, bold, revs, underl;
u8 d;
u16 c;
if(((unsigned)xx > 200) || ((unsigned) yy > 200)) {
printk("refusing to putcs %p %p %p %d %d %d (%p)\n",
conp, p, s, count, yy, xx, __builtin_return_address(0));
return;
}
dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
if(((u32)dest0&0xf0000000)!=0xf0000000) {
printk("refusing to putcs %p %p %p %d %d %d (%p) %p = %p + %d * %d * %ld + %d\n",
conp, p, s, count, yy, xx, __builtin_return_address(0),
dest0, p->screen_base, yy, fontheight(p), p->next_line,
xx);
return;
}
c = scr_readw(s);
bold = attr_bold(p, c);
revs = attr_reverse(p, c);
underl = attr_underline(p, c);
while (count--) {
c = scr_readw(s++) & p->charmask;
dest = dest0++;
cdat = p->fontdata+c*fontheight(p);
for (rows = fontheight(p); rows--; dest += p->next_line) {
d = *cdat++;
if (0 && underl && !rows)
d = 0xff;
else if (0 && bold)
d |= d>>1;
if (revs)
d = ~d;
writeb_hp (d, dest);
}
}
}
static void fbcon_sti_revc(struct display *p,
int xx, int yy)
{
u8 *dest, d;
u_int rows;
dest = p->screen_base+yy*fontheight(p)*p->next_line+xx;
for (rows = fontheight(p); rows--; dest += p->next_line) {
d = readb_hp(dest);
writeb_hp (~d, dest);
}
}
static void
fbcon_sti_clear_margins(struct vc_data *conp,
struct display *p,
int bottom_only)
{
u8 *dest;
int height, bottom;
int inverse = conp ? attr_reverse(p,conp->vc_video_erase_char) : 0;
/* XXX Need to handle right margin? */
height = p->var.yres - conp->vc_rows * fontheight(p);
if (!height)
return;
bottom = conp->vc_rows + p->yscroll;
if (bottom >= p->vrows)
bottom -= p->vrows;
dest = p->screen_base + bottom * fontheight(p) * p->next_line;
if (inverse)
memset_tohp(dest, 0xffffffff, height * p->next_line);
else
memset_tohp(dest, 0x00000000, height * p->next_line);
}
/*
* `switch' for the low level operations
*/
struct display_switch fbcon_sti = {
setup: fbcon_sti_setup,
bmove: fbcon_sti_bmove,
clear: fbcon_sti_clear,
putc: fbcon_sti_putc,
putcs: fbcon_sti_putcs,
revc: fbcon_sti_revc,
clear_margins: fbcon_sti_clear_margins,
fontwidthmask: FONTWIDTH(8)
};
MODULE_LICENSE("GPL");