/*
* linux/drivers/video/fbcon-mac.c -- Low level frame buffer operations for
* x bpp packed pixels, font width != 8
*
* Created 26 Dec 1997 by Michael Schmitz
* Based on the old macfb.c 6x11 code by Randy Thelen
*
* This driver is significantly slower than the 8bit font drivers
* and would probably benefit from splitting into drivers for each depth.
*
* 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
*/
#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 <video/fbcon.h>
#include <video/fbcon-mac.h>
/*
* variable bpp packed pixels
*/
static void plot_pixel_mac(struct display *p, int bw, int pixel_x,
int pixel_y);
static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y);
void fbcon_mac_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;
}
/*
* Macintosh
*/
#define PIXEL_BLACK_MAC 0
#define PIXEL_WHITE_MAC 1
#define PIXEL_INVERT_MAC 2
void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx,
int height, int width)
{
int i, j;
u8 *dest, *src;
int l,r,t,b,w,lo,s;
int dl,dr,dt,db,dw,dlo;
int move_up;
src = (u8 *) (p->screen_base + sy * fontheight(p) * p->next_line);
dest = (u8 *) (p->screen_base + dy * fontheight(p) * p->next_line);
if( sx == 0 && width == p->conp->vc_cols) {
s = height * fontheight(p) * p->next_line;
fb_memmove(dest, src, s);
return;
}
l = sx * fontwidth(p);
r = l + width * fontwidth(p);
t = sy * fontheight(p);
b = t + height * fontheight(p);
dl = dx * fontwidth(p);
dr = dl + width * fontwidth(p);
dt = dy * fontheight(p);
db = dt + height * fontheight(p);
/* w is the # pixels between two long-aligned points, left and right */
w = (r&~31) - ((l+31)&~31);
dw = (dr&~31) - ((dl+31)&~31);
/* lo is the # pixels between the left edge and a long-aligned left pixel */
lo = ((l+31)&~31) - l;
dlo = ((dl+31)&~31) - dl;
/* if dx != sx then, logic has to align the left and right edges for fast moves */
if (lo != dlo) {
lo = ((l+7)&~7) - l;
dlo = ((dl+7)&~7) - dl;
w = (r&~7) - ((l+7)&~7);
dw = (dr&~7) - ((dl+7)&~7);
if (lo != dlo) {
unsigned char err_str[128];
unsigned short err_buf[256];
unsigned long cnt, len;
sprintf( err_str, "ERROR: Shift algorithm: sx=%d,sy=%d,dx=%d,dy=%d,w=%d,h=%d,bpp=%d",
sx,sy,dx,dy,width,height,p->var.bits_per_pixel);
len = strlen(err_str);
for (cnt = 0; cnt < len; cnt++)
err_buf[cnt] = 0x700 | err_str[cnt];
fbcon_mac_putcs(p->conp, p, err_buf, len, 0, 0);
/* pause for the user */
printk( "ERROR: shift algorithm...\n" );
mdelay(5000);
return;
}
}
s = 0;
switch (p->var.bits_per_pixel) {
case 1:
s = w >> 3;
src += lo >> 3;
dest += lo >> 3;
break;
case 2:
s = w >> 2;
src += lo >> 2;
dest += lo >> 2;
break;
case 4:
s = w >> 1;
src += lo >> 1;
dest += lo >> 1;
break;
case 8:
s = w;
src += lo;
dest += lo;
break;
case 16:
s = w << 1;
src += lo << 1;
dest += lo << 1;
break;
case 32:
s = w << 2;
src += lo << 2;
dest += lo << 2;
break;
}
if (sy <= sx) {
i = b;
move_up = 0;
src += height * fontheight(p);
dest += height * fontheight(p);
} else {
i = t;
move_up = 1;
}
while (1) {
for (i = t; i < b; i++) {
j = l;
for (; j & 31 && j < r; j++)
plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i);
if (j < r) {
fb_memmove(dest, src, s);
if (move_up) {
dest += p->next_line;
src += p->next_line;
} else {
dest -= p->next_line;
src -= p->next_line;
}
j += w;
}
for (; j < r; j++)
plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i);
}
if (move_up) {
i++;
if (i >= b)
break;
} else {
i--;
if (i < t)
break;
}
}
}
void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, int sx,
int height, int width)
{
int pixel;
int i, j;
int inverse;
u8 *dest;
int l,r,t,b,w,lo,s;
inverse = conp ? attr_reverse(p,conp->vc_attr) : 0;
pixel = inverse ? PIXEL_WHITE_MAC : PIXEL_BLACK_MAC;
dest = (u8 *) (p->screen_base + sy * fontheight(p) * p->next_line);
if( sx == 0 && width == p->conp->vc_cols) {
s = height * fontheight(p) * p->next_line;
if (inverse)
fb_memclear(dest, s);
else
fb_memset255(dest, s);
}
l = sx * fontwidth(p);
r = l + width * fontwidth(p);
t = sy * fontheight(p);
b = t + height * fontheight(p);
/* w is the # pixels between two long-aligned points, left and right */
w = (r&~31) - ((l+31)&~31);
/* lo is the # pixels between the left edge and a long-aligned left pixel */
lo = ((l+31)&~31) - l;
s = 0;
switch (p->var.bits_per_pixel) {
case 1:
s = w >> 3;
dest += lo >> 3;
break;
case 2:
s = w >> 2;
dest += lo >> 2;
break;
case 4:
s = w >> 1;
dest += lo >> 1;
break;
case 8:
s = w;
dest += lo;
break;
case 16:
s = w << 1;
dest += lo << 1;
break;
case 32:
s = w << 2;
dest += lo << 2;
break;
}
for (i = t; i < b; i++) {
j = l;
for (; j & 31 && j < r; j++)
plot_pixel_mac(p, pixel, j, i);
if (j < r) {
if (PIXEL_WHITE_MAC == pixel)
fb_memclear(dest, s);
else
fb_memset255(dest, s);
dest += p->next_line;
j += w;
}
for (; j < r; j++)
plot_pixel_mac(p, pixel, j, i);
}
}
void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c, int yy,
int xx)
{
u8 *cdat;
u_int rows, bold, ch_reverse, ch_underline;
u8 d;
int j;
cdat = p->fontdata+(c&p->charmask)*fontheight(p);
bold = attr_bold(p,c);
ch_reverse = attr_reverse(p,c);
ch_underline = attr_underline(p,c);
for (rows = 0; rows < fontheight(p); rows++) {
d = *cdat++;
if (!conp->vc_can_do_color) {
if (ch_underline && rows == (fontheight(p)-2))
d = 0xff;
else if (bold)
d |= d>>1;
if (ch_reverse)
d = ~d;
}
for (j = 0; j < fontwidth(p); j++) {
plot_pixel_mac(p, (d & 0x80) >> 7, (xx*fontwidth(p)) + j, (yy*fontheight(p)) + rows);
d <<= 1;
}
}
}
void fbcon_mac_putcs(struct vc_data *conp, struct display *p,
const unsigned short *s, int count, int yy, int xx)
{
u16 c;
while (count--) {
c = scr_readw(s++);
fbcon_mac_putc(conp, p, c, yy, xx++);
}
}
void fbcon_mac_revc(struct display *p, int xx, int yy)
{
u_int rows, j;
for (rows = 0; rows < fontheight(p); rows++) {
for (j = 0; j < fontwidth(p); j++) {
plot_pixel_mac (p, PIXEL_INVERT_MAC, (xx*fontwidth(p))+j, (yy*fontheight(p))+rows);
}
}
}
static inline void plot_helper(u8 *dest, u8 bit, int bw)
{
switch (bw) {
case PIXEL_BLACK_MAC:
fb_writeb( fb_readb(dest) | bit, dest );
break;
case PIXEL_WHITE_MAC:
fb_writeb( fb_readb(dest) & (~bit), dest );
break;
case PIXEL_INVERT_MAC:
fb_writeb( fb_readb(dest) ^ bit, dest );
break;
default:
printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
}
}
/*
* plot_pixel_mac
*/
static void plot_pixel_mac(struct display *p, int bw, int pixel_x, int pixel_y)
{
u8 *dest, bit;
u16 *dest16, pix16;
u32 *dest32, pix32;
/* There *are* 68k Macs that support more than 832x624, you know :-) */
if (pixel_x < 0 || pixel_y < 0 || pixel_x >= p->var.xres || pixel_y >= p->var.yres) {
printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y);
mdelay(1000);
return;
}
switch (p->var.bits_per_pixel) {
case 1:
dest = (u8 *) ((pixel_x >> 3) + p->screen_base + pixel_y * p->next_line);
bit = 0x80 >> (pixel_x & 7);
plot_helper(dest, bit, bw);
break;
case 2:
dest = (u8 *) ((pixel_x >> 2) + p->screen_base + pixel_y * p->next_line);
bit = 0xC0 >> ((pixel_x & 3) << 1);
plot_helper(dest, bit, bw);
break;
case 4:
dest = (u8 *) ((pixel_x >> 1) + p->screen_base + pixel_y * p->next_line);
bit = 0xF0 >> ((pixel_x & 1) << 2);
plot_helper(dest, bit, bw);
break;
case 8:
dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line);
bit = 0xFF;
plot_helper(dest, bit, bw);
break;
/* FIXME: You can't access framebuffer directly like this! */
case 16:
dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line);
pix16 = 0xFFFF;
switch (bw) {
case PIXEL_BLACK_MAC: *dest16 = ~pix16; break;
case PIXEL_WHITE_MAC: *dest16 = pix16; break;
case PIXEL_INVERT_MAC: *dest16 ^= pix16; break;
default: printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
}
break;
case 32:
dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line);
pix32 = 0xFFFFFFFF;
switch (bw) {
case PIXEL_BLACK_MAC: *dest32 = ~pix32; break;
case PIXEL_WHITE_MAC: *dest32 = pix32; break;
case PIXEL_INVERT_MAC: *dest32 ^= pix32; break;
default: printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
}
break;
}
}
static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y)
{
u8 *dest, bit;
u16 *dest16;
u32 *dest32;
u8 pixel=0;
switch (p->var.bits_per_pixel) {
case 1:
dest = (u8 *) ((pixel_x / 8) + p->screen_base + pixel_y * p->next_line);
bit = 0x80 >> (pixel_x & 7);
pixel = *dest & bit;
break;
case 2:
dest = (u8 *) ((pixel_x / 4) + p->screen_base + pixel_y * p->next_line);
bit = 0xC0 >> (pixel_x & 3);
pixel = *dest & bit;
break;
case 4:
dest = (u8 *) ((pixel_x / 2) + p->screen_base + pixel_y * p->next_line);
bit = 0xF0 >> (pixel_x & 1);
pixel = *dest & bit;
break;
case 8:
dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line);
pixel = *dest;
break;
case 16:
dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line);
pixel = *dest16 ? 1 : 0;
break;
case 32:
dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line);
pixel = *dest32 ? 1 : 0;
break;
}
return pixel ? PIXEL_BLACK_MAC : PIXEL_WHITE_MAC;
}
/*
* `switch' for the low level operations
*/
struct display_switch fbcon_mac = {
setup: fbcon_mac_setup,
bmove: fbcon_redraw_bmove,
clear: fbcon_redraw_clear,
putc: fbcon_mac_putc,
putcs: fbcon_mac_putcs,
revc: fbcon_mac_revc,
fontwidthmask: FONTWIDTHRANGE(1,8)
};
#ifdef MODULE
MODULE_LICENSE("GPL");
int init_module(void)
{
return 0;
}
void cleanup_module(void)
{}
#endif /* MODULE */
/*
* Visible symbols for modules
*/
EXPORT_SYMBOL(fbcon_mac);
EXPORT_SYMBOL(fbcon_mac_setup);
EXPORT_SYMBOL(fbcon_mac_bmove);
EXPORT_SYMBOL(fbcon_mac_clear);
EXPORT_SYMBOL(fbcon_mac_putc);
EXPORT_SYMBOL(fbcon_mac_putcs);
EXPORT_SYMBOL(fbcon_mac_revc);